diff --git a/.gitignore b/.gitignore index acdedceca9..501a16a28b 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,5 @@ src/ /seed/static/vendors/bower_components/fine-uploader/ docs/build/ .project -.pydevproject \ No newline at end of file +.pydevproject +dump.rdb diff --git a/BE/settings/dev.py b/BE/settings/dev.py index 3ceb6b2185..d7eef9a3a3 100644 --- a/BE/settings/dev.py +++ b/BE/settings/dev.py @@ -49,7 +49,7 @@ LOGGING = { 'version': 1, 'disable_existing_loggers': True, - # set up some log message handers to chose from + # set up some log message handlers to choose from 'handlers': { 'sentry': { 'level': 'ERROR', diff --git a/README-osx.md b/README-osx.md index e08f8cb224..3866c5c002 100644 --- a/README-osx.md +++ b/README-osx.md @@ -4,18 +4,27 @@ These instructions are for installing and running SEED on Mac OSX in development ## Prerequisites -These instructions assume you have/use [Macports](https://www.macports.org/). +These instructions assume you have/use [Macports](https://www.macports.org/). The workflow has been testing with homebrew as well, but is not directly supported. You system should have the following dependencies already installed: -Although you _could_ install Python packages globally, the easiest way to install Python packages is with [virtualenv](https://virtualenv.pypa.io/en/latest/) and [virtualenvwrapper](https://virtualenvwrapper.readthedocs.org/en/latest/). Setting these up first will help avoid polluting your base Python installation and make it much easier to switch between different versions of the code. +* git (`port install git` or `brew install git`) +* Mercurial (`port install hg` or `brew install mercurial`) -Once you have these installed, creating and entering a new virtualenv called "``seed``" for SEED development is as easy as: +(Recommended) - mkvirtualenv --python=python2.7 seed - +* [virtualenv](https://virtualenv.pypa.io/en/latest/) and [virtualenvwrapper](https://virtualenvwrapper.readthedocs.org/en/latest/). + * Although you _could_ install Python packages globally, this is the easiest way to install Python packages. Setting these up first will help avoid polluting your base Python installation and make it much easier to switch between different versions of the code. + + pip install virtualenv + pip install virtualenvwrapper + + * Follow instructions on [virtualenvwrapper](https://virtualenvwrapper.readthedocs.org/en/latest/) to setup your environment. + * Once you have these installed, creating and entering a new virtualenv called "``seed``" for SEED development is as easy as: + + mkvirtualenv --python=python2.7 seed ## Install PostgreSQL 9.4 -Perform the following commands as 'root' +Perform the following commands as 'root' if using port sudo su - root @@ -25,7 +34,12 @@ Install Postgres 9.4 # init db mkdir -p /opt/local/var/db/postgresql94/defaultdb chown postgres:postgres /opt/local/var/db/postgresql94/defaultdb - + + # homebrew + brew install postgres + # follow the post install instructions to add to launchagents or call manually with `postgres -D /usr/local/var/postgres` + # Skip the remaining Postgres instructions + Finish initializing the DB sudo su postgres -c '/opt/local/lib/postgresql94/bin/initdb -D /opt/local/var/db/postgresql94/defaultdb' @@ -44,11 +58,10 @@ Start Postgres Switch to postgres user - sudo su - postgres - PATH=$PATH:/opt/local/lib/postgresql94/bin/ + sudo su - postgres + PATH=$PATH:/opt/local/lib/postgresql94/bin/ -Configure Postgresql. Replace 'seeddb', 'seeduser' with desired db/user. -seedpass +Configure PostgreSQL. Replace 'seeddb', 'seeduser' with desired db/user. By default use password `seedpass` when prompted createdb seeddb createuser -P seeduser @@ -60,9 +73,11 @@ Now exit any root environments, becoming just yourself (even though it's not tha Run these commands as your normal user id. -Change to a virtualenv (using virtualenvwrapper) or do the following as a superuser. A virtualenv is usually better for development. +Change to a virtualenv (using virtualenvwrapper) or do the following as a superuser. A virtualenv is usually better for development. Set the virtualenv to seed. + + workon seed -Make sure PostgreSQL command line scripts are in your PATH +Make sure PostgreSQL command line scripts are in your PATH (if using port) export PATH=$PATH:/opt/local/lib/postgresql94/bin @@ -85,13 +100,11 @@ Install library with `setup.py` First, install [npm](https://www.npmjs.com/) if you haven't already. You can do this by installing from [nodejs.org](http://nodejs.org/), or use Macports: + # port sudo port install npm -### Install libraries - -Then run, from the top-level, a script to install the JS libraries: - - ./bin/install_javascript_dependencies.sh + # homebrew + brew install npm ## Configure Django and its back-end DBs @@ -150,7 +163,7 @@ If you want to do any API testing (and of course you do!), you will need to add an API KEY for this user. You can do this in postgresql directly: - psql94 seeddb seeduser + psql seeddb seeduser seeddb=> update landing_seeduser set api_key='DEADBEEF' where id=1; The 'secret' key DEADBEEF is hard-coded into the test scripts. @@ -159,8 +172,13 @@ The 'secret' key DEADBEEF is hard-coded into the test scripts. You need to manually install Redis for Celery to work. + # port sudo port install redis + # homebrew + brew install redis + # follow the post install instructions to add to launchagents or call manually with `redis-server` + ### Install Javascript dependencies The JS dependencies are installed using node.js package management (npm), with diff --git a/green_button/xml_importer.py b/green_button/xml_importer.py index 9c5909e5aa..9275b1dc08 100644 --- a/green_button/xml_importer.py +++ b/green_button/xml_importer.py @@ -119,7 +119,7 @@ def interval_data(reading_xml_data): :returns: dictionary representing a time series reading with keys 'cost', 'value', 'start_time', and 'duration'. """ - cost = reading_xml_data['cost'] + cost = reading_xml_data.get('cost') value = reading_xml_data['value'] time_period = reading_xml_data['timePeriod'] @@ -155,8 +155,8 @@ def meter_data(raw_meter_meta): # this function currently assumes those types are present and does # not check for any other types - currency = params_data['currency'] - power_of_ten_multiplier = params_data['powerOfTenMultiplier'] + currency = params_data.get('currency') + power_of_ten_multiplier = params_data.get('powerOfTenMultiplier') uom = params_data['uom'] result = { diff --git a/seed/decorators.py b/seed/decorators.py index d8cd035071..d9183a429b 100644 --- a/seed/decorators.py +++ b/seed/decorators.py @@ -22,6 +22,7 @@ def _get_lock_key(func_name, import_file_pk): def get_prog_key(func_name, import_file_pk): + """Return the progress key for the cache""" return _get_cache_key( PROGRESS_CACHE_PREFIX.format(func_name), import_file_pk ) @@ -29,14 +30,14 @@ def get_prog_key(func_name, import_file_pk): def increment_cache(key, increment): """Increment cache by value increment, never exceed 100.""" - value = cache.get(key) or 0.0 - value = float(value) + value = cache.get(key) or {'status': 'parsing', 'progress': 0.0} + value = float(value['progress']) if value + increment >= 100.0: value = 100.0 else: value += increment - cache.set(key, value) + cache.set(key, {'status': 'parsing', 'progress': value}) def lock_and_track(fn, *args, **kwargs): diff --git a/seed/models.py b/seed/models.py index 49a9638b1a..e34bb8cb5a 100644 --- a/seed/models.py +++ b/seed/models.py @@ -152,15 +152,16 @@ def get_ancestors(building): source_type { 2: ASSESSED_BS, 3: PORTFOLIO_BS, - 4: COMPOSITE_BS + 4: COMPOSITE_BS, + 6: GREEN_BUTTON_BS } :param building: BuildingSnapshot inst. :returns: list of BuildingSnapshot inst., ancestors of building """ ancestors = [] - parents = building.parents.filter(source_type__in=[2, 3, 4]) - ancestors.extend(parents.filter(source_type__in=[2, 3])) + parents = building.parents.filter(source_type__in=[2, 3, 4, 6]) + ancestors.extend(parents.filter(source_type__in=[2, 3, 6])) for p in parents: ancestors.extend(get_ancestors(p)) return ancestors diff --git a/seed/search.py b/seed/search.py index edd78eee5e..b82f8082d9 100644 --- a/seed/search.py +++ b/seed/search.py @@ -8,6 +8,7 @@ # python import operator import json +import re # django from django.db.models import Q @@ -184,49 +185,90 @@ def is_column(k, columns): sanitized = strip_suffixes(k, ['__lt', '__gt', '__lte', '__gte']) if sanitized in columns: return True - else: - return False + return False + + def is_string_query(q): + return isinstance(q, basestring) + + def is_exact_match(q): + if is_string_query(q): + return re.match(r"""^(["'])(.+)\1$""", q) + return False + + def is_empty_match(q): + if is_string_query(q): + return re.match(r"""^(["'])\1$""", q) + return False - query_dict = {} + # Build query as Q objects so we can AND and OR. + query_filters = Q() for k, v in other_params.iteritems(): in_columns = is_column(k, db_columns) - if in_columns and k != 'q' and v is not None and v != '': - if ('__lt' in k or + if in_columns and k != 'q' and v: + + # Is this query surrounded by matching quotes? + exact_match = is_exact_match(v) + empty_match = is_empty_match(v) + + if exact_match: + query_filters &= Q(**{"%s__exact" % k: exact_match.group(2)}) + elif empty_match: + query_filters &= Q(**{"%s__exact" % k: ''}) | Q(**{"%s__isnull" % k: True}) + elif ('__lt' in k or '__lte' in k or '__gt' in k or '__gte' in k or '__isnull' in k or k == 'import_file_id' or k == 'source_type'): - query_dict["%s" % k] = v + query_filters &= Q(**{"%s" % k: v}) else: - query_dict["%s__icontains" % k] = v + query_filters &= Q(**{"%s__icontains" % k: v}) - queryset = queryset.filter(**query_dict) + queryset = queryset.filter(query_filters) # handle extra_data with json_query for k, v in other_params.iteritems(): - if (not is_column(k, db_columns)) and k != 'q' and v != '': - if k.endswith(('__gt', '__gte')): + if (not is_column(k, db_columns)) and k != 'q' and v: + + # Is this query surrounded by matching quotes? + exact_match = is_exact_match(v) + empty_match = is_empty_match(v) + + + # If querying for empty matches, do a hack-y 'contains' query on + # the json field to check if the field exists. We're checking + # existence because empty mapped fields are not saved in the + # extra_data json field if they contain no data. + # + # When we bump to Django 1.7, we can switch to the newer + # django-pgjson package, and use the new "HAS" operator syntax. + # - nicholasserra + if empty_match: + queryset = queryset.exclude(extra_data__contains='"%s":' % k) + continue + + conditions = { + 'value': v + } + + if exact_match: + conditions['value'] = exact_match.group(2) + conditions['key_cast'] = 'text' + elif k.endswith(('__gt', '__gte')): k = strip_suffixes(k, ['__gt', '__gte']) - cond = '>' - key_cast = 'float' + conditions['cond'] = '>' + conditions['key_cast'] = 'float' elif k.endswith(('__lt', '__lte')): k = strip_suffixes(k, ['__lt', '__lte']) - cond = '<' - key_cast = 'float' + conditions['cond'] = '<' + conditions['key_cast'] = 'float' else: - cond = 'LIKE' - key_cast = 'text' - v = "%{0}%".format(v) - case_insensitive = key_cast == 'text' - - queryset = queryset.json_query( - k, - cond=cond, - key_cast=key_cast, - value=v, - case_insensitive=case_insensitive, - ) + conditions['cond'] = 'LIKE' + conditions['key_cast'] = 'text' + conditions['value'] = "%{0}%".format(v) + conditions['case_insensitive'] = True + + queryset = queryset.json_query(k, **conditions) return queryset diff --git a/seed/static/seed/css/mapping.css b/seed/static/seed/css/mapping.css index c586218d56..7c41a9634f 100644 --- a/seed/static/seed/css/mapping.css +++ b/seed/static/seed/css/mapping.css @@ -7,9 +7,9 @@ margin: 10px; } .fa-arrows { - padding-right: 7px; - color: #444; - } + padding-right: 7px; + color: #444; +} .tree-handle { padding: 10px; background: #428bca; diff --git a/seed/static/seed/js/controllers/admin_controller.js b/seed/static/seed/js/controllers/admin_controller.js index 0bec88ec4c..321ee8fec8 100644 --- a/seed/static/seed/js/controllers/admin_controller.js +++ b/seed/static/seed/js/controllers/admin_controller.js @@ -162,6 +162,8 @@ angular.module('BE.seed.controller.admin', []) function(data){ //success fn org.remove_message = "success"; get_organizations(); + }, function(data){ //failure fn + // Do nothing }, org // progress bar obj ); diff --git a/seed/static/seed/js/controllers/building_detail_controller.js b/seed/static/seed/js/controllers/building_detail_controller.js index bbc78c6fbd..c67c93ffef 100644 --- a/seed/static/seed/js/controllers/building_detail_controller.js +++ b/seed/static/seed/js/controllers/building_detail_controller.js @@ -32,6 +32,15 @@ angular.module('BE.seed.controller.building_detail', []) $scope.building_copy = {}; $scope.data_columns = []; $scope.audit_logs = audit_payload.audit_logs; + $scope.green_button_filenames = []; + + // gather green button filenames + building_payload.imported_buildings.forEach(function(e) { + if (e.source_type == 6) { // GREEN_BUTTON_BS + $scope.green_button_filenames.push(e.import_file_name); + } + }); + // set the tab $scope.section = $location.hash(); /** diff --git a/seed/static/seed/js/controllers/building_list_controller.js b/seed/static/seed/js/controllers/building_list_controller.js index f9e2ef4e86..e30e33ecec 100644 --- a/seed/static/seed/js/controllers/building_list_controller.js +++ b/seed/static/seed/js/controllers/building_list_controller.js @@ -66,6 +66,7 @@ angular.module('BE.seed.controller.building_list', []) */ var get_columns = function() { $scope.assessor_fields = all_columns.fields; + $scope.search.init_storage(); $scope.columns = $scope.search.generate_columns( all_columns.fields, default_columns.columns, diff --git a/seed/static/seed/js/controllers/data_upload_modal_ctrl.js b/seed/static/seed/js/controllers/data_upload_modal_ctrl.js index d9bac3ed74..250676c9e4 100644 --- a/seed/static/seed/js/controllers/data_upload_modal_ctrl.js +++ b/seed/static/seed/js/controllers/data_upload_modal_ctrl.js @@ -18,11 +18,14 @@ * ng-switch-when="8" == Add files to your Data Set. * ng-switch-when="9" == Add files to {$ dataset.name $} * ng-switch-when="10" == No matches found + * ng-switch-when="11" == Confirm Save Mappings? + * ng-switch-when="12" == Error Processing Data */ angular.module('BE.seed.controller.data_upload_modal', []) .controller('data_upload_modal_ctrl', [ '$scope', '$modalInstance', + '$log', 'step', 'dataset', '$timeout', @@ -34,6 +37,7 @@ angular.module('BE.seed.controller.data_upload_modal', []) function ( $scope, $modalInstance, + $log, step, dataset, $timeout, @@ -161,7 +165,6 @@ angular.module('BE.seed.controller.data_upload_modal', []) } if (event_message === "upload_complete") { var current_step = $scope.step.number; - var data_is_green_button = $scope.uploader.status_message = "upload complete"; $scope.dataset.import_file_id = file.file_id; @@ -216,6 +219,8 @@ angular.module('BE.seed.controller.data_upload_modal', []) mapping_service.start_mapping(file_id).then(function (dataa){ monitor_mapping(dataa.progress_key, file_id); }); + }, function(data) { + // Do nothing }, $scope.uploader); }; @@ -232,6 +237,8 @@ angular.module('BE.seed.controller.data_upload_modal', []) matching_service.start_system_matching(file_id).then(function (dataa){ monitor_matching(dataa.progress_key, file_id); }); + }, function(data) { + // Do nothing }, $scope.uploader); }; @@ -248,6 +255,8 @@ angular.module('BE.seed.controller.data_upload_modal', []) $scope.uploader.in_progress = false; $scope.uploader.progress = 1; $scope.step.number = 5; + }, function(data) { + // Do nothing }, $scope.uploader); }; @@ -271,6 +280,11 @@ angular.module('BE.seed.controller.data_upload_modal', []) $scope.step.number = 3; } + }, function(data){ + $log.error(data.message); + if (data.hasOwnProperty('stacktrace')) $log.error(data.stacktrace); + $scope.step_12_error_message = data.message; + $scope.step.number = 12; }, $scope.uploader); }); }; @@ -311,6 +325,8 @@ angular.module('BE.seed.controller.data_upload_modal', []) $scope.step.number = 10; } }); + }, function(data) { + // Do nothing }, $scope.uploader ); diff --git a/seed/static/seed/js/controllers/dataset_detail_controller.js b/seed/static/seed/js/controllers/dataset_detail_controller.js index 0fbee7d83d..3b339e1743 100644 --- a/seed/static/seed/js/controllers/dataset_detail_controller.js +++ b/seed/static/seed/js/controllers/dataset_detail_controller.js @@ -12,7 +12,7 @@ angular.module('BE.seed.controller.dataset_detail', []) function ($scope, dataset_payload, $log, dataset_service, $modal, urls) { $scope.dataset = dataset_payload.dataset; - $log.info({dataset_payload: dataset_payload}); + $log.info('dataset_payload:', dataset_payload); $scope.confirm_delete = function (file) { var yes = confirm("Are you sure you want to PERMANENTLY delete '" + file.name + "'?"); diff --git a/seed/static/seed/js/controllers/mapping_controller.js b/seed/static/seed/js/controllers/mapping_controller.js index 81decdfe50..fa0b273dec 100644 --- a/seed/static/seed/js/controllers/mapping_controller.js +++ b/seed/static/seed/js/controllers/mapping_controller.js @@ -480,6 +480,8 @@ angular.module('BE.seed.controller.mapping', []) 1.0, // progress multiplier function(data){ //success fn $scope.get_mapped_buildings(); + }, function(data) { //failure fn + // Do nothing }, $scope.import_file // progress bar obj ); diff --git a/seed/static/seed/js/controllers/matching_controller.js b/seed/static/seed/js/controllers/matching_controller.js index c28b56244b..8700cc7dce 100644 --- a/seed/static/seed/js/controllers/matching_controller.js +++ b/seed/static/seed/js/controllers/matching_controller.js @@ -130,6 +130,29 @@ angular.module('BE.seed.controller.matching', []) $scope.next_page_disabled = $scope.current_page === $scope.num_pages; }; + + /** + * first_page: triggered when the `first` paging button is clicked, it + * sets the results to the first page and shows that page + */ + $scope.pagination.first_page = function() { + $scope.current_page = 1; + $scope.filter_search(); + }; + + /** + * last_page: triggered when the `last` paging button is clicked, it + * sets the results to the last page and shows that page + */ + $scope.pagination.last_page = function() { + $scope.current_page = $scope.num_pages; + $scope.filter_search(); + }; + + /** + * next_page: triggered when the `next` paging button is clicked, it + * increments the page of the results, and fetches that page + */ $scope.pagination.next_page = function() { $scope.current_page += 1; if ($scope.current_page > $scope.num_pages) { @@ -137,6 +160,11 @@ angular.module('BE.seed.controller.matching', []) } $scope.filter_search(); }; + + /** + * prev_page: triggered when the `previous` paging button is clicked, it + * decrements the page of the results, and fetches that page + */ $scope.pagination.prev_page = function() { $scope.current_page -= 1; if ($scope.current_page < 1) { diff --git a/seed/static/seed/js/controllers/menu_controller.js b/seed/static/seed/js/controllers/menu_controller.js index 850776a509..4764984966 100644 --- a/seed/static/seed/js/controllers/menu_controller.js +++ b/seed/static/seed/js/controllers/menu_controller.js @@ -50,16 +50,22 @@ angular.module('BE.seed.controller.menu', []) $scope.menu.create_project_error = false; $scope.menu.create_project_error_message = ""; $scope.saving_indicator = false; + $scope.menu.loading = false; $scope.menu.route_load_error = false; $scope.menu.user = {}; $scope.$on("$routeChangeError", function(event, current, previous, rejection) { + $scope.menu.loading = false; $scope.menu.route_load_error = true; if (rejection === "not authorized" || rejection === "Your page could not be located!") { $scope.menu.error_message = rejection; } }); + $scope.$on("$routeChangeStart", function($event, next, current) { + $scope.menu.loading = next.controller === "mapping_controller"; + }); $scope.$on("$routeChangeSuccess", function() { + $scope.menu.loading = false; $scope.menu.route_load_error = false; }); $scope.$on('app_error', function(event, data){ diff --git a/seed/static/seed/js/directives/beUploader.js b/seed/static/seed/js/directives/beUploader.js index 458669bf29..b3d99fda97 100644 --- a/seed/static/seed/js/directives/beUploader.js +++ b/seed/static/seed/js/directives/beUploader.js @@ -11,7 +11,7 @@ * `message` - string - options: "upload_submitted", * "upload_in_progress", "upload_complete", and "invalid_extension" * `filename` - string name of the file uploaded - * `progress` - JS object with keys `loaded` and `total` wehere + * `progress` - JS object with keys `loaded` and `total` where * `loaded` / `total` * 100.0 is the percent uploaded * importrecord - int or string - id of import record or dataset * @@ -111,7 +111,7 @@ var makeS3Uploader = function(scope, element, attrs, filename) { * onComplete: overloaded callback that calls the callback defined * in the element attribute unless the upload failed, which will * fire a window alert. Passes as arguments to the callback - * a message indicating upload has copmleted, "upload_complete", and + * a message indicating upload has completed, "upload_complete", and * the filename. */ onComplete: function(id, fileName, responseJSON) { @@ -242,7 +242,7 @@ var makeFileSystemUploader = function(scope, element, attrs, filename) { * onComplete: overloaded callback that calls the callback defined * in the element attribute unless the upload failed, which will * fire a window alert. Passes as arguments to the callback - * a message indicating upload has copmleted, "upload_complete", and + * a message indicating upload has completed, "upload_complete", and * the filename. */ onComplete: function(id, fileName, responseJSON) { diff --git a/seed/static/seed/js/filters/stripImportPrefix.js b/seed/static/seed/js/filters/stripImportPrefix.js index 32dbb2a52d..4f824a5b4d 100644 --- a/seed/static/seed/js/filters/stripImportPrefix.js +++ b/seed/static/seed/js/filters/stripImportPrefix.js @@ -10,11 +10,11 @@ angular.module('stripImportPrefix', []).filter('stripImportPrefix', [ function($filter) { /** ids are sometime prefixed by the Import Record id. * e.g. import 28 would prefix all assessor data ids with 'IMP28-' and - * stripImportPrefix would stip out the 'IMP28-'s from the html and only + * stripImportPrefix would strip out the 'IMP28-'s from the html and only * display the ids. * * Usage: building.id = "IMP12-007" - * HTML: {{ buidling.id | stripImportPrefix }} + * HTML: {{ building.id | stripImportPrefix }} * compiles to: 007 * JS : stripImportPrefix(building.id) * returns: "007" diff --git a/seed/static/seed/js/seed.js b/seed/static/seed/js/seed.js index d8399b288a..27e0c37b79 100644 --- a/seed/static/seed/js/seed.js +++ b/seed/static/seed/js/seed.js @@ -256,8 +256,16 @@ SEED_app.config(['$routeProvider', function ($routeProvider) { 'search_payload': ['building_services', '$route', function(building_services, $route){ var params = $route.current.params; var q = params.q || ""; + + // Check session storage for order and sort values. + var orderBy = (typeof(Storage) !== "undefined" && sessionStorage.getItem('seedBuildingOrderBy') !== null) ? + sessionStorage.getItem('seedBuildingOrderBy') : ""; + + var sortReverse = (typeof(Storage) !== "undefined" && sessionStorage.getItem('seedBuildingSortReverse') !== null) ? + JSON.parse(sessionStorage.getItem('seedBuildingSortReverse')) : false; + // params: (query, number_per_page, page_number, order_by, sort_reverse, other_params, project_id) - return building_services.search_buildings(q, 10, 1, "", false, params, null); + return building_services.search_buildings(q, 10, 1, orderBy, sortReverse, params, null); }], 'default_columns': ['user_service', function(user_service){ return user_service.get_default_columns(); diff --git a/seed/static/seed/js/services/search_service.js b/seed/static/seed/js/services/search_service.js index 8f1c56e15f..6a78b1faae 100644 --- a/seed/static/seed/js/services/search_service.js +++ b/seed/static/seed/js/services/search_service.js @@ -78,6 +78,18 @@ angular.module('BE.seed.service.search', []) * functions */ + search_service.init_storage = function () { + // Check session storage for order and sort values. + if (typeof(Storage) !== "undefined" && sessionStorage.getItem('seedBuildingOrderBy') !== null) { + saas.order_by = sessionStorage.getItem('seedBuildingOrderBy'); + saas.sort_column = sessionStorage.getItem('seedBuildingOrderBy'); + } + + if (typeof(Storage) !== "undefined" && sessionStorage.getItem('seedBuildingSortReverse') !== null) { + saas.sort_reverse = JSON.parse(sessionStorage.getItem('seedBuildingSortReverse')); + } + }; + /** * sanitize_params: removes filter params with null or undefined values */ @@ -194,6 +206,24 @@ angular.module('BE.seed.service.search', []) this.showing.start = ((this.current_page - 1)*this.number_per_page) + 1; }; + /** + * first_page: triggered when the `first` paging button is clicked, it + * sets the page to the first in the results, and fetches that page + */ + search_service.first_page = function() { + this.current_page = 1; + this.search_buildings(); + }; + + /** + * last_page: triggered when the `last` paging button is clicked, it + * sets the page to the last in the results, and fetches that page + */ + search_service.last_page = function() { + this.current_page = this.num_pages; + this.search_buildings(); + }; + /** * next_page: triggered when the `next` paging button is clicked, it * increments the page of the results, and fetches that page @@ -306,6 +336,12 @@ angular.module('BE.seed.service.search', []) saas.sort_column = this.sort_column; } } + + if (typeof(Storage) !== "undefined") { + sessionStorage.setItem('seedBuildingOrderBy', saas.sort_column); + sessionStorage.setItem('seedBuildingSortReverse', saas.sort_reverse); + } + saas.order_by = this.sort_column; saas.current_page = 1; saas.search_buildings(); diff --git a/seed/static/seed/js/services/uploader_service.js b/seed/static/seed/js/services/uploader_service.js index a08fa2bad3..a58a35c424 100644 --- a/seed/static/seed/js/services/uploader_service.js +++ b/seed/static/seed/js/services/uploader_service.js @@ -115,28 +115,29 @@ angular.module('BE.seed.service.uploader', []).factory('uploader_service', [ * @param {number} offset: where to start the progress bar * @param {number} multiplier: multiplier for progress val * @param {fn} success_fn: function to call when progress is done + * @param {fn} failure_fn: function to call when progress is done and the result was not success * @param {obj} progress_bar_obj: progress bar object, attr 'progress' * is set with the progress */ - uploader_factory.check_progress_loop = function(progress_key, offset, multiplier, success_fn, progress_bar_obj, debug) { + uploader_factory.check_progress_loop = function(progress_key, offset, multiplier, success_fn, failure_fn, progress_bar_obj, debug) { if (typeof debug === 'undefined') { - debug = false; + debug = false; } uploader_factory.check_progress(progress_key).then(function (data){ - if (debug) { - console.log({progress: data.progress}); - } - var stop = $timeout(function(){ - progress_bar_obj.progress = (data.progress * multiplier) + offset; - if (data.progress < 100) { - uploader_factory.check_progress_loop(progress_key, offset, multiplier, success_fn, progress_bar_obj, debug); - } else { - success_fn(data); - } - }, 750); - }, function (data, status) { - // reject promise - console.log(data, status); + if (debug) { + console.log({progress: data.progress}); + } + var stop = $timeout(function(){ + progress_bar_obj.progress = (data.progress * multiplier) + offset; + if (data.progress < 100) { + uploader_factory.check_progress_loop(progress_key, offset, multiplier, success_fn, failure_fn, progress_bar_obj, debug); + } else { + success_fn(data); + } + }, 750); + }, function (data) { + // reject promise + failure_fn(data); }); }; diff --git a/seed/static/seed/partials/about.html b/seed/static/seed/partials/about.html index 8687301246..7048270c77 100644 --- a/seed/static/seed/partials/about.html +++ b/seed/static/seed/partials/about.html @@ -24,8 +24,8 @@

Development Team:

Funding from: U.S. Department of Energy

Software developer: Building Energy

-

Version

-

1.1.4

+

Version

+

1.1.5

diff --git a/seed/static/seed/partials/accounts.html b/seed/static/seed/partials/accounts.html index 0e37c1304e..ed82d56a97 100644 --- a/seed/static/seed/partials/accounts.html +++ b/seed/static/seed/partials/accounts.html @@ -12,29 +12,29 @@

Organizations

-
+

Pending Organization Invites

-
+
- + - - - - - - + + + + + +
-
+
@@ -44,32 +44,32 @@

Organizations I Manage

@@ -79,33 +79,33 @@

Organizations I Belong To

-
+
- + - - + + - - - + - - + +
Organization Name Number of Buildings Your Role Organization Owner(s)
+ {$ org.name $} {$ org.name $} {$ org.num_buildings $} {$ org.user_role || "-" $} + {$ org.user_role || "-" $} {$ owner.first_name $} {$ owner.last_name $}
-
+
diff --git a/seed/static/seed/partials/add_project.html b/seed/static/seed/partials/add_project.html index 966827856c..c7a366fe2a 100644 --- a/seed/static/seed/partials/add_project.html +++ b/seed/static/seed/partials/add_project.html @@ -13,12 +13,12 @@

Create a New Project

-
-
- - Sorry. The project name {$ project.name $} is already in use. -
-
+
+
+ + Sorry. The project name {$ project.name $} is already in use. +
+
diff --git a/seed/static/seed/partials/building_area_section.html b/seed/static/seed/partials/building_area_section.html index 9ca30a2b93..c636462c3f 100644 --- a/seed/static/seed/partials/building_area_section.html +++ b/seed/static/seed/partials/building_area_section.html @@ -2,14 +2,14 @@ diff --git a/seed/static/seed/partials/building_audit_log.html b/seed/static/seed/partials/building_audit_log.html index de20a356e1..20c5bbce19 100644 --- a/seed/static/seed/partials/building_audit_log.html +++ b/seed/static/seed/partials/building_audit_log.html @@ -2,14 +2,14 @@
-

Audit Log & Notes

+

Audit Log & Notes

@@ -25,12 +25,12 @@

Audit Log & Notes

- - {$ entry.user.first_name $} {$ entry.user.last_name $} + + {$ entry.user.first_name $} {$ entry.user.last_name $} {$ entry.action_note || entry.action $} - {$ entry.action_note $} edit - {$ entry.created | date:"MM/dd/yyyy 'at' h:mma" $} - + {$ entry.action_note $} edit + {$ entry.created | date:"MM/dd/yyyy 'at' h:mma" $} +
diff --git a/seed/static/seed/partials/building_contacts_section.html b/seed/static/seed/partials/building_contacts_section.html index bf42e23c71..586c0d6b34 100644 --- a/seed/static/seed/partials/building_contacts_section.html +++ b/seed/static/seed/partials/building_contacts_section.html @@ -2,14 +2,14 @@
-

Contact Information

+

Contact Information

diff --git a/seed/static/seed/partials/building_detail.html b/seed/static/seed/partials/building_detail.html index cf5bbc3867..a882b6d696 100644 --- a/seed/static/seed/partials/building_detail.html +++ b/seed/static/seed/partials/building_detail.html @@ -14,6 +14,7 @@

{$ building.address_line_1 $}

Building Info Projects Log & Notes + Energy
diff --git a/seed/static/seed/partials/building_detail_section.html b/seed/static/seed/partials/building_detail_section.html index 3c3c16ae99..2ce0ebe388 100644 --- a/seed/static/seed/partials/building_detail_section.html +++ b/seed/static/seed/partials/building_detail_section.html @@ -2,14 +2,14 @@
-

Building Information

+

Building Information

diff --git a/seed/static/seed/partials/building_energy_section.html b/seed/static/seed/partials/building_energy_section.html index c78c3f877d..32da451300 100644 --- a/seed/static/seed/partials/building_energy_section.html +++ b/seed/static/seed/partials/building_energy_section.html @@ -2,76 +2,36 @@
-

Energy Data

+

Energy Data

- - - - - - - - - - - - - - - - -
FieldMaster - {$ i.import_file_name $} - - {$ i.import_file_name $} - -
{$ f.title $} - {$ building[f.sort_column] $} - {$ building[f.sort_column] | date:'shortDate' $} - {$ building[f.sort_column] | number:f.num_digits $} - - - {$ building.extra_data[f.sort_column] | number:0 $} -
- -
-
- -
-
- {$ i[f.sort_column] $} - {$ i[f.sort_column] | date:'shortDate' $} - {$ i[f.sort_column] | number:f.num_digits $} - - {$ i.extra_data[f.sort_column] | number:f.num_digits $} - - {$ i[f.sort_column] $} - {$ i[f.sort_column] | date:'shortDate' $} - {$ i[f.sort_column] | number:f.num_digits $} - - {$ i.extra_data[f.sort_column] | number:f.num_digits $} - - - {$ i[f.sort_column] $} - {$ i[f.sort_column] | date:'shortDate' $} - {$ i[f.sort_column] | number:f.num_digits $} - - {$ i.extra_data[f.sort_column] | number:f.num_digits $} - -
-
+ + + + + + + + + + + + + + +
Uploaded Green Button Files
There are no green button files for this building
{$ f $}
+
diff --git a/seed/static/seed/partials/building_projects_section.html b/seed/static/seed/partials/building_projects_section.html index eb4850219a..3c780f9b54 100644 --- a/seed/static/seed/partials/building_projects_section.html +++ b/seed/static/seed/partials/building_projects_section.html @@ -2,14 +2,14 @@
-

Projects That Include This Building

+

Projects That Include This Building

diff --git a/seed/static/seed/partials/buildings.html b/seed/static/seed/partials/buildings.html index 145df30255..98af3ef7e6 100644 --- a/seed/static/seed/partials/buildings.html +++ b/seed/static/seed/partials/buildings.html @@ -7,38 +7,38 @@

{$ search.number_matching_search | number:0 $} Buildings

- +
-
- Buildings ListList Settings -
+
+ Buildings ListList Settings +
-
-
- - You successfully created the project {$ project.name $}. -
-
+
+
+ + You successfully created the project {$ project.name $}. +
+
-
- +
@@ -57,80 +57,80 @@

{$ search.number_matching_search | number:0

@@ -149,11 +149,11 @@
diff --git a/seed/static/seed/partials/buildings_settings.html b/seed/static/seed/partials/buildings_settings.html index 2972c3faca..162ed3a5bf 100644 --- a/seed/static/seed/partials/buildings_settings.html +++ b/seed/static/seed/partials/buildings_settings.html @@ -7,74 +7,74 @@

Buildings

- +
-
- Buildings ListList Settings -
+
+ Buildings ListList Settings +
-
-

Building List Settings

-
-
- -
+
+

Building List Settings

+
+
+ +
-

Add Shared Buildings.

-
- -
-

There are {$ fields.length | number $} columns of data available to you.

+

Add Shared Buildings.

+
+ +
+

There are {$ fields.length | number $} columns of data available to you.

Select columns from the list below to make them appear in your Buildings List table.

-
-
+
+
- + - - + + - +
Column Name
- - + +
- - - - - - + + + + + +
- - - {$ field.title $} -
+ + + {$ field.title $} +
-
+
- +
diff --git a/seed/static/seed/partials/buildings_table.html b/seed/static/seed/partials/buildings_table.html index 441a5bbdf8..4dfc801880 100644 --- a/seed/static/seed/partials/buildings_table.html +++ b/seed/static/seed/partials/buildings_table.html @@ -75,14 +75,16 @@
buildings
-
+
Showing {$ search.showing.start | number:0 $} to {$ search.showing.end | number:0 $} of {$ search.number_matching_search | number:0 $} buildings
-
+
diff --git a/seed/static/seed/partials/concat_modal.html b/seed/static/seed/partials/concat_modal.html index caaa52dd6e..30679d5bd1 100644 --- a/seed/static/seed/partials/concat_modal.html +++ b/seed/static/seed/partials/concat_modal.html @@ -45,7 +45,7 @@
Drag headers from your list on the left into the area below to combine them.
A preview of your concatenated data will appear in the box below.
- {$ c.raw_data[0] $} + {$ c.raw_data[0] $}
diff --git a/seed/static/seed/partials/create_organization_modal.html b/seed/static/seed/partials/create_organization_modal.html index 0bc525a79e..2e4f08cc6e 100644 --- a/seed/static/seed/partials/create_organization_modal.html +++ b/seed/static/seed/partials/create_organization_modal.html @@ -1,35 +1,35 @@
- - + +
diff --git a/seed/static/seed/partials/data_upload_modal.html b/seed/static/seed/partials/data_upload_modal.html index 1632793a28..0500711e69 100644 --- a/seed/static/seed/partials/data_upload_modal.html +++ b/seed/static/seed/partials/data_upload_modal.html @@ -10,110 +10,111 @@ + diff --git a/seed/static/seed/partials/mapping.html b/seed/static/seed/partials/mapping.html index a491756ab1..0fc03dca5c 100644 --- a/seed/static/seed/partials/mapping.html +++ b/seed/static/seed/partials/mapping.html @@ -184,8 +184,8 @@

Data Mapping & Validation ({$ import_file.name $})

- - + +
diff --git a/seed/static/seed/partials/matching.html b/seed/static/seed/partials/matching.html index 54258a7385..d7d4a7f95d 100644 --- a/seed/static/seed/partials/matching.html +++ b/seed/static/seed/partials/matching.html @@ -14,56 +14,56 @@

Data Matching ({$ import_file.dataset.name $})

{$ alert.msg $} -
- - -
-
-

HOW THE SYSTEM AUTO-MATCHES YOUR BUILDINGS:

-

Your source data file(s) are presented in the table on the left. All buildings where a possible data match exists are presented in a table on the right. The system attempts to auto-match building records using shared unique IDs like: PM Proprty ID, Tax Lot ID, and Custom IDs as well as Address information. Where the system believes a match exists between a record in your source file and an existing building record it will auto-check the 'match' checkbox — effectively making a match between these buildings. A confidence number (0 - 100%) is included to indicate the systems confidence that this match is correct.

-
-
-

FIELDS NEEDED TO MAKE BUILDING MATCHES:

-

The following is a list of the fields we use to match up buildings between different records. The more data you have in these four (4) fields the better our system will auto-match your buildings.

-

Tax Lot ID PM Property ID

-

Custom ID Address Line 1

-
-
-
- -
-
-

HOW TO MANUALLY MATCH YOUR BUILDINGS:

-

You can review and edit each individual match by clicking one of the linked IDs in the table. This will open a new table that shows the source data for the individual building record you are attempting to match. Underneath this building is a searchable list of all potential existing building matches. Use this table to explore all your building records and to select single or multiple building records to match you source data to.

-
-
-

VIEW/HIDE COLUMNS

-

You can customize the columns displayed in the table below by clicking the 'View/Hide Columns' button and then selecting the column headers you want to review from the modal window.

-
-
+
+ + +
+
+

HOW THE SYSTEM AUTO-MATCHES YOUR BUILDINGS:

+

Your source data file(s) are presented in the table on the left. All buildings where a possible data match exists are presented in a table on the right. The system attempts to auto-match building records using shared unique IDs like: PM Proprty ID, Tax Lot ID, and Custom IDs as well as Address information. Where the system believes a match exists between a record in your source file and an existing building record it will auto-check the 'match' checkbox — effectively making a match between these buildings. A confidence number (0 - 100%) is included to indicate the systems confidence that this match is correct.

+
+
+

FIELDS NEEDED TO MAKE BUILDING MATCHES:

+

The following is a list of the fields we use to match up buildings between different records. The more data you have in these four (4) fields the better our system will auto-match your buildings.

+

Tax Lot ID PM Property ID

+

Custom ID Address Line 1

+
+
+
+ +
+
+

HOW TO MANUALLY MATCH YOUR BUILDINGS:

+

You can review and edit each individual match by clicking one of the linked IDs in the table. This will open a new table that shows the source data for the individual building record you are attempting to match. Underneath this building is a searchable list of all potential existing building matches. Use this table to explore all your building records and to select single or multiple building records to match you source data to.

+
+
+

VIEW/HIDE COLUMNS

+

You can customize the columns displayed in the table below by clicking the 'View/Hide Columns' button and then selecting the column headers you want to review from the modal window.

+
+
-
- -
-
-
+ + + +
+
-
- -
+
+ +
- +
- + - + diff --git a/seed/static/seed/partials/matching_detail.html b/seed/static/seed/partials/matching_detail.html index b7507ce38c..d247539217 100644 --- a/seed/static/seed/partials/matching_detail.html +++ b/seed/static/seed/partials/matching_detail.html @@ -59,19 +59,19 @@

Matching ({$ import_file.file_name $})

- - - - - {$ building.property_id $} - {$ building.extra_data['Custom Property ID 1 - ID'] $} - {$ building.property_name $} - {$ building.address_line_1 $} - {$ building.year_built $} - {$ building.postal_code $} - {$ building.property_floor_area_bldg_park | number:0 $} - - + + + + + {$ building.property_id $} + {$ building.extra_data['Custom Property ID 1 - ID'] $} + {$ building.property_name $} + {$ building.address_line_1 $} + {$ building.year_built $} + {$ building.postal_code $} + {$ building.property_floor_area_bldg_park | number:0 $} + + @@ -92,25 +92,27 @@

Matching ({$ import_file.file_name $})

diff --git a/seed/static/seed/partials/matching_detail_table.html b/seed/static/seed/partials/matching_detail_table.html index e78b14d538..8e7f07a600 100644 --- a/seed/static/seed/partials/matching_detail_table.html +++ b/seed/static/seed/partials/matching_detail_table.html @@ -26,7 +26,7 @@ - Building from Source: {$ file_select.file.name $} Matched! Next unmatched building. + Building from Source: {$ file_select.file.name $} Matched! Next unmatched building. @@ -34,7 +34,7 @@ {$ building[c.sort_column] $} - Potential Matches from Source: Existing Buildings + Potential Matches from Source: Existing Buildings @@ -62,14 +62,16 @@
buildings
-
+
{$ search.number_matching_search | number:0 $} search results
-
+
diff --git a/seed/static/seed/partials/matching_list_table.html b/seed/static/seed/partials/matching_list_table.html index a34cf71097..919b455830 100644 --- a/seed/static/seed/partials/matching_list_table.html +++ b/seed/static/seed/partials/matching_list_table.html @@ -6,23 +6,23 @@
-
-
- - -
-
+ +
+ + +
+
-
- - -
-
- +
+ + +
+
+
@@ -88,7 +88,7 @@ - {$ b.confidence * 100 | number:0 $}% + {$ b.confidence * 100 | number:0 $}% @@ -125,14 +125,16 @@
buildings
-
+
Showing {$ showing.start | number:0 $} to {$ showing.end | number:0 $} of {$ number_matching_search | number:0 $} buildings ({$ unmatched_buildings $} unmatched)
-
+
diff --git a/seed/static/seed/partials/members.html b/seed/static/seed/partials/members.html index 3a2455f352..dafc461f5d 100644 --- a/seed/static/seed/partials/members.html +++ b/seed/static/seed/partials/members.html @@ -1,7 +1,7 @@
+
- Remove + Remove diff --git a/seed/static/seed/partials/new_member_modal.html b/seed/static/seed/partials/new_member_modal.html index cdfefcd0fb..05678dd9d7 100644 --- a/seed/static/seed/partials/new_member_modal.html +++ b/seed/static/seed/partials/new_member_modal.html @@ -1,40 +1,40 @@
- - + +
diff --git a/seed/static/seed/partials/new_project_modal.html b/seed/static/seed/partials/new_project_modal.html index 043b148ab2..c69c40a6cc 100644 --- a/seed/static/seed/partials/new_project_modal.html +++ b/seed/static/seed/partials/new_project_modal.html @@ -19,16 +19,16 @@
-
- -
+
+ +