From 8e09d6925fdecf9790c1ef0959c7ce18018d7cb9 Mon Sep 17 00:00:00 2001 From: tanshu Date: Wed, 10 Jun 2015 18:08:58 +0530 Subject: [PATCH] Figure out how to move the Controller resolve in the IIFE, maybe every component has its own router. --- soter/__init__.py | 50 +-- soter/static/app/shared/angular_service.js | 457 +++++++++++++++++++++ soter/static/app/user/user.controller.js | 44 ++ soter/static/app/user/user.service.js | 14 + 4 files changed, 542 insertions(+), 23 deletions(-) create mode 100644 soter/static/app/shared/angular_service.js create mode 100644 soter/static/app/user/user.controller.js create mode 100644 soter/static/app/user/user.service.js diff --git a/soter/__init__.py b/soter/__init__.py index 150244a..ec1cc64 100644 --- a/soter/__init__.py +++ b/soter/__init__.py @@ -15,28 +15,32 @@ def main(global_config, **settings): config.add_route('home', '/') - # - # Hello world test endpoint. - # If ?auth=true is passed then it will run OAuth validation on the request. - # - config.add_route('hello', '/v{version}/hello.json') - - # - # Action endpoints - # All action endpoints follow the same convention. - # Everything in []'s are optional - # /action/{id}[/{additional}].json - config.add_route('api_picture_id', '/picture/{id}') - config.add_route('api_picture', '/picture') - config.add_route('api_picture_list', '/pictures') - - config.add_route('album_id', '/album/{id}') - config.add_route('album', '/album') - config.add_route('album_list', '/albums') - - config.add_route('api_album_id', '/a/{id}') - config.add_route('api_album_list', '/a/') - config.add_route('api_album', '/a') - + add_route(config,'picture', '/picture') + add_route(config,'album', '/album') + add_route(config,'user', '/user') + add_route(config,'group', '/group') config.scan() return config.make_wsgi_app() + + +def pluralize(word, num=None): + if num is None or num != 1: + if word.endswith('y'): + return word[:-1] + 'ies' + elif word[-1] in 'sx' or word[-2:] in ['sh', 'ch']: + return word + 'es' + elif word.endswith('an'): + return word[:-2] + 'en' + else: + return word + 's' + return word + + +def add_route(config, name, url, has_list=True, variable='id'): + config.add_route(name + '_' + variable, url + '/{' + variable + '}') + config.add_route(name, url) + if has_list: + config.add_route(name + '_list', pluralize(url)) + + config.add_route('api_' + name + '_' + variable, '/v1' + url + '/{' + variable + '}') + config.add_route('api_' + name, '/v1' + url) diff --git a/soter/static/app/shared/angular_service.js b/soter/static/app/shared/angular_service.js new file mode 100644 index 0000000..b9eae90 --- /dev/null +++ b/soter/static/app/shared/angular_service.js @@ -0,0 +1,457 @@ +'use strict'; + +var overlordService = angular.module('overlord.service', ['ngResource']); + +overlordService.factory('IssueGrid', ['$resource', function ($resource) { + return $resource('/api/Issues/Services/:date'); +}]); + +overlordService.factory('Voucher', ['$resource', function ($resource) { + return $resource('/api/Voucher/:id', + {id: '@VoucherID'}, { + post: {method: 'POST', params: {post: true}}, + save: {method: 'POST', transformRequest: function (orgData, headersGetter) { + var data = angular.copy(orgData); + + function dataURLtoBlob(dataURL) { + var re = /^data:([\w/\-\.]+);\w+,(.*)$/, + m = dataURL.match(re), + mimeString = m[1], + byteString = atob(m[2]), + ab = new ArrayBuffer(byteString.length), + dw = new DataView(ab), + i; + + for (i = 0; i < byteString.length; i++) { + dw.setUint8(i, byteString.charCodeAt(i)); + } + return new Blob([ab], {type: mimeString}); + } + + var fd = new FormData(), + files = [], + i; + for (i = data.Files.length - 1; i >= 0; i--) { + var item = data.Files[i]; + if (!item.ID) { + fd.append('f' + i, dataURLtoBlob(item.Resized)); + fd.append('t' + i, dataURLtoBlob(item.Thumbnail)); + data.Files.splice(i, 1); + } + } + fd.append('model', angular.toJson(data)); + var headers = headersGetter(); + angular.forEach(headers, function (value, key) { + delete headers[key]; + }); + headers['Content-Type'] = undefined; + return fd; + }} + }); +}]); + +overlordService.factory('Account', ['$resource', function ($resource) { + return $resource('/api/Account/:id', + {id: '@LedgerID'}, { + query: {method: 'GET', params: {list: true}, isArray: true}, + autocomplete: {method: 'GET', params: {term: ''}, isArray: true} + }); +}]); + +overlordService.factory('Recipe', ['$resource', function ($resource) { + return $resource('/api/Recipe/:id', + {id: '@RecipeID'}, { + query: {method: 'GET', params: {list: true}, isArray: true} + }); +}]); + +overlordService.factory('Employee', ['$resource', function ($resource) { + return $resource('/api/Employee/:id', + {id: '@LedgerID'}, { + query: {method: 'GET', params: {list: true}, isArray: true}, + autocomplete: {method: 'GET', params: {term: ''}, isArray: true} + }); +}]); + +overlordService.factory('Ledger', ['$resource', function ($resource) { + return $resource('/api/Ledger/:id'); +}]); + +overlordService.factory('Reconcile', ['$resource', function ($resource) { + return $resource('/api/Reconcile/:id', {id: '@Account.LedgerID'}); +}]); + +overlordService.factory('ProductLedger', ['$resource', function ($resource) { + return $resource('/api/ProductLedger/:id'); +}]); + +overlordService.factory('CostCenter', ['$resource', function ($resource) { + return $resource('/api/CostCenter/:id', + {id: '@CostCenterID'}, { + query: {method: 'GET', params: {list: true}, isArray: true} + }); +}]); + +overlordService.factory('AccountType', ['$resource', function ($resource) { + return $resource('/api/AccountTypes'); +}]); + +overlordService.factory('Group', ['$resource', function ($resource) { + return $resource('/api/Group/:id', + {id: '@GroupID'}, { + query: {method: 'GET', params: {list: true}, isArray: true} + }); +}]); + +overlordService.factory('Client', ['$resource', function ($resource) { + return $resource('/api/Client/:id', + {id: '@ClientID'}, { + query: {method: 'GET', params: {list: true}, isArray: true} + }); +}]); + +overlordService.factory('Product', ['$resource', function ($resource) { + return $resource('/api/Product/:id', + {id: '@ProductID'}, { + query: {method: 'GET', params: {list: true}, isArray: true}, + autocomplete: {method: 'GET', params: {term: ''}, isArray: true} + }); +}]); + +overlordService.factory('Batch', ['$resource', function ($resource) { + return $resource('/api/Batch', {}, { + autocomplete: {method: 'GET', params: {term: ''}, isArray: true} + }); +}]); + +overlordService.factory('ProductGroup', ['$resource', function ($resource) { + return $resource('/api/ProductGroup/:id', + {id: '@ProductGroupID'}, { + query: {method: 'GET', params: {list: true}, isArray: true} + }); +}]); + +overlordService.factory('CostCenter', ['$resource', function ($resource) { + return $resource('/api/CostCenter/:id', + {id: '@CostCenterID'}, { + query: {method: 'GET', params: {list: true}, isArray: true} + }); +}]); + +overlordService.factory('TrialBalance', ['$resource', function ($resource) { + return $resource('/api/TrialBalance/:date'); +}]); + +overlordService.factory('ClosingStock', ['$resource', function ($resource) { + return $resource('/api/ClosingStock/:date'); +}]); + +overlordService.factory('CashFlow', ['$resource', function ($resource) { + return $resource('/api/CashFlow/:id'); +}]); + +overlordService.factory('Daybook', ['$resource', function ($resource) { + return $resource('/api/Daybook'); +}]); + +overlordService.factory('Unposted', ['$resource', function ($resource) { + return $resource('/api/Unposted'); +}]); + +overlordService.factory('ProfitLoss', ['$resource', function ($resource) { + return $resource('/api/ProfitLoss'); +}]); + +overlordService.factory('StockMovement', ['$resource', function ($resource) { + return $resource('/api/StockMovement'); +}]); + +overlordService.factory('BalanceSheet', ['$resource', function ($resource) { + return $resource('/api/BalanceSheet/:date'); +}]); + +overlordService.factory('NetTransactions', ['$resource', function ($resource) { + return $resource('/api/NetTransactions'); +}]); + +overlordService.factory('Purchases', ['$resource', function ($resource) { + return $resource('/api/Purchases'); +}]); + +overlordService.factory('PurchaseEntries', ['$resource', function ($resource) { + return $resource('/api/PurchaseEntries'); +}]); + +overlordService.factory('RawMaterialCost', ['$resource', function ($resource) { + return $resource('/api/RawMaterialCost/:id'); +}]); + +overlordService.factory('Auth', ['$resource', function ($resource) { + return $resource('/api/Auth', {}, { + login: {method: 'GET', params: {list: true}, isArray: true} + }); +}]); + +overlordService.factory('Attendance', ['$resource', function ($resource) { + return $resource('/api/Attendance/:date', {date: '@Date'}); +}]); + +overlordService.factory('AttendanceTypes', ['$resource', function ($resource) { + return $resource('/api/AttendanceTypes'); +}]); + +overlordService.factory('EmployeeAttendance', ['$resource', function ($resource) { + return $resource('/api/EmployeeAttendance/:id', {id: '@Employee.LedgerID'}); +}]); + +overlordService.factory('Account', ['$resource', function ($resource) { + return $resource('/api/Account/:id', + {id: '@LedgerID'}, { + query: {method: 'GET', params: {list: true}, isArray: true}, + autocomplete: {method: 'GET', params: {term: ''}, isArray: true} + }); +}]); +overlordService.factory('Message', ['$resource', function ($resource) { + return $resource('/api/Message/:id', + {id: '@ThreadID'}, { + query: {method: 'GET', params: {list: true}} + }); +}]); + +overlordService.factory('Tag', ['$resource', function ($resource) { + return $resource('/api/Tags'); +}]); + +overlordService.factory('Tokenizer', ['$filter', function ($filter) { + var parseFilterString = function (q, searchInfo) { + // '[^']+'|"[^"]+"|[^ ]+ == Match a space delimited string or quoted string. ms for short + // ((ms\s*:\s*ms)|ms) == Match string : Match string OR Match string + var re = /((('[^']+'|"[^"]+"|[^\s]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))|('[^']+'|"[^"]+"|[^\s]+))/g, + comparator = searchInfo.comparator || {}, + sorter = searchInfo.sorter || {}, + flagger = searchInfo.flags || {}, + defaultKey = searchInfo.def, + matches = [], + sorting = [], + flags = {}, + operators = { + '<': function (a, b) { + return a < b; + }, + '>': function (a, b) { + return a > b; + }, + '<=': function (a, b) { + return a <= b; + }, + '>=': function (a, b) { + return a >= b; + }, + '==': function (a, b) { + return a === b; + }, + '!=': function (a, b) { + return a !== b; + } + }, + comparators = { + 'text': function (obj, text) { + if (text.indexOf('!') === 0) { + return obj.toLowerCase().indexOf(text.substr(1)) <= -1; + } else { + return obj.toLowerCase().indexOf(text) > -1; + } + }, + 'boolean': function (obj, text) { + return obj === isTrue(text); + }, + 'numeric': function (obj, text) { + var op = opLength(text); + if (op) { + return operators[text.substr(0, op)](obj, Number(text.substr(op).trim())); + } else { + return obj.toString().indexOf(text) > -1; + } + }, + 'true': function () { + return true; + } + }; + + + function isTrue(value) { + return !_.any(['f', 'fa', 'fal', 'fals', 'false', 'n', 'no', '0'], function (item) { + return item === value; + }); + } + + function is(ch, chars) { + return chars.indexOf(ch) !== -1; + } + + function pushObject(comparator, value) { + return {'Col': comparator.Col, 'Comparator': comparators[comparator.Comparator], 'Value': value}; + } + + function opLength(operator) { + var i, + ops = ['<=', '<', '>=', '>', '==', '!=']; + for (i = 0; i < ops.length; i++) { + if (operator.indexOf(ops[i]) === 0) { + return ops[i].length; + } + } + return 0; + } + + _(q.match(re)) + .reduce(function (accumulator, item) { + var key, + value; + if (item.indexOf(':') === -1) { + key = ''; + value = item; + } else { + key = item.substr(0, item.indexOf(':')).trim(); + value = item.substr(item.indexOf(':') + 1, item.length).trim(); + } + if (key.indexOf("'") !== -1 || key.indexOf('"') !== -1) { + key = key.substring(1, key.length - 1).trim(); + } + if (value.indexOf("'") !== -1 || value.indexOf('"') !== -1) { + value = key.substring(1, value.length - 1).trim(); + } + if (value !== '') { + accumulator.push({Key: key, Value: value}); + } + return accumulator; + }, []) + .forEach(function (item) { + var key = item.Key, + value = item.Value; + + if (key === '' && value.length > 1 && is(value.charAt(0), '+-') && value.substr(1) in sorter) { + sorting.push(value.charAt(0) + sorter[value.substr(1).toLowerCase()]); + } else { + key = key || defaultKey; + if (key in comparator) { + matches.push(pushObject(comparator[key], value)); + } else if (key in flagger) { + flags[key] = value; + } + } + }); + return {'q': matches, 'o': sorting, 'f': flags}; + }; + var doFilter = _.memoize(function (q, array, matches) { + var filterExpressions = matches.q, + sortPredicates = matches.o, + filterCount = filterExpressions.length; + var arrayCopy = filterCount === 0 ? array : _.filter(array, function (item) { + return _.every(filterExpressions, function (expression) { + return expression.Comparator(item[expression.Col], expression.Value); + }); + }); + arrayCopy = sortPredicates.length === 0 ? arrayCopy : $filter('orderBy')(arrayCopy, sortPredicates); + return arrayCopy; + }); + + return {parseFilterString: parseFilterString, doFilter: doFilter}; +}]); + +overlordService.factory("ReaderPromise", ["$q", function ($q) { + var onLoad = function (reader, deferred) { + return function () { + deferred.resolve(reader.result); + }; + }; + + var onError = function (reader, deferred) { + return function () { + deferred.reject(reader.result); + }; + }; + + var onProgress = function (reader, scope) { + return function (event) { + scope.$emit("fileProgress", + { + total: event.total, + loaded: event.loaded + }); + }; + }; + + var getReader = function (deferred, scope) { + var reader = new FileReader(); + reader.onload = onLoad(reader, deferred); + reader.onerror = onError(reader, deferred, scope); + if (!angular.isUndefined(scope)) { + reader.onprogress = onProgress(reader, scope); + } + return reader; + }; + + var readAsDataURL = function (file, scope) { + var deferred = $q.defer(), + reader = getReader(deferred, scope); + reader.readAsDataURL(file); + return deferred.promise; + }; + + return { + readAsDataURL: readAsDataURL + }; +}]); + +overlordService.factory("uploadedImageResizer", ["$q", "ReaderPromise", function ($q, ReaderPromise) { + function resizeImage(file, MAX_WIDTH, MAX_HEIGHT) { + var image = file, + canvas = document.createElement('canvas'), + ctx = canvas.getContext("2d"), + img = document.createElement("img"), + ratio = 1; + + img.src = image; + var width = img.width, + height = img.height; + + if (width > MAX_WIDTH) { + ratio = MAX_WIDTH / width; + } + if (height > MAX_HEIGHT && MAX_HEIGHT / height < ratio) { + ratio = MAX_HEIGHT / height; + } + if (ratio === 1) { + return file; + } + + width *= ratio; + height *= ratio; + + canvas.width = width; + canvas.height = height; + ctx.drawImage(img, 0, 0, width, height); + return canvas.toDataURL("image/jpeg", 0.95); + } + + var ProcessUpload = function (uploadedFile, oldFiles) { + var file = {File: uploadedFile.file}, + exists = _.any(oldFiles, function (value) { + return angular.equals(value.File, file.File); + }); + + if (exists) { + return; + } + ReaderPromise.readAsDataURL(uploadedFile.file).then(function (result) { + file.Original = result; + file.Thumbnail = resizeImage(result, 100, 150); + file.Resized = resizeImage(result, 825, 1170); + }); + oldFiles.push(file); + }; + + + return ProcessUpload; +}]); diff --git a/soter/static/app/user/user.controller.js b/soter/static/app/user/user.controller.js new file mode 100644 index 0000000..60f4c22 --- /dev/null +++ b/soter/static/app/user/user.controller.js @@ -0,0 +1,44 @@ +'use strict'; + +angular.module('soter') + .controller('UserListController', UserListController); + +var UserListController = ['$scope', 'users', function ($scope, users) { + $scope.info = users; +}]; + +UserListController.resolve = { + users: ['User', function (User) { + return User.query({}).$promise; + }] +}; + +var UserCtrl = ['$scope', '$location', 'user', function ($scope, $location, user) { + $scope.user = user; + + $scope.save = function () { + $scope.user.$save(function () { + $scope.toasts.push({Type: 'Success', Message: ''}); + $location.path('/Users'); + }, function (data) { + $scope.toasts.push({Type: 'Danger', Message: data.data}); + }); + }; + + $scope.delete = function () { + $scope.user.$delete(function () { + $scope.toasts.push({Type: 'Success', Message: ''}); + $location.path('/Users'); + }, function (data) { + $scope.toasts.push({Type: 'Danger', Message: data.data}); + }); + }; + $('#txtName').focus(); +}]; + +UserCtrl.resolve = { + user: ['$route', 'User', function ($route, User) { + var id = $route.current.params.id; + return User.get({id: id}).$promise; + }] +}; diff --git a/soter/static/app/user/user.service.js b/soter/static/app/user/user.service.js new file mode 100644 index 0000000..22b37e6 --- /dev/null +++ b/soter/static/app/user/user.service.js @@ -0,0 +1,14 @@ +(function () { + 'use strict'; + + angular.module('soter').factory('User', User); + + var User = ['$resource', function ($resource) { + return $resource('/api/User/:id', + {id: '@UserID'}, { + query: {method: 'GET', params: {list: true}, isArray: true}, + names: {method: 'GET', params: {names: true}, isArray: true} + }); + }] +}); +