[PATCH] Upstream patch - 05102023

This commit is contained in:
Parthiv Patel
2023-10-05 08:35:00 +00:00
parent 56742c04f3
commit 1e867ba00b
8 changed files with 269 additions and 25 deletions
+6 -1
View File
@@ -306,6 +306,11 @@ def download_iot_handlers(auto=True):
_logger.error('Could not reach configured server')
_logger.error('A error encountered : %s ' % e)
def compute_iot_handlers_addon_name(handler_kind, handler_file_name):
# TODO: replace with `removesuffix` (for Flectra version using an IoT image that use Python >= 3.9)
return "flectra.addons.hw_drivers.iot_handlers.{handler_kind}.{handler_name}".\
format(handler_kind=handler_kind, handler_name=handler_file_name.replace('.py', ''))
def load_iot_handlers():
"""
This method loads local files: 'flectra/addons/hw_drivers/iot_handlers/drivers' and
@@ -317,7 +322,7 @@ def load_iot_handlers():
filesList = os.listdir(path)
for file in filesList:
path_file = os.path.join(path, file)
spec = util.spec_from_file_location(file, path_file)
spec = util.spec_from_file_location(compute_iot_handlers_addon_name(directory, file), path_file)
if spec:
module = util.module_from_spec(spec)
spec.loader.exec_module(module)
+155 -5
View File
@@ -11,8 +11,8 @@ import subprocess
import sys
import threading
from flectra import http
from flectra.http import Response
from flectra import http, tools
from flectra.http import Response, request
from flectra.modules.module import get_resource_path
from flectra.addons.hw_drivers.main import iot_devices
@@ -104,8 +104,46 @@ class IoTboxHomepage(web.Home):
else:
return homepage_template.render(self.get_homepage_data())
@http.route('/list_handlers', type='http', auth='none', website=True)
def list_handlers(self):
@http.route('/list_handlers', type='http', auth='none', methods=['GET', 'POST'], website=True, csrf=False)
def list_handlers(self, **post):
AVAILABLE_LOG_LEVELS = ('debug', 'info', 'warning', 'error')
if request.httprequest.method == 'POST':
IOT_LOGGING_PREFIX = 'iot-logging-'
INTERFACE_PREFIX = 'interface-'
DRIVER_PREFIX = 'driver-'
AVAILABLE_LOG_LEVELS_WITH_PARENT = AVAILABLE_LOG_LEVELS + ('parent',)
need_config_save = False
for post_request_key, log_level_or_parent in post.items():
if not post_request_key.startswith(IOT_LOGGING_PREFIX):
# probably a new post request payload argument not related to logging
continue
post_request_key = post_request_key[len(IOT_LOGGING_PREFIX):]
if post_request_key == 'root':
need_config_save |= self._update_logger_level('', log_level_or_parent, AVAILABLE_LOG_LEVELS)
elif post_request_key == 'flectra':
need_config_save |= self._update_logger_level('flectra', log_level_or_parent, AVAILABLE_LOG_LEVELS)
need_config_save |= self._update_logger_level(
'werkzeug',
log_level_or_parent if log_level_or_parent != 'debug' else 'info',
AVAILABLE_LOG_LEVELS)
elif post_request_key.startswith(INTERFACE_PREFIX):
logger_name = post_request_key[len(INTERFACE_PREFIX):]
need_config_save |= self._update_logger_level(logger_name, log_level_or_parent, AVAILABLE_LOG_LEVELS_WITH_PARENT, 'interfaces')
elif post_request_key.startswith(DRIVER_PREFIX):
logger_name = post_request_key[len(DRIVER_PREFIX):]
need_config_save |= self._update_logger_level(logger_name, log_level_or_parent, AVAILABLE_LOG_LEVELS_WITH_PARENT, 'drivers')
else:
_logger.warning('Unhandled iot logger: %s', post_request_key)
# Update and save the config file (in case of IoT box reset)
if need_config_save:
# TODO: replace mount calls `with writable():` (in version >= 16.0)
subprocess.call(["sudo", "mount", "-o", "remount,rw", "/"])
tools.config.save()
subprocess.call(["sudo", "mount", "-o", "remount,ro", "/"])
drivers_list = []
for driver in os.listdir(get_resource_path('hw_drivers', 'iot_handlers/drivers')):
if driver != '__pycache__':
@@ -114,12 +152,19 @@ class IoTboxHomepage(web.Home):
for interface in os.listdir(get_resource_path('hw_drivers', 'iot_handlers/interfaces')):
if interface != '__pycache__':
interfaces_list.append(interface)
return handler_list_template.render({
'title': "Flectra's IoT Box - Handlers list",
'breadcrumb': 'Handlers list',
'drivers_list': drivers_list,
'interfaces_list': interfaces_list,
'server': helpers.get_flectra_server_url()
'server': helpers.get_flectra_server_url(),
'root_logger_log_level': self._get_logger_effective_level_str(logging.getLogger()),
'flectra_current_log_level': self._get_logger_effective_level_str(logging.getLogger('flectra')),
'recommended_log_level': 'warning',
'available_log_levels': AVAILABLE_LOG_LEVELS,
'drivers_logger_info': self._get_iot_handlers_logger(drivers_list, 'drivers'),
'interfaces_logger_info': self._get_iot_handlers_logger(interfaces_list, 'interfaces'),
})
@http.route('/load_iot_handlers', type='http', auth='none', website=True)
@@ -361,3 +406,108 @@ class IoTboxHomepage(web.Home):
self.clean_partition()
_logger.error('A error encountered : %s ' % e)
return Response(str(e), status=500)
def _get_logger_effective_level_str(self, logger):
return logging.getLevelName(logger.getEffectiveLevel()).lower()
def _get_iot_handler_logger(self, handler_name, handler_folder_name):
"""
Get Flectra Iot logger given an IoT handler name
:param handler_name: name of the IoT handler
:param handler_folder_name: IoT handler folder name (interfaces or drivers)
:return: logger if any, False otherwise
"""
flectra_addon_handler_path = helpers.compute_iot_handlers_addon_name(handler_folder_name, handler_name)
return flectra_addon_handler_path in logging.Logger.manager.loggerDict.keys() and \
logging.getLogger(flectra_addon_handler_path)
def _update_logger_level(self, logger_name, new_level, available_log_levels, handler_folder=False):
"""
Update (if necessary) Flectra's configuration and logger to the given logger_name to the given level.
The responsibility of saving the config file is not managed here.
:param logger_name: name of the logging logger to change level
:param new_level: new log level to set for this logger
:param available_log_levels: iterable of logs levels allowed (for initial check)
:param handler_folder: optional string of the IoT handler folder name ('interfaces' or 'drivers')
:return: wherever some changes were performed or not on the config
"""
if new_level not in available_log_levels:
_logger.warning('Unknown level to set on logger %s: %s', logger_name, new_level)
return False
if handler_folder:
logger = self._get_iot_handler_logger(logger_name, handler_folder)
if not logger:
_logger.warning('Unable to change log level for logger %s as logger missing', logger_name)
return False
logger_name = logger.name
FLECTRA_TOOL_CONFIG_HANDLER_NAME = 'log_handler'
LOG_HANDLERS = tools.config[FLECTRA_TOOL_CONFIG_HANDLER_NAME]
LOGGER_PREFIX = logger_name + ':'
IS_NEW_LEVEL_PARENT = new_level == 'parent'
if not IS_NEW_LEVEL_PARENT:
intended_to_find = LOGGER_PREFIX + new_level.upper()
if intended_to_find in LOG_HANDLERS:
# There is nothing to do, the entry is already inside
return False
# We remove every occurrence for the given logger
log_handlers_without_logger = [
log_handler for log_handler in LOG_HANDLERS if not log_handler.startswith(LOGGER_PREFIX)
]
if IS_NEW_LEVEL_PARENT:
# We must check that there is no existing entries using this logger (whatever the level)
if len(log_handlers_without_logger) == len(LOG_HANDLERS):
return False
# We add if necessary new logger entry
# If it is "parent" it means we want it to inherit from the parent logger.
# In order to do this we have to make sure that no entries for the logger exists in the
# `log_handler` (which is the case at this point as long as we don't re-add an entry)
tools.config[FLECTRA_TOOL_CONFIG_HANDLER_NAME] = log_handlers_without_logger
new_level_upper_case = new_level.upper()
if not IS_NEW_LEVEL_PARENT:
new_entry = [LOGGER_PREFIX + new_level_upper_case]
tools.config[FLECTRA_TOOL_CONFIG_HANDLER_NAME] += new_entry
_logger.debug('Adding to flectra config log_handler: %s', new_entry)
# Update the logger dynamically
real_new_level = logging.NOTSET if IS_NEW_LEVEL_PARENT else new_level_upper_case
_logger.debug('Change logger %s level to %s', logger_name, real_new_level)
logging.getLogger(logger_name).setLevel(real_new_level)
return True
def _get_iot_handlers_logger(self, handlers_name, iot_handler_folder_name):
"""
:param handlers_name: List of IoT handler string to search the loggers of
:param iot_handler_folder_name: name of the handler folder ('interfaces' or 'drivers')
:return:
{
<iot_handler_name_1> : {
'level': <logger_level_1>,
'is_using_parent_level': <logger_use_parent_level_or_not_1>,
'parent_name': <logger_parent_name_1>,
},
...
}
"""
handlers_loggers_level = dict()
for handler_name in handlers_name:
handler_logger = self._get_iot_handler_logger(handler_name, iot_handler_folder_name)
if not handler_logger:
# Might happen if the file didn't define a logger (or not init yet)
handlers_loggers_level[handler_name] = False
_logger.debug('Unable to find logger for handler %s', handler_name)
continue
logger_parent = handler_logger.parent
handlers_loggers_level[handler_name] = {
'level': self._get_logger_effective_level_str(handler_logger),
'is_using_parent_level': handler_logger.level == logging.NOTSET,
'parent_name': logger_parent.name,
'parent_level': self._get_logger_effective_level_str(logger_parent),
}
return handlers_loggers_level
@@ -23,22 +23,98 @@
</script>
{% endblock %}
{% block content %}
<h2 class="text-center text-green">Interfaces list</h2>
<table align="center" width="50%" cellpadding="3">
{% for interfaces in interfaces_list -%}
<tr><td>{{ interfaces }}</td></tr>
{%- endfor %}
</table>
<h2 class="text-center text-green">Drivers list</h2>
<table align="center" width="50%" cellpadding="3">
{% for driver in drivers_list -%}
<tr><td>{{ driver }}</td></tr>
{%- endfor %}
</table>
{% if server %}
<div style="margin-top: 20px;" class="text-center">
<a id="load_handler_btn" class="btn" href='/load_iot_handlers'>Load handlers</a>
<h2 class="text-center text-green">Logging</h2>
<form method="post">
<div style="text-align: left;display:block;">
<label for="iot-logging-root">Root log level (Current value: {{root_logger_log_level}}):</label>
<select name="iot-logging-root" id="iot-logging-root">
{% for log_level in available_log_levels %}
<option value="{{ log_level }}" {{ "selected" if log_level == root_logger_log_level }}>
{{ log_level | capitalize }} {{ "(Recommended)" if log_level == recommended_log_level }}
</option>
{% endfor %}
</select>
<label for="iot-logging-flectra">Flectra log level (Current value: {{flectra_current_log_level}}):</label>
<select name="iot-logging-flectra" id="iot-logging-flectra">
{% for log_level in available_log_levels %}
<option value="{{ log_level }}" {{ "selected" if log_level == flectra_current_log_level }}>
{{ log_level | capitalize }} {{ "(Recommended)" if log_level == recommended_log_level }}
</option>
{% endfor %}
</select>
</div>
<h2 class="text-center text-green">Interfaces list</h2>
<table align="center" width="80%" cellpadding="3">
<tr>
<th>Name</th>
<th>Log Level</th>
</tr>
{% for interface in interfaces_list -%}
<tr>
<td>{{ interface }}</td>
<td>
{% set interface_logger_info = interfaces_logger_info[interface] %}
{% if interface_logger_info != False %}
<select name="iot-logging-interface-{{interface}}">
<option value="parent" {{ "selected" if interface_logger_info.is_using_parent_level }}>
Same as {{ interface_logger_info.parent_name | capitalize }} ({{ interface_logger_info.parent_level | capitalize }})
</option>
<option style="font-size: 1pt; background-color: black;" disabled>&nbsp;</option>
{% for log_level in available_log_levels %}
<option value="{{ log_level }}" {{ "selected" if not interface_logger_info.is_using_parent_level and log_level == interface_logger_info.level }}>
{{ log_level | capitalize }}
</option>
{% endfor %}
</select>
{% else %}
<span class="font-small">Logger uninitialised</span>
{% endif %}
</td>
</tr>
{%- endfor %}
</table>
<h2 class="text-center text-green">Drivers list</h2>
<table align="center" width="80%" cellpadding="3">
<tr>
<th>Name</th>
<th>Log Level</th>
</tr>
{% for driver in drivers_list -%}
<tr>
<td>{{ driver }}</td>
<td>
{% set driver_logger_info = drivers_logger_info[driver] %}
{% if driver_logger_info != False %}
<select name="iot-logging-driver-{{driver}}">
<option value="parent" {{ "selected" if driver_logger_info.is_using_parent_level }}>
Same as {{ driver_logger_info.parent_name | capitalize }} ({{ driver_logger_info.parent_level | capitalize }})
</option>
<option style="font-size: 1pt; background-color: black;" disabled>&nbsp;</option>
{% for log_level in available_log_levels %}
<option value="{{ log_level }}" {{ "selected" if not driver_logger_info.is_using_parent_level and log_level == driver_logger_info.level }}>
{{ log_level | capitalize }}
</option>
{% endfor %}
</select>
{% else %}
<span class="font-small">Logger uninitialised</span>
{% endif %}
</td>
</tr>
{%- endfor %}
</table>
<div style="margin-top: 20px;" class="text-center">
{% if server %}
<a id="load_handler_btn" class="btn" href='/load_iot_handlers'>Load handlers</a>
{% endif %}
<input class="btn" type="submit" value="Update Logs Level Value"/>
</div>
</form>
{% if server %}
<div class="text-center font-small" style="margin: 10px auto;">
You can clear the handlers configuration
<form style="display: inline-block;margin-left: 4px;" action='/handlers_clear'>
@@ -182,7 +182,7 @@
<template id="report_ddt">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-set="lang" t-value="o.partner_id.lang if o.partner_id.lang else o.env.lang"/>
<t t-set="lang" t-value="o._get_report_lang()"/>
<t t-call="l10n_it_stock_ddt.report_ddt_view" t-lang="lang"/>
</t>
</t>
+5
View File
@@ -5,6 +5,7 @@
<record id="ieps_8_sale" model="account.tax.template">
<field name="sequence" eval="0"/>
<field name="chart_template_id" ref="mx_coa"/>
<field name="active" eval="False"/>
<field name="name">IEPS 8% VENTAS</field>
<field name="description">IEPS 8%</field>
<field name="amount">8</field>
@@ -85,6 +86,7 @@
<record id="ieps_25_sale" model="account.tax.template">
<field name="sequence" eval="2"/>
<field name="chart_template_id" ref="mx_coa"/>
<field name="active" eval="False"/>
<field name="name">IEPS 25% VENTAS</field>
<field name="description">IEPS 25%</field>
<field name="amount">25</field>
@@ -165,6 +167,7 @@
<record id="ieps_26_5_sale" model="account.tax.template">
<field name="sequence" eval="4"/>
<field name="chart_template_id" ref="mx_coa"/>
<field name="active" eval="False"/>
<field name="name">IEPS 26.5% VENTAS</field>
<field name="description">IEPS 26.5%</field>
<field name="amount">26.5</field>
@@ -245,6 +248,7 @@
<record id="ieps_30_sale" model="account.tax.template">
<field name="sequence" eval="6"/>
<field name="chart_template_id" ref="mx_coa"/>
<field name="active" eval="False"/>
<field name="name">IEPS 30% VENTAS</field>
<field name="description">IEPS 30%</field>
<field name="amount">30</field>
@@ -325,6 +329,7 @@
<record id="ieps_53_sale" model="account.tax.template">
<field name="sequence" eval="8"/>
<field name="chart_template_id" ref="mx_coa"/>
<field name="active" eval="False"/>
<field name="name">IEPS 53% VENTAS</field>
<field name="description">IEPS 53%</field>
<field name="amount">53</field>
@@ -1,6 +1,7 @@
[options]
data_dir = /var/run/odoo
log_level = error
log_handler = :WARNING
log_level = warn
logfile = /var/log/odoo/odoo-server.log
pidfile = /var/run/odoo/odoo.pid
limit_time_cpu = 600
@@ -78,12 +78,19 @@ $(function () {
});
},
previous: function () {
const slideSuggestOverlay = this.$("#slide_suggest");
if (!slideSuggestOverlay.hasClass('d-none')) {
// Hide suggested slide overlay before changing page nb.
slideSuggestOverlay.addClass('d-none');
this.$('#next, #last').removeClass('disabled');
return;
}
var self = this;
this.pdf_viewer.previousPage().then(function (pageNum) {
if (pageNum) {
self.on_rendered_page(pageNum);
}
self.$("#slide_suggest").addClass('d-none');
slideSuggestOverlay.addClass('d-none');
});
},
first: function () {
@@ -6,4 +6,4 @@ furniture_certification_answer_1_p1_q2_3,furniture_certification_answer_1,furnit
furniture_certification_answer_1_p1_q3,furniture_certification_answer_1,furniture_certification_page_1_question_3,False,text_box,,,,"I really liked the videos, you are awesome!",,
furniture_certification_answer_2_p1_q1,furniture_certification_answer_2,furniture_certification_page_1_question_1,False,suggestion,,,,,furniture_certification_page_1_question_1_choice_3,
furniture_certification_answer_2_p1_q2,furniture_certification_answer_2,furniture_certification_page_1_question_2,False,suggestion,,,,,furniture_certification_page_1_question_2_choice_4,
furniture_certification_answer_2_p1_q3,furniture_certification_answer_2,furniture_certification_page_1_question_3,true,,,,,,,
furniture_certification_answer_2_p1_q3,furniture_certification_answer_2,furniture_certification_page_1_question_3,true,,,,,,,
1 id user_input_id:id question_id:id skipped answer_type value_char_box value_numerical_box value_date value_text_box suggested_answer_id:id matrix_row_id:id
6 furniture_certification_answer_1_p1_q3 furniture_certification_answer_1 furniture_certification_page_1_question_3 False text_box I really liked the videos, you are awesome!
7 furniture_certification_answer_2_p1_q1 furniture_certification_answer_2 furniture_certification_page_1_question_1 False suggestion furniture_certification_page_1_question_1_choice_3
8 furniture_certification_answer_2_p1_q2 furniture_certification_answer_2 furniture_certification_page_1_question_2 False suggestion furniture_certification_page_1_question_2_choice_4
9 furniture_certification_answer_2_p1_q3 furniture_certification_answer_2 furniture_certification_page_1_question_3 true