A 任务计划Cron新增实时显示预估执行时间特性

pull/137/head
vapao 2020-06-13 09:44:06 +08:00
parent 1b0b2cfabd
commit 4daa6f80a2
3 changed files with 63 additions and 2 deletions

View File

@ -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),
] ]

View File

@ -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)

View File

@ -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)}/>