diff --git a/README.md b/README.md index 8e863ad..a635552 100644 --- a/README.md +++ b/README.md @@ -35,10 +35,11 @@ Allows authorized users to create, edit, and manage document pages. Support for --- -## 🛠 What's New (v0.2.18) +## 🛠 What's New (v0.2.19) -- Fixed an issue where selected Department or Category could occasionally lose visual highlight after collapsing and expanding sidebar groups. -- Improved sidebar behavior so the previously selected item is restored more reliably after expanding a group. +- Fixed a profile crash path where malformed user data could trigger `'NoneType' object has no attribute 'split'`. +- Hardened profile data normalization so missing `username` values are safely handled as empty strings. +- Improved profile update payload formatting to avoid extra spaces and unstable name parsing. --- diff --git a/README_RU.md b/README_RU.md index fba1239..7546482 100644 --- a/README_RU.md +++ b/README_RU.md @@ -35,10 +35,11 @@ --- -## 🛠 Что нового (v0.2.18) +## 🛠 Что нового (v0.2.19) -- Исправлена ошибка, из-за которой при сворачивании и разворачивании групп в боковой панели иногда визуально пропадала подсветка выбранного Отдела или Категории. -- Улучшено поведение боковой панели: после разворачивания группы выбранный пункт теперь восстанавливается стабильнее. +- Исправлен сценарий сбоя профиля, при котором некорректные данные пользователя могли приводить к ошибке `'NoneType' object has no attribute 'split'`. +- Усилена нормализация данных профиля: при отсутствии `username` теперь безопасно используется пустая строка. +- Улучшено формирование данных при сохранении профиля, чтобы избежать лишних пробелов и нестабильного разбора имени. --- diff --git a/modules/main/mvc/main_controller.py b/modules/main/mvc/main_controller.py index 9c407d3..a94145a 100644 --- a/modules/main/mvc/main_controller.py +++ b/modules/main/mvc/main_controller.py @@ -390,7 +390,7 @@ def _show_profile_dialog(self) -> None: return try: - user_data = self.model.get_full_user_data() + user_data = self._sanitize_profile_user_data(self.model.get_full_user_data()) if not user_data: logger.warning("Could not retrieve user data for profile.") return @@ -423,6 +423,17 @@ def _show_profile_dialog(self) -> None: except Exception as e: self._handle_error(e, "Ошибка профиля") + @staticmethod + def _sanitize_profile_user_data(user_data: dict | None) -> dict | None: + """Normalizes profile payload to prevent UI errors on malformed values.""" + if not isinstance(user_data, dict): + return None + + sanitized = dict(user_data) + username = sanitized.get("username") + sanitized["username"] = "" if username is None else str(username).strip() + return sanitized + def _on_profile_update_finished(self, new_data: dict): """Handles successful profile update.""" @@ -1284,7 +1295,10 @@ def _handle_error(self, e: Exception, title: str = "Ошибка") -> None: message="Срок действия сессии истек. Пожалуйста, войдите снова." ) else: - logger.error(f"{title}: {e}") + logger.error( + f"{title}: {e}", + exc_info=(type(e), e, e.__traceback__) + ) msg = get_friendly_error_message(e) NotificationService().show_toast( notification_type="error", diff --git a/modules/main/mvc/main_model.py b/modules/main/mvc/main_model.py index 2136bd5..0e77716 100644 --- a/modules/main/mvc/main_model.py +++ b/modules/main/mvc/main_model.py @@ -364,9 +364,20 @@ def _normalize_user_data(data: dict | None) -> dict | None: nested_user = data.get("user") if isinstance(nested_user, dict): - return nested_user + normalized = dict(nested_user) + else: + normalized = dict(data) - return data + # Defensive normalization for UI code paths expecting string fields. + # Keep payload shape unchanged: do not add new keys. + if "username" in normalized: + username = normalized.get("username") + normalized["username"] = "" if username is None else str(username).strip() + + if "department" in normalized and normalized.get("department") is None: + normalized["department"] = "" + + return normalized def _get_departments(self) -> list[dict]: diff --git a/modules/profile/profile_dialog.py b/modules/profile/profile_dialog.py index 71d2175..ad672fb 100644 --- a/modules/profile/profile_dialog.py +++ b/modules/profile/profile_dialog.py @@ -137,11 +137,12 @@ def _split_username(username) -> list[str]: def get_updated_data(self) -> dict: """Returns the updated user data from the form.""" + first_name = self.ui.firstname_lineEdit.text().strip() + last_name = self.ui.lastname_lineEdit.text().strip() + username = " ".join(part for part in (first_name, last_name) if part) + return { - "username": " ".join([ - self.ui.firstname_lineEdit.text(), - self.ui.lastname_lineEdit.text() - ]), + "username": username, "department_id": self.ui.department_comboBox.currentData() } diff --git a/utils/whats_new_modal.py b/utils/whats_new_modal.py index 56f1754..11a8c54 100644 --- a/utils/whats_new_modal.py +++ b/utils/whats_new_modal.py @@ -17,12 +17,13 @@ RELEASE_NOTES = { - "0.2.18": [ + "0.2.19": [ { - "title": "Стабильность выделения в боковой панели", + "title": "Стабильность профиля", "items": [ - "Исправлена ошибка, из-за которой при сворачивании и разворачивании групп в боковой панели иногда визуально пропадала подсветка выбранного Отдела или Категории.", - "После разворачивания группы выбранный пункт теперь восстанавливается стабильнее.", + "Исправлен сценарий сбоя профиля при некорректном формате данных пользователя (ошибка вида: 'NoneType' object has no attribute 'split').", + "Нормализация данных профиля усилена: отсутствующий username теперь безопасно обрабатывается как пустая строка.", + "Формирование имени при сохранении профиля улучшено: лишние пробелы удаляются, чтобы избежать нестабильного разбора.", ], }, ],