Task moving functionality working

pull/1031/head
Piero Toffanin 2021-08-04 16:20:51 -04:00
rodzic 9a70c07aca
commit 9bb8b1bed5
11 zmienionych plików z 74 dodań i 38 usunięć

Wyświetl plik

@ -310,9 +310,6 @@ class Task(models.Model):
self.move_assets(self.__original_project_id, self.project.id) self.move_assets(self.__original_project_id, self.project.id)
self.__original_project_id = self.project.id self.__original_project_id = self.project.id
# Autovalidate on save
self.full_clean()
super(Task, self).save(*args, **kwargs) super(Task, self).save(*args, **kwargs)
def assets_path(self, *args): def assets_path(self, *args):

Wyświetl plik

@ -123,7 +123,7 @@ def build_plugins():
# Check for webpack.config.js (if we need to build it) # Check for webpack.config.js (if we need to build it)
if plugin.path_exists("public/webpack.config.js"): if plugin.path_exists("public/webpack.config.js"):
if settings.DEV and webpack_watch_process_count() <= 2: if settings.DEV and webpack_watch_process_count() <= 2 and settings.DEV_WATCH_PLUGINS:
logger.info("Running webpack with watcher for {}".format(plugin.get_name())) logger.info("Running webpack with watcher for {}".format(plugin.get_name()))
subprocess.Popen(['webpack-cli', '--watch'], cwd=plugin.get_path("public")) subprocess.Popen(['webpack-cli', '--watch'], cwd=plugin.get_path("public"))
elif not plugin.path_exists("public/build"): elif not plugin.path_exists("public/build"):

Wyświetl plik

@ -95,6 +95,10 @@ class FormDialog extends React.Component {
hide(){ hide(){
this.setState({showModal: false}); this.setState({showModal: false});
if (this.props.onHide) this.props.onHide(); if (this.props.onHide) this.props.onHide();
if (this.serverRequest){
this.serverRequest.abort();
this.serverRequest = null;
}
} }
handleSave(e){ handleSave(e){
@ -105,13 +109,20 @@ class FormDialog extends React.Component {
let formData = {}; let formData = {};
if (this.props.getFormData) formData = this.props.getFormData(); if (this.props.getFormData) formData = this.props.getFormData();
this.props.saveAction(formData).fail(e => { this.serverRequest = this.props.saveAction(formData);
this.setState({error: e.message || (e.responseJSON || {}).detail || e.responseText || _("Could not apply changes")}); if (this.serverRequest){
}).always(() => { this.serverRequest.fail(e => {
this.setState({error: e.message || (e.responseJSON || {}).detail || e.responseText || _("Could not apply changes")});
}).always(() => {
this.setState({saving: false});
this.serverRequest = null;
}).done(() => {
this.hide();
});
}else{
this.setState({saving: false}); this.setState({saving: false});
}).done(() => {
this.hide(); this.hide();
}); }
} }
handleDelete(){ handleDelete(){

Wyświetl plik

@ -27,7 +27,7 @@ class MoveTaskDialog extends React.Component {
super(props); super(props);
this.state = { this.state = {
projectId: props.task.project.id, projectId: props.task.project,
projects: [], projects: [],
loading: true loading: true
}; };
@ -37,7 +37,7 @@ class MoveTaskDialog extends React.Component {
} }
getFormData(){ getFormData(){
return this.state; return {project: this.state.projectId};
} }
onShow(){ onShow(){
@ -82,20 +82,22 @@ class MoveTaskDialog extends React.Component {
getFormData={this.getFormData} getFormData={this.getFormData}
onShow={this.onShow} onShow={this.onShow}
ref={(domNode) => { this.dialog = domNode; }}> ref={(domNode) => { this.dialog = domNode; }}>
{!this.state.loading ? <div style={{minHeight: '50px'}}>
<div className="form-group"> {!this.state.loading ?
<label className="col-sm-2 control-label">{_("Project")}</label> <div className="form-group">
<div className="col-sm-10"> <label className="col-sm-2 control-label">{_("Project")}</label>
<select className="form-control" <div className="col-sm-10">
value={this.state.projectId} <select className="form-control"
onChange={this.handleProjectChange}> value={this.state.projectId}
{this.state.projects.map(p => onChange={this.handleProjectChange}>
<option value={p.id} key={p.id}>{p.name}</option> {this.state.projects.map(p =>
)} <option value={p.id} key={p.id}>{p.name}</option>
</select> )}
</select>
</div>
</div> </div>
: <i className="fa fa-circle-notch fa-spin fa-fw name-loading"></i>}
</div> </div>
: <i className="fa fa-circle-notch fa-spin fa-fw name-loading"></i>}
</FormDialog> </FormDialog>
); );
} }

Wyświetl plik

@ -85,6 +85,12 @@ class ProjectList extends Paginated {
}); });
} }
handleTaskMoved = (task) => {
if (this["projectListItem_" + task.project]){
this["projectListItem_" + task.project].newTaskAdded();
}
}
render() { render() {
if (this.state.loading){ if (this.state.loading){
return (<div className="project-list text-center"><i className="fa fa-sync fa-spin fa-2x fa-fw"></i></div>); return (<div className="project-list text-center"><i className="fa fa-sync fa-spin fa-2x fa-fw"></i></div>);
@ -95,9 +101,11 @@ class ProjectList extends Paginated {
<ul className={"list-group project-list " + (this.state.refreshing ? "refreshing" : "")}> <ul className={"list-group project-list " + (this.state.refreshing ? "refreshing" : "")}>
{this.state.projects.map(p => ( {this.state.projects.map(p => (
<ProjectListItem <ProjectListItem
ref={(domNode) => { this["projectListItem_" + p.id] = domNode }}
key={p.id} key={p.id}
data={p} data={p}
onDelete={this.handleDelete} onDelete={this.handleDelete}
onTaskMoved={this.handleTaskMoved}
history={this.props.history} /> history={this.props.history} />
))} ))}
</ul> </ul>

Wyświetl plik

@ -20,7 +20,8 @@ class ProjectListItem extends React.Component {
static propTypes = { static propTypes = {
history: PropTypes.object.isRequired, history: PropTypes.object.isRequired,
data: PropTypes.object.isRequired, // project json data: PropTypes.object.isRequired, // project json
onDelete: PropTypes.func onDelete: PropTypes.func,
onTaskMoved: PropTypes.func,
} }
constructor(props){ constructor(props){
@ -305,8 +306,9 @@ class ProjectListItem extends React.Component {
this.refresh(); this.refresh();
} }
taskMoved(){ taskMoved(task){
this.refresh(); this.refresh();
if (this.props.onTaskMoved) this.props.onTaskMoved(task);
} }
handleDelete(){ handleDelete(){
@ -575,7 +577,7 @@ class ProjectListItem extends React.Component {
ref={this.setRef("taskList")} ref={this.setRef("taskList")}
source={`/api/projects/${data.id}/tasks/?ordering=-created_at`} source={`/api/projects/${data.id}/tasks/?ordering=-created_at`}
onDelete={this.taskDeleted} onDelete={this.taskDeleted}
onMove={this.taskMoved} onTaskMoved={this.taskMoved}
hasPermission={this.hasPermission} hasPermission={this.hasPermission}
history={this.props.history} history={this.props.history}
/> : ""} /> : ""}

Wyświetl plik

@ -10,7 +10,7 @@ class TaskList extends React.Component {
history: PropTypes.object.isRequired, history: PropTypes.object.isRequired,
source: PropTypes.string.isRequired, // URL where to load task list source: PropTypes.string.isRequired, // URL where to load task list
onDelete: PropTypes.func, onDelete: PropTypes.func,
onMove: PropTypes.func, onTaskMoved: PropTypes.func,
hasPermission: PropTypes.func.isRequired hasPermission: PropTypes.func.isRequired
} }
@ -71,11 +71,9 @@ class TaskList extends React.Component {
if (this.props.onDelete) this.props.onDelete(id); if (this.props.onDelete) this.props.onDelete(id);
} }
moveTask(id){ moveTask = (task) => {
this.setState({ this.refresh();
tasks: this.state.tasks.filter(t => t.id !== id) if (this.props.onTaskMoved) this.props.onTaskMoved(task);
});
if (this.props.onMove) this.props.onMove(id);
} }
render() { render() {
@ -85,7 +83,7 @@ class TaskList extends React.Component {
}else if (this.state.error){ }else if (this.state.error){
message = (<span>{interpolate(_("Error: %(error)s"), {error: this.state.error})} <a href="javascript:void(0);" onClick={this.retry}>{_("Try again")}</a></span>); message = (<span>{interpolate(_("Error: %(error)s"), {error: this.state.error})} <a href="javascript:void(0);" onClick={this.retry}>{_("Try again")}</a></span>);
}else if (this.state.tasks.length === 0){ }else if (this.state.tasks.length === 0){
message = (<span>{_("This project has no tasks. Create one by uploading some images!")}</span>); message = (<span></span>);
} }
return ( return (

Wyświetl plik

@ -370,6 +370,18 @@ class TaskListItem extends React.Component {
}; };
} }
moveTaskAction = (formData) => {
if (formData.project !== this.state.task.project){
return $.ajax({
url: `/api/projects/${this.state.task.project}/tasks/${this.state.task.id}/`,
contentType: 'application/json',
data: JSON.stringify(formData),
dataType: 'json',
type: 'PATCH'
}).done(this.props.onMove);
}else return false;
}
render() { render() {
const task = this.state.task; const task = this.state.task;
const name = task.name !== null ? task.name : interpolate(_("Task #%(number)s"), { number: task.id }); const name = task.name !== null ? task.name : interpolate(_("Task #%(number)s"), { number: task.id });
@ -626,10 +638,9 @@ class TaskListItem extends React.Component {
} }
// Ability to change options // Ability to change options
const canAddDelPerms = this.props.hasPermission("add") && this.props.hasPermission("delete");
const editable = [statusCodes.FAILED, statusCodes.COMPLETED, statusCodes.CANCELED].indexOf(task.status) !== -1; const editable = [statusCodes.FAILED, statusCodes.COMPLETED, statusCodes.CANCELED].indexOf(task.status) !== -1;
if (canAddDelPerms){ if (this.props.hasPermission("change")){
if (editable || (!task.processing_node)){ if (editable || (!task.processing_node)){
taskActions.push(<li key="edit"><a href="javascript:void(0)" onClick={this.startEditing}><i className="glyphicon glyphicon-pencil"></i>{_("Edit")}</a></li>); taskActions.push(<li key="edit"><a href="javascript:void(0)" onClick={this.startEditing}><i className="glyphicon glyphicon-pencil"></i>{_("Edit")}</a></li>);
} }
@ -661,7 +672,7 @@ class TaskListItem extends React.Component {
task={task} task={task}
ref={(domNode) => { this.moveTaskDialog = domNode; }} ref={(domNode) => { this.moveTaskDialog = domNode; }}
onHide={() => this.setState({showMoveDialog: false})} onHide={() => this.setState({showMoveDialog: false})}
saveAction={() => {}} saveAction={this.moveTaskAction}
/> />
: ""} : ""}
<div className="row"> <div className="row">

Wyświetl plik

@ -32,6 +32,7 @@ services:
- WO_DEBUG - WO_DEBUG
- WO_BROKER - WO_BROKER
- WO_DEV - WO_DEV
- WO_DEV_WATCH_PLUGINS
restart: unless-stopped restart: unless-stopped
oom_score_adj: 0 oom_score_adj: 0
broker: broker:

Wyświetl plik

@ -82,6 +82,10 @@ case $key in
export WO_DEBUG=YES export WO_DEBUG=YES
shift # past argument shift # past argument
;; ;;
--dev-watch-plugins)
export WO_DEV_WATCH_PLUGINS=YES
shift # past argument
;;
--dev) --dev)
export WO_DEBUG=YES export WO_DEBUG=YES
export WO_DEV=YES export WO_DEV=YES
@ -148,6 +152,7 @@ usage(){
echo " --ssl-insecure-port-redirect <port> Insecure port number to redirect from when SSL is enabled (default: $DEFAULT_SSL_INSECURE_PORT_REDIRECT)" echo " --ssl-insecure-port-redirect <port> Insecure port number to redirect from when SSL is enabled (default: $DEFAULT_SSL_INSECURE_PORT_REDIRECT)"
echo " --debug Enable debug for development environments (default: disabled)" echo " --debug Enable debug for development environments (default: disabled)"
echo " --dev Enable development mode. In development mode you can make modifications to WebODM source files and changes will be reflected live. (default: disabled)" echo " --dev Enable development mode. In development mode you can make modifications to WebODM source files and changes will be reflected live. (default: disabled)"
echo " --dev-watch-plugins Automatically build plugins while in dev mode. (default: disabled)"
echo " --broker Set the URL used to connect to the celery broker (default: $DEFAULT_BROKER)" echo " --broker Set the URL used to connect to the celery broker (default: $DEFAULT_BROKER)"
echo " --detached Run WebODM in detached mode. This means WebODM will run in the background, without blocking the terminal (default: disabled)" echo " --detached Run WebODM in detached mode. This means WebODM will run in the background, without blocking the terminal (default: disabled)"
exit exit

Wyświetl plik

@ -52,6 +52,7 @@ WORKER_RUNNING = sys.argv[2:3] == ["worker"]
# SECURITY WARNING: don't run with debug turned on a public facing server! # SECURITY WARNING: don't run with debug turned on a public facing server!
DEBUG = os.environ.get('WO_DEBUG', 'YES') == 'YES' or TESTING DEBUG = os.environ.get('WO_DEBUG', 'YES') == 'YES' or TESTING
DEV = os.environ.get('WO_DEV', 'NO') == 'YES' and not TESTING DEV = os.environ.get('WO_DEV', 'NO') == 'YES' and not TESTING
DEV_WATCH_PLUGINS = DEV and os.environ.get('WO_DEV_WATCH_PLUGINS', 'NO') == 'YES'
SESSION_COOKIE_SECURE = CSRF_COOKIE_SECURE = os.environ.get('WO_SSL', 'NO') == 'YES' SESSION_COOKIE_SECURE = CSRF_COOKIE_SECURE = os.environ.get('WO_SSL', 'NO') == 'YES'
INTERNAL_IPS = ['127.0.0.1'] INTERNAL_IPS = ['127.0.0.1']