schedule editing
This commit is contained in:
parent
b58ff6286d
commit
3255ee33c0
4 changed files with 181 additions and 8 deletions
63
diffido.py
63
diffido.py
|
@ -87,23 +87,84 @@ class BaseHandler(tornado.web.RequestHandler):
|
||||||
self.set_status(status)
|
self.set_status(status)
|
||||||
self.write({'error': True, 'message': message})
|
self.write({'error': True, 'message': message})
|
||||||
|
|
||||||
|
def build_success(self, message='', status=200):
|
||||||
|
"""Build and write a success message.
|
||||||
|
|
||||||
|
:param message: textual message
|
||||||
|
:type message: str
|
||||||
|
:param status: HTTP status code
|
||||||
|
:type status: int
|
||||||
|
"""
|
||||||
|
self.set_status(status)
|
||||||
|
self.write({'error': False, 'message': message})
|
||||||
|
|
||||||
|
|
||||||
class SchedulesHandler(BaseHandler):
|
class SchedulesHandler(BaseHandler):
|
||||||
def read_schedules(self):
|
def read_schedules(self):
|
||||||
if not os.path.isfile(SCHEDULES_FILE):
|
if not os.path.isfile(SCHEDULES_FILE):
|
||||||
return {}
|
return {'schedules': {}}
|
||||||
|
try:
|
||||||
with open(SCHEDULES_FILE, 'r') as fd:
|
with open(SCHEDULES_FILE, 'r') as fd:
|
||||||
return json.loads(fd.read())
|
return json.loads(fd.read())
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error('unable to read %s: %s' % (SCHEDULES_FILE, e))
|
||||||
|
return {'schedules': {}}
|
||||||
|
|
||||||
def write_schedules(self, schedules):
|
def write_schedules(self, schedules):
|
||||||
with open(SCHEDULES_FILE, 'w') as fd:
|
with open(SCHEDULES_FILE, 'w') as fd:
|
||||||
fd.write(json.dumps(schedules, indent=2))
|
fd.write(json.dumps(schedules, indent=2))
|
||||||
|
|
||||||
|
def next_id(self, schedules):
|
||||||
|
ids = schedules.get('schedules', {}).keys()
|
||||||
|
if not ids:
|
||||||
|
return 1
|
||||||
|
return max([int(i) for i in ids]) + 1
|
||||||
|
|
||||||
|
def _get_schedule(self, id_):
|
||||||
|
schedules = self.read_schedules()
|
||||||
|
data = schedules.get('schedules', {}).get(id_, {})
|
||||||
|
data['id'] = str(id_)
|
||||||
|
return data
|
||||||
|
|
||||||
@gen.coroutine
|
@gen.coroutine
|
||||||
def get(self, id_=None, *args, **kwargs):
|
def get(self, id_=None, *args, **kwargs):
|
||||||
|
if id_ is not None:
|
||||||
|
self.write({'schedule': self._get_schedule(id_)})
|
||||||
|
return
|
||||||
schedules = self.read_schedules()
|
schedules = self.read_schedules()
|
||||||
self.write(schedules)
|
self.write(schedules)
|
||||||
|
|
||||||
|
@gen.coroutine
|
||||||
|
def put(self, id_=None, *args, **kwargs):
|
||||||
|
if id_ is None:
|
||||||
|
return self.build_error(message='update action requires an ID')
|
||||||
|
data = self.clean_body
|
||||||
|
schedules = self.read_schedules()
|
||||||
|
if id_ not in schedules.get('schedules', {}):
|
||||||
|
return self.build_error(message='schedule %s not found' % id_)
|
||||||
|
schedules['schedules'][id_] = data
|
||||||
|
self.write_schedules(schedules)
|
||||||
|
self.write(self._get_schedule(id_=id_))
|
||||||
|
|
||||||
|
@gen.coroutine
|
||||||
|
def post(self, *args, **kwargs):
|
||||||
|
data = self.clean_body
|
||||||
|
schedules = self.read_schedules()
|
||||||
|
id_ = str(self.next_id(schedules))
|
||||||
|
schedules['schedules'][id_] = data
|
||||||
|
self.write_schedules(schedules)
|
||||||
|
self.write(self._get_schedule(id_=id_))
|
||||||
|
|
||||||
|
@gen.coroutine
|
||||||
|
def delete(self, id_=None, *args, **kwargs):
|
||||||
|
if id_ is None:
|
||||||
|
return self.build_error(message='an ID must be specified')
|
||||||
|
schedules = self.read_schedules()
|
||||||
|
if id_ in schedules.get('schedules', {}):
|
||||||
|
del schedules['schedules'][id_]
|
||||||
|
self.write_schedules(schedules)
|
||||||
|
self.build_success(message='removed schedule %s' % id_)
|
||||||
|
|
||||||
|
|
||||||
class TemplateHandler(BaseHandler):
|
class TemplateHandler(BaseHandler):
|
||||||
"""Handler for the / path."""
|
"""Handler for the / path."""
|
||||||
|
|
1
dist/base.html
vendored
1
dist/base.html
vendored
|
@ -6,6 +6,7 @@
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic|Material+Icons">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic|Material+Icons">
|
||||||
<link rel="stylesheet" href="/static/css/vue-material.min.css">
|
<link rel="stylesheet" href="/static/css/vue-material.min.css">
|
||||||
<link rel="stylesheet" href="/static/css/themes/default.css">
|
<link rel="stylesheet" href="/static/css/themes/default.css">
|
||||||
|
<script src="/static/js/lodash.min.js"></script>
|
||||||
<script src="/static/js/vue.js"></script>
|
<script src="/static/js/vue.js"></script>
|
||||||
<script src="/static/js/vue-material.min.js"></script>
|
<script src="/static/js/vue-material.min.js"></script>
|
||||||
<script src="/static/js/axios.min.js"></script>
|
<script src="/static/js/axios.min.js"></script>
|
||||||
|
|
21
dist/index.html
vendored
21
dist/index.html
vendored
|
@ -10,7 +10,7 @@
|
||||||
</md-table-toolbar>
|
</md-table-toolbar>
|
||||||
<md-table-row slot="md-table-row" slot-scope="{item}">
|
<md-table-row slot="md-table-row" slot-scope="{item}">
|
||||||
<md-table-cell md-label="#" md-sort-by="id" md-numeric>${ item.id }</md-table-cell>
|
<md-table-cell md-label="#" md-sort-by="id" md-numeric>${ item.id }</md-table-cell>
|
||||||
<md-table-cell md-label="title" md-sort-by="title">${ item.title }</md-table-cell>
|
<md-table-cell md-label="title" md-sort-by="title"><a :href="settingsUrl(item)">${ item.title }</a></md-table-cell>
|
||||||
<md-table-cell md-label="url" md-sort-by="email">${ item.url }</md-table-cell>
|
<md-table-cell md-label="url" md-sort-by="email">${ item.url }</md-table-cell>
|
||||||
</md-table-row>
|
</md-table-row>
|
||||||
</md-table>
|
</md-table>
|
||||||
|
@ -31,12 +31,25 @@ var app = new Vue({
|
||||||
mounted: function() {
|
mounted: function() {
|
||||||
this.getSchedules();
|
this.getSchedules();
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
settingsUrl: function(item) {
|
||||||
|
if (!(item && item.id !== undefined)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return '/schedule.html?id=' + item.id;
|
||||||
|
},
|
||||||
getSchedules: function() {
|
getSchedules: function() {
|
||||||
self = this;
|
self = this;
|
||||||
var data = axios.get('/api/schedules').then(function(response) {
|
var data = axios.get('/api/schedules').then(function(response) {
|
||||||
console.log(response);
|
console.log(response);
|
||||||
self.schedules = response.data.schedules || [];
|
var schedules = [];
|
||||||
|
_.forEach(response.data.schedules || {}, function(value, key) {
|
||||||
|
value.id = key;
|
||||||
|
schedules.push(value);
|
||||||
|
});
|
||||||
|
self.schedules = schedules;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,8 +64,8 @@ body {
|
||||||
|
|
||||||
.md-table {
|
.md-table {
|
||||||
width: 60%;
|
width: 60%;
|
||||||
min-height: 200px;
|
min-height: 400px;
|
||||||
max-height: 600px;
|
max-height: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
100
dist/schedule.html
vendored
100
dist/schedule.html
vendored
|
@ -1,5 +1,103 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
setting page {{ id }}
|
<div id="app">
|
||||||
|
<div class="md-layout">
|
||||||
|
<div class="md-layout-item" md-card>
|
||||||
|
<md-field>
|
||||||
|
<label>title</label>
|
||||||
|
<md-input v-model="schedule.title"></md-input>
|
||||||
|
</md-field>
|
||||||
|
<md-field>
|
||||||
|
<label>url</label>
|
||||||
|
<md-input v-model="schedule.url"></md-input>
|
||||||
|
</md-field>
|
||||||
|
<md-field>
|
||||||
|
<label>notify email</label>
|
||||||
|
<md-input v-model="schedule.email"></md-input>
|
||||||
|
</md-field>
|
||||||
|
<md-field>
|
||||||
|
<label>XPath selector</label>
|
||||||
|
<md-input v-model="schedule.xpath"></md-input>
|
||||||
|
</md-field>
|
||||||
|
<md-field>
|
||||||
|
<label>minimum change</label>
|
||||||
|
<md-input v-model="schedule.minimum_change"></md-input>
|
||||||
|
</md-field>
|
||||||
|
<md-switch v-model="schedule.enabled">enabled</md-switch>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<md-button class="md-raised md-primary" @click="saveSchedule()">save</md-button>
|
||||||
|
<md-button class="md-accent" :disabled="!hasID()" @click="deleteSchedule()">delete</md-button>
|
||||||
|
<md-button href="/">cancel</md-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
Vue.use(VueMaterial.default);
|
||||||
|
|
||||||
|
var app = new Vue({
|
||||||
|
el: '#app',
|
||||||
|
delimiters: ['${', '}'],
|
||||||
|
data: {
|
||||||
|
{% if isinstance(id, str) %}
|
||||||
|
id: "{{id}}",
|
||||||
|
{% else %}
|
||||||
|
id: null,
|
||||||
|
{% end %}
|
||||||
|
schedule: {}
|
||||||
|
},
|
||||||
|
mounted: function() {
|
||||||
|
this.getSchedule(this.id);
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
hasID: function() {
|
||||||
|
return !(this.id === undefined || this.id === null || this.id === '');
|
||||||
|
},
|
||||||
|
getSchedule: function() {
|
||||||
|
if (!this.hasID()) {
|
||||||
|
this.schedule = {enabled: true};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self = this;
|
||||||
|
var data = axios.get('/api/schedules/' + this.id).then(function(response) {
|
||||||
|
self.schedule = response.data.schedule || {};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
saveSchedule: function() {
|
||||||
|
var data = this.schedule;
|
||||||
|
if (this.hasID()) {
|
||||||
|
axios.put('/api/schedules/' + this.id, data).then(function(response) {
|
||||||
|
console.log(response);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
axios.post('/api/schedules', data).then(function(response) {
|
||||||
|
console.log(response);
|
||||||
|
window.location = '/schedule.html?id=' + response.data.id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteSchedule: function() {
|
||||||
|
if (!this.hasID()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
axios.delete('/api/schedules/' + this.id).then(function(response) {
|
||||||
|
window.location = '/';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
Loading…
Reference in a new issue