2********************************************************************************
4@brief configuration settings
5********************************************************************************
10import configparser
as cfg
11from configparser
import ConfigParser, DuplicateSectionError, DuplicateOptionError
13from collections
import OrderedDict
14from typing
import Any, TYPE_CHECKING
15from datetime
import datetime
18from Source.Util.app_data import read_user_settings, write_user_settings, read_articles_settings, write_articles_settings, \
19 ItemNumber, EUser, D_DEFAULT_USER, D_DEFAULT_ITEM, S_GENERAL, DEFAULT_CODE, L_ALLOWED_USER, \
20 S_DEVICE, S_USER_TIMEOUT, S_AUTO_OPEN, S_FREE_AUTO_LOGOUT, S_NAME, S_PW, S_PRINTS, S_PRINT_ARTICLE, S_PRINT_FREE_PRICE, \
21 I_GROUP_1, I_GROUP_2, I_GROUP_3, S_TAX, S_USER_TAX, S_GROUP_1, S_GROUP_2, S_GROUP_3, S_HEADER, S_HEADER_1, S_HEADER_2, \
22 S_GROUP, S_PRICE, S_BREAK_LINE, S_USER_VISIBLE, S_PRINT, S_MARK, S_BACKGROUND
26S_ITEM_FILE =
"articles.ini"
27S_ITEM_TEMP_FILE =
"_temp_articles.ini"
28S_USER_FILE =
"user.ini"
29S_USER_TEMP_FILE =
"_temp_user.ini"
31log = logging.getLogger(__title__)
36 @brief Check if string can convert to float.
37 @param value : value to check
38 @return status if string can convert to float
43 except (TypeError, ValueError):
50 @brief Check if string can convert to integer.
51 @param value : value to check
52 @return status if string can convert to integer
57 except (TypeError, ValueError):
62def get_dict_float(d_dict: dict[str, dict[str, str]], s_section: str, s_key: str) -> float:
64 @brief Get floating value in dictionary.
65 @param d_dict : dictionary
66 @param s_section : section
68 @return get floating value from dictionary. 0 if not present
71 if s_section
in d_dict:
72 value = d_dict[s_section].get(s_key)
75 f_float = float(value)
79def get_dict_int(d_dict: dict[str, dict[str, str]], s_section: str, s_key: str) -> int:
81 @brief Get integer value in dictionary.
82 @param d_dict : dictionary
83 @param s_section : section
85 @return get integer value from dictionary. 0 if not present
88 if s_section
in d_dict:
89 value = d_dict[s_section].get(s_key)
96def get_dict_value(d_dict: dict[str, dict[str, str]], s_section: str, s_key: str) -> str:
98 @brief Get value from dictionary.
99 @param d_dict : dictionary
100 @param s_section : section
102 @return get integer value from dictionary. Nothing if not present
104 if s_section
in d_dict
and s_key
in d_dict[s_section]:
105 value = str(d_dict[s_section][s_key])
113 @brief Class Configuration: Read/Write configuration data (user or articles)
114 @param ui : main window object
127 @brief Update extended article status.
128 @param prevent_resize : prevent resize
130 item_number_size = ItemNumber.MIN
131 for i
in range(ItemNumber.MAX, ItemNumber.MIN, -1):
134 if s_item_name !=
"":
135 if i_item_number > ItemNumber.EXT:
136 item_number_size = ItemNumber.MAX
138 if i_item_number > ItemNumber.MIN:
139 item_number_size = ItemNumber.EXT
143 if not prevent_resize:
144 self.
ui.model.c_monitor.resize_window()
148 @brief Get smart card user status.
149 @return start card user activation status
152 if not origin_device_name.isalnum():
155 device_name = origin_device_name
160 @brief Get timeout for inactive user.
161 @return logout timeout
167 @brief Get drawer auto open status.
168 @return cash drawer auto open status
171 return not bool(s_select ==
"False")
175 @brief Get free user auto logout status.
176 @return free user auto logout status
179 return not bool(s_select ==
"False")
183 @brief Get name of user (only for order staff).
184 @param user : get name of this user
191 @brief Get password of user.
192 @param user : get password of this user
193 @return password of user
199 @brief Get first bon header
200 @return first bon header string
206 @brief Get second bon header
207 @return second bon header string
213 @brief Get tax value from group
214 @param i_group : get tax of this group (1-3)
215 @return tax value of group
217 if i_group == I_GROUP_1:
219 elif i_group == I_GROUP_2:
227 @brief Get user involvement
228 @return user involvement
234 @brief Get article print status
235 @return article print status
238 return not bool(s_select ==
"False")
242 @brief Get print free user price status
243 @return print free price status
246 return not bool(s_select ==
"False")
251 @param i_item_number : item number (1-30)
259 @brief Get item name for button. (with new lines)
260 @param i_item_number : item number (1-30)
261 @return name of item ("" for not present user)
263 if i_item_number > ItemNumber.MAX:
264 i_item_number -= ItemNumber.MAX
269 @brief Get item price
270 @param i_item_number : item number (1-30)
271 @return price of item
278 @brief Get item group
279 @param i_item_number : item number (1-30)
280 @return group of item
283 if i_group
not in [I_GROUP_1, I_GROUP_2, I_GROUP_3]:
289 @brief Get visible user for item
290 @param i_item_number : item number (1-30)
291 @return visible user for item
294 user_values = [user.value
for user
in EUser]
295 if s_user
in user_values:
296 e_user =
EUser(s_user)
303 @brief Get item print status
304 @param i_item_number : item number (1-30)
305 @return print status of item
308 return not bool(s_select ==
"False")
312 @brief Get item mark status
313 @param i_item_number : item number (1-30)
314 @return mark status of item
317 return bool(s_select ==
"True")
321 @brief Get item background
322 @param i_item_number : item number (1-30)
323 @return mark status of item
330 @brief Store actual user data
332 write_user_settings(self.
d_user)
336 @brief Read user file configuration and save in settings.
337 @param s_file : file top read
338 @param b_remove_file : [True] = delete file; [False] = don't delete file
339 @return status if data was imported
341 log.debug(
"Import edited or new user file: %s", s_file)
345 if config_data
is not None:
346 d_read_user_dict = config_data
347 if any(value
in d_read_user_dict
for value
in L_ALLOWED_USER):
350 b_set_data = self.
ui.confirm_dialog([
"Import file?",
"Datei importieren?"],
351 l_optional_text=[f
"This file does not contain any user data:\n{s_file}",
352 f
"Diese Datei enthält keine Benutzer Daten:\n{s_file}"])
354 if (
not self.
ui.model.c_auth.check_user_login(EUser.ADMIN))
and (EUser.ADMIN.value
in d_read_user_dict):
355 del d_read_user_dict[EUser.ADMIN.value]
356 if EUser.ADMIN.value
not in d_read_user_dict:
357 if EUser.ADMIN.value
in self.
d_user:
358 d_read_user_dict[EUser.ADMIN.value] = self.
d_user[EUser.ADMIN.value]
360 self.
ui.set_status(
"No Admin data to restore",
True)
361 self.
d_user = d_read_user_dict.copy()
363 if EUser.ADMIN.value
not in self.
d_user:
364 self.
d_user[EUser.ADMIN.value] = D_DEFAULT_USER[EUser.ADMIN.value].copy()
365 self.
d_user[EUser.ADMIN.value][S_PW] = DEFAULT_CODE
366 self.
ui.set_status([f
"No Admin data. Password is set to default: {DEFAULT_CODE}",
367 f
"Keine Admin Daten. Passwort wurde zurückgesetzt: {DEFAULT_CODE}"],
True)
368 if S_PW
not in self.
d_user[EUser.ADMIN.value]:
369 self.
d_user[EUser.ADMIN.value][S_PW] = DEFAULT_CODE
370 self.
ui.set_status([f
"No Admin Password data. Password is set to default: {DEFAULT_CODE}",
371 f
"Kein Admin Passwort. Passwort wurde zurückgesetzt: {DEFAULT_CODE}"],
True)
372 if not self.
d_user[EUser.ADMIN.value][S_PW].isdigit()
and self.
d_user[EUser.ADMIN.value][S_PW] !=
"":
373 self.
d_user[EUser.ADMIN.value][S_PW] = DEFAULT_CODE
374 self.
ui.set_status([f
"Admin password is invalid. Password is set to default: {DEFAULT_CODE}",
375 f
"Admin Passwort ist ungültig. Passwort wurde zurückgesetzt: {DEFAULT_CODE}"],
True)
376 if not self.
ui.test_mode:
380 self.
ui.set_status([
"User configuration changed",
"Benutzer Konfiguration wurde geändert"])
383 b_edit = self.
ui.confirm_dialog([
"Invalid. Edit again?",
"Ungültig. Erneut bearbeiten?"])
385 self.
ui.edit_user_in_notepad(s_file)
387 self.
ui.set_status([
"Unable to read user configuration with duplicate users",
388 "Benutzerkonfiguration mit doppelten Benutzern kann nicht gelesen werden"],
True)
390 self.
ui.set_status([
"Unable to import user configuration with duplicate users",
391 "Importieren der Benutzerkonfiguration mit doppelten Benutzern nicht möglich"],
True)
396 @brief Read articles file configuration and save in settings.
397 @param s_file : file top read
398 @param b_remove_file : [True] = delete file; [False] = don't delete file
399 @return status if data was imported
401 log.debug(
"Import edited or new articles file: %s", s_file)
405 if config_data
is not None:
406 l_possible_articles = [str(i)
for i
in range(1, ItemNumber.MAX + 1)]
407 if any(value
in config_data
for value
in l_possible_articles):
410 b_set_data = self.
ui.confirm_dialog([
"Import file?",
"Datei importieren?"],
411 l_optional_text=[f
"This file does not contain any articles data:\n{s_file}",
412 f
"Diese Datei enthält keine Artikel Daten:\n{s_file}"])
416 if not self.
ui.test_mode:
417 write_articles_settings(self.
d_item)
423 self.
ui.set_status([
"Articles configuration changed",
"Artikel Konfiguration wurde geändert"])
426 b_edit = self.
ui.confirm_dialog([
"Invalid. Edit again?",
"Ungültig. Erneut bearbeiten?"])
428 self.
ui.edit_items_in_notepad(s_file)
430 self.
ui.set_status([
"Unable to read articles configuration with duplicate numbers",
431 "Artikelkonfiguration mit doppelten Positionen kann nicht gelesen werden"],
True)
433 self.
ui.set_status([
"Unable to import articles configuration with duplicate numbers",
434 "Importieren der Artikelkonfiguration mit doppelten Positionen nicht möglich"],
True)
436 self.
ui.update_screen()
441 @brief Try to read config data to handle duplicate items.
442 @param s_file : file to read
443 @return config data from file
447 except (DuplicateSectionError, DuplicateOptionError)
as e:
453def sort_dict(d_dict_to_sort: dict[str, Any], d_order_reference: dict[str, Any]) -> dict[str, Any]:
455 @brief Sort dictionary.
456 @param d_dict_to_sort : dictionary to sort
457 @param d_order_reference : reference dictionary to sort in this order
458 @return return sorted dictionary
460 sorted_dict = OrderedDict()
461 unsorted_dict = OrderedDict()
463 for key, _order
in d_order_reference.items():
464 if key
in d_dict_to_sort:
465 value = d_dict_to_sort[key]
467 if isinstance(value, dict):
468 sorted_dict[key] =
sort_dict(value, d_order_reference[key])
470 sorted_dict[key] = value
473 for key, value
in d_dict_to_sort.items():
474 if key
not in d_order_reference:
475 if isinstance(value, dict):
476 unsorted_dict[key] =
sort_dict(value, {})
478 unsorted_dict[key] = value
480 sorted_dict.update(unsorted_dict)
486 @brief Read data from configuration file.
487 @param s_file_name : file to read
488 @return return configuration of the file
490 d_data: dict[str, dict[str, str | list[str]]] = {}
491 parser = ConfigParser()
492 with codecs.open(s_file_name, mode=
"r", encoding=
"utf-8")
as file:
493 parser.read_file(file)
494 for s_section
in parser.sections():
495 d_data[s_section] = {}
496 for key, value
in parser.items(s_section):
498 if b_list_key
and value.startswith(
'[')
and value.endswith(
']'):
499 s_entries = value[1:-1].split(
", ")
500 l_entries = [s_char.strip(
'\'\"')
for s_char
in s_entries]
501 d_data[s_section][key] = l_entries
503 d_data[s_section][key] = value
509 @brief Write configuration to file.
510 @param s_file_name : write to this file
511 @param d_dict : dictionary to write
513 parser = cfg.ConfigParser()
514 for s_ini_section
in d_dict:
515 parser.add_section(s_ini_section)
516 for s_ini_key
in d_dict[s_ini_section]:
517 s_ini_value = d_dict[s_ini_section][s_ini_key]
518 if isinstance(s_ini_value, list):
519 s_ini_value =
', '.join(f
'"{item}"' for item
in s_ini_value)
520 s_ini_value = f
"[{s_ini_value}]"
521 parser.set(s_ini_section, s_ini_key, s_ini_value)
522 with open(s_file_name, mode=
"w", encoding=
"utf-8")
as o_cfg_file:
523 parser.write(o_cfg_file)
Class Configuration: Read/Write configuration data (user or articles)
str get_header2(self)
Get second bon header.
None update_extended_article_status(self, bool prevent_resize=False)
Update extended article status.
str get_item_background(self, int i_item_number)
Get item background.
str get_item_name(self, int i_item_number)
Get item name.
str get_header1(self)
Get first bon header.
bool get_free_auto_logout(self)
Get free user auto logout status.
float get_logout_time(self)
Get timeout for inactive user.
dict[str, Any]|None handle_config_data_read(self, str s_file)
Try to read config data to handle duplicate items.
str get_user_pw(self, str user)
Get password of user.
bool get_item_print_status(self, int i_item_number)
Get item print status.
bool read_item_file(self, str s_file, bool b_remove_file=False)
Read articles file configuration and save in settings.
bool get_auto_open(self)
Get drawer auto open status.
bool read_user_file(self, str s_file, bool b_remove_file=False)
Read user file configuration and save in settings.
EUser|None get_item_visible_user(self, int i_item_number)
Get visible user for item.
None store_user_data(self)
Store actual user data.
str get_device_name(self)
Get smart card user status.
float get_group_tax(self, int i_group)
Get tax value from group.
float get_item_price(self, int i_item_number)
Get item price.
bool get_print_free_price_status(self)
Get print free user price status.
bool get_item_mark_status(self, int i_item_number)
Get item mark status.
str get_item_button_name(self, int i_item_number)
Get item name for button.
int get_item_group(self, int i_item_number)
Get item group.
str get_user_name(self, str user)
Get name of user (only for order staff).
bool b_smartcard_id_present
None __init__(self, "MainWindow" ui)
bool get_print_article_status(self)
Get article print status.
float get_user_tax(self)
Get user involvement.
dict[str, dict[str, str|list[str]]] read_data_from_config_file(str s_file_name)
Read data from configuration file.
float get_dict_float(dict[str, dict[str, str]] d_dict, str s_section, str s_key)
Get floating value in dictionary.
dict[str, Any] sort_dict(dict[str, Any] d_dict_to_sort, dict[str, Any] d_order_reference)
Sort dictionary.
bool is_int(Any value)
Check if string can convert to integer.
str get_dict_value(dict[str, dict[str, str]] d_dict, str s_section, str s_key)
Get value from dictionary.
int get_dict_int(dict[str, dict[str, str]] d_dict, str s_section, str s_key)
Get integer value in dictionary.
bool is_float(Any value)
Check if string can convert to float.
None write_config_to_file(str s_file_name, dict[str, Any] d_dict)
Write configuration to file.