Moved UI into its own Django app.

This commit is contained in:
Chris Church
2013-05-08 11:41:00 -04:00
parent 839d681b70
commit eff2e65c9a
170 changed files with 88490 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* APIDefaults
*
*
*/
angular.module('APIDefaults', ['RestServices', 'Utilities'])
.factory('GetAPIDefaults', ['Alert', 'Rest', '$rootScope', function(Alert, Rest, $rootScope) {
return function(key) {
//Reload a related collection on pagination or search change
var answer;
var result = {};
var cnt=0;
function lookup(key) {
var result = {};
for (id in $rootScope.apiDefaults) {
if (id == key || id.iterator == key) {
result[id] = defaults[id];
break;
}
}
return result;
}
function wait() {
var answer;
if ( result == {} && cnt < 5) {
cnt++;
setTimeout(1000, wait());
}
else {
if (result.status == 'success') {
return lookup(key);
}
}
}
if ($rootScope.apiDefaults == null || $rootScope.apiDefaults == undefined) {
var result = {};
var url = '/api/v1';
Rest.setUrl(url);
Rest.get()
.success( function(data, status, headers, config) {
defaults = data;
for (var id in defaults) {
switch (id) {
case 'organizations':
dafaults[id].iterator = 'organization';
break;
case 'jobs':
defaults[id].iterator = 'job';
break;
case 'users':
defaults[id].iterator = 'user';
break;
case 'teams':
defaults[id].iterator = 'team';
break;
case 'hosts':
defaults[id].iterator = 'host';
break;
case 'groups':
defaults[id].iterator = 'group';
break;
case 'projects':
defaults[id].iterator = 'project';
break;
}
}
$rootScope.apiDefaults = defaults;
result = {status: 'success'};
})
.error( function(data, status, headers, config) {
result = {status: 'error', msg: 'Call to ' + url + ' failed. GET returned status: ' + status};
});
return wait();
}
else {
return lookup(key);
}
}
}]);

View File

@@ -0,0 +1,58 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* Helper
* Routines shared amongst the hosts controllers
*/
angular.module('HostHelper', [ 'RestServices', 'Utilities', 'OrganizationListDefinition',
'SearchHelper', 'PaginateHelper', 'ListGenerator' ])
.factory('LookUpInventoryInit', ['Alert', 'Rest', 'InventoryList', 'GenerateList', 'SearchInit', 'PaginateInit',
function(Alert, Rest, InventoryList, GenerateList, SearchInit, PaginateInit) {
return function(params) {
var scope = params.scope;
// Show pop-up to select organization
scope.lookUpInventory = function() {
var list = InventoryList;
var listGenerator = GenerateList;
var listScope = listGenerator.inject(list, { mode: 'lookup', hdr: 'Select Inventory' });
var defaultUrl = '/api/v1/inventories';
listScope.selectAction = function() {
var found = false;
for (var i=0; i < listScope[list.name].length; i++) {
if (listScope[list.iterator + "_" + listScope[list.name][i].id + "_class"] == "success") {
found = true;
scope['inventory'] = listScope[list.name][i].id;
scope['inventory_name'] = listScope[list.name][i].name;
scope['host_form'].$setDirty();
listGenerator.hide();
}
}
if (found == false) {
Alert('No Selection', 'Click on a row to select an Inventory before clicking the Select button.');
}
}
listScope.toggle_inventory = function(id) {
// when user clicks a row, remove 'success' class from all rows except clicked-on row
if (listScope[list.name]) {
for (var i=0; i < listScope[list.name].length; i++) {
listScope[list.iterator + "_" + listScope[list.name][i].id + "_class"] = "";
}
}
if (id != null && id != undefined) {
listScope[list.iterator + "_" + id + "_class"] = "success";
}
}
SearchInit({ scope: listScope, set: list.name, list: list, url: defaultUrl });
PaginateInit({ scope: listScope, list: list, url: defaultUrl, mode: 'lookup' });
scope.search(list.iterator);
listScope.toggle_inventory(scope.inventory);
}
}
}]);

View File

@@ -0,0 +1,129 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* InventoryHelper
* Routines shared amongst the inventory controllers
*/
angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationListDefinition',
'SearchHelper', 'PaginateHelper', 'ListGenerator' ])
.factory('SetListeners', ['Alert', 'Rest', function(Alert, Rest) {
return function(params) {
var scope = params.scope;
var set = params.set;
var iterator = params.iterator;
// Listeners to perform lookups after main inventory list loads
scope.$on('resultFound', function(e, results, lookup_results) {
if ( lookup_results.length == results.length ) {
key = 'organization';
property = 'organization_name';
for (var i=0; i < results.length; i++) {
for (var j=0; j < lookup_results.length; j++) {
if (results[i][key] == lookup_results[j].id) {
results[i][property] = lookup_results[j].value;
}
}
}
scope[iterator + 'SearchSpin'] = false;
scope[set] = results;
}
});
scope.$on('refreshFinished', function(e, results) {
// Loop through the result set (sent to us by the search helper) and
// lookup the id and name of each organization. After each lookup
// completes, call resultFound.
var lookup_results = [];
for (var i = 0; i < results.length; i++) {
Rest.setUrl('/api/v1/organizations/' + results[i].organization + '/');
Rest.get()
.success( function( data, status, headers, config) {
lookup_results.push({ id: data.id, value: data.name });
scope.$emit('resultFound', results, lookup_results);
})
.error( function( data, status, headers, config) {
lookup_results.push({ id: 'error' });
scope.$emit('resultFound', results, lookup_results);
});
}
});
}
}])
.factory('LookUpOrganizationInit', ['Alert', 'Rest', 'OrganizationList', 'GenerateList', 'SearchInit', 'PaginateInit',
function(Alert, Rest, OrganizationList, GenerateList, SearchInit, PaginateInit) {
return function(params) {
var scope = params.scope;
// Show pop-up to select organization
scope.lookUpOrganization = function() {
var list = OrganizationList;
var listGenerator = GenerateList;
var listScope = listGenerator.inject(list, { mode: 'lookup', hdr: 'Select Organization' });
var defaultUrl = '/api/v1/organizations';
listScope.selectAction = function() {
var found = false;
for (var i=0; i < listScope[list.name].length; i++) {
if (listScope[list.iterator + "_" + listScope[list.name][i].id + "_class"] == "success") {
found = true;
scope['organization'] = listScope[list.name][i].id;
scope['organization_name'] = listScope[list.name][i].name;
scope['inventory_form'].$setDirty();
listGenerator.hide();
}
}
if (found == false) {
Alert('No Selection', 'Click on a row to select an Organization before clicking the Select button.');
}
}
listScope.toggle_organization = function(id) {
// when user clicks a row, remove 'success' class from all rows except clicked-on row
if (listScope[list.name]) {
for (var i=0; i < listScope[list.name].length; i++) {
listScope[list.iterator + "_" + listScope[list.name][i].id + "_class"] = "";
}
}
if (id != null && id != undefined) {
listScope[list.iterator + "_" + id + "_class"] = "success";
}
}
SearchInit({ scope: listScope, set: list.name, list: list, url: defaultUrl });
PaginateInit({ scope: listScope, list: list, url: defaultUrl, mode: 'lookup' });
scope.search(list.iterator);
listScope.toggle_organization(scope.organization);
}
}
}])
.factory('TreeInit', ['Alert', 'Rest', function(Alert, Rest) {
return function(groups) {
Rest.setUrl(groups);
Rest.get()
.success( function(data, status, headers, config) {
var treeData = [];
for (var i=0; i < data.results.length; i++) {
treeData.push({
data: data.results[i].name,
state: 'closed',
attr: { id: data.results[i] }
});
}
$('#tree-view').jstree({ "json_data": { data: treeData },
plugins: ['themes', 'json_data', 'ui'] });
})
.error( function(data, status, headers, config) {
Alert('Error', 'Failed to laod tree data. Url: ' + groups + ' GET status: ' + status);
});
}
}]);

View File

@@ -0,0 +1,51 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* PaginateHelper
*
* All the parts for controlling the search widget on
* related collections.
*
* PaginateInit({
* scope: <scope>,
* list: <form object used by FormGenerator>
* url: <
* });
*
*/
angular.module('PaginateHelper', ['RefreshHelper'])
.factory('PaginateInit', [ 'Refresh', function(Refresh) {
return function(params) {
var scope = params.scope;
var list = params.list;
var defaultUrl = params.url;
var mode = (params.mode) ? params.mode : null;
scope[list.iterator + 'Page'] = 0;
if (mode == 'lookup') {
scope[list.iterator + 'PageSize'] = 5;
}
else {
scope[list.iterator + 'PageSize'] = 20;
}
scope.nextSet = function(set, iterator) {
scope[iterator + 'Page']++;
Refresh({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'NextUrl'] });
};
scope.prevSet = function(set, iterator) {
scope[iterator + 'Page']--;
Refresh({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'PrevUrl'] });
};
scope.changePageSize = function(set, iterator) {
// Called when a new page size is selected
scope[iterator + 'Page'] = 0;
Refresh({ scope: scope, set: set, iterator: iterator, url: defaultUrl });
}
}
}]);

View File

@@ -0,0 +1,43 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* RefreshRelatedHelper
*
* Used to refresh a related set whenever pagination or filter options change.
*
* RefreshRelated({
* scope: <current scope>,
* set: <model>,
* iterator: <model name in singular form (i.e. organization),
* url: <the api url to call>
* });
*
*/
angular.module('RefreshRelatedHelper', ['RestServices', 'Utilities'])
.factory('RefreshRelated', ['Alert', 'Rest', function(Alert, Rest) {
return function(params) {
var scope = params.scope;
var set = params.set;
var iterator = params.iterator;
var url = params.url;
url.replace(/page_size\=\d+/,''); //stop repeatedly appending page_size
Rest.setUrl(url);
Rest.get({ params: { page_size: scope[iterator + 'PageSize'] }})
.success( function(data, status, headers, config) {
scope[set] = data['results'];
scope[iterator + 'NextUrl'] = data.next;
scope[iterator + 'PrevUrl'] = data.previous;
scope[iterator + 'Count'] = data.count;
scope[iterator + 'PageCount'] = Math.ceil((data.count / scope[iterator + 'PageSize']));
scope[iterator + 'SearchSpin'] = false;
})
.error ( function(data, status, headers, config) {
scope[iterator + 'SearchSpin'] = true;
Alert('Error!', 'Failed to retrieve related set: ' + set + '. GET returned status: ' + status);
});
}
}]);

View File

@@ -0,0 +1,53 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* RefreshHelper
*
* Used to refresh a related set whenever pagination or filter options change.
*
* RefreshRelated({
* scope: <current scope>,
* set: <model>,
* iterator: <model name in singular form (i.e. organization),
* url: <the api url to call>
* });
*
*/
angular.module('RefreshHelper', ['RestServices', 'Utilities'])
.factory('Refresh', ['Alert', 'Rest', function(Alert, Rest) {
return function(params) {
var scope = params.scope;
var set = params.set;
var iterator = params.iterator;
var url = params.url;
url.replace(/page_size\=\d+/,''); //stop repeatedly appending page_size
url += scope[iterator + 'SearchParams'];
Rest.setUrl(url);
Rest.get({ params: { page_size: scope[iterator + 'PageSize'] }})
.success( function(data, status, headers, config) {
scope[iterator + 'NextUrl'] = data.next;
scope[iterator + 'PrevUrl'] = data.previous;
scope[iterator + 'Count'] = data.count;
scope[iterator + 'PageCount'] = Math.ceil((data.count / scope[iterator + 'PageSize']));
if (set == 'inventories') {
lookup_results = [];
scope.$emit('refreshFinished', data['results']);
}
else if (set == 'teams') {
lookup_results = [];
scope.$emit('TeamRefreshFinished', data['results']);
}
else {
scope[iterator + 'SearchSpin'] = false;
scope[set] = data['results'];
}
})
.error ( function(data, status, headers, config) {
scope[iterator + 'SearchSpin'] = true;
Alert('Error!', 'Failed to retrieve ' + set + '. GET returned status: ' + status);
});
}
}]);

View File

@@ -0,0 +1,52 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* RelatedPaginateHelper
*
* All the parts for controlling the search widget on
* related collections.
*
* RelatedPaginateInit({
* scope: <scope>,
* relatedSets: <array of related collections {model_name, url, iterator}>,
* form: <form object used by FormGenerator>
* });
*
*/
angular.module('RelatedPaginateHelper', ['RefreshRelatedHelper'])
.factory('RelatedPaginateInit', [ 'RefreshRelated', function(RefreshRelated) {
return function(params) {
var scope = params.scope;
var relatedSets = params.relatedSets;
for (var key in relatedSets){
scope[relatedSets[key].iterator + 'Page'] = 0;
scope[relatedSets[key].iterator + 'PageSize'] = 20;
}
scope.nextSet = function(set, iterator) {
scope[iterator + 'Page']++;
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'NextUrl'] });
};
scope.prevSet = function(set, iterator) {
scope[iterator + 'Page']--;
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: scope[iterator + 'PrevUrl'] });
};
scope.changePageSize = function(set, iterator) {
// Called when a new page size is selected
var defaultUrl;
scope[iterator + 'Page'] = 0;
for (var key in relatedSets) {
if (key == set) {
defaultUrl = relatedSets[key].url;
break;
}
}
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: defaultUrl });
}
}
}]);

View File

@@ -0,0 +1,80 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* RelatedSearchHelper
*
* All the parts for controlling the search widget on
* related collections.
*
* SearchInit({
* scope: <scope>,
* relatedSets: <array of related collections {model_name, url, iterator}>,
* form: <form object used by FormGenerator>
* });
*
*
*/
angular.module('RelatedSearchHelper', ['RestServices', 'Utilities','RefreshRelatedHelper'])
.factory('RelatedSearchInit', ['Alert', 'Rest', 'RefreshRelated', function(Alert, Rest, RefreshRelated) {
return function(params) {
var scope = params.scope;
var relatedSets = params.relatedSets;
var form = params.form;
// Set default values
for (var set in form.related) {
for (var fld in form.related[set].fields) {
if (form.related[set].fields[fld].key) {
scope[form.related[set].iterator + 'SearchField'] = fld
scope[form.related[set].iterator + 'SearchFieldLabel'] = form.related[set].fields[fld].label;
break;
}
}
scope[form.related[set].iterator + 'SearchType'] = 'contains';
scope[form.related[set].iterator + 'SearchTypeLabel'] = 'Contains';
}
// Functions to handle search widget changes
scope.setSearchField = function(model, fld, label) {
scope[model + 'SearchFieldLabel'] = label;
scope[model + 'SearchField'] = fld;
scope.search(model);
}
scope.setSearchType = function(model, type, label) {
scope[model + 'SearchTypeLabel'] = label;
scope[model + 'SearchType'] = type;
scope.search(model);
}
scope.search = function(model) {
scope[model + 'SearchSpin'] = true;
var set, url, iterator, default_order;
for (var key in relatedSets) {
if (relatedSets[key].iterator == model) {
set = key;
iterator = relatedSets[key].iterator;
url = relatedSets[key].url;
for (var fld in form.related[key].fields) {
if (form.related[key].fields[fld].key) {
default_order = fld;
}
}
break;
}
}
if (scope[model + 'SearchValue'] != '' && scope[model + 'SearchValue'] != undefined) {
url += '?' + scope[model + 'SearchField'] +
'__' + scope[model + 'SearchType'] + '=' + escape(scope[model + 'SearchValue']);
url += (default_order) ? '&order_by=' + escape(default_order) : '';
}
else {
url += (default_order) ? '?order_by=' + escape(default_order) : '';
}
RefreshRelated({ scope: scope, set: set, iterator: iterator, url: url });
}
}
}]);

View File

@@ -0,0 +1,84 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* SearchHelper
*
* All the parts for controlling the search widget on
* related collections.
*
* SearchInit({
* scope: <scope>,
* set: <model name (i.e. organizations),
* name was given in ng-repeat>
* url: <default api url used to load data>
* list: <list object used by ListGenerator>
* });
*
*/
angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper'])
.factory('SearchInit', ['Alert', 'Rest', 'Refresh', function(Alert, Rest, Refresh) {
return function(params) {
var scope = params.scope;
var set = params.set;
var defaultUrl = params.url;
var list = params.list;
var iterator = list.iterator;
var default_order;
// Set default values
for (fld in list.fields) {
if (list.fields[fld].key) {
default_order = fld;
scope[iterator + 'SearchField'] = fld
scope[iterator + 'SearchFieldLabel'] = list.fields[fld].label;
break;
}
}
scope[iterator + 'SearchType'] = 'icontains';
scope[iterator + 'SearchTypeLabel'] = 'Contains';
scope[iterator + 'SearchParams'] = '';
scope[iterator + 'SearchValue'] = '';
// Functions to handle search widget changes
scope.setSearchField = function(iterator, fld, label) {
scope[iterator + 'SearchFieldLabel'] = label;
scope[iterator + 'SearchField'] = fld;
scope.search(iterator);
}
scope.setSearchType = function(iterator, type, label) {
scope[iterator + 'SearchTypeLabel'] = label;
scope[iterator + 'SearchType'] = type;
scope.search(iterator);
}
scope.search = function(iterator) {
//
// need to be able to search by related set. Ex: /api/v1/inventories/?organization__name__icontains=
//
scope[iterator + 'SearchSpin'] = true;
var url = defaultUrl;
if (scope[iterator + 'SearchValue'] != '' && scope[iterator + 'SearchValue'] != undefined) {
if (list.fields[scope[iterator + 'SearchField']].sourceModel) {
// handle fields whose source is a related model e.g. inventories.organization
scope[iterator + 'SearchParams'] = '?' + list.fields[scope[iterator + 'SearchField']].sourceModel + '__' +
list.fields[scope[iterator + 'SearchField']].sourceField + '__' +
scope[iterator + 'SearchType'] + '=' + escape(scope[iterator + 'SearchValue']);
scope[iterator + 'SearchParams'] += (default_order) ? '&order_by=' + escape(default_order) : '';
}
else {
scope[iterator + 'SearchParams'] = '?' + scope[iterator + 'SearchField'] + '__' +
scope[iterator + 'SearchType'] + '=' + escape(scope[iterator + 'SearchValue']);
scope[iterator + 'SearchParams'] += (default_order) ? '&order_by=' + escape(default_order) : '';
}
}
else {
scope[iterator + 'SearchParams'] = '';
scope[iterator + 'SearchParams'] += (default_order) ? '?order_by=' + escape(default_order) : '';
}
Refresh({ scope: scope, set: set, iterator: iterator, url: url });
}
}
}]);

View File

@@ -0,0 +1,109 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* TeamHelper
* Routines shared amongst the team controllers
*/
angular.module('TeamHelper', [ 'RestServices', 'Utilities', 'OrganizationListDefinition',
'SearchHelper', 'PaginateHelper', 'ListGenerator' ])
.factory('SetTeamListeners', ['Alert', 'Rest', function(Alert, Rest) {
return function(params) {
var scope = params.scope;
var set = params.set;
var iterator = params.iterator;
// Listeners to perform lookups after main inventory list loads
scope.$on('TeamResultFound', function(e, results, lookup_results) {
if ( lookup_results.length == results.length ) {
key = 'organization';
property = 'organization_name';
for (var i=0; i < results.length; i++) {
for (var j=0; j < lookup_results.length; j++) {
if (results[i][key] == lookup_results[j].id) {
results[i][property] = lookup_results[j].value;
}
}
}
scope[iterator + 'SearchSpin'] = false;
scope[set] = results;
}
});
scope.$on('TeamRefreshFinished', function(e, results) {
// Loop through the result set (sent to us by the search helper) and
// lookup the id and name of each organization. After each lookup
// completes, call resultFound.
var lookup_results = [];
for (var i = 0; i < results.length; i++) {
Rest.setUrl('/api/v1/organizations/' + results[i].organization + '/');
Rest.get()
.success( function( data, status, headers, config) {
console.log('here!');
console.log(data);
lookup_results.push({ id: data.id, value: data.name });
scope.$emit('TeamResultFound', results, lookup_results);
})
.error( function( data, status, headers, config) {
lookup_results.push({ id: 'error' });
scope.$emit('TeamResultFound', results, lookup_results);
});
}
});
}
}])
.factory('TeamLookUpOrganizationInit', ['Alert', 'Rest', 'OrganizationList', 'GenerateList', 'SearchInit', 'PaginateInit',
function(Alert, Rest, OrganizationList, GenerateList, SearchInit, PaginateInit) {
return function(params) {
var scope = params.scope;
// Show pop-up to select organization
scope.lookUpOrganization = function() {
var list = OrganizationList;
var listGenerator = GenerateList;
var listScope = listGenerator.inject(list, { mode: 'lookup', hdr: 'Select Organization' });
var defaultUrl = '/api/v1/organizations';
listScope.selectAction = function() {
var found = false;
for (var i=0; i < listScope[list.name].length; i++) {
if (listScope[list.iterator + "_" + listScope[list.name][i].id + "_class"] == "success") {
found = true;
scope['organization'] = listScope[list.name][i].id;
scope['organization_name'] = listScope[list.name][i].name;
scope['team_form'].$setDirty();
listGenerator.hide();
}
}
if (found == false) {
Alert('No Selection', 'Click on a row to select an Organization before clicking the Select button.');
}
}
listScope.toggle_organization = function(id) {
// when user clicks a row, remove 'success' class from all rows except clicked-on row
if (listScope[list.name]) {
for (var i=0; i < listScope[list.name].length; i++) {
listScope[list.iterator + "_" + listScope[list.name][i].id + "_class"] = "";
}
}
if (id != null && id != undefined) {
listScope[list.iterator + "_" + id + "_class"] = "success";
}
}
SearchInit({ scope: listScope, set: list.name, list: list, url: defaultUrl });
PaginateInit({ scope: listScope, list: list, url: defaultUrl, mode: 'lookup' });
scope.search(list.iterator);
listScope.toggle_organization(scope.organization);
}
}
}]);