Skip to content

Commit

Permalink
Merge pull request #3139 from SEED-platform/Add-Create-Sensors
Browse files Browse the repository at this point in the history
Add Create Sensor button
  • Loading branch information
haneslinger committed Feb 22, 2022
2 parents 20db9b9 + 5f33b20 commit 1408325
Show file tree
Hide file tree
Showing 14 changed files with 492 additions and 0 deletions.
62 changes: 62 additions & 0 deletions seed/data_importer/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@
Column,
ColumnMapping,
Meter,
Property,
PropertyState,
PropertyView,
TaxLotView,
TaxLotState,
Sensor,
DATA_STATE_IMPORT,
DATA_STATE_MAPPING,
DATA_STATE_MATCHING,
Expand Down Expand Up @@ -740,6 +742,10 @@ def finish_raw_save(results, file_pk, progress_key):

new_summary = _append_meter_import_results_to_summary(results, progress_data.summary())
finished_progress_data = progress_data.finish_with_success(new_summary)
elif import_file.source_type == "SensorMetaData":
import_file.cycle_id = None
new_summary = _append_sensor_import_results_to_summary(results)
finished_progress_data = progress_data.finish_with_success(new_summary)
else:
finished_progress_data = progress_data.finish_with_success()

Expand Down Expand Up @@ -821,6 +827,46 @@ def _save_greenbutton_data_create_tasks(file_pk, progress_key):
return chord(tasks, interval=15)(finish_raw_save.s(file_pk, progress_data.key))


@shared_task
def _save_sensor_data_create_tasks(file_pk, progress_key):
"""
Create or Edit Sensor tasks. Creates mutliple sensors for the same property.
"""
progress_data = ProgressData.from_key(progress_key)

import_file = ImportFile.objects.get(pk=file_pk)
property_id = import_file.matching_results_data['property_id']
sensor_property = Property.objects.get(id=property_id)

# matching_results_data gets cleared out since the field wasn't meant for this
import_file.matching_results_data = {}
import_file.save()

parser = reader.MCMParser(import_file.local_file)
sensor_data = list(parser.data)

sensors = []
for sensor_datum in sensor_data:
s, _ = Sensor.objects.get_or_create(**{
"column_name": sensor_datum["column_name"],
"sensor_property": sensor_property
})
s.display_name = sensor_datum["display_name"]
s.location_identifier = sensor_datum["location_identifier"]
s.description = sensor_datum["description"]
s.sensor_type = sensor_datum["type"]
s.units = sensor_datum["units"]

s.save()
sensors.append(s)

# add in the proposed_imports into the progress key to be used later. (This used to be the summary).
progress_data.total = 0
progress_data.save()

return finish_raw_save(sensors, file_pk, progress_data.key)


@shared_task
def _save_greenbutton_data_task(readings, meter_id, meter_usage_point_id, progress_key):
"""
Expand Down Expand Up @@ -1027,6 +1073,20 @@ def _append_meter_import_results_to_summary(import_results, incoming_summary):
return incoming_summary


def _append_sensor_import_results_to_summary(import_results):
return [
{
"display_name": sensor.display_name,
"type": sensor.sensor_type,
"location_identifier": sensor.location_identifier,
"units": sensor.units,
"column_name": sensor.column_name,
"description": sensor.description,
}
for sensor in import_results
]


@shared_task
def _save_raw_data_create_tasks(file_pk, progress_key):
"""
Expand Down Expand Up @@ -1105,6 +1165,8 @@ def save_raw_data(file_pk):
_save_pm_meter_usage_data_create_tasks.s(file_pk, progress_data.key).delay()
elif import_file.source_type == 'GreenButton':
_save_greenbutton_data_create_tasks.s(file_pk, progress_data.key).delay()
elif import_file.source_type == 'SensorMetaData':
_save_sensor_data_create_tasks.s(file_pk, progress_data.key).delay()
else:
_save_raw_data_create_tasks.s(file_pk, progress_data.key).delay()
except StopIteration:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,32 @@ angular.module('BE.seed.controller.inventory_detail_sensors', [])
.controller('inventory_detail_sensors_controller', [
'$scope',
'$stateParams',
'$uibModal',
'$window',
'cycles',
'dataset_service',
'inventory_service',
'inventory_payload',
'sensors',
'sensor_service',
'property_sensor_usage',
'spinner_utility',
'urls',
'organization_payload',
function (
$scope,
$stateParams,
$uibModal,
$window,
cycles,
dataset_service,
inventory_service,
inventory_payload,
sensors,
sensor_service,
property_sensor_usage,
spinner_utility,
urls,
organization_payload,
) {
spinner_utility.show();
Expand Down Expand Up @@ -217,6 +223,29 @@ angular.module('BE.seed.controller.inventory_detail_sensors', [])
spinner_utility.hide();
});
};

$scope.open_sensor_upload_modal = function () {
$uibModal.open({
templateUrl: urls.static_url + 'seed/partials/sensor_upload_modal.html',
controller: 'sensor_upload_modal_controller',
resolve: {
filler_cycle: function () {
return $scope.filler_cycle;
},
organization_id: function () {
return $scope.organization.id;
},
view_id: function () {
return $scope.inventory.view_id;
},
datasets: function () {
return dataset_service.get_datasets().then(function (result) {
return result.datasets;
});
}
}
});
};

$scope.inventory_display_name = function (property_type) {
let error = '';
Expand Down
192 changes: 192 additions & 0 deletions seed/static/seed/js/controllers/sensor_upload_modal_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
angular.module('BE.seed.controller.sensor_upload_modal', [])
.controller('sensor_upload_modal_controller', [
'$scope',
'$state',
'$uibModalInstance',
'uiGridConstants',
'filler_cycle',
'dataset_service',
'organization_id',
'uploader_service',
'view_id',
'datasets',
function (
$scope,
$state,
$uibModalInstance,
uiGridConstants,
filler_cycle,
dataset_service,
organization_id,
uploader_service,
view_id,
datasets
) {
$scope.step = {
number: 1
};
$scope.view_id = view_id;
$scope.selectedCycle = filler_cycle;
$scope.organization_id = organization_id;
$scope.datasets = datasets;

if (datasets.length) $scope.selectedDataset = datasets[0];

$scope.uploader = {
invalid_file_contents: false,
invalid_csv_extension_alert: false,
progress: 0,
status_message: ''
};

$scope.datasetChanged = function (dataset) {
// set selectedDataset to null to rerender button
$scope.selectedDataset = null;
$scope.selectedDataset = dataset;
};

$scope.cancel = function () {
// If step 2, GB import confirmation was not accepted by user, so delete file
if ($scope.step.number === 2) {
dataset_service.delete_file($scope.file_id).then(function (/*results*/) {
$uibModalInstance.dismiss('cancel');
});
} else {
$uibModalInstance.dismiss('cancel');
}
};

$scope.uploaderfunc = function (event_message, file/*, progress*/) {
switch (event_message) {
case 'invalid_extension':
$scope.$apply(function () {
$scope.uploader.invalid_csv_extension_alert = true;
$scope.uploader.invalid_file_contents = false;
});
break;

case 'upload_complete':
$scope.file_id = file.file_id;
$scope.filename = file.filename;
show_confirmation_info();
break;
}
};

var saveFailure = function (error) {
// Delete file and present error message

// file_id source varies depending on which step the error occurs
var file_id = $scope.file_id || error.config.data.file_id;
dataset_service.delete_file(file_id);

$scope.uploader.invalid_csv_extension_alert = false;
$scope.uploader.invalid_file_contents = true;

// Be sure user is back to step 1 where the error is shown and they can upload another file
$scope.step.number = 1;
};

var base_sensor_col_defs = [{
field: 'display_name',
enableHiding: false,
type: 'string'
}, {
field: 'type',
enableHiding: false
}, {
field: 'location_identifier',
displayName: 'location identifier',
enableHiding: false
},{
field: 'units',
enableHiding: false
}, {
field: 'column_name',
enableHiding: false
},{
field: 'description',
enableHiding: false
}];

var successfully_imported_col_def = {
field: 'successfully_imported',
enableHiding: false
};

var grid_rows_to_display = function (data) {
return Math.min(data.length, 5);
};

var show_confirmation_info = function () {
uploader_service.sensors_preview($scope.file_id, $scope.organization_id, $scope.view_id).then(function (result) {
$scope.proposed_imports_options = {
data: result.proposed_imports,
columnDefs: base_sensor_col_defs,
enableColumnResizing: true,
enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
enableVerticalScrollbar: result.proposed_imports.length <= 5 ? uiGridConstants.scrollbars.NEVER : uiGridConstants.scrollbars.WHEN_NEEDED,
minRowsToShow: grid_rows_to_display(result.proposed_imports)
};

var modal_element = angular.element(document.getElementsByClassName('modal-dialog'));
modal_element.addClass('modal-lg');

$scope.step.number = 2;
}).catch(saveFailure);
};

var saveSuccess = function (progress_data) {
// recheck progress in order to ensure message has been appended to progress_data
uploader_service.check_progress(progress_data.progress_key).then(function (data) {
$scope.uploader.status_message = 'saving complete';
$scope.uploader.progress = 100;
buildImportResults(data.message);
$scope.step.number = 4;
});
};

var buildImportResults = function (message) {
var col_defs = base_sensor_col_defs;
if (_.has(message, '[0].errors')) {
col_defs.push({
field: 'errors',
enableHiding: false
});
}

$scope.import_result_options = {
data: message,
columnDefs: col_defs,
enableColumnResizing: true,
enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
enableVerticalScrollbar: message.length <= 5 ? uiGridConstants.scrollbars.NEVER : uiGridConstants.scrollbars.WHEN_NEEDED,
minRowsToShow: grid_rows_to_display(message)
};
};

$scope.accept_sensors = function () {
uploader_service.save_raw_data($scope.file_id, $scope.selectedCycle).then(function (data) {
$scope.uploader.status_message = 'saving data';
$scope.uploader.progress = 0;
$scope.step.number = 3;

var progress = _.clamp(data.progress, 0, 100);

uploader_service.check_progress_loop(
data.progress_key,
progress,
1 - (progress / 100),
saveSuccess,
saveFailure, // difficult to reach this as failures should be caught in confirmation step
$scope.uploader
);
});
};

$scope.refresh_page = function () {
$state.reload();
$uibModalInstance.dismiss('cancel');
};

}]);
2 changes: 2 additions & 0 deletions seed/static/seed/js/directives/sdUploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ var sdUploaderFineUploader = function (scope, element/*, attrs, filename*/) {
uploader = makeBuildingSyncUpdater(scope, element, ['xml']);
} else if (scope.sourcetype === 'GreenButton') {
uploader = makeFileSystemUploader(scope, element, ['xml']);
} else if (scope.sourcetype === 'SensorMetaData') {
uploader = makeFileSystemUploader(scope, element, ['csv', 'xlsx']);
} else if (scope.sourcetype === 'GeoJSON') {
uploader = makeFileSystemUploader(scope, element, ['json', 'geojson']);
} else {
Expand Down
1 change: 1 addition & 0 deletions seed/static/seed/js/seed.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ angular.module('BE.seed.controllers', [
'BE.seed.controller.export_inventory_modal',
'BE.seed.controller.geocode_modal',
'BE.seed.controller.green_button_upload_modal',
'BE.seed.controller.sensor_upload_modal',
'BE.seed.controller.inventory_cycles',
'BE.seed.controller.inventory_detail',
'BE.seed.controller.inventory_detail_analyses',
Expand Down
9 changes: 9 additions & 0 deletions seed/static/seed/js/services/uploader_service.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,5 +175,14 @@ angular.module('BE.seed.service.uploader', []).factory('uploader_service', [
});
};

uploader_factory.sensors_preview = function (file_id, org_id, view_id) {
return $http.get(
'/api/v3/import_files/' + file_id + '/sensors_preview/',
{ params: { organization_id: org_id, view_id } }
).then(function (response) {
return response.data;
});
};

return uploader_factory;
}]);

0 comments on commit 1408325

Please sign in to comment.