introduce run job now
This commit is contained in:
parent
caf2c88ae1
commit
24a6fe7d91
3 changed files with 97 additions and 28 deletions
72
diffido.py
72
diffido.py
|
@ -69,7 +69,15 @@ def read_schedules():
|
|||
return {'schedules': {}}
|
||||
try:
|
||||
with open(SCHEDULES_FILE, 'r') as fd:
|
||||
return json.loads(fd.read())
|
||||
schedules = json.loads(fd.read())
|
||||
for id_ in schedules.get('schedules', {}).keys():
|
||||
schedule = schedules['schedules'][id_]
|
||||
try:
|
||||
schedule['last_history'] = get_last_history(id_)
|
||||
except:
|
||||
schedule['last_history'] = {}
|
||||
continue
|
||||
return schedules
|
||||
except Exception as e:
|
||||
logger.error('unable to read %s: %s' % (SCHEDULES_FILE, e))
|
||||
return {'schedules': {}}
|
||||
|
@ -104,7 +112,7 @@ def next_id(schedules):
|
|||
return str(max([int(i) for i in ids]) + 1)
|
||||
|
||||
|
||||
def get_schedule(id_, add_id=True):
|
||||
def get_schedule(id_, add_id=True, add_history=False):
|
||||
"""Return information about a single schedule
|
||||
|
||||
:param id_: ID of the schedule
|
||||
|
@ -118,6 +126,8 @@ def get_schedule(id_, add_id=True):
|
|||
except Exception:
|
||||
return {}
|
||||
data = schedules.get('schedules', {}).get(id_, {})
|
||||
if add_history and data:
|
||||
data['last_history'] = get_last_history(id_)
|
||||
if add_id:
|
||||
data['id'] = str(id_)
|
||||
return data
|
||||
|
@ -161,6 +171,9 @@ def run_job(id_=None, *args, **kwargs):
|
|||
if not url:
|
||||
return False
|
||||
logger.debug('running job id:%s title:%s url: %s' % (id_, schedule.get('title', ''), url))
|
||||
if not schedule.get('enabled'):
|
||||
logger.info('not running job %s: disabled' % id_)
|
||||
return True
|
||||
req = requests.get(url, allow_redirects=True, timeout=(30.10, 240))
|
||||
content = req.text
|
||||
xpath = schedule.get('xpath')
|
||||
|
@ -219,9 +232,10 @@ def run_job(id_=None, *args, **kwargs):
|
|||
# send notification
|
||||
diff = get_diff(id_).get('diff')
|
||||
if not diff:
|
||||
return
|
||||
return True
|
||||
send_email(to=email, subject='%s page changed' % schedule.get('title'),
|
||||
body='changes:\n\n%s' % diff)
|
||||
return True
|
||||
|
||||
|
||||
def safe_run_job(id_=None, *args, **kwargs):
|
||||
|
@ -263,20 +277,27 @@ def send_email(to, subject='diffido', body='', from_=None):
|
|||
return True
|
||||
|
||||
|
||||
def get_history(id_):
|
||||
def get_history(id_, limit=None, add_info=False):
|
||||
"""Read the history of a schedule
|
||||
|
||||
:param id_: ID of the schedule
|
||||
:type id_: str
|
||||
:param limit: number of entries to fetch
|
||||
:type limit: int
|
||||
:param add_info: add information about the schedule itself
|
||||
:type add_info: int
|
||||
:returns: information about the schedule and its history
|
||||
:rtype: dict"""
|
||||
def _history(id_, queue):
|
||||
def _history(id_, limit, queue):
|
||||
os.chdir('storage/%s' % id_)
|
||||
p = subprocess.Popen([GIT_CMD, 'log', '--pretty=oneline', '--shortstat'], stdout=subprocess.PIPE)
|
||||
cmd = [GIT_CMD, 'log', '--pretty=oneline', '--shortstat']
|
||||
if limit is not None:
|
||||
cmd.append('-%s' % limit)
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||
stdout, _ = p.communicate()
|
||||
queue.put(stdout)
|
||||
queue = multiprocessing.Queue()
|
||||
p = multiprocessing.Process(target=_history, args=(id_, queue))
|
||||
p = multiprocessing.Process(target=_history, args=(id_, limit, queue))
|
||||
p.start()
|
||||
res = queue.get().decode('utf-8')
|
||||
p.join()
|
||||
|
@ -287,13 +308,26 @@ def get_history(id_):
|
|||
info['deletions'] = int(info['deletions'] or 0)
|
||||
info['changes'] = max(info['insertions'], info['deletions'])
|
||||
history.append(info)
|
||||
lastid = None
|
||||
last_id = None
|
||||
if history and 'id' in history[0]:
|
||||
lastid = history[0]['id']
|
||||
last_id = history[0]['id']
|
||||
for idx, item in enumerate(history):
|
||||
item['seq'] = idx + 1
|
||||
schedule = get_schedule(id_)
|
||||
return {'history': history, 'lastid': lastid, 'schedule': schedule}
|
||||
data = {'history': history, 'last_id': last_id}
|
||||
if add_info:
|
||||
data['schedule'] = get_schedule(id_)
|
||||
return data
|
||||
|
||||
|
||||
def get_last_history(id_):
|
||||
"""Read the last history entry of a schedule
|
||||
|
||||
:param id_: ID of the schedule
|
||||
:type id_: str
|
||||
:returns: information about the schedule and its history
|
||||
:rtype: dict"""
|
||||
history = get_history(id_, limit=1)
|
||||
return history.get('history', [{}])[0]
|
||||
|
||||
|
||||
def get_diff(id_, commit_id='HEAD', old_commit_id=None):
|
||||
|
@ -505,7 +539,7 @@ class SchedulesHandler(BaseHandler):
|
|||
def get(self, id_=None, *args, **kwargs):
|
||||
"""Get a schedule."""
|
||||
if id_ is not None:
|
||||
return self.write({'schedule': get_schedule(id_)})
|
||||
return self.write({'schedule': get_schedule(id_, add_history=True)})
|
||||
schedules = read_schedules()
|
||||
self.write(schedules)
|
||||
|
||||
|
@ -547,6 +581,15 @@ class SchedulesHandler(BaseHandler):
|
|||
self.build_success(message='removed schedule %s' % id_)
|
||||
|
||||
|
||||
class RunScheduleHandler(BaseHandler):
|
||||
"""Reset schedules handler."""
|
||||
@gen.coroutine
|
||||
def post(self, id_, *args, **kwargs):
|
||||
if run_job(id_):
|
||||
return self.build_success('job run')
|
||||
self.build_error('job not run')
|
||||
|
||||
|
||||
class ResetSchedulesHandler(BaseHandler):
|
||||
"""Reset schedules handler."""
|
||||
@gen.coroutine
|
||||
|
@ -558,7 +601,7 @@ class HistoryHandler(BaseHandler):
|
|||
"""History handler."""
|
||||
@gen.coroutine
|
||||
def get(self, id_, *args, **kwargs):
|
||||
self.write(get_history(id_))
|
||||
self.write(get_history(id_, add_info=True))
|
||||
|
||||
|
||||
class DiffHandler(BaseHandler):
|
||||
|
@ -614,12 +657,15 @@ def serve():
|
|||
scheduler=scheduler)
|
||||
|
||||
_reset_schedules_path = r'schedules/reset'
|
||||
_schedule_run_path = r'schedules/(?P<id_>\d+)/run'
|
||||
_schedules_path = r'schedules/?(?P<id_>\d+)?'
|
||||
_history_path = r'history/?(?P<id_>\d+)'
|
||||
_diff_path = r'diff/(?P<id_>\d+)/(?P<commit_id>[0-9a-f]+)/?(?P<old_commit_id>[0-9a-f]+)?/?'
|
||||
application = tornado.web.Application([
|
||||
(r'/api/%s' % _reset_schedules_path, ResetSchedulesHandler, init_params),
|
||||
(r'/api/v%s/%s' % (API_VERSION, _reset_schedules_path), ResetSchedulesHandler, init_params),
|
||||
(r'/api/%s' % _schedule_run_path, RunScheduleHandler, init_params),
|
||||
(r'/api/v%s/%s' % (API_VERSION, _schedule_run_path), RunScheduleHandler, init_params),
|
||||
(r'/api/%s' % _schedules_path, SchedulesHandler, init_params),
|
||||
(r'/api/v%s/%s' % (API_VERSION, _schedules_path), SchedulesHandler, init_params),
|
||||
(r'/api/%s' % _history_path, HistoryHandler, init_params),
|
||||
|
|
13
dist/history.html
vendored
13
dist/history.html
vendored
|
@ -21,8 +21,8 @@
|
|||
</md-table-toolbar>
|
||||
<md-table-row slot="md-table-row" slot-scope="{item}">
|
||||
<md-table-cell>
|
||||
(<a v-if="item.seq > 1" :href="'/diff.html?id=' + id + '&oldid=' + item.id + '&diff=' + lastid">cur</a><span v-if="item.seq == 1">cur</span> | <a :href="'/diff.html?id=' + id + '&diff=' + item.id">prev</a>)
|
||||
<md-radio name="oldid" v-model="oldid" :value="item.id" v-if="item.seq > 1" :seq="item.seq"></md-radio><span id="placeholder" v-if="item.seq == 1"> ---- </span>
|
||||
(<a v-if="item.seq > 1" :href="'/diff.html?id=' + id + '&oldid=' + item.id + '&diff=' + last_id">cur</a><span v-if="item.seq == 1">cur</span> | <a :href="'/diff.html?id=' + id + '&diff=' + item.id">prev</a>)
|
||||
<md-radio name="oldid" v-model="oldid" :value="item.id" v-if="item.seq > 1" :seq="item.seq"></md-radio><span class="placeholder" v-if="item.seq == 1"></span>
|
||||
<md-radio name="diff" v-model="diff" :value="item.id" :seq="item.seq"></md-radio>
|
||||
</md-table-cell>
|
||||
<md-table-cell md-label="commit ID" md-sort-by="id">${ item.id }</md-table-cell>
|
||||
|
@ -50,7 +50,7 @@ var app = new Vue({
|
|||
filtered_history: [],
|
||||
oldid: null,
|
||||
diff: null,
|
||||
lasstid: null,
|
||||
last_id: null,
|
||||
{% if isinstance(id, str) %}
|
||||
id: "{{id}}",
|
||||
{% else %}
|
||||
|
@ -67,7 +67,7 @@ var app = new Vue({
|
|||
self.history = response.data.history;
|
||||
self.updateFilter();
|
||||
self.schedule = response.data.schedule;
|
||||
self.lastid = response.data.lastid;
|
||||
self.last_id = response.data.last_id;
|
||||
});
|
||||
},
|
||||
updateFilter: function() {
|
||||
|
@ -96,5 +96,10 @@ body {
|
|||
height: 80%;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
width: 36px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
</style>
|
||||
{% end %}
|
||||
|
|
40
dist/index.html
vendored
40
dist/index.html
vendored
|
@ -21,14 +21,24 @@
|
|||
<md-table id="schedules-table" v-model="schedules">
|
||||
<md-table-row slot="md-table-row" slot-scope="{item}">
|
||||
<md-table-cell md-label="#" md-sort-by="id" md-numeric><a :href="'/schedule.html?id=' + item.id">${ item.id }</a></md-table-cell>
|
||||
<md-table-cell md-label="enabled" md-sort-by="enabled">
|
||||
<md-icon v-if="item.enabled">check_box</md-icon>
|
||||
<md-icon v-if="!item.enabled">check_box_outline_blank</md-icon>
|
||||
</md-table-cell>
|
||||
<md-table-cell md-label="title" md-sort-by="title"><a :href="'/schedule.html?id=' + item.id">${ item.title }</a></md-table-cell>
|
||||
<md-table-cell md-label="url" md-sort-by="email"><a :href="item.url" target="_new">${ item.url }</a></md-table-cell>
|
||||
<md-table-cell md-label="url" md-sort-by="url"><a :href="item.url" target="_new">${ item.url }</a></md-table-cell>
|
||||
<md-table-cell md-label="trigger" md-sort-by="trigger">${ triggerString(item) }</md-table-cell>
|
||||
<md-table-cell md-label="history" md-sort-by="email">
|
||||
<md-table-cell md-label="last check" md-sort-by="last_history">${ item.last_history && item.last_history.message }</md-table-cell>
|
||||
<md-table-cell md-label="history">
|
||||
<md-button :href="'/history.html?id=' + item.id" class="md-icon-button md-primary">
|
||||
<md-icon>history</md-icon>
|
||||
</md-button>
|
||||
</md-table-cell>
|
||||
<md-table-cell md-label="run now">
|
||||
<md-button class="md-icon-button md-primary" @click="runSchedule(item.id)">
|
||||
<md-icon>play_circle_outline</md-icon>
|
||||
</md-button>
|
||||
</md-table-cell>
|
||||
</md-table-row>
|
||||
</md-table>
|
||||
</md-card-content>
|
||||
|
@ -50,11 +60,9 @@ var app = new Vue({
|
|||
mounted: function() {
|
||||
this.getSchedules();
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
methods: {
|
||||
getSchedules: function() {
|
||||
self = this;
|
||||
var self = this;
|
||||
var data = axios.get('/api/schedules').then(function(response) {
|
||||
var schedules = [];
|
||||
_.forEach(response.data.schedules || {}, function(value, key) {
|
||||
|
@ -69,18 +77,28 @@ var app = new Vue({
|
|||
return 'cron: ' + item.cron_crontab;
|
||||
}
|
||||
if (item.trigger == 'interval') {
|
||||
trigger = 'interval: ';
|
||||
var text = 'interval: ';
|
||||
var pieces = [];
|
||||
_.each(['weeks', 'days', 'hours', 'minutes', 'seconds'], function(value, key) {
|
||||
if ('interval_' + value) {
|
||||
if (trigger) {
|
||||
trigger = trigger + ' ';
|
||||
var int_val = item['interval_' + value];
|
||||
if (int_val && parseInt(int_val)) {
|
||||
var unit = value;
|
||||
if (int_val == 1) {
|
||||
unit = unit.slice(0, -1);
|
||||
}
|
||||
trigger = trigger + ''
|
||||
pieces.push('' + int_val + ' ' + unit);
|
||||
}
|
||||
});
|
||||
return trigger;
|
||||
text = text + _.join(pieces, ', ');
|
||||
return text;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
runSchedule: function(id) {
|
||||
var self = this;
|
||||
var data = axios.post('/api/schedules/' + id + '/run').then(function(response) {
|
||||
setTimeout(self.getSchedules, 2500);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue