schedule editing

This commit is contained in:
Davide Alberani 2018-01-20 14:56:34 +01:00
parent b58ff6286d
commit 3255ee33c0
4 changed files with 181 additions and 8 deletions

View file

@ -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': {}}
with open(SCHEDULES_FILE, 'r') as fd: try:
return json.loads(fd.read()) with open(SCHEDULES_FILE, 'r') as fd:
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
View file

@ -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
View file

@ -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
View file

@ -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 %}