mirror of https://github.com/openspug/spug
A 任务计划Cron新增实时显示预估执行时间特性
parent
1b0b2cfabd
commit
4daa6f80a2
|
@ -8,4 +8,5 @@ from .views import *
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', Schedule.as_view()),
|
path('', Schedule.as_view()),
|
||||||
path('<int:t_id>/', HistoryView.as_view()),
|
path('<int:t_id>/', HistoryView.as_view()),
|
||||||
|
path('run_time/', next_run_time),
|
||||||
]
|
]
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
# Released under the MIT License.
|
# Released under the MIT License.
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from django_redis import get_redis_connection
|
from django_redis import get_redis_connection
|
||||||
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from apscheduler.triggers.cron import CronTrigger
|
from apscheduler.triggers.cron import CronTrigger
|
||||||
|
from apps.schedule.scheduler import Scheduler
|
||||||
from apps.schedule.models import Task, History
|
from apps.schedule.models import Task, History
|
||||||
from apps.schedule.executors import dispatch
|
from apps.schedule.executors import dispatch
|
||||||
from apps.host.models import Host
|
from apps.host.models import Host
|
||||||
|
@ -135,3 +137,29 @@ class HistoryView(View):
|
||||||
'output': out})
|
'output': out})
|
||||||
data['duration'] = f"{data['duration'] / len(outputs):.3f}"
|
data['duration'] = f"{data['duration'] / len(outputs):.3f}"
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def next_run_time(request):
|
||||||
|
form, error = JsonParser(
|
||||||
|
Argument('rule', help='参数错误'),
|
||||||
|
Argument('start', required=False),
|
||||||
|
Argument('stop', required=False)
|
||||||
|
).parse(request.body)
|
||||||
|
if error is None:
|
||||||
|
try:
|
||||||
|
minute, hour, day, month, week = form.rule.split()
|
||||||
|
week = Scheduler.week_map[week]
|
||||||
|
trigger = CronTrigger(minute=minute, hour=hour, day=day, month=month, day_of_week=week,
|
||||||
|
start_date=form.start, end_date=form.stop)
|
||||||
|
except (ValueError, KeyError):
|
||||||
|
return json_response({'success': False, 'msg': '无效的执行规则'})
|
||||||
|
scheduler = BackgroundScheduler(timezone=settings.TIME_ZONE)
|
||||||
|
scheduler.start()
|
||||||
|
job = scheduler.add_job(lambda: None, trigger)
|
||||||
|
run_time = job.next_run_time
|
||||||
|
scheduler.shutdown()
|
||||||
|
if run_time:
|
||||||
|
return json_response({'success': True, 'msg': run_time.strftime('%Y-%m-%d %H:%M:%S')})
|
||||||
|
else:
|
||||||
|
return json_response({'success': False, 'msg': '无法被触发'})
|
||||||
|
return json_response(error=error)
|
||||||
|
|
|
@ -20,10 +20,13 @@ class ComForm extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.isFirstRender = true;
|
this.isFirstRender = true;
|
||||||
|
this.lastFetchId = 0;
|
||||||
|
this._fetchNextRunTime = lds.debounce(this._fetchNextRunTime, 500);
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: false,
|
loading: false,
|
||||||
type: null,
|
type: null,
|
||||||
page: 0,
|
page: 0,
|
||||||
|
nextRunTime: null,
|
||||||
args: {[store.record['trigger']]: store.record['trigger_args']},
|
args: {[store.record['trigger']]: store.record['trigger_args']},
|
||||||
command: store.record['command'],
|
command: store.record['command'],
|
||||||
}
|
}
|
||||||
|
@ -100,7 +103,35 @@ class ComForm extends React.Component {
|
||||||
handleCronArgs = (key, value) => {
|
handleCronArgs = (key, value) => {
|
||||||
let args = this.state.args['cron'] || {};
|
let args = this.state.args['cron'] || {};
|
||||||
args = Object.assign(args, {[key]: value});
|
args = Object.assign(args, {[key]: value});
|
||||||
this.setState({args: Object.assign(this.state.args, {cron: args})})
|
this.setState({args: Object.assign(this.state.args, {cron: args})}, () => {
|
||||||
|
if (key === 'rule') {
|
||||||
|
value = value.trim();
|
||||||
|
if (value.split(' ').length === 5) {
|
||||||
|
this.setState({nextRunTime: <Icon type="loading"/>});
|
||||||
|
this._fetchNextRunTime()
|
||||||
|
} else {
|
||||||
|
this.setState({nextRunTime: null})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.setState({nextRunTime: <Icon type="loading"/>});
|
||||||
|
this._fetchNextRunTime()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
_fetchNextRunTime = () => {
|
||||||
|
this.lastFetchId += 1;
|
||||||
|
const fetchId = this.lastFetchId;
|
||||||
|
const args = this._parse_args('cron');
|
||||||
|
http.post('/api/schedule/run_time/', JSON.parse(args))
|
||||||
|
.then(({success, msg}) => {
|
||||||
|
if (fetchId !== this.lastFetchId) return;
|
||||||
|
if (success) {
|
||||||
|
this.setState({nextRunTime: <span style={{fontSize: 12, color: '#52c41a'}}>{msg}</span>})
|
||||||
|
} else {
|
||||||
|
this.setState({nextRunTime: <span style={{fontSize: 12, color: '#ff4d4f'}}>{msg}</span>})
|
||||||
|
}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
verifyButtonStatus = () => {
|
verifyButtonStatus = () => {
|
||||||
|
@ -118,7 +149,7 @@ class ComForm extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const info = store.record;
|
const info = store.record;
|
||||||
const {getFieldDecorator} = this.props.form;
|
const {getFieldDecorator} = this.props.form;
|
||||||
const {page, args, loading, showTmp} = this.state;
|
const {page, args, loading, showTmp, nextRunTime} = this.state;
|
||||||
const [b1, b2, b3] = this.verifyButtonStatus();
|
const [b1, b2, b3] = this.verifyButtonStatus();
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
@ -230,6 +261,7 @@ class ComForm extends React.Component {
|
||||||
<Tabs.TabPane tab="UNIX Cron" key="cron">
|
<Tabs.TabPane tab="UNIX Cron" key="cron">
|
||||||
<Form.Item required label="执行规则" help="兼容Cron风格,可参考官方例子">
|
<Form.Item required label="执行规则" help="兼容Cron风格,可参考官方例子">
|
||||||
<Input
|
<Input
|
||||||
|
suffix={nextRunTime || <span/>}
|
||||||
value={lds.get(args, 'cron.rule')}
|
value={lds.get(args, 'cron.rule')}
|
||||||
placeholder="例如每天凌晨1点执行:0 1 * * *"
|
placeholder="例如每天凌晨1点执行:0 1 * * *"
|
||||||
onChange={e => this.handleCronArgs('rule', e.target.value)}/>
|
onChange={e => this.handleCronArgs('rule', e.target.value)}/>
|
||||||
|
|
Loading…
Reference in New Issue