Add compact feature

pull/1628/head
Piero Toffanin 2025-03-11 15:11:48 -04:00
rodzic bf1118fbb3
commit 78a2c1ca18
7 zmienionych plików z 59 dodań i 10 usunięć

Wyświetl plik

@ -153,6 +153,10 @@ class TaskViewSet(viewsets.ViewSet):
def remove(self, *args, **kwargs): def remove(self, *args, **kwargs):
return self.set_pending_action(pending_actions.REMOVE, *args, perms=('delete_project', ), **kwargs) return self.set_pending_action(pending_actions.REMOVE, *args, perms=('delete_project', ), **kwargs)
@action(detail=True, methods=['post'])
def compact(self, *args, **kwargs):
return self.set_pending_action(pending_actions.COMPACT, *args, perms=('delete_project', ), **kwargs)
@action(detail=True, methods=['get']) @action(detail=True, methods=['get'])
def output(self, request, pk=None, project_pk=None): def output(self, request, pk=None, project_pk=None):
""" """

Wyświetl plik

@ -235,6 +235,7 @@ class Task(models.Model):
(pending_actions.RESTART, 'RESTART'), (pending_actions.RESTART, 'RESTART'),
(pending_actions.RESIZE, 'RESIZE'), (pending_actions.RESIZE, 'RESIZE'),
(pending_actions.IMPORT, 'IMPORT'), (pending_actions.IMPORT, 'IMPORT'),
(pending_actions.COMPACT, 'COMPACT'),
) )
TASK_PROGRESS_LAST_VALUE = 0.85 TASK_PROGRESS_LAST_VALUE = 0.85
@ -808,6 +809,14 @@ class Task(models.Model):
# Stop right here! # Stop right here!
return return
elif self.pending_action == pending_actions.COMPACT:
logger.info("Compacting {}".format(self))
time.sleep(2) # Purely to make sure the user sees the "compacting..." message in the UI since this is so fast
self.compact()
self.pending_action = None
self.save()
return
if self.processing_node: if self.processing_node:
# Need to update status (first time, queued or running?) # Need to update status (first time, queued or running?)
if self.uuid and self.status in [None, status_codes.QUEUED, status_codes.RUNNING]: if self.uuid and self.status in [None, status_codes.QUEUED, status_codes.RUNNING]:
@ -1142,6 +1151,18 @@ class Task(models.Model):
plugin_signals.task_removed.send_robust(sender=self.__class__, task_id=task_id) plugin_signals.task_removed.send_robust(sender=self.__class__, task_id=task_id)
def compact(self):
# Remove all images
images_path = self.task_path()
images = [os.path.join(images_path, i) for i in self.scan_images()]
for im in images:
try:
os.unlink(im)
except Exception as e:
logger.warning(e)
self.update_size(commit=True)
def set_failure(self, error_message): def set_failure(self, error_message):
logger.error("FAILURE FOR {}: {}".format(self, error_message)) logger.error("FAILURE FOR {}: {}".format(self, error_message))
self.last_error = error_message self.last_error = error_message
@ -1157,7 +1178,8 @@ class Task(models.Model):
def check_if_canceled(self): def check_if_canceled(self):
# Check if task has been canceled/removed # Check if task has been canceled/removed
if Task.objects.only("pending_action").get(pk=self.id).pending_action in [pending_actions.CANCEL, if Task.objects.only("pending_action").get(pk=self.id).pending_action in [pending_actions.CANCEL,
pending_actions.REMOVE]: pending_actions.REMOVE,
pending_actions.COMPACT]:
raise TaskInterruptedException() raise TaskInterruptedException()
def resize_images(self): def resize_images(self):

Wyświetl plik

@ -3,3 +3,4 @@ REMOVE = 2
RESTART = 3 RESTART = 3
RESIZE = 4 RESIZE = 4
IMPORT = 5 IMPORT = 5
COMPACT = 6

Wyświetl plik

@ -4,7 +4,8 @@ const CANCEL = 1,
REMOVE = 2, REMOVE = 2,
RESTART = 3, RESTART = 3,
RESIZE = 4, RESIZE = 4,
IMPORT = 5; IMPORT = 5,
COMPACT = 6;
let pendingActions = { let pendingActions = {
[CANCEL]: { [CANCEL]: {
@ -21,6 +22,9 @@ let pendingActions = {
}, },
[IMPORT]: { [IMPORT]: {
descr: _("Importing...") descr: _("Importing...")
},
[COMPACT]: {
descr: _("Compacting...")
} }
}; };
@ -30,6 +34,7 @@ export default {
RESTART: RESTART, RESTART: RESTART,
RESIZE: RESIZE, RESIZE: RESIZE,
IMPORT: IMPORT, IMPORT: IMPORT,
COMPACT: COMPACT,
description: function(pendingAction) { description: function(pendingAction) {
if (pendingActions[pendingAction]) return pendingActions[pendingAction].descr; if (pendingActions[pendingAction]) return pendingActions[pendingAction].descr;

Wyświetl plik

@ -239,7 +239,7 @@ class NewTaskPanel extends React.Component {
ref={(domNode) => { if (domNode) this.taskForm = domNode; }} ref={(domNode) => { if (domNode) this.taskForm = domNode; }}
/> />
{this.state.editTaskFormLoaded && this.props.showAlign && this.state.showMapPreview ? {this.state.editTaskFormLoaded && this.props.showAlign && this.state.showMapPreview && this.state.alignTasks.length > 0 ?
<div> <div>
<div className="form-group"> <div className="form-group">
<label className="col-sm-2 control-label">{_("Alignment")}</label> <label className="col-sm-2 control-label">{_("Alignment")}</label>

Wyświetl plik

@ -462,9 +462,11 @@ class TaskListItem extends React.Component {
const disabled = this.state.actionButtonsDisabled || const disabled = this.state.actionButtonsDisabled ||
([pendingActions.CANCEL, ([pendingActions.CANCEL,
pendingActions.REMOVE, pendingActions.REMOVE,
pendingActions.COMPACT,
pendingActions.RESTART].indexOf(task.pending_action) !== -1); pendingActions.RESTART].indexOf(task.pending_action) !== -1);
const editable = this.props.hasPermission("change") && [statusCodes.FAILED, statusCodes.COMPLETED, statusCodes.CANCELED].indexOf(task.status) !== -1; const editable = this.props.hasPermission("change") && [statusCodes.FAILED, statusCodes.COMPLETED, statusCodes.CANCELED].indexOf(task.status) !== -1;
const actionLoading = this.state.actionLoading; const actionLoading = this.state.actionLoading;
const showAssetButtons = task.status === statusCodes.COMPLETED;
let expanded = ""; let expanded = "";
if (this.state.expanded){ if (this.state.expanded){
@ -484,7 +486,7 @@ class TaskListItem extends React.Component {
}); });
}; };
if (task.status === statusCodes.COMPLETED){ if (showAssetButtons){
if (task.available_assets.indexOf("orthophoto.tif") !== -1 || task.available_assets.indexOf("dsm.tif") !== -1){ if (task.available_assets.indexOf("orthophoto.tif") !== -1 || task.available_assets.indexOf("dsm.tif") !== -1){
addActionButton(" " + _("View Map"), "btn-primary", "fa fa-globe", () => { addActionButton(" " + _("View Map"), "btn-primary", "fa fa-globe", () => {
location.href = `/map/project/${task.project}/task/${task.id}/`; location.href = `/map/project/${task.project}/task/${task.id}/`;
@ -534,7 +536,7 @@ class TaskListItem extends React.Component {
} }
actionButtons = (<div className="action-buttons"> actionButtons = (<div className="action-buttons">
{task.status === statusCodes.COMPLETED ? {showAssetButtons ?
<AssetDownloadButtons task={this.state.task} disabled={disabled} /> <AssetDownloadButtons task={this.state.task} disabled={disabled} />
: ""} : ""}
{actionButtons.map(button => { {actionButtons.map(button => {
@ -672,7 +674,6 @@ class TaskListItem extends React.Component {
</div> </div>
</div> </div>
<div className="row clearfix"> <div className="row clearfix">
<ErrorMessage bind={[this, 'actionError']} />
{actionButtons} {actionButtons}
</div> </div>
<TaskPluginActionButtons task={task} disabled={disabled} /> <TaskPluginActionButtons task={task} disabled={disabled} />
@ -734,6 +735,10 @@ class TaskListItem extends React.Component {
type = 'neutral'; type = 'neutral';
} }
if (task.pending_action === pendingActions.COMPACT){
statusIcon = 'fa fa-cog fa-spin fa-fw';
}
statusLabel = getStatusLabel(status, type, progress); statusLabel = getStatusLabel(status, type, progress);
} }
@ -764,9 +769,16 @@ class TaskListItem extends React.Component {
if (this.props.hasPermission("delete")){ if (this.props.hasPermission("delete")){
taskActions.push( taskActions.push(
<li key="sep" role="separator" className="divider"></li>, <li key="sep" role="separator" className="divider"></li>
); );
if (task.status === statusCodes.COMPLETED){
addTaskAction(_("Compact"), "fa fa-database", this.genActionApiCall("compact", {
confirm: _("Compacting will free disk space by permanently deleting the original images used for processing. It will no longer be possible to restart the task. Maps and models will remain in place. Continue?"),
defaultError: _("Cannot compact task.")
}));
}
addTaskAction(_("Delete"), "fa fa-trash", this.genActionApiCall("remove", { addTaskAction(_("Delete"), "fa fa-trash", this.genActionApiCall("remove", {
confirm: _("All information related to this task, including images, maps and models will be deleted. Continue?"), confirm: _("All information related to this task, including images, maps and models will be deleted. Continue?"),
defaultError: _("Cannot delete task.") defaultError: _("Cannot delete task.")
@ -816,6 +828,7 @@ class TaskListItem extends React.Component {
: ""} : ""}
</div> </div>
</div> </div>
<ErrorMessage bind={[this, 'actionError']} />
{expanded} {expanded}
</div> </div>
); );

Wyświetl plik

@ -71,7 +71,11 @@ class ProcessingNode(models.Model):
self.api_version = info.version self.api_version = info.version
self.queue_count = info.task_queue_count self.queue_count = info.task_queue_count
self.max_images = info.max_images
if isinstance(info.max_images, (int, float)):
self.max_images = max(0, info.max_images)
else:
self.max_images = None
self.engine_version = info.engine_version self.engine_version = info.engine_version
self.engine = info.engine self.engine = info.engine