Add TimeDelta field

And remove float_time widget.

issue4026
review10891002
This commit is contained in:
Cédric Krier committed 2015-02-09 23:04:01 +01:00
1 parent 1e83cfc41c
commit a8aa4eaa92
6 files changed
+278 -48

No files matched your search

+147 -2
View File
@@ -181,10 +181,155 @@
return date;
};
Sao.common.text_to_float_time = function(text, conversion, digit) {
// TODO
Sao.common.timedelta = {};
Sao.common.timedelta.DEFAULT_CONVERTER = {
's': 1
};
Sao.common.timedelta.DEFAULT_CONVERTER.m =
Sao.common.timedelta.DEFAULT_CONVERTER.s * 60;
Sao.common.timedelta.DEFAULT_CONVERTER.h =
Sao.common.timedelta.DEFAULT_CONVERTER.m * 60;
Sao.common.timedelta.DEFAULT_CONVERTER.d =
Sao.common.timedelta.DEFAULT_CONVERTER.h * 24;
Sao.common.timedelta.DEFAULT_CONVERTER.w =
Sao.common.timedelta.DEFAULT_CONVERTER.d * 7;
Sao.common.timedelta.DEFAULT_CONVERTER.M =
Sao.common.timedelta.DEFAULT_CONVERTER.w * 4;
Sao.common.timedelta.DEFAULT_CONVERTER.Y =
Sao.common.timedelta.DEFAULT_CONVERTER.d * 365;
Sao.common.timedelta._get_separator = function() {
// TODO translate
return {
Y: 'Y',
M: 'M',
w: 'w',
d: 'd',
h: 'h',
m: 'm',
s: 's'
};
};
Sao.common.timedelta.format = function(value, converter) {
if (!value) {
return '';
}
if (!converter) {
converter = Sao.common.timedelta.DEFAULT_CONVERTER;
}
var text = [];
value = value.asSeconds();
var sign = '';
if (value < 0) {
sign = '-';
}
value = Math.abs(value);
converter = Object.keys(converter).map(function(key) {
return [key, converter[key]];
});
converter.sort(function(first, second) {
return second[1] - first[1];
});
var values = [];
var k, v;
for (var i = 0; i < converter.length; i++) {
k = converter[i][0];
v = converter[i][1];
var part = Math.floor(value / v);
value -= part * v;
values.push(part);
}
for (i = 0; i < converter.length - 3; i++) {
k = converter[i][0];
v = values[i];
if (v) {
text.push(v + Sao.common.timedelta._get_separator()[k]);
}
}
if (jQuery(values.slice(-3)).is(function(i, v) { return v; }) ||
jQuery.isEmptyObject(text)) {
var time = values.slice(-3, -1);
time = ('00' + time[0]).slice(-2) + ':' + ('00' + time[1]).slice(-2);
if (values.slice(-1)[0] || value) {
time += ':' + ('00' + values.slice(-1)[0]).slice(-2);
}
text.push(time);
}
text = sign + text.reduce(function(p, c) {
if (p) {
return p + ' ' + c;
} else {
return c;
}
});
if (value) {
if (!jQuery(values.slice(-3)).is(function(i, v) { return v; })) {
// Add space if no time
text += ' ';
}
text += ('' + value.toFixed(6)).slice(1);
}
return text;
};
Sao.common.timedelta.parse = function(text, converter) {
if (!text) {
return null;
}
if (!converter) {
converter = Sao.common.timedelta.DEFAULT_CONVERTER;
}
var separators = Sao.common.timedelta._get_separator();
var separator;
for (var k in separators) {
separator = separators[k];
text = text.replace(separator, separator + ' ');
}
var seconds = 0;
var sec;
var parts = text.split(' ');
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
if (part.contains(':')) {
var subparts = part.split(':');
var subconverter = [
converter.h, converter.m, converter.s];
for (var j = 0;
j < Math.min(subparts.length, subconverter.length);
j ++) {
var t = subparts[j];
var v = subconverter[j];
sec = Math.abs(Number(t)) * v;
if (!isNaN(sec)) {
seconds += sec;
}
}
} else {
var found = false;
for (var key in separators) {
separator =separators[key];
if (part.endsWith(separator)) {
part = part.slice(0, -separator.length);
sec = Math.abs(parseInt(part, 10)) * converter[key];
if (!isNaN(sec)) {
seconds += sec;
}
found = true;
break;
}
}
if (!found) {
sec = Math.abs(Number(part));
if (!isNaN(sec)) {
seconds += sec;
}
}
}
}
if (text.contains('-')) {
seconds *= -1;
}
return Sao.TimeDelta(null, seconds);
};
Sao.common.ModelAccess = Sao.class_(Object, {
init: function() {
+22
View File
@@ -983,6 +983,8 @@
return Sao.field.Date;
case 'time':
return Sao.field.Time;
case 'timedelta':
return Sao.field.TimeDelta;
case 'float':
return Sao.field.Float;
case 'numeric':
@@ -1297,6 +1299,26 @@
}
});
Sao.field.TimeDelta = Sao.class_(Sao.field.Field, {
_default: null,
converter: function(record) {
// TODO allow local context converter
return record.model.session.context[this.description.converter];
},
set_client: function(record, value, force_change) {
if (typeof(value) == 'string') {
value = Sao.common.timedelta.parse(value, this.converter(record));
}
Sao.field.TimeDelta._super.set_client.call(
this, record, value, force_change);
},
get_client: function(record) {
var value = Sao.field.TimeDelta._super.get_client.call(
this, record);
return Sao.common.timedelta.format(value, this.converter(record));
}
});
Sao.field.Float = Sao.class_(Sao.field.Field, {
_default: null,
default_digits: [16, 2],
+8
View File
@@ -123,6 +123,9 @@
value = Sao.Time(value.hour, value.minute,
value.second, value.microsecond / 1000);
break;
case 'timedelta':
value = Sao.TimeDelta(null, value.seconds);
break;
case 'buffer':
// javascript's atob does not understand linefeed
// characters
@@ -195,6 +198,11 @@
'second': value.second(),
'microsecond': value.millisecond() * 1000
};
} else if (value.isTimeDelta) {
value = {
'__class__': 'timedelta',
'seconds': value.asSeconds()
};
} else if (value instanceof Sao.Decimal) {
value = {
'__class__': 'Decimal',
+14
View File
@@ -120,6 +120,20 @@ var Sao = {};
return time;
};
Sao.TimeDelta = function(days, seconds,
milliseconds, minutes, hours, weeks) {
var timedelta = moment.duration({
days: days,
seconds: seconds,
milliseconds: milliseconds,
minutes: minutes,
hours: hours,
weeks: weeks
});
timedelta.isTimeDelta = true;
return timedelta;
};
Sao.config = {};
Sao.config.limit = 1000;
Sao.config.display_size = 20;
+48 -46
View File
@@ -57,6 +57,8 @@
return Sao.View.Tree.DateColumn;
case 'time':
return Sao.View.Tree.TimeColumn;
case 'timedelta':
return Sao.View.Tree.TimeDeltaColumn;
case 'one2many':
return Sao.View.Tree.One2ManyColumn;
case 'many2many':
@@ -68,8 +70,6 @@
case 'float':
case 'numeric':
return Sao.View.Tree.FloatColumn;
case 'float_time':
return Sao.View.Tree.FloatTimeColumn;
case 'integer':
case 'biginteger':
return Sao.View.Tree.IntegerColumn;
@@ -1048,6 +1048,10 @@
}
});
Sao.View.Tree.TimeDeltaColumn = Sao.class_(Sao.View.Tree.CharColumn, {
class_: 'column-timedelta'
});
Sao.View.Tree.One2ManyColumn = Sao.class_(Sao.View.Tree.CharColumn, {
class_: 'column-one2many',
update_text: function(cell, record) {
@@ -1059,19 +1063,6 @@
class_: 'column-many2many'
});
Sao.View.Tree.FloatTimeColumn = Sao.class_(Sao.View.Tree.CharColumn, {
class_: 'column-float_time',
init: function(model, attributes) {
Sao.View.Tree.FloatTimeColumn._super.init.call(this, model,
attributes);
this.conv = null; // TODO
},
update_text: function(cell, record) {
cell.text(Sao.common.text_to_float_time(
this.field.get_client(record), this.conv));
}
});
Sao.View.Tree.ImageColumn = Sao.class_(Sao.View.Tree.CharColumn, {
class_: 'column-image',
get_cell: function() {
@@ -1756,6 +1747,8 @@
return Sao.View.Form.DateTime;
case 'time':
return Sao.View.Form.Time;
case 'timedelta':
return Sao.View.Form.TimeDelta;
case 'integer':
case 'biginteger':
return Sao.View.Form.Integer;
@@ -1764,8 +1757,6 @@
return Sao.View.Form.Float;
case 'selection':
return Sao.View.Form.Selection;
case 'float_time':
return Sao.View.Form.FloatTime;
case 'boolean':
return Sao.View.Form.Boolean;
case 'text':
@@ -2012,6 +2003,37 @@
}
});
Sao.View.Form.TimeDelta = Sao.class_(Sao.View.Form.Widget, {
class_: 'form-timedelta',
init: function(field_name, model, attributes) {
Sao.View.Form.TimeDelta._super.init.call(this, field_name, model,
attributes);
this.el = jQuery('<div/>', {
'class': this.class_
});
this.input = jQuery('<input/>', {
'type': 'text',
'class': 'form-control input-sm'
}).appendTo(this.el);
this.el.change(this.focus_out.bind(this));
},
display: function(record, field) {
Sao.View.Form.TimeDelta._super.display.call(this, record, field);
if (record) {
var value = record.field_get_client(this.field_name);
this.input.val(value || '');
} else {
this.input.val('');
}
},
set_value: function(record, field) {
field.set_client(record, this.input.val());
},
set_readonly: function(readonly) {
this.input.prop('disabled', readonly);
}
});
Sao.View.Form.Integer = Sao.class_(Sao.View.Form.Char, {
class_: 'form-integer',
init: function(field_name, model, attributes) {
@@ -2130,24 +2152,6 @@
}
});
Sao.View.Form.FloatTime = Sao.class_(Sao.View.Form.Char, {
class_: 'form-float-time',
init: function(field_name, model, attributes) {
Sao.View.Form.FloatTime._super.init.call(this, field_name, model,
attributes);
this.conv = null; // TODO
},
display: function(record, field) {
Sao.View.Form.FloatTime._super.display.call(this, record, field);
if (record) {
var value = record.field_get_client(this.field_name);
this.input.val(Sao.common.text_to_float_time(value, this.conv));
} else {
this.input.val('');
}
}
});
Sao.View.Form.Boolean = Sao.class_(Sao.View.Form.Widget, {
class_: 'form-boolean',
init: function(field_name, model, attributes) {
@@ -3368,8 +3372,6 @@
return Sao.View.EditableTree.Float;
case 'selection':
return Sao.View.EditableTree.Selection;
case 'float_time':
return Sao.View.EditableTree.FloatTime;
case 'boolean':
return Sao.View.EditableTree.Boolean;
case 'many2one':
@@ -3420,6 +3422,15 @@
}
});
Sao.View.EditableTree.TimeDelta = Sao.class_(Sao.View.Form.TimeDelta, {
class_: 'editabletree-timedelta',
init: function(field_name, model, attributes) {
Sao.View.EditableTree.TimeDelta._super.init.call(this, field_name,
model, attributes);
Sao.View.EditableTree.editable_mixin(this);
}
});
Sao.View.EditableTree.Integer = Sao.class_(Sao.View.Form.Integer, {
class_: 'editabletree-integer',
init: function(field_name, model, attributes) {
@@ -3447,15 +3458,6 @@
}
});
Sao.View.EditableTree.FloatTime = Sao.class_(Sao.View.Form.FloatTime, {
class_: 'editabletree-float_time',
init: function(field_name, model, attributes) {
Sao.View.EditableTree.FloatTime._super.init.call(this, field_name,
model, attributes);
Sao.View.EditableTree.editable_mixin(this);
}
});
Sao.View.EditableTree.Boolean = Sao.class_(Sao.View.Form.Boolean, {
class_: 'editabletree-boolean',
init: function(field_name, model, attributes) {
+39
View File
@@ -840,6 +840,45 @@
});
});
var timedelta_tests = [
[null, ''],
[Sao.TimeDelta(3, 0, 0, 30, 5), '3d 05:30'],
[Sao.TimeDelta(0, 0, 0, 0, 0, 48), '12M'],
[Sao.TimeDelta(0, 0, 0, 0, 0, 50), '12M 2w'],
[Sao.TimeDelta(365), '1Y'],
[Sao.TimeDelta(0, 10, 0, 5, 2), '02:05:10'],
[Sao.TimeDelta(0, 0, 42, 15), '00:15:00.042000'],
[Sao.TimeDelta(1, 0, 42), '1d .042000'],
[Sao.TimeDelta(0, -1), '-00:00:01'],
[Sao.TimeDelta(-1, 0, 0, -30, -5), '-1d 05:30']
];
QUnit.test('timedelta.format', function() {
timedelta_tests.forEach(function(test) {
var timedelta = test[0];
var text = test[1];
QUnit.equal(Sao.common.timedelta.format(timedelta), text,
'timedelta.format(' + JSON.stringify(timedelta) + ')');
});
});
QUnit.test('timedelta.parse', function() {
function asSeconds(timedelta) {
if (timedelta) {
return timedelta.asSeconds();
} else {
return timedelta;
}
}
timedelta_tests.forEach(function(test) {
var timedelta = test[0];
var text = test[1];
QUnit.equal(asSeconds(Sao.common.timedelta.parse(text)),
asSeconds(timedelta),
'timedelta.format(' + JSON.stringify(timedelta) + ')');
});
});
QUnit.test('DomainParser.group', function() {
var parser = new Sao.common.DomainParser({
'name': {