Commit fcc0bc74 authored by Rafael Monnerat's avatar Rafael Monnerat

Update from upstream/master

parents 049fd1cb 29e138eb
...@@ -25,76 +25,17 @@ ...@@ -25,76 +25,17 @@
# #
############################################################################## ##############################################################################
import zipfile, cStringIO, re import re
import xmlrpclib, base64
from Products.CMFCore.utils import getToolByName
def extractContent(data):
"""
extract text content from ODF data
directly by unzipping (no need for oood here)
"""
# XXX probably not used - to really get text content it should
# strip xml too
cs = cStringIO.StringIO()
cs.write(data)
try:
z = zipfile.ZipFile(cs)
except zipfile.BadZipfile:
cs.close()
return ''
s = z.read('content.xml')
cs.close()
z.close()
return s
###### XXX these methods repeat what is in OOoDocument class
# maybe redundant, but we need to access them from Script (Python)
def convertToOdf(self, name, data):
"""
convert data into ODF format
to be used in ingestion when we don't yet have an ERP5 object
to work with (and we for example have to figure out portal_type)
"""
sp = mkProxy(self)
kw = sp.run_convert(name,base64.encodestring(data))
odf = base64.decodestring(kw['data'])
return odf
def mkProxy(self):
pref = getToolByName(self,'portal_preferences')
adr = pref.getPreferredDmsOoodocServerAddress()
nr = pref.getPreferredDmsOoodocServerPortNumber()
if adr is None or nr is None:
raise Exception('you should set conversion server coordinates in preferences')
sp = xmlrpclib.ServerProxy('http://%s:%d' % (adr,nr), allow_none=True)
return sp
def generateFile(self, name, data, format): # pylint: disable=redefined-builtin
sp = mkProxy(self)
kw = sp.run_generate(name, data, None, format)
res = base64.decodestring(kw['data'])
return res
def getAttrFromFilename(self, fname):
"""
parse file name using regexp specified in preferences
"""
rx_parse = re.compile(self.portal_preferences.getPreferredDmsFilenameRegexp())
m = rx_parse.match(fname)
if m is None:
return {}
return m.groupdict()
def getLastWorkflowDate(self, state_name='simulation_state', state=('released','public')): def getLastWorkflowDate(self, state_name='simulation_state', state=('released','public')):
'''we can make something more generic out of it '''we can make something more generic out of it
or JP says "there is an API for it" and we trash this one''' or JP says "there is an API for it" and we trash this one'''
if not hasattr(self, 'workflow_history'): if not hasattr(self, 'workflow_history'):
return None return None
for wflow in self.workflow_history.values(): for wflow in self.workflow_history.values():
if wflow is None or len(wflow) == 0: continue # empty history if wflow is None or len(wflow) == 0:
if wflow[0].get(state_name) is None: continue # not the right one continue # empty history
if wflow[0].get(state_name) is None:
continue # not the right one
for i in range(len(wflow)): for i in range(len(wflow)):
ch = wflow[-1-i] ch = wflow[-1-i]
act = ch.get('action', '') act = ch.get('action', '')
...@@ -105,16 +46,6 @@ def getLastWorkflowDate(self, state_name='simulation_state', state=('released',' ...@@ -105,16 +46,6 @@ def getLastWorkflowDate(self, state_name='simulation_state', state=('released','
############################################################################# #############################################################################
# Mail management # Mail management
def findAddress(txt):
"""
find email address in a string
"""
validchars = r'0-9A-Za-z.\-_'
r=re.compile('[%s]+@[%s]+' % (validchars,validchars))
m=r.search(txt)
return m and m.group()
def extractParams(txt): def extractParams(txt):
""" """
extract parameters given in mail body extract parameters given in mail body
......
...@@ -56,7 +56,7 @@ var EnemyDroneAPI = /** @class */ (function () { ...@@ -56,7 +56,7 @@ var EnemyDroneAPI = /** @class */ (function () {
/* /*
** Function called on every drone update, right before onUpdate AI script ** Function called on every drone update, right before onUpdate AI script
*/ */
EnemyDroneAPI.prototype.internal_update = function (context, delta_time) { EnemyDroneAPI.prototype.internal_position_update = function (context, delta_time) {
context._speed += context._acceleration * delta_time / 1000; context._speed += context._acceleration * delta_time / 1000;
if (context._speed > context._maxSpeed) { if (context._speed > context._maxSpeed) {
context._speed = context._maxSpeed; context._speed = context._maxSpeed;
...@@ -81,7 +81,7 @@ var EnemyDroneAPI = /** @class */ (function () { ...@@ -81,7 +81,7 @@ var EnemyDroneAPI = /** @class */ (function () {
/* /*
** Function called on every drone update, right after onUpdate AI script ** Function called on every drone update, right after onUpdate AI script
*/ */
EnemyDroneAPI.prototype.internal_post_update = function (drone) { EnemyDroneAPI.prototype.internal_info_update = function (drone) {
var _this = this, drone_position = drone.getCurrentPosition(), drone_info; var _this = this, drone_position = drone.getCurrentPosition(), drone_info;
if (drone_position) { if (drone_position) {
drone_info = { drone_info = {
...@@ -307,6 +307,9 @@ var EnemyDroneAPI = /** @class */ (function () { ...@@ -307,6 +307,9 @@ var EnemyDroneAPI = /** @class */ (function () {
EnemyDroneAPI.prototype.getMaxHeight = function () { EnemyDroneAPI.prototype.getMaxHeight = function () {
return 800; return 800;
}; };
EnemyDroneAPI.prototype.getOnUpdateInterval = function () {
return 0;
};
EnemyDroneAPI.prototype.getFlightParameters = function () { EnemyDroneAPI.prototype.getFlightParameters = function () {
return this._flight_parameters; return this._flight_parameters;
}; };
......
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.60631.26636.59528</string> </value> <value> <string>1015.63148.58654.57634</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1709288499.16</float> <float>1713426942.44</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -6,6 +6,7 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -6,6 +6,7 @@ var FixedWingDroneAPI = /** @class */ (function () {
"use strict"; "use strict";
var DEFAULT_SPEED = 16, var DEFAULT_SPEED = 16,
PARACHUTE_SPEED = 8,
EARTH_GRAVITY = 9.81, EARTH_GRAVITY = 9.81,
LOITER_LIMIT = 30, LOITER_LIMIT = 30,
MAX_ACCELERATION = 6, MAX_ACCELERATION = 6,
...@@ -48,7 +49,7 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -48,7 +49,7 @@ var FixedWingDroneAPI = /** @class */ (function () {
throw new Error('max acceleration must be superior to 0'); throw new Error('max acceleration must be superior to 0');
} }
drone._minSpeed = this.getMinSpeed(); drone._minSpeed = this.getMinSpeed();
if (drone._minSpeed <= 0) { if (drone._minSpeed < 0) {
throw new Error('min speed must be superior to 0'); throw new Error('min speed must be superior to 0');
} }
drone._maxSpeed = this.getMaxSpeed(); drone._maxSpeed = this.getMaxSpeed();
...@@ -97,9 +98,13 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -97,9 +98,13 @@ var FixedWingDroneAPI = /** @class */ (function () {
/* /*
** Function called on every drone update, right before onUpdate AI script ** Function called on every drone update, right before onUpdate AI script
*/ */
FixedWingDroneAPI.prototype.internal_update = function (context, delta_time) { FixedWingDroneAPI.prototype.internal_position_update = function (context, delta_time) {
if (context.position.z > 0) {
this._updateSpeed(context, delta_time); this._updateSpeed(context, delta_time);
this._updatePosition(context, delta_time); this._updatePosition(context, delta_time);
} else {
context.setDirection(0, 0, 0);
}
context._controlMesh.computeWorldMatrix(true); context._controlMesh.computeWorldMatrix(true);
context._mesh.computeWorldMatrix(true); context._mesh.computeWorldMatrix(true);
...@@ -107,7 +112,7 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -107,7 +112,7 @@ var FixedWingDroneAPI = /** @class */ (function () {
/* /*
** Function called on every drone update, right after onUpdate AI script ** Function called on every drone update, right after onUpdate AI script
*/ */
FixedWingDroneAPI.prototype.internal_post_update = function (drone) { FixedWingDroneAPI.prototype.internal_info_update = function (drone) {
var _this = this, drone_position = drone.getCurrentPosition(), drone_info; var _this = this, drone_position = drone.getCurrentPosition(), drone_info;
/*if (_this._start_altitude > 0) { //TODO move start_altitude here /*if (_this._start_altitude > 0) { //TODO move start_altitude here
_this.reachAltitude(drone); _this.reachAltitude(drone);
...@@ -486,8 +491,8 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -486,8 +491,8 @@ var FixedWingDroneAPI = /** @class */ (function () {
function (altitude_diff, max_climb_rate, speed, max_pitch) { function (altitude_diff, max_climb_rate, speed, max_pitch) {
var maxVerticalSpeed = var maxVerticalSpeed =
Math.min(altitude_diff, Math.min(max_climb_rate, speed)); Math.min(altitude_diff, Math.min(max_climb_rate, speed));
return (this._toDeg(Math.asin(maxVerticalSpeed / speed)) > max_pitch) ? return (this._toDeg(Math.asin(maxVerticalSpeed / speed)) > max_pitch)
speed * Math.sin(this._toRad(max_pitch)) ? speed * Math.sin(this._toRad(max_pitch))
: maxVerticalSpeed; : maxVerticalSpeed;
}; };
FixedWingDroneAPI.prototype._toRad = function (angle) { FixedWingDroneAPI.prototype._toRad = function (angle) {
...@@ -511,11 +516,16 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -511,11 +516,16 @@ var FixedWingDroneAPI = /** @class */ (function () {
}; };
FixedWingDroneAPI.prototype.land = function (drone) { FixedWingDroneAPI.prototype.land = function (drone) {
var drone_pos = drone.getCurrentPosition(); var drone_pos = drone.getCurrentPosition();
drone.setTargetCoordinates( this._flight_parameters.drone.minSpeed = 0;
drone._speed = 0;
drone._acceleration = EARTH_GRAVITY;
this._flight_parameters.drone.maxSinkRate = PARACHUTE_SPEED;
this._flight_parameters.drone.minPitchAngle = -90;
drone._internal_setTargetCoordinates(
drone_pos.latitude, drone_pos.latitude,
drone_pos.longitude, drone_pos.longitude,
0, -PARACHUTE_SPEED,
drone.get3DSpeed() PARACHUTE_SPEED
); );
this._is_ready_to_fly = false; this._is_ready_to_fly = false;
this._is_landing = true; this._is_landing = true;
...@@ -538,6 +548,9 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -538,6 +548,9 @@ var FixedWingDroneAPI = /** @class */ (function () {
FixedWingDroneAPI.prototype.getMaxHeight = function () { FixedWingDroneAPI.prototype.getMaxHeight = function () {
return 800; return 800;
}; };
FixedWingDroneAPI.prototype.getOnUpdateInterval = function () {
return this._flight_parameters.drone.onUpdateInterval;
};
FixedWingDroneAPI.prototype.getFlightParameters = function () { FixedWingDroneAPI.prototype.getFlightParameters = function () {
return this._flight_parameters; return this._flight_parameters;
}; };
......
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.60733.7318.44953</string> </value> <value> <string>1015.64140.4755.42274</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1709564488.61</float> <float>1713430403.75</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.65026.25145.27272</string> </value> <value> <string>1016.21978.22579.46609</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1709560433.33</float> <float>1714742387.62</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -381,6 +381,19 @@ ...@@ -381,6 +381,19 @@
<div class="line"></div> <div class="line"></div>
<!-- getMaxCommandFrequency -->
<h4 class="item-name" id="getMaxCommandFrequency"><span>getMaxCommandFrequency</span><span>: void</span></h4>
<p class="item-descr">Get maximum frequency in hertz at which direction commands (setTargetCoordinates and loiter) can be called.</p>
<div>
<h5 class="item-param-1">Example</h5>
</div>
<p class="item-param-1">me.getMaxCommandFrequency();<br>
</p>
<div class="line"></div>
<!-- takeOff --> <!-- takeOff -->
<h4 class="item-name" id="takeOff"><span>takeOff</span><span>: void</span></h4> <h4 class="item-name" id="takeOff"><span>takeOff</span><span>: void</span></h4>
<p class="item-descr">Trigger drone's takeoff (has only effect on multicopters as fixed wings drones need to take off manually).</p> <p class="item-descr">Trigger drone's takeoff (has only effect on multicopters as fixed wings drones need to take off manually).</p>
......
...@@ -244,7 +244,7 @@ ...@@ -244,7 +244,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.65292.62763.3464</string> </value> <value> <string>1015.13928.44848.25668</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -264,7 +264,7 @@ ...@@ -264,7 +264,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1709564153.67</float> <float>1710867839.5</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -185,6 +185,7 @@ var OperatorAPI = /** @class */ (function () { ...@@ -185,6 +185,7 @@ var OperatorAPI = /** @class */ (function () {
DRAW = true, DRAW = true,
LOG = true, LOG = true,
LOG_TIME = 1662.7915426540285, LOG_TIME = 1662.7915426540285,
ONUPDATE_INTERVAL = 100,
LOGIC_FILE_LIST = [ LOGIC_FILE_LIST = [
'gadget_erp5_page_drone_capture_flag_logic.js', 'gadget_erp5_page_drone_capture_flag_logic.js',
'gadget_erp5_page_drone_capture_map_utils.js', 'gadget_erp5_page_drone_capture_map_utils.js',
...@@ -566,6 +567,17 @@ var OperatorAPI = /** @class */ (function () { ...@@ -566,6 +567,17 @@ var OperatorAPI = /** @class */ (function () {
"hidden": 0, "hidden": 0,
"type": "IntegerField" "type": "IntegerField"
}, },
"my_onupdate_interval": {
"description": "Minimum interval (in milliseconds) between 2 executions of onUpdate function as well as periodicity to send telemetry to the swarm",
"title": "OnUpdate interval",
"default": gadget.state.onupdate_interval,
"css_class": "",
"required": 1,
"editable": 1,
"key": "onupdate_interval",
"hidden": 0,
"type": "IntegerField"
},
"my_drone_min_speed": { "my_drone_min_speed": {
"description": "", "description": "",
"title": "Drone min speed", "title": "Drone min speed",
...@@ -708,7 +720,7 @@ var OperatorAPI = /** @class */ (function () { ...@@ -708,7 +720,7 @@ var OperatorAPI = /** @class */ (function () {
form_definition: { form_definition: {
group_list: [[ group_list: [[
"left", "left",
[["my_simulation_speed"], ["my_simulation_time"], [["my_simulation_speed"], ["my_simulation_time"], ["my_onupdate_interval"],
["my_number_of_drones"], ["my_map_seed"]] ["my_number_of_drones"], ["my_map_seed"]]
], [ ], [
"right", "right",
...@@ -845,6 +857,7 @@ var OperatorAPI = /** @class */ (function () { ...@@ -845,6 +857,7 @@ var OperatorAPI = /** @class */ (function () {
"maxSinkRate": parseFloat(gadget.state.drone_max_sink_rate), "maxSinkRate": parseFloat(gadget.state.drone_max_sink_rate),
"maxClimbRate": parseFloat(gadget.state.drone_max_climb_rate), "maxClimbRate": parseFloat(gadget.state.drone_max_climb_rate),
"maxCommandFrequency": parseFloat(gadget.state.drone_max_command_frequency), "maxCommandFrequency": parseFloat(gadget.state.drone_max_command_frequency),
"onUpdateInterval": parseInt(gadget.state.onupdate_interval, 10),
"list": drone_list "list": drone_list
}, },
"gameTime": parseInt(gadget.state.simulation_time, 10), "gameTime": parseInt(gadget.state.simulation_time, 10),
...@@ -970,6 +983,7 @@ var OperatorAPI = /** @class */ (function () { ...@@ -970,6 +983,7 @@ var OperatorAPI = /** @class */ (function () {
drone_max_speed: MAX_SPEED, drone_max_speed: MAX_SPEED,
drone_speed: DEFAULT_SPEED, drone_speed: DEFAULT_SPEED,
drone_min_speed: MIN_SPEED, drone_min_speed: MIN_SPEED,
onupdate_interval: ONUPDATE_INTERVAL,
simulation_time: SIMULATION_TIME, simulation_time: SIMULATION_TIME,
simulation_speed: SIMULATION_SPEED, simulation_speed: SIMULATION_SPEED,
operator_init_msg: {}, operator_init_msg: {},
......
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.48194.32365.65399</string> </value> <value> <string>1016.21921.52144.47906</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1708709921.07</float> <float>1714738877.83</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -3,8 +3,10 @@ ...@@ -3,8 +3,10 @@
(function (window, rJS, domsugar, document) { (function (window, rJS, domsugar, document) {
"use strict"; "use strict";
var SIMULATION_SPEED = 10, var SIMULATION_SPEED = 1,
SIMULATION_TIME = 270, LOOP_INTERVAL = 1000 / 60,
ON_UPDATE_INTERVAL = LOOP_INTERVAL,
SIMULATION_TIME = LOOP_INTERVAL / 1000,
MIN_LAT = 45.6364, MIN_LAT = 45.6364,
MAX_LAT = 45.65, MAX_LAT = 45.65,
MIN_LON = 14.2521, MIN_LON = 14.2521,
...@@ -74,9 +76,10 @@ ...@@ -74,9 +76,10 @@
' me.getCurrentPosition().longitude\n' + ' me.getCurrentPosition().longitude\n' +
' ).toFixed(8),\n' + ' ).toFixed(8),\n' +
' time_interval = timestamp - me.start_time,\n' + ' time_interval = timestamp - me.start_time,\n' +
' expected_interval = 1000 / 60,\n' + ' expected_interval = ' + LOOP_INTERVAL + ',\n' +
' expectedDistance = (me.getSpeed() * expected_interval / 1000).toFixed(8);\n' + ' expectedDistance = (me.getSpeed() * expected_interval / 1000).toFixed(8);\n' +
' assert(time_interval, Math.floor(expected_interval), "Timestamp");\n' + ' assert(time_interval.toFixed(4), expected_interval.toFixed(4), "Timestamp");\n' +
' assert(Date.now(), timestamp, "Date");\n' +
' assert(realDistance, expectedDistance, "Distance");\n' + ' assert(realDistance, expectedDistance, "Distance");\n' +
' current_position.latitude = current_position.latitude.toFixed(7);\n' + ' current_position.latitude = current_position.latitude.toFixed(7);\n' +
' compare(current_position, {\n' + ' compare(current_position, {\n' +
...@@ -84,7 +87,6 @@ ...@@ -84,7 +87,6 @@
' longitude: me.initialPosition.longitude,\n' + ' longitude: me.initialPosition.longitude,\n' +
' altitude: me.initialPosition.altitude\n' + ' altitude: me.initialPosition.altitude\n' +
' });\n' + ' });\n' +
' me.exit(me.land());\n' +
'};', '};',
DRAW = true, DRAW = true,
LOG = true, LOG = true,
...@@ -120,19 +122,19 @@ ...@@ -120,19 +122,19 @@
"script_content": DEFAULT_SCRIPT_CONTENT}; "script_content": DEFAULT_SCRIPT_CONTENT};
} }
map_json = { map_json = {
"height": parseInt(map_height, 10), "height": map_height,
"start_AMSL": parseFloat(start_AMSL), "start_AMSL": start_AMSL,
"min_lat": parseFloat(MIN_LAT), "min_lat": MIN_LAT,
"max_lat": parseFloat(MAX_LAT), "max_lat": MAX_LAT,
"min_lon": parseFloat(MIN_LON), "min_lon": MIN_LON,
"max_lon": parseFloat(MAX_LON), "max_lon": MAX_LON,
"flag_list": [], "flag_list": [],
"obstacle_list" : [], "obstacle_list" : [],
"enemy_list" : [], "enemy_list" : [],
"initial_position": { "initial_position": {
"longitude": parseFloat(INIT_LON), "longitude": INIT_LON,
"latitude": parseFloat(INIT_LAT), "latitude": INIT_LAT,
"altitude": parseFloat(INIT_ALT) "altitude": INIT_ALT
} }
}; };
operator_init_msg = { operator_init_msg = {
...@@ -142,20 +144,21 @@ ...@@ -142,20 +144,21 @@
game_parameters_json = { game_parameters_json = {
"debug_test_mode": true, "debug_test_mode": true,
"drone": { "drone": {
"maxAcceleration": parseInt(MAX_ACCELERATION, 10), "maxAcceleration": MAX_ACCELERATION,
"maxDeceleration": parseInt(MAX_DECELERATION, 10), "maxDeceleration": MAX_DECELERATION,
"minSpeed": parseInt(MIN_SPEED, 10), "minSpeed": MIN_SPEED,
"speed": parseFloat(DEFAULT_SPEED), "speed": DEFAULT_SPEED,
"maxSpeed": parseInt(MAX_SPEED, 10), "maxSpeed": MAX_SPEED,
"maxRoll": parseFloat(MAX_ROLL), "maxRoll": MAX_ROLL,
"minPitchAngle": parseFloat(MIN_PITCH), "minPitchAngle": MIN_PITCH,
"maxPitchAngle": parseFloat(MAX_PITCH), "maxPitchAngle": MAX_PITCH,
"maxSinkRate": parseFloat(MAX_SINK_RATE), "maxSinkRate": MAX_SINK_RATE,
"maxClimbRate": parseFloat(MAX_CLIMB_RATE), "maxClimbRate": MAX_CLIMB_RATE,
"onUpdateInterval": ON_UPDATE_INTERVAL,
"list": DRONE_LIST "list": DRONE_LIST
}, },
"gameTime": parseInt(SIMULATION_TIME, 10), "gameTime": SIMULATION_TIME,
"simulation_speed": parseInt(SIMULATION_SPEED, 10), "simulation_speed": SIMULATION_SPEED,
"latency": { "latency": {
"information": 0, "information": 0,
"communication": 0 "communication": 0
...@@ -214,19 +217,24 @@ ...@@ -214,19 +217,24 @@
}) })
.push(function (result) { .push(function (result) {
var div = domsugar('div', { text: "CONSOLE LOG ENTRIES:" }), lines, var div = domsugar('div', { text: "CONSOLE LOG ENTRIES:" }), lines,
l, node; l, test_log_node = document.querySelector('.test_log');
document.querySelector('.container').parentNode.appendChild(div); document.querySelector('.container').parentNode.appendChild(div);
function createLogNode(message) { function appendToTestLog(test_log_node, message) {
var log_node = document.createElement("div"), var log_node = document.createElement("div"),
textNode = document.createTextNode(message); textNode = document.createTextNode(message);
log_node.appendChild(textNode); log_node.appendChild(textNode);
return log_node; test_log_node.appendChild(log_node);
} }
lines = result.console_log.split('\n'); lines = result.console_log.split('\n');
for (l = 0; l < lines.length; l += 1) { for (l = 0; l < lines.length; l += 1) {
node = createLogNode(lines[l]); if (lines[l] !== 'TIMEOUT!') {
document.querySelector('.test_log').appendChild(node); appendToTestLog(test_log_node, lines[l]);
} else {
appendToTestLog(test_log_node, 'Timeout: OK');
return;
} }
}
appendToTestLog(test_log_node, 'Timeout: FAILED');
}, function (error) { }, function (error) {
return gadget.notifySubmitted({message: "Error: " + error.message, return gadget.notifySubmitted({message: "Error: " + error.message,
status: 'error'}); status: 'error'});
......
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.60680.20078.34286</string> </value> <value> <string>1015.64187.34381.50346</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1709286290.59</float> <float>1713428877.4</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -80,7 +80,7 @@ var DroneLogAPI = /** @class */ (function () { ...@@ -80,7 +80,7 @@ var DroneLogAPI = /** @class */ (function () {
/* /*
** Function called on every drone update, right before onUpdate AI script ** Function called on every drone update, right before onUpdate AI script
*/ */
DroneLogAPI.prototype.internal_update = function (context, delta_time) { DroneLogAPI.prototype.internal_position_update = function (context, delta_time) {
var updateSpeed; var updateSpeed;
context._speed += context._acceleration * delta_time / 1000; context._speed += context._acceleration * delta_time / 1000;
if (context._speed > context._maxSpeed) { if (context._speed > context._maxSpeed) {
...@@ -104,7 +104,7 @@ var DroneLogAPI = /** @class */ (function () { ...@@ -104,7 +104,7 @@ var DroneLogAPI = /** @class */ (function () {
/* /*
** Function called on every drone update, right after onUpdate AI script ** Function called on every drone update, right after onUpdate AI script
*/ */
DroneLogAPI.prototype.internal_post_update = function (drone) { DroneLogAPI.prototype.internal_info_update = function (drone) {
return; return;
}; };
DroneLogAPI.prototype.internal_setTargetCoordinates = DroneLogAPI.prototype.internal_setTargetCoordinates =
...@@ -213,6 +213,9 @@ var DroneLogAPI = /** @class */ (function () { ...@@ -213,6 +213,9 @@ var DroneLogAPI = /** @class */ (function () {
DroneLogAPI.prototype.getMaxCommandFrequency = function () { DroneLogAPI.prototype.getMaxCommandFrequency = function () {
return Infinity; return Infinity;
}; };
DroneLogAPI.prototype.getOnUpdateInterval = function () {
return 0;
};
return DroneLogAPI; return DroneLogAPI;
}()); }());
\ No newline at end of file
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.60631.26636.59528</string> </value> <value> <string>1015.64101.28159.26163</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1709288518.39</float> <float>1713425784.77</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -7,6 +7,7 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -7,6 +7,7 @@ var FixedWingDroneAPI = /** @class */ (function () {
// var TAKEOFF_RADIUS = 60, // var TAKEOFF_RADIUS = 60,
var DEFAULT_SPEED = 16, var DEFAULT_SPEED = 16,
PARACHUTE_SPEED = 8,
EARTH_GRAVITY = 9.81, EARTH_GRAVITY = 9.81,
LOITER_LIMIT = 30, LOITER_LIMIT = 30,
MAX_ACCELERATION = 6, MAX_ACCELERATION = 6,
...@@ -52,7 +53,7 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -52,7 +53,7 @@ var FixedWingDroneAPI = /** @class */ (function () {
throw new Error('max acceleration must be superior to 0'); throw new Error('max acceleration must be superior to 0');
} }
drone._minSpeed = this.getMinSpeed(); drone._minSpeed = this.getMinSpeed();
if (drone._minSpeed <= 0) { if (drone._minSpeed < 0) {
throw new Error('min speed must be superior to 0'); throw new Error('min speed must be superior to 0');
} }
drone._maxSpeed = this.getMaxSpeed(); drone._maxSpeed = this.getMaxSpeed();
...@@ -101,7 +102,7 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -101,7 +102,7 @@ var FixedWingDroneAPI = /** @class */ (function () {
/* /*
** Function called on every drone update, right before onUpdate AI script ** Function called on every drone update, right before onUpdate AI script
*/ */
FixedWingDroneAPI.prototype.internal_update = function (context, delta_time) { FixedWingDroneAPI.prototype.internal_position_update = function (context, delta_time) {
this._updateSpeed(context, delta_time); this._updateSpeed(context, delta_time);
this._updatePosition(context, delta_time); this._updatePosition(context, delta_time);
...@@ -111,7 +112,7 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -111,7 +112,7 @@ var FixedWingDroneAPI = /** @class */ (function () {
/* /*
** Function called on every drone update, right after onUpdate AI script ** Function called on every drone update, right after onUpdate AI script
*/ */
FixedWingDroneAPI.prototype.internal_post_update = function (drone) { FixedWingDroneAPI.prototype.internal_info_update = function (drone) {
var _this = this, drone_position = drone.getCurrentPosition(), drone_info; var _this = this, drone_position = drone.getCurrentPosition(), drone_info;
/*if (_this._start_altitude > 0) { //TODO move start_altitude here /*if (_this._start_altitude > 0) { //TODO move start_altitude here
_this.reachAltitude(drone); _this.reachAltitude(drone);
...@@ -373,8 +374,6 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -373,8 +374,6 @@ var FixedWingDroneAPI = /** @class */ (function () {
var processed_coordinates = var processed_coordinates =
this._mapManager.convertToLocalCoordinates(lat, lon, z); this._mapManager.convertToLocalCoordinates(lat, lon, z);
processed_coordinates.z -= this._map_dict.start_AMSL; processed_coordinates.z -= this._map_dict.start_AMSL;
//this._last_altitude_point_reached = -1;
//this.takeoff_path = [];
return processed_coordinates; return processed_coordinates;
}; };
FixedWingDroneAPI.prototype.getCurrentPosition = function (x, y, z) { FixedWingDroneAPI.prototype.getCurrentPosition = function (x, y, z) {
...@@ -470,11 +469,16 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -470,11 +469,16 @@ var FixedWingDroneAPI = /** @class */ (function () {
}; };
FixedWingDroneAPI.prototype.land = function (drone) { FixedWingDroneAPI.prototype.land = function (drone) {
var drone_pos = drone.getCurrentPosition(); var drone_pos = drone.getCurrentPosition();
drone.setTargetCoordinates( this._flight_parameters.drone.minSpeed = 0;
drone._speed = 0;
drone._acceleration = EARTH_GRAVITY;
this._flight_parameters.drone.maxSinkRate = PARACHUTE_SPEED;
this._flight_parameters.drone.minPitchAngle = -90;
drone._internal_setTargetCoordinates(
drone_pos.latitude, drone_pos.latitude,
drone_pos.longitude, drone_pos.longitude,
0, 0,
drone.get3DSpeed() PARACHUTE_SPEED
); );
this._is_ready_to_fly = false; this._is_ready_to_fly = false;
this._is_landing = true; this._is_landing = true;
...@@ -497,6 +501,9 @@ var FixedWingDroneAPI = /** @class */ (function () { ...@@ -497,6 +501,9 @@ var FixedWingDroneAPI = /** @class */ (function () {
FixedWingDroneAPI.prototype.getMaxHeight = function () { FixedWingDroneAPI.prototype.getMaxHeight = function () {
return 800; return 800;
}; };
FixedWingDroneAPI.prototype.getOnUpdateInterval = function () {
return this._flight_parameters.drone.onUpdateInterval;
};
FixedWingDroneAPI.prototype.getFlightParameters = function () { FixedWingDroneAPI.prototype.getFlightParameters = function () {
return this._flight_parameters; return this._flight_parameters;
}; };
......
...@@ -240,7 +240,7 @@ ...@@ -240,7 +240,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.60733.7318.44953</string> </value> <value> <string>1015.64203.48820.61559</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -260,7 +260,7 @@ ...@@ -260,7 +260,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1709562449.93</float> <float>1713429850.09</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -240,7 +240,7 @@ ...@@ -240,7 +240,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.65027.38618.31573</string> </value> <value> <string>1016.21987.28184.16844</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -260,7 +260,7 @@ ...@@ -260,7 +260,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1709560536.57</float> <float>1714742619.34</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -122,11 +122,13 @@ ...@@ -122,11 +122,13 @@
' }\n' + ' }\n' +
' return;\n' + ' return;\n' +
' }\n' + ' }\n' +
' me.exit(0);\n' + ' if (!me.isLanding()) { me.land() };\n' +
' if (me.getCurrentPosition().altitude <= 0) { me.exit(0) };\n' +
'};', '};',
DRAW = true, DRAW = true,
LOG = true, LOG = true,
LOG_TIME = 1662.7915426540285, LOG_TIME = 1662.7915426540285,
ONUPDATE_INTERVAL = 100,
DRONE_LIST = [], DRONE_LIST = [],
WIDTH = 680, WIDTH = 680,
HEIGHT = 340, HEIGHT = 340,
...@@ -191,6 +193,17 @@ ...@@ -191,6 +193,17 @@
"hidden": 0, "hidden": 0,
"type": "IntegerField" "type": "IntegerField"
}, },
"my_onupdate_interval": {
"description": "Minimum interval (in milliseconds) between 2 executions of onUpdate function as well as periodicity to send telemetry to the swarm",
"title": "OnUpdate interval",
"default": ONUPDATE_INTERVAL,
"css_class": "",
"required": 1,
"editable": 1,
"key": "onupdate_interval",
"hidden": 0,
"type": "IntegerField"
},
"my_drone_min_speed": { "my_drone_min_speed": {
"description": "", "description": "",
"title": "Drone min speed", "title": "Drone min speed",
...@@ -444,8 +457,8 @@ ...@@ -444,8 +457,8 @@
form_definition: { form_definition: {
group_list: [[ group_list: [[
"left", "left",
[["my_simulation_speed"], ["my_simulation_time"], ["my_number_of_drones"], [["my_simulation_speed"], ["my_simulation_time"], ["my_onupdate_interval"],
["my_minimum_latitud"], ["my_maximum_latitud"], ["my_number_of_drones"], ["my_minimum_latitud"], ["my_maximum_latitud"],
["my_minimum_longitud"], ["my_maximum_longitud"], ["my_minimum_longitud"], ["my_maximum_longitud"],
["my_init_pos_lat"], ["my_init_pos_lon"], ["my_init_pos_alt"], ["my_init_pos_lat"], ["my_init_pos_lon"], ["my_init_pos_alt"],
["my_map_height"]] ["my_map_height"]]
...@@ -494,7 +507,8 @@ ...@@ -494,7 +507,8 @@
"maxPitchAngle": parseFloat(options.drone_max_pitch), "maxPitchAngle": parseFloat(options.drone_max_pitch),
"maxSinkRate": parseFloat(options.drone_max_sink_rate), "maxSinkRate": parseFloat(options.drone_max_sink_rate),
"maxClimbRate": parseFloat(options.drone_max_climb_rate), "maxClimbRate": parseFloat(options.drone_max_climb_rate),
"maxCommandFrequency": parseFloat(options.drone_max_command_frequency) "maxCommandFrequency": parseFloat(options.drone_max_command_frequency),
"onUpdateInterval": parseInt(options.onupdate_interval, 10)
}, },
"gameTime": parseInt(options.simulation_time, 10), "gameTime": parseInt(options.simulation_time, 10),
"simulation_speed": parseInt(options.simulation_speed, 10), "simulation_speed": parseInt(options.simulation_speed, 10),
......
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.60725.60577.24917</string> </value> <value> <string>1015.64120.46679.6946</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1709289017.41</float> <float>1713425712.2</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -3,11 +3,10 @@ ...@@ -3,11 +3,10 @@
(function (window, rJS, domsugar, document, URLSearchParams, Blob) { (function (window, rJS, domsugar, document, URLSearchParams, Blob) {
"use strict"; "use strict";
var SIMULATION_SPEED = 10, var SIMULATION_SPEED = 1,
SIMULATION_TIME = 270, LOOP_INTERVAL = 1000 / 60,
MAP_SIZE = 600, ON_UPDATE_INTERVAL = LOOP_INTERVAL,
map_height = 700, SIMULATION_TIME = LOOP_INTERVAL / 1000,
start_AMSL = 595,
DEFAULT_SPEED = 16, DEFAULT_SPEED = 16,
MAX_ACCELERATION = 6, MAX_ACCELERATION = 6,
MAX_DECELERATION = 1, MAX_DECELERATION = 1,
...@@ -19,7 +18,6 @@ ...@@ -19,7 +18,6 @@
MAX_CLIMB_RATE = 8, MAX_CLIMB_RATE = 8,
MAX_SINK_RATE = 3, MAX_SINK_RATE = 3,
NUMBER_OF_DRONES = 1, NUMBER_OF_DRONES = 1,
FLAG_WEIGHT = 5,
MIN_LAT = 45.6364, MIN_LAT = 45.6364,
MAX_LAT = 45.65, MAX_LAT = 45.65,
MIN_LON = 14.2521, MIN_LON = 14.2521,
...@@ -78,9 +76,10 @@ ...@@ -78,9 +76,10 @@
' me.getCurrentPosition().longitude\n' + ' me.getCurrentPosition().longitude\n' +
' ).toFixed(8),\n' + ' ).toFixed(8),\n' +
' time_interval = timestamp - me.start_time,\n' + ' time_interval = timestamp - me.start_time,\n' +
' expected_interval = 1000 / 60,\n' + ' expected_interval = ' + LOOP_INTERVAL + ',\n' +
' expectedDistance = (me.getSpeed() * expected_interval / 1000).toFixed(8);\n' + ' expectedDistance = (me.getSpeed() * expected_interval / 1000).toFixed(8);\n' +
' assert(time_interval.toFixed(4), expected_interval.toFixed(4), "Timestamp");\n' + ' assert(time_interval.toFixed(4), expected_interval.toFixed(4), "Timestamp");\n' +
' assert(Date.now(), timestamp, "Date");\n' +
' assert(realDistance, expectedDistance, "Distance");\n' + ' assert(realDistance, expectedDistance, "Distance");\n' +
' current_position.latitude = current_position.latitude.toFixed(7);\n' + ' current_position.latitude = current_position.latitude.toFixed(7);\n' +
' compare(current_position, {\n' + ' compare(current_position, {\n' +
...@@ -88,7 +87,6 @@ ...@@ -88,7 +87,6 @@
' longitude: me.initialPosition.longitude,\n' + ' longitude: me.initialPosition.longitude,\n' +
' altitude: me.initialPosition.altitude\n' + ' altitude: me.initialPosition.altitude\n' +
' });\n' + ' });\n' +
' me.exit(me.land());\n' +
'};', '};',
DRAW = true, DRAW = true,
LOG = true, LOG = true,
...@@ -175,35 +173,36 @@ ...@@ -175,35 +173,36 @@
game_parameters_json = { game_parameters_json = {
"debug_test_mode": true, "debug_test_mode": true,
"drone": { "drone": {
"maxAcceleration": parseInt(MAX_ACCELERATION, 10), "maxAcceleration": MAX_ACCELERATION,
"maxDeceleration": parseInt(MAX_DECELERATION, 10), "maxDeceleration": MAX_DECELERATION,
"minSpeed": parseInt(MIN_SPEED, 10), "minSpeed": MIN_SPEED,
"speed": parseFloat(DEFAULT_SPEED), "speed": DEFAULT_SPEED,
"maxSpeed": parseInt(MAX_SPEED, 10), "maxSpeed": MAX_SPEED,
"maxRoll": parseFloat(MAX_ROLL), "maxRoll": MAX_ROLL,
"minPitchAngle": parseFloat(MIN_PITCH), "minPitchAngle": MIN_PITCH,
"maxPitchAngle": parseFloat(MAX_PITCH), "maxPitchAngle": MAX_PITCH,
"maxSinkRate": parseFloat(MAX_SINK_RATE), "maxSinkRate": MAX_SINK_RATE,
"maxClimbRate": parseFloat(MAX_CLIMB_RATE) "maxClimbRate": MAX_CLIMB_RATE,
"onUpdateInterval": ON_UPDATE_INTERVAL
}, },
"gameTime": parseInt(SIMULATION_TIME, 10), "gameTime": SIMULATION_TIME,
"simulation_speed": parseInt(SIMULATION_SPEED, 10), "simulation_speed": SIMULATION_SPEED,
"latency": { "latency": {
"information": 0, "information": 0,
"communication": 0 "communication": 0
}, },
"map": { "map": {
"min_lat": parseFloat(MIN_LAT), "min_lat": MIN_LAT,
"max_lat": parseFloat(MAX_LAT), "max_lat": MAX_LAT,
"min_lon": parseFloat(MIN_LON), "min_lon": MIN_LON,
"max_lon": parseFloat(MAX_LON), "max_lon": MAX_LON,
"height": parseInt(HEIGHT), "height": HEIGHT,
"start_AMSL": parseFloat(start_AMSL) "start_AMSL": start_AMSL
}, },
"initialPosition": { "initialPosition": {
"longitude": parseFloat(INIT_LON), "longitude": INIT_LON,
"latitude": parseFloat(INIT_LAT), "latitude": INIT_LAT,
"altitude": parseFloat(INIT_ALT) "altitude": INIT_ALT
}, },
"draw_flight_path": DRAW, "draw_flight_path": DRAW,
"temp_flight_path": true, "temp_flight_path": true,
...@@ -257,19 +256,27 @@ ...@@ -257,19 +256,27 @@
return form_gadget.getContent(); return form_gadget.getContent();
}) })
.push(function (result) { .push(function (result) {
var div = domsugar('div', { text: "CONSOLE LOG ENTRIES:" }); var div = domsugar('div', { text: "CONSOLE LOG ENTRIES:" }),
lines = result.console_log.split('\n'),
line_nb,
node,
test_log_node = document.querySelector('.test_log');;
document.querySelector('.container').parentNode.appendChild(div); document.querySelector('.container').parentNode.appendChild(div);
function createLogNode(message) { function appendToTestLog(test_log_node, message) {
var node = document.createElement("div"); var log_node = document.createElement("div"),
var textNode = document.createTextNode(message); textNode = document.createTextNode(message);
node.appendChild(textNode); log_node.appendChild(textNode);
return node; test_log_node.appendChild(log_node);
}
for (line_nb = 0; line_nb < lines.length; line_nb += 1) {
if (lines[line_nb] !== 'TIMEOUT!') {
appendToTestLog(test_log_node, lines[line_nb]);
} else {
appendToTestLog(test_log_node, 'Timeout: OK');
return;
} }
var lines = result.console_log.split('\n');
for (var i = 0;i < lines.length;i++) {
var node = createLogNode(lines[i]);
document.querySelector('.test_log').appendChild(node);
} }
appendToTestLog(test_log_node, 'Timeout: FAILED');
}, function (error) { }, function (error) {
return gadget.notifySubmitted({message: "Error: " + error.message, return gadget.notifySubmitted({message: "Error: " + error.message,
status: 'error'}); status: 'error'});
......
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>1014.60681.20667.37171</string> </value> <value> <string>1015.64176.45813.63488</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -266,7 +266,7 @@ ...@@ -266,7 +266,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1709286436.53</float> <float>1713428376.53</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -25,9 +25,13 @@ ...@@ -25,9 +25,13 @@
# #
############################################################################## ##############################################################################
import base64
import binascii
import json import json
import typing import typing
import six
from six.moves.urllib.parse import unquote from six.moves.urllib.parse import unquote
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from typing import Any, Callable, Optional from typing import Any, Callable, Optional
from erp5.component.document.OpenAPITypeInformation import OpenAPIOperation, OpenAPIParameter from erp5.component.document.OpenAPITypeInformation import OpenAPIOperation, OpenAPIParameter
...@@ -258,10 +262,8 @@ class OpenAPIService(XMLObject): ...@@ -258,10 +262,8 @@ class OpenAPIService(XMLObject):
parameter, parameter,
parameter.getJSONSchema(), parameter.getJSONSchema(),
) )
requestBody = self.validateParameter( requestBody = self.validateRequestBody(
'request body',
operation.getRequestBodyValue(request), operation.getRequestBodyValue(request),
{},
operation.getRequestBodyJSONSchema(request), operation.getRequestBodyJSONSchema(request),
) )
if requestBody: if requestBody:
...@@ -296,6 +298,37 @@ class OpenAPIService(XMLObject): ...@@ -296,6 +298,37 @@ class OpenAPIService(XMLObject):
parameter_name=parameter_name, e=e.message), str(e)) parameter_name=parameter_name, e=e.message), str(e))
return parameter_value return parameter_value
security.declareProtected(
Permissions.AccessContentsInformation, 'validateRequestBody')
def validateRequestBody(self, parameter_value, schema):
# type: (str, dict) -> Any
"""Validate the request body raising a ParameterValidationError
when the parameter is not valid according to the corresponding schema.
"""
if schema is not None:
if schema.get('type') == 'string':
if schema.get('format') == 'base64':
try:
return base64.b64decode(parameter_value)
except (binascii.Error, TypeError) as e:
if isinstance(e, TypeError):
# BBB on python2 this raises a generic type error
# but we don't want to ignore potential TypeErrors
# on python3 here
if six.PY3:
raise
raise ParameterValidationError(
'Error validating request body: {e}'.format(e=str(e)))
elif schema.get('format') == 'binary':
return parameter_value or b''
return self.validateParameter(
'request body',
parameter_value,
{},
schema,
)
def executeMethod(self, request): def executeMethod(self, request):
# type: (HTTPRequest) -> Any # type: (HTTPRequest) -> Any
operation = self.getMatchingOperation(request) operation = self.getMatchingOperation(request)
......
...@@ -90,7 +90,7 @@ ModuleSecurityInfo(__name__).declarePublic( ...@@ -90,7 +90,7 @@ ModuleSecurityInfo(__name__).declarePublic(
) )
# On python2, make sure we use UTF-8 strings for the json schemas, so that we don't # On python2, make sure we use UTF-8 strings for the json schemas, so that we don't
# have ugly u' prefixs in the reprs. This also transforms the collections.OrderedDict # have ugly u' prefixes in the reprs. This also transforms the collections.OrderedDict
# to simple dicts, because the former also have an ugly representation. # to simple dicts, because the former also have an ugly representation.
# http://stackoverflow.com/a/13105359 # http://stackoverflow.com/a/13105359
if six.PY2: if six.PY2:
...@@ -105,7 +105,7 @@ if six.PY2: ...@@ -105,7 +105,7 @@ if six.PY2:
return [byteify(element) for element in string] return [byteify(element) for element in string]
elif isinstance(string, tuple): elif isinstance(string, tuple):
return tuple(byteify(element) for element in string) return tuple(byteify(element) for element in string)
elif isinstance(string, unicode): elif isinstance(string, six.text_type):
return string.encode('utf-8') return string.encode('utf-8')
else: else:
return string return string
...@@ -175,7 +175,9 @@ class OpenAPIOperation(dict): ...@@ -175,7 +175,9 @@ class OpenAPIOperation(dict):
# type: (HTTPRequest) -> Optional[dict] # type: (HTTPRequest) -> Optional[dict]
"""Returns the schema for the request body, or None if no `requestBody` defined """Returns the schema for the request body, or None if no `requestBody` defined
""" """
request_content_type = request.getHeader('content-type') exact_request_content_type = request.getHeader('content-type')
wildcard_request_content_type = '%s/*' % ((exact_request_content_type or '').split('/')[0])
for request_content_type in exact_request_content_type, wildcard_request_content_type, '*/*':
# TODO there might be $ref ? # TODO there might be $ref ?
request_body_definition = self.get( request_body_definition = self.get(
'requestBody', {'content': {}})['content'].get(request_content_type) 'requestBody', {'content': {}})['content'].get(request_content_type)
...@@ -340,7 +342,10 @@ class OpenAPITypeInformation(ERP5TypeInformation): ...@@ -340,7 +342,10 @@ class OpenAPITypeInformation(ERP5TypeInformation):
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
def getSchema(self): def getSchema(self):
stream = io.BytesIO(self.getTextContent() or b'{}') text_content = self.getTextContent() or '{}'
if six.PY3:
text_content = text_content.encode()
stream = io.BytesIO(text_content)
if self.getContentType() == 'application/x-yaml': if self.getContentType() == 'application/x-yaml':
try: try:
import yaml # pylint:disable=import-error import yaml # pylint:disable=import-error
......
...@@ -25,6 +25,13 @@ ...@@ -25,6 +25,13 @@
# #
############################################################################## ##############################################################################
import six
# pylint:disable=no-name-in-module
if six.PY2:
from base64 import encodestring as base64_encodebytes
else:
from base64 import encodebytes as base64_encodebytes
# pylint:enable=no-name-in-module
import io import io
import json import json
import unittest import unittest
...@@ -39,7 +46,7 @@ from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase ...@@ -39,7 +46,7 @@ from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
class OpenAPITestCase(ERP5TypeTestCase): class OpenAPITestCase(ERP5TypeTestCase):
_type_id = NotImplemented # type: str _type_id = NotImplemented # type: str
_open_api_schema = NotImplemented # type: bytes _open_api_schema = NotImplemented # type: str
_open_api_schema_content_type = 'application/json' _open_api_schema_content_type = 'application/json'
_public_api = True _public_api = True
...@@ -375,7 +382,7 @@ class TestOpenAPIServicePetController(OpenAPIPetStoreTestCase): ...@@ -375,7 +382,7 @@ class TestOpenAPIServicePetController(OpenAPIPetStoreTestCase):
class TestOpenAPIServiceYaml(OpenAPITestCase): class TestOpenAPIServiceYaml(OpenAPITestCase):
_type_id = 'Test Open API YAML' _type_id = 'Test Open API YAML'
_open_api_schema_content_type = 'application/x-yaml' _open_api_schema_content_type = 'application/x-yaml'
_open_api_schema = b''' _open_api_schema = '''
openapi: 3.0.3 openapi: 3.0.3
info: info:
title: TestOpenAPIServiceYaml title: TestOpenAPIServiceYaml
...@@ -456,7 +463,7 @@ class TestPathParameterSerialization(OpenAPITestCase): ...@@ -456,7 +463,7 @@ class TestPathParameterSerialization(OpenAPITestCase):
} }
} }
} }
}).encode() })
def test_primitive_parameter_serialization(self): def test_primitive_parameter_serialization(self):
self.addPythonScript( self.addPythonScript(
...@@ -532,7 +539,7 @@ class TestQueryParameterSerialization(OpenAPITestCase): ...@@ -532,7 +539,7 @@ class TestQueryParameterSerialization(OpenAPITestCase):
} }
} }
} }
}).encode() })
def test_array_parameter_serialization(self): def test_array_parameter_serialization(self):
self.addPythonScript( self.addPythonScript(
...@@ -707,7 +714,7 @@ class TestOpenAPINonAsciiParameters(OpenAPIPetStoreTestCase): ...@@ -707,7 +714,7 @@ class TestOpenAPINonAsciiParameters(OpenAPIPetStoreTestCase):
class TestOpenAPICommonParameters(OpenAPIPetStoreTestCase): class TestOpenAPICommonParameters(OpenAPIPetStoreTestCase):
_type_id = 'Test Open API Common Parameters' _type_id = 'Test Open API Common Parameters'
_open_api_schema = ( _open_api_schema = (
b''' '''
{ {
"openapi": "3.0.3", "openapi": "3.0.3",
"info": { "info": {
...@@ -718,7 +725,7 @@ class TestOpenAPICommonParameters(OpenAPIPetStoreTestCase): ...@@ -718,7 +725,7 @@ class TestOpenAPICommonParameters(OpenAPIPetStoreTestCase):
''' '''
# https://swagger.io/docs/specification/describing-parameters/#common-for-path # https://swagger.io/docs/specification/describing-parameters/#common-for-path
b''' '''
"/common-for-path": { "/common-for-path": {
"parameters": [ "parameters": [
{ {
...@@ -749,7 +756,7 @@ class TestOpenAPICommonParameters(OpenAPIPetStoreTestCase): ...@@ -749,7 +756,7 @@ class TestOpenAPICommonParameters(OpenAPIPetStoreTestCase):
},''' },'''
# https://swagger.io/docs/specification/describing-parameters/#common-for-various-paths # https://swagger.io/docs/specification/describing-parameters/#common-for-various-paths
b''' '''
"/common-for-various-paths": { "/common-for-various-paths": {
"get": { "get": {
"operationId": "testGET2", "operationId": "testGET2",
...@@ -761,7 +768,7 @@ class TestOpenAPICommonParameters(OpenAPIPetStoreTestCase): ...@@ -761,7 +768,7 @@ class TestOpenAPICommonParameters(OpenAPIPetStoreTestCase):
''' '''
# here we also excercice $refs in parameter schemas # here we also excercice $refs in parameter schemas
b''' '''
"$ref": "#/components/schemas/custom-number" "$ref": "#/components/schemas/custom-number"
} }
}, },
...@@ -781,7 +788,7 @@ class TestOpenAPICommonParameters(OpenAPIPetStoreTestCase): ...@@ -781,7 +788,7 @@ class TestOpenAPICommonParameters(OpenAPIPetStoreTestCase):
# https://spec.openapis.org/oas/v3.1.0#fixed-fields-6 # https://spec.openapis.org/oas/v3.1.0#fixed-fields-6
# $refs: Allows for a referenced definition of this path item. # $refs: Allows for a referenced definition of this path item.
# The referenced structure MUST be in the form of a Path Item Object. # The referenced structure MUST be in the form of a Path Item Object.
b''' '''
"/alias": { "/alias": {
"$ref": "#/paths/~1common-for-path" "$ref": "#/paths/~1common-for-path"
} }
...@@ -895,7 +902,7 @@ class TestOpenAPIMissingParameters(OpenAPIPetStoreTestCase): ...@@ -895,7 +902,7 @@ class TestOpenAPIMissingParameters(OpenAPIPetStoreTestCase):
} }
} }
} }
}).encode() })
def test_required_query(self): def test_required_query(self):
self.addPythonScript( self.addPythonScript(
...@@ -980,7 +987,7 @@ class TestOpenAPIErrorHandling(OpenAPIPetStoreTestCase): ...@@ -980,7 +987,7 @@ class TestOpenAPIErrorHandling(OpenAPIPetStoreTestCase):
self.addPythonScript( self.addPythonScript(
'TestPetStoreOpenAPI_findPetsByStatus', 'TestPetStoreOpenAPI_findPetsByStatus',
'status', 'status',
'1/0', '1//0',
) )
response = self.publish( response = self.publish(
self.connector.getPath() + '/pet/findByStatus?status=available') self.connector.getPath() + '/pet/findByStatus?status=available')
...@@ -1097,7 +1104,7 @@ class TestPathParameterAndAcquisition(OpenAPIPetStoreTestCase): ...@@ -1097,7 +1104,7 @@ class TestPathParameterAndAcquisition(OpenAPIPetStoreTestCase):
""" """
def afterSetUp(self): def afterSetUp(self):
super(TestPathParameterAndAcquisition, self).afterSetUp() super(TestPathParameterAndAcquisition, self).afterSetUp()
if not '789' in self.portal.portal_web_services.objectIds(): if '789' not in self.portal.portal_web_services.objectIds():
self.portal.portal_web_services.newContent( self.portal.portal_web_services.newContent(
id='789', id='789',
portal_type=self.portal.portal_web_services.allowedContentTypes() portal_type=self.portal.portal_web_services.allowedContentTypes()
...@@ -1242,3 +1249,72 @@ class TestURLPathWithWebSiteAndVirtualHost(OpenAPIPetStoreTestCase): ...@@ -1242,3 +1249,72 @@ class TestURLPathWithWebSiteAndVirtualHost(OpenAPIPetStoreTestCase):
self.connector.getRelativeUrl() self.connector.getRelativeUrl()
)) ))
self.assertEqual(response.getBody(), b'"ok"') self.assertEqual(response.getBody(), b'"ok"')
class TestOpenAPIRequestBody(OpenAPITestCase):
_type_id = 'Test Open API Request Body'
_open_api_schema = json.dumps(
{
'openapi': '3.0.3',
'info': {
'title': 'TestOpenAPIRequestBody',
'version': '0.0.0'
},
'paths': {
'/post': {
'post': {
'operationId': 'testPostByContentType',
'requestBody': {
'content': {
'image/*': {
'schema': {
'type': 'string',
'format': 'binary',
}
},
'application/x-base64': {
'schema': {
'type': 'string',
'format': 'base64',
}
}
}
}
}
}
}
})
def test_request_body_content_encoding(self):
self.addPythonScript(
'TestOpenAPIRequestBody_testPostByContentType',
'body=None',
'container.REQUEST.RESPONSE.setHeader("Content-Type", "application/octet-stream")\n'
'return body',
)
response = self.publish(
self.connector.getPath() + '/post',
request_method='POST',
stdin=io.BytesIO(b'png file content'),
env={"CONTENT_TYPE": 'image/png'})
self.assertEqual(response.getBody(), b'png file content')
self.assertEqual(response.getStatus(), 200)
response = self.publish(
self.connector.getPath() + '/post',
request_method='POST',
stdin=io.BytesIO(base64_encodebytes(b'base64 file content')),
env={"CONTENT_TYPE": 'application/x-base64'})
self.assertEqual(response.getBody(), b'base64 file content')
self.assertEqual(response.getStatus(), 200)
response = self.publish(
self.connector.getPath() + '/post',
request_method='POST',
stdin=io.BytesIO(b'not base64'),
env={"CONTENT_TYPE": 'application/x-base64'})
self.assertEqual(response.getStatus(), 400)
body = json.loads(response.getBody())
self.assertEqual(body['type'], 'parameter-validation-error')
self.assertIn('Error validating request body:', body['title'])
...@@ -12,8 +12,19 @@ for open_order_line in context.objectValues(): ...@@ -12,8 +12,19 @@ for open_order_line in context.objectValues():
if getattr(item.aq_explicit, 'updateSimulation', None) is not None and \ if getattr(item.aq_explicit, 'updateSimulation', None) is not None and \
item not in subscription_item_set: item not in subscription_item_set:
subscription_item_set.add(item) subscription_item_set.add(item)
# If start_date is in futur, do not look for unreachable period
stop_date = item.getNextPeriodicalDate(max(now, ob.getStartDate())) # Logic duplicated from SubscriptionItem.py
start_date = ob.getStartDate()
# if there is no stop_date, block the generation
# to today
stop_date = ob.getStopDate()
current_date = start_date
if (start_date == stop_date) or (stop_date is None):
# stop_date seems acquired from start_date
stop_date = now
while current_date < stop_date:
current_date = item.getNextPeriodicalDate(current_date)
# Do not expand subscription item if there is # Do not expand subscription item if there is
# no new simulation movement to create # no new simulation movement to create
# (expand always reindex the full simulation tree, # (expand always reindex the full simulation tree,
...@@ -21,8 +32,8 @@ for open_order_line in context.objectValues(): ...@@ -21,8 +32,8 @@ for open_order_line in context.objectValues():
simulation_movement_list = portal.portal_simulation.getMovementHistoryList( simulation_movement_list = portal.portal_simulation.getMovementHistoryList(
portal_type='Simulation Movement', portal_type='Simulation Movement',
aggregate__uid=item.getUid(), aggregate__uid=item.getUid(),
from_date=stop_date, from_date=current_date,
at_date=stop_date, at_date=current_date,
only_accountable=False, only_accountable=False,
) )
if len(simulation_movement_list) == 0: if len(simulation_movement_list) == 0:
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_list</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_list</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>View</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/ReceiptRecognitionModule_viewReceiptRecognitionList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>receipt_convert</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>Modify portal content</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Convert Receipt</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/ReceiptRecognition_convertImage</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>View</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/ReceiptRecognition_view</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Extension Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>ReceiptRecognition</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>extension.erp5.ReceiptRecognition</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Extension Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<module>
<id>receipt_recognition_module</id>
<permission_list>
<permission type='tuple'>
<name>Access Transient Objects</name>
<role>Assignee</role>
<role>Assignor</role>
<role>Associate</role>
<role>Auditor</role>
<role>Author</role>
<role>Manager</role>
</permission>
<permission type='tuple'>
<name>Access contents information</name>
<role>Assignee</role>
<role>Assignor</role>
<role>Associate</role>
<role>Auditor</role>
<role>Author</role>
<role>Manager</role>
</permission>
<permission type='tuple'>
<name>Access session data</name>
<role>Assignee</role>
<role>Assignor</role>
<role>Associate</role>
<role>Auditor</role>
<role>Author</role>
<role>Manager</role>
</permission>
<permission type='tuple'>
<name>Add portal content</name>
<role>Assignor</role>
<role>Author</role>
<role>Manager</role>
</permission>
<permission type='tuple'>
<name>Add portal folders</name>
<role>Assignor</role>
<role>Author</role>
<role>Manager</role>
</permission>
<permission type='tuple'>
<name>Change local roles</name>
<role>Assignor</role>
<role>Manager</role>
</permission>
<permission type='tuple'>
<name>Copy or Move</name>
<role>Assignee</role>
<role>Assignor</role>
<role>Associate</role>
<role>Auditor</role>
<role>Author</role>
<role>Manager</role>
</permission>
<permission type='tuple'>
<name>Delete objects</name>
<role>Assignor</role>
<role>Manager</role>
</permission>
<permission type='tuple'>
<name>List folder contents</name>
<role>Assignee</role>
<role>Assignor</role>
<role>Associate</role>
<role>Auditor</role>
<role>Author</role>
<role>Manager</role>
</permission>
<permission type='tuple'>
<name>Modify portal content</name>
<role>Assignor</role>
<role>Manager</role>
</permission>
<permission type='tuple'>
<name>View</name>
<role>Assignee</role>
<role>Assignor</role>
<role>Associate</role>
<role>Auditor</role>
<role>Manager</role>
</permission>
<permission type='tuple'>
<name>View History</name>
<role>Assignee</role>
<role>Assignor</role>
<role>Associate</role>
<role>Auditor</role>
<role>Author</role>
<role>Manager</role>
</permission>
</permission_list>
<portal_type>Receipt Recognition Module</portal_type>
<title>Receipt Recognition</title>
</module>
\ No newline at end of file
<allowed_content_type_list>
<portal_type id="Receipt Recognition Module">
<item>Receipt Recognition</item>
</portal_type>
</allowed_content_type_list>
\ No newline at end of file
<base_category_list>
<portal_type id="Receipt Recognition Module">
<item>business_application</item>
</portal_type>
</base_category_list>
\ No newline at end of file
<property_sheet_list>
<portal_type id="Receipt Recognition">
<item>Document</item>
</portal_type>
</property_sheet_list>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_property_domain_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>acquire_local_roles</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>content_icon</string> </key>
<value> <string>folder_icon.gif</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>factory</string> </key>
<value> <string>addFolder</string> </value>
</item>
<item>
<key> <string>filter_content_types</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<tuple>
<string>module</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Receipt Recognition Module</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Base Type</string> </value>
</item>
<item>
<key> <string>searchable_text_property_id</string> </key>
<value>
<tuple>
<string>title</string>
<string>description</string>
<string>reference</string>
<string>short_title</string>
</tuple>
</value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>Folder</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>short_title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value> <string>erp5_ui</string> </value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>short_title</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value> <string>erp5_ui</string> </value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>title</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>acquire_local_roles</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>content_icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>factory</string> </key>
<value> <string>addXMLObject</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Receipt Recognition</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Base Type</string> </value>
</item>
<item>
<key> <string>searchable_text_property_id</string> </key>
<value>
<tuple>
<string>title</string>
<string>description</string>
<string>reference</string>
<string>short_title</string>
</tuple>
</value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>XMLObject</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Folder" module="OFS.Folder"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_receipt_recognition</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>Base_doSelect</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list>
<string>listbox</string>
</list>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ReceiptRecognitionModule_viewReceiptRecognitionList</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>ReceiptRecognitionModule_viewReceiptRecognitionList</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_list</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Receipt Recognition</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>columns</string>
<string>portal_types</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>listbox</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>columns</string> </key>
<value>
<list>
<tuple>
<string>title</string>
<string>Title</string>
</tuple>
<tuple>
<string>total</string>
<string>Total</string>
</tuple>
<tuple>
<string>follow_up_title</string>
<string>Image</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_list_mode_listbox</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value>
<list>
<tuple>
<string>Receipt Recognition</string>
<string>Receipt Recognition</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>portal_types</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Receipt Recognition</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
image = context.getFollowUpValue()
if image is not None:
try:
total = container.ReceiptRecognition_getReceiptValue(image.getData())
message = "Total found"
context.edit(
total = total,
)
except ValueError as e:
message = "Could not find value, please submit it manually"
else:
message = "Cannot find the image"
if batch_mode:
return
context.Base_redirect(
'view', keep_items = dict(portal_status_message=message, my_source="test"))
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>batch_mode=False, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ReceiptRecognition_convertImage</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>getReceiptValue</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>ReceiptRecognition</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ReceiptRecognition_getReceiptValue</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>Base_edit</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>edit_order</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>my_title</string>
<string>my_total</string>
<string>my_follow_up_title</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ReceiptRecognition_view</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>ReceiptRecognition_view</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_view</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Receipt Recognition</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>update_action_title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>portal_type</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_follow_up_title</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_relation_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value>
<list>
<tuple>
<string>Image</string>
<string>Image</string>
</tuple>
</list>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Follow Up</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_title</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Title</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_total</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Total</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Receipt Recognition Module | view
Receipt Recognition | receipt_convert
Receipt Recognition | view
\ No newline at end of file
extension.erp5.ReceiptRecognition
\ No newline at end of file
receipt_recognition_module
\ No newline at end of file
Receipt Recognition Module | Receipt Recognition
\ No newline at end of file
Receipt Recognition Module | business_application
\ No newline at end of file
Receipt Recognition
Receipt Recognition Module
\ No newline at end of file
Receipt Recognition | Document
\ No newline at end of file
erp5_receipt_recognition
\ No newline at end of file
erp5_full_text_mroonga_catalog
\ No newline at end of file
erp5_receipt_recognition
\ No newline at end of file
0.1.0
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Image" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value> <string>1cbedf94ee11f2ecf3c3784438c7ce4e</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>image/jpeg</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>filename</string> </key>
<value> <string>IMG_0107.jpg</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>2048</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_receipt_recognition_test_image_sample_001</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Image</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Receipt00</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>1536</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Image" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value> <string>72d018e3e436cf176233eb75e48f6914</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>image/jpeg</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>filename</string> </key>
<value> <string>IMG_0105.jpg</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>2048</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_receipt_recognition_test_image_sample_002</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Image</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Receipt01</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>1536</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Image" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value> <string>ca8b2ab2191e7ac2dbc80817b97728bc</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>image/jpeg</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>filename</string> </key>
<value> <string>IMG_0106.jpg</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>2048</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_receipt_recognition_test_image_sample_003</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Image</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Receipt02</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>1536</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Image" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value> <string>709b63a3648d5ccd920743f997367e6e</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>image/jpeg</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>filename</string> </key>
<value> <string>IMG_0104.jpg</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>2048</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_receipt_recognition_test_image_sample_004</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Image</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Receipt03</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>1536</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Image" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value> <string>7f9696e9dc5ce485adf05663dd635480</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>image/jpeg</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>filename</string> </key>
<value> <string>IMG_0101.jpg</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>2048</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_receipt_recognition_test_image_sample_005</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Image</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Receipt04</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>1536</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Image" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value> <string>909a682d7bfd0131ef39405edacc52cc</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>image/jpeg</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>filename</string> </key>
<value> <string>IMG_0095.jpg</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>2048</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_receipt_recognition_test_image_sample_006</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Image</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Receipt05</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>1536</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Image" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
<string>Owner</string>
</tuple>
</value>
</item>
<item>
<key> <string>_count</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_mt_index</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>_tree</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value> <string>14e4ef10a849cd6897e8f8f981a37ccb</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>image/jpeg</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>filename</string> </key>
<value> <string>IMG_0102.jpg</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>2048</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>erp5_receipt_recognition_test_image_sample_007</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Image</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Receipt06</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>1536</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Length" module="BTrees.Length"/>
</pickle>
<pickle> <int>0</int> </pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="OOBTree" module="BTrees.OOBTree"/>
</pickle>
<pickle>
<none/>
</pickle>
</record>
</ZopeData>
erp5_dms
erp5_receipt_recognition
\ No newline at end of file
image_module/erp5_receipt_recognition_test_image_sample_001
image_module/erp5_receipt_recognition_test_image_sample_002
image_module/erp5_receipt_recognition_test_image_sample_003
image_module/erp5_receipt_recognition_test_image_sample_004
image_module/erp5_receipt_recognition_test_image_sample_005
image_module/erp5_receipt_recognition_test_image_sample_006
image_module/erp5_receipt_recognition_test_image_sample_007
image_module/erp5_receipt_recognition_test_image_sample_008
image_module/erp5_receipt_recognition_test_image_sample_009
\ No newline at end of file
test.erp5.testReceiptRecognition
\ No newline at end of file
erp5_full_text_mroonga_catalog
\ No newline at end of file
erp5_receipt_recognition_test
\ No newline at end of file
...@@ -279,7 +279,7 @@ ...@@ -279,7 +279,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>_text</string> </key> <key> <string>_text</string> </key>
<value> <string>python: modules[\'datetime\'].timedelta(seconds=context.getProperty(\'duration\'))</string> </value> <value> <string>python: str(modules[\'datetime\'].timedelta(seconds=int(context.getProperty(\'duration\'))))</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
import datetime import datetime
from Products.PythonScripts.standard import Object from Products.PythonScripts.standard import Object
return [Object(duration=datetime.timedelta(seconds=context.getProperty('duration', 0)), return [Object(duration=str(datetime.timedelta(seconds=int(context.getProperty('duration', 0)))),
all_tests=context.getProperty('all_tests'), all_tests=context.getProperty('all_tests'),
errors=context.getProperty('errors'), errors=context.getProperty('errors'),
failures=context.getProperty('failures'), failures=context.getProperty('failures'),
......
...@@ -279,7 +279,7 @@ ...@@ -279,7 +279,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>_text</string> </key> <key> <string>_text</string> </key>
<value> <string>python: modules[\'datetime\'].timedelta(seconds=cell.getProperty(\'duration\'))</string> </value> <value> <string>python: str(modules[\'datetime\'].timedelta(seconds=int(cell.getProperty(\'duration\'))))</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -64,7 +64,6 @@ import struct ...@@ -64,7 +64,6 @@ import struct
from base64 import b64encode, b64decode from base64 import b64encode, b64decode
from Products.ERP5Type.Message import translateString from Products.ERP5Type.Message import translateString
from zLOG import LOG, INFO, WARNING from zLOG import LOG, INFO, WARNING
from base64 import decodestring
import subprocess import subprocess
import time import time
from Products.ERP5Type.Utils import bytes2str from Products.ERP5Type.Utils import bytes2str
...@@ -404,16 +403,6 @@ class TemplateTool (BaseTool): ...@@ -404,16 +403,6 @@ class TemplateTool (BaseTool):
bt.setPublicationUrl(url) bt.setPublicationUrl(url)
return bt return bt
security.declareProtected('Import/Export objects', 'importBase64EncodedText')
def importBase64EncodedText(self, file_data=None, id=None, REQUEST=None,
batch_mode=False, **kw):
"""
Import Business Template from passed base64 encoded text.
"""
import_file = StringIO(decodestring(file_data))
return self.importFile(import_file = import_file, id = id, REQUEST = REQUEST,
batch_mode = batch_mode, **kw)
security.declareProtected('Import/Export objects', 'importFile') security.declareProtected('Import/Export objects', 'importFile')
def importFile(self, import_file=None, id=None, REQUEST=None, def importFile(self, import_file=None, id=None, REQUEST=None,
batch_mode=False, **kw): batch_mode=False, **kw):
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment