diff --git a/locale/en_US/LC_MESSAGES/django.mo b/locale/en_US/LC_MESSAGES/django.mo index 50ec7730b3..19d51eb18d 100644 Binary files a/locale/en_US/LC_MESSAGES/django.mo and b/locale/en_US/LC_MESSAGES/django.mo differ diff --git a/locale/en_US/LC_MESSAGES/django.po b/locale/en_US/LC_MESSAGES/django.po index 056062f813..e449040656 100644 --- a/locale/en_US/LC_MESSAGES/django.po +++ b/locale/en_US/LC_MESSAGES/django.po @@ -1,15 +1,14 @@ msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-10-06 12:15-0700\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"X-Generator: lokalise.com\n" +"Project-Id-Version: SEED Platform\n" +"PO-Revision-Date: 2026-03-10 20:51\n" +"Last-Translator: lokalise.com\n" +"Language-Team: lokalise.com\n\n" +"Language: en_US\n" msgid "# Of Files" msgstr "# Of Files" @@ -2948,7 +2947,7 @@ msgid "ORGANIZATION_WUI_DISPLAY_UNITS" msgstr "Measurement unit display for Water Use Intensity (WUI)" msgid "ORG_MATCH_MERGE_LINK_WARNING" -msgstr "WARNING: You have modified the criteria that your organization's data will be matched and merged against. Once a column has been added to an organization's matching criteria it cannot be removed while the organization has inventory." +msgstr "WARNING: You have modified the criteria that your organization's data will be matched and merged against. If the organization is using more than one access levels: once a column has been added to an organization's matching criteria it cannot be removed while the organization has inventory. Matching criteria can be removed in organizations not using access levels." msgid "OVERWRITE_COLUMN_DATA_QUESTION" msgstr "Overwrite data?" diff --git a/locale/es/LC_MESSAGES/django.mo b/locale/es/LC_MESSAGES/django.mo index 587316e6c5..1ac65a7c39 100644 Binary files a/locale/es/LC_MESSAGES/django.mo and b/locale/es/LC_MESSAGES/django.mo differ diff --git a/locale/es/LC_MESSAGES/django.po b/locale/es/LC_MESSAGES/django.po index 1794744b59..c789897607 100644 --- a/locale/es/LC_MESSAGES/django.po +++ b/locale/es/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Generator: lokalise.com\n" "Project-Id-Version: SEED Platform\n" -"PO-Revision-Date: 2025-12-11 17:45\n" +"PO-Revision-Date: 2026-03-10 20:51\n" "Last-Translator: lokalise.com\n" "Language-Team: lokalise.com\n\n" "Language: es\n" @@ -3751,9 +3751,8 @@ msgstr "Visualización de la unidad de medida del consumo de agua" msgid "ORGANIZATION_WUI_DISPLAY_UNITS" msgstr "Visualización de la unidad de medida para la intensidad de uso del agua (WUI)" -#, fuzzy msgid "ORG_MATCH_MERGE_LINK_WARNING" -msgstr "ADVERTENCIA: Ha modificado los criterios con los que se compararán y combinarán los datos de su organización. Una vez que se ha añadido una columna a los criterios de correspondencia de una organización, no se puede eliminar mientras la organización tenga inventario." +msgstr "ADVERTENCIA: Ha modificado los criterios con los que se compararán y fusionarán los datos de su organización. Si la organización utiliza más de un nivel de acceso, una vez que se haya añadido una columna a los criterios de coincidencia de una organización, no se podrá eliminar mientras la organización tenga inventario. Los criterios de coincidencia se pueden eliminar en organizaciones que no utilicen niveles de acceso." #, fuzzy msgid "OVERWRITE_COLUMN_DATA_QUESTION" diff --git a/locale/fr_CA/LC_MESSAGES/django.mo b/locale/fr_CA/LC_MESSAGES/django.mo index bb0f74f724..1691fb455a 100644 Binary files a/locale/fr_CA/LC_MESSAGES/django.mo and b/locale/fr_CA/LC_MESSAGES/django.mo differ diff --git a/locale/fr_CA/LC_MESSAGES/django.po b/locale/fr_CA/LC_MESSAGES/django.po index d17750669b..141b0db10d 100644 --- a/locale/fr_CA/LC_MESSAGES/django.po +++ b/locale/fr_CA/LC_MESSAGES/django.po @@ -1,15 +1,14 @@ msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-10-04 14:26-0700\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"X-Generator: lokalise.com\n" +"Project-Id-Version: SEED Platform\n" +"PO-Revision-Date: 2026-03-10 20:51\n" +"Last-Translator: lokalise.com\n" +"Language-Team: lokalise.com\n\n" +"Language: fr_CA\n" msgid "# Of Files" msgstr "Nombre de fichiers" @@ -2970,7 +2969,7 @@ msgid "ORGANIZATION_WUI_DISPLAY_UNITS" msgstr "Affichage de l'unité de mesure de l'intensité d'utilisation de l'eau (WUI)" msgid "ORG_MATCH_MERGE_LINK_WARNING" -msgstr "AVERTISSEMENT: Vous avez modifié les critères selon lesquels les données de votre organisation seront mises en correspondance et fusionnées. Une fois qu'une colonne a été ajoutée aux critères de correspondance d'une organisation, elle ne peut pas être supprimée tant que l'organisation dispose d'un inventaire." +msgstr "AVERTISSEMENT: Vous avez modifié les critères de correspondance et de fusion des données de votre organisation. Si l’organisation utilise plusieurs niveaux d’accès: une fois qu’une colonne a été ajoutée aux critères de correspondance, elle ne peut plus être supprimée tant que l’organisation dispose de stocks. Les critères de correspondance peuvent être supprimés dans les organisations n’utilisant pas de niveaux d’accès." msgid "OVERWRITE_COLUMN_DATA_QUESTION" msgstr "Écraser les données?" diff --git a/seed/static/seed/js/controllers/column_settings_controller.js b/seed/static/seed/js/controllers/column_settings_controller.js index 29c6638f30..2ae2e010fb 100644 --- a/seed/static/seed/js/controllers/column_settings_controller.js +++ b/seed/static/seed/js/controllers/column_settings_controller.js @@ -15,6 +15,8 @@ angular.module('SEED.controller.column_settings', []).controller('column_setting 'auth_payload', 'columns_service', 'modified_service', + 'organization_service', + 'uploader_service', 'spinner_utility', 'urls', 'naturalSort', @@ -33,6 +35,8 @@ angular.module('SEED.controller.column_settings', []).controller('column_setting auth_payload, columns_service, modified_service, + organization_service, + uploader_service, spinner_utility, urls, naturalSort, @@ -125,7 +129,7 @@ angular.module('SEED.controller.column_settings', []).controller('column_setting $scope.matching_status = (column) => { if (column.is_extra_data) return 'ineligible'; - if ($scope.org.inventory_count && initial_matching_ids.includes(column.id)) return 'locked'; + if ($scope.org.access_level_names.length > 1 && $scope.org.inventory_count && initial_matching_ids.includes(column.id)) return 'locked'; return 'eligible'; }; @@ -249,6 +253,30 @@ angular.module('SEED.controller.column_settings', []).controller('column_setting $scope.isModified = () => modified_service.isModified(); + $scope.complete_column_update = function () { + const matching_criteria_changed = _.find(_.values(diff), (delta) => _.has(delta, 'is_matching_criteria')); + + if (matching_criteria_changed) { + // reset the spinner and run whole org match merge link + spinner_utility.show(undefined, $('.display')[0]); + + organization_service.match_merge_link($scope.org.id, $scope.inventory_type).then((response) => { + uploader_service.check_progress_loop( + response.progress_key, + 0, + 1, + (response) => { + organization_service.get_match_merge_link_result($scope.org.id, response.unique_id).then(() => column_update_complete()); + }, + () => {}, + { progress: 0 } + ); + }); + } else { + column_update_complete(); + } + }; + // Table Sorting const default_sort_toggle = () => { $scope.column_sort = 'default'; @@ -360,6 +388,7 @@ angular.module('SEED.controller.column_settings', []).controller('column_setting } modified_service.resetModified(); + $scope.modal_instance.dismiss(); $state.reload(); }; @@ -378,14 +407,7 @@ angular.module('SEED.controller.column_settings', []).controller('column_setting return; } - const modal_instance = $scope.open_confirm_column_settings_modal(); - modal_instance.result - .then((data) => { - column_update_complete(data); - }) - .catch(() => { - // User cancelled - }); + $scope.modal_instance = $scope.open_confirm_column_settings_modal(); }; $scope.open_create_column_modal = () => $uibModal.open({ @@ -412,7 +434,8 @@ angular.module('SEED.controller.column_settings', []).controller('column_setting columns_service: () => columns_service, spinner_utility: () => spinner_utility, table_name: () => ($scope.inventory_type === 'properties' ? 'PropertyState' : 'TaxLotState'), - $q: () => $q + $q: () => $q, + complete_column_update: () => $scope.complete_column_update } }); diff --git a/seed/static/seed/js/controllers/confirm_column_settings_modal_controller.js b/seed/static/seed/js/controllers/confirm_column_settings_modal_controller.js index 0ab123c7d9..01b12d690d 100644 --- a/seed/static/seed/js/controllers/confirm_column_settings_modal_controller.js +++ b/seed/static/seed/js/controllers/confirm_column_settings_modal_controller.js @@ -15,7 +15,9 @@ angular.module('SEED.controller.confirm_column_settings_modal', []).controller(' 'proposed_changes', 'columns_service', 'spinner_utility', + 'uiGridGroupingConstants', '$q', + 'complete_column_update', '$interval', 'uploader_service', 'table_name', @@ -35,7 +37,9 @@ angular.module('SEED.controller.confirm_column_settings_modal', []).controller(' proposed_changes, columns_service, spinner_utility, + uiGridGroupingConstants, $q, + complete_column_update, $interval, uploader_service, table_name, @@ -220,6 +224,7 @@ angular.module('SEED.controller.confirm_column_settings_modal', []).controller(' 0, 1, (response) => { + complete_column_update(); $scope.result = `${response.message} in ${$scope.elapsed}`; $scope.state = 'done'; $interval.cancel($scope.interval); @@ -260,6 +265,205 @@ angular.module('SEED.controller.confirm_column_settings_modal', []).controller(' } }; + // Preview + // Agg function returning last value of matching criteria field (all should be the same if they match) + $scope.matching_field_value = function (aggregation, fieldValue) { + aggregation.value = fieldValue; + }; + + function prioritize_sort(grid, sortColumns) { + // To maintain grouping while giving users the ability to have some sorting, + // matching columns are given top priority followed by the hidden linking ID column. + // Lastly, non-matching columns are given next priority so that users can sort within a grouped set. + if (sortColumns.length > 1) { + const matching_cols = _.filter(sortColumns, (col) => col.colDef.is_matching_criteria); + const linking_id_col = _.find(sortColumns, ['name', 'id']); + const remaining_cols = _.filter(sortColumns, (col) => !col.colDef.is_matching_criteria && !(col.name === 'id')); + sortColumns = matching_cols.concat(linking_id_col).concat(remaining_cols); + _.forEach(sortColumns, (col, index) => { + col.sort.priority = index; + }); + } + } + + // Takes raw cycle-partitioned records and returns array of cycle-aware records + const format_preview_records = (raw_inventory) => _.reduce(raw_inventory, (all_records, records, cycle_id) => { + const cycle = _.find($scope.cycles, { id: parseInt(cycle_id, 10) }); + _.forEach(records, (record) => { + record.cycle_name = cycle.name; + record.cycle_start = cycle.start; + all_records.push(record); + }); + return all_records; + }, []); + + // Builds preview columns using non-extra_data columns + function build_preview_columns() { + // create copy in order to not change original column objects. + const preview_column_defs = _.reject(_.cloneDeep($scope.columns), 'is_extra_data'); + const default_min_width = 50; + const autopin_width = 100; + const column_def_defaults = { + headerCellFilter: 'translate', + minWidth: default_min_width, + width: 125, + groupingShowAggregationMenu: false + }; + + _.map(preview_column_defs, (col) => { + const options = {}; + if (col.data_type === 'datetime') { + options.cellFilter = 'date:\'yyyy-MM-dd h:mm a\''; + options.filter = inventory_service.dateFilter(); + } else { + options.filter = inventory_service.combinedFilter(); + } + + // For matching criteria values, always pin left and show values in aggregate rows. + if ($scope.proposed_matching_criteria_columns.includes(col.column_name)) { + col.pinnedLeft = true; + + // Help indicate matching columns are given preferred sort priority + col.displayName += '*'; + options.headerCellClass = 'matching-column-header'; + + options.customTreeAggregationFn = $scope.matching_field_value; + options.width = autopin_width; + } + return _.defaults(col, options, column_def_defaults); + }); + + // Grouping Settings + preview_column_defs.unshift( + { + displayName: 'Linking ID', + grouping: { groupPriority: 0 }, + name: 'id', + sort: { priority: 0, direction: 'desc' }, + pinnedLeft: true, + visible: false, + suppressRemoveSort: true, // since grouping relies on sorting + minWidth: default_min_width, + width: autopin_width + }, + { + name: 'cycle_name', + displayName: 'Cycle', + pinnedLeft: true, + treeAggregationType: uiGridGroupingConstants.aggregation.COUNT, + customTreeAggregationFinalizerFn: function (aggregation) { + aggregation.rendered = `total cycles: ${aggregation.value}`; + }, + minWidth: default_min_width, + width: autopin_width, + groupingShowAggregationMenu: false + }, + { + name: 'cycle_start', + displayName: 'Cycle Start', + cellFilter: 'date:\'yyyy-MM-dd\'', + filter: inventory_service.dateFilter(), + type: 'date', + sort: { priority: 1, direction: 'asc' }, + pinnedLeft: true, + minWidth: default_min_width, + width: autopin_width, + groupingShowAggregationMenu: false + } + ); + + return preview_column_defs; + } + + // Initialize preview table as empty for now. + $scope.match_merge_link_preview = { + data: 'data', + enableColumnResizing: true, + enableFiltering: true, + onRegisterApi: function (gridApi) { + $scope.gridApi = gridApi; + + // used to allow filtering for child branches of grouping tree + $scope.gridApi.table_category = 'year-over-year'; + + $scope.gridApi.core.on.filterChanged($scope, () => { + // This is a workaround for losing the state of expanded rows during filtering. + _.delay($scope.gridApi.treeBase.expandAllRows, 500); + }); + + // Prioritized to maintain grouping. + $scope.gridApi.core.on.sortChanged($scope, prioritize_sort); + } + }; + + // Preview Loading Helpers + function build_proposed_matching_columns(result) { + // Summarize proposed matching_criteria_columns for pinning and to create preview + const criteria_additions = _.filter($scope.change_summary_data, (change) => change.is_matching_criteria); + const criteria_removals = _.filter($scope.change_summary_data, (change) => change.is_matching_criteria === false); + + $scope.criteria_changes = { + add: _.map(criteria_additions, 'column_name'), + remove: _.map(criteria_removals, 'column_name') + }; + + let base_and_add; + if ($scope.inventory_type === 'properties') { + base_and_add = _.union(result.PropertyState, $scope.criteria_changes.add); + } else { + base_and_add = _.union(result.TaxLotState, $scope.criteria_changes.add); + } + $scope.proposed_matching_criteria_columns = _.difference(base_and_add, $scope.criteria_changes.remove); + } + + function build_preview(summary) { + $scope.data = format_preview_records(summary); + $scope.preview_columns = build_preview_columns(); + $scope.match_merge_link_preview.columnDefs = $scope.preview_columns; + } + + function preview_loading_complete() { + $scope.preview_loading = false; + spinner_utility.hide(); + } + + function get_preview() { + // Use new proposed matching_criteria_columns to request a preview then render this preview. + const spinner_options = { + scale: 0.40, + position: 'relative', + left: '100%' + }; + spinner_utility.show(spinner_options, $('#spinner_placeholder')[0]); + + organization_service.match_merge_link_preview($scope.org_id, $scope.inventory_type, $scope.criteria_changes) + .then((response) => { + uploader_service.check_progress_loop( + response.progress_key, + 0, + 1, + (completion_notice) => { + organization_service.get_match_merge_link_result($scope.org_id, completion_notice.unique_id) + .then(build_preview) + .then(preview_loading_complete); + }, + () => { /* Do nothing */ }, + { progress: 0 } + ); + }); + } + + // Get and Show Preview (If matching criteria changes exist.) + $scope.matching_criteria_exists = _.find(_.values($scope.change_summary_data), (delta) => _.has(delta, 'is_matching_criteria')); + + if ($scope.matching_criteria_exists) { + $scope.preview_loading = true; + + organization_service.matching_criteria_columns($scope.org_id) + .then(build_proposed_matching_columns) + .then(get_preview); + } + $scope.setRunningState = () => { $scope.eta = $scope.etaFn(); if ($scope.eta) { diff --git a/seed/static/seed/js/services/organization_service.js b/seed/static/seed/js/services/organization_service.js index d80c61eca8..13bd14229b 100644 --- a/seed/static/seed/js/services/organization_service.js +++ b/seed/static/seed/js/services/organization_service.js @@ -164,6 +164,22 @@ angular.module('SEED.service.organization', []).factory('organization_service', organization_factory.reset_all_passwords = (org_id) => $http.post(`/api/v3/organizations/${org_id}/reset_all_passwords/`).then((response) => response.data); + organization_factory.match_merge_link = (org_id, inventory_type) => $http.post(`/api/v3/organizations/${org_id}/match_merge_link/`, { + inventory_type + }).then((response) => response.data); + + organization_factory.geocoding_columns = (org_id) => $http.get(`/api/v3/organizations/${org_id}/geocoding_columns/`).then((response) => response.data); + + organization_factory.match_merge_link_preview = (org_id, inventory_type, criteria_change_columns) => $http.post(`/api/v3/organizations/${org_id}/match_merge_link_preview/`, { + inventory_type, + add: criteria_change_columns.add, + remove: criteria_change_columns.remove + }).then((response) => response.data); + + organization_factory.get_match_merge_link_result = (org_id, match_merge_link_id) => $http.get(`/api/v3/organizations/${org_id}/match_merge_link_result/?match_merge_link_id=${match_merge_link_id}`).then((response) => response.data); + + organization_factory.reset_all_passwords = (org_id) => $http.post(`/api/v3/organizations/${org_id}/reset_all_passwords/`).then((response) => response.data); + organization_factory.insert_sample_data = (org_id) => $http.get(`/api/v3/organizations/${org_id}/insert_sample_data/`).then((response) => response.data); /** diff --git a/seed/static/seed/locales/en_US.json b/seed/static/seed/locales/en_US.json index 96f92dc847..6a06a636a2 100644 --- a/seed/static/seed/locales/en_US.json +++ b/seed/static/seed/locales/en_US.json @@ -965,7 +965,7 @@ "OR": "OR", "ORGANIZATION_WATER_USE_DISPLAY_UNITS": "Measurement unit display for Water Use", "ORGANIZATION_WUI_DISPLAY_UNITS": "Measurement unit display for Water Use Intensity (WUI)", - "ORG_MATCH_MERGE_LINK_WARNING": "WARNING: You have modified the criteria that your organization's data will be matched and merged against. Once a column has been added to an organization's matching criteria it cannot be removed while the organization has inventory.", + "ORG_MATCH_MERGE_LINK_WARNING": "WARNING: You have modified the criteria that your organization's data will be matched and merged against. If the organization is using more than one access levels: once a column has been added to an organization's matching criteria it cannot be removed while the organization has inventory. Matching criteria can be removed in organizations not using access levels.", "OVERWRITE_COLUMN_DATA_QUESTION": "Overwrite data?", "Occupied Floor Area": "Occupied Floor Area", "Only Show Populated Columns": "Only Show Populated Columns", diff --git a/seed/static/seed/locales/es.json b/seed/static/seed/locales/es.json index cca7122dec..c60801e12d 100644 --- a/seed/static/seed/locales/es.json +++ b/seed/static/seed/locales/es.json @@ -965,7 +965,7 @@ "OR": "O", "ORGANIZATION_WATER_USE_DISPLAY_UNITS": "Visualización de la unidad de medida del consumo de agua", "ORGANIZATION_WUI_DISPLAY_UNITS": "Visualización de la unidad de medida para la intensidad de uso del agua (WUI)", - "ORG_MATCH_MERGE_LINK_WARNING": "ADVERTENCIA: Ha modificado los criterios con los que se compararán y combinarán los datos de su organización. Una vez que se ha añadido una columna a los criterios de correspondencia de una organización, no se puede eliminar mientras la organización tenga inventario.", + "ORG_MATCH_MERGE_LINK_WARNING": "ADVERTENCIA: Ha modificado los criterios con los que se compararán y fusionarán los datos de su organización. Si la organización utiliza más de un nivel de acceso, una vez que se haya añadido una columna a los criterios de coincidencia de una organización, no se podrá eliminar mientras la organización tenga inventario. Los criterios de coincidencia se pueden eliminar en organizaciones que no utilicen niveles de acceso.", "OVERWRITE_COLUMN_DATA_QUESTION": "¿Sobreescribir datos?", "Occupied Floor Area": "Superficie ocupada", "Only Show Populated Columns": "Mostrar sólo columnas rellenas", diff --git a/seed/static/seed/locales/fr_CA.json b/seed/static/seed/locales/fr_CA.json index 2104abcb3f..614248e33f 100644 --- a/seed/static/seed/locales/fr_CA.json +++ b/seed/static/seed/locales/fr_CA.json @@ -965,7 +965,7 @@ "OR": "OU", "ORGANIZATION_WATER_USE_DISPLAY_UNITS": "Affichage de l'unité de mesure pour l'utilisation de l'eau", "ORGANIZATION_WUI_DISPLAY_UNITS": "Affichage de l'unité de mesure de l'intensité d'utilisation de l'eau (WUI)", - "ORG_MATCH_MERGE_LINK_WARNING": "AVERTISSEMENT: Vous avez modifié les critères selon lesquels les données de votre organisation seront mises en correspondance et fusionnées. Une fois qu'une colonne a été ajoutée aux critères de correspondance d'une organisation, elle ne peut pas être supprimée tant que l'organisation dispose d'un inventaire.", + "ORG_MATCH_MERGE_LINK_WARNING": "AVERTISSEMENT: Vous avez modifié les critères de correspondance et de fusion des données de votre organisation. Si l’organisation utilise plusieurs niveaux d’accès: une fois qu’une colonne a été ajoutée aux critères de correspondance, elle ne peut plus être supprimée tant que l’organisation dispose de stocks. Les critères de correspondance peuvent être supprimés dans les organisations n’utilisant pas de niveaux d’accès.", "OVERWRITE_COLUMN_DATA_QUESTION": "Écraser les données?", "Occupied Floor Area": "Surface occupée", "Only Show Populated Columns": "Afficher uniquement les colonnes remplies", diff --git a/seed/static/seed/partials/confirm_column_settings_modal.html b/seed/static/seed/partials/confirm_column_settings_modal.html index 641ff047fd..4a09dbea28 100644 --- a/seed/static/seed/partials/confirm_column_settings_modal.html +++ b/seed/static/seed/partials/confirm_column_settings_modal.html @@ -1,10 +1,14 @@ -