Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Desktop app #6

Merged
merged 14 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ across mobile, web, and desktop environments.
| Quality Control | - | ✅ | ✅ |
| Billing and Invoicing | - | ✅ | ✅ |
| Yard Management | - | ✅ | ✅ |
| Security and Compliance | ✅ | ✅ | ✅ |
| System Administration | - | ✅ | ✅ |
| Offline Database Management | - | - | ✅ |
| Advanced Reporting | - | - | ✅ |
Expand All @@ -55,7 +54,6 @@ across mobile, web, and desktop environments.
| Batch Processing | - | - | ✅ |
| Barcode and Label Design | - | - | ✅ |
| Advanced Search and Filter | - | ✅ | ✅ |
| Data Analysis Tools | - | - | ✅ |
| System Diagnostics | - | - | ✅ |
| Simulation and Modeling | - | - | ✅ |
| Training Mode | - | - | ✅ |
Expand Down
Empty file added desktop_app/__init__.py
Empty file.
6 changes: 5 additions & 1 deletion desktop_app/resources/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,9 @@
"request_timeout": 10,
"font": "Candara",
"font_size": "Large",
"two_factor_auth": true
"icons_path": "resources/icons",
"styles_path": "resources/styles",
"templates_path": "resources/templates",
"translations_path": "resources/translations",
"organization_domain": "nexusware.com"
}
Binary file added desktop_app/resources/icons/bell.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added desktop_app/resources/icons/bell_unread.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added desktop_app/resources/icons/eye_hide.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added desktop_app/resources/icons/eye_view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added desktop_app/src/__init__.py
Empty file.
174 changes: 84 additions & 90 deletions desktop_app/src/main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import sys

from PySide6.QtCore import QFile, QTextStream, QTranslator
from PySide6.QtCore import QFile, QTextStream, QTranslator, QDir
from PySide6.QtGui import QIcon, QFont
from PySide6.QtWidgets import QApplication, QMessageBox
from requests import HTTPError

from desktop_app.src.ui.components.error_dialog import global_exception_handler
from public_api.api import APIClient
from public_api.api import UsersAPI
from services.authentication import AuthenticationService
Expand All @@ -16,107 +17,100 @@
from utils.logger import setup_logger


def load_stylesheet(filename):
file = QFile(filename)
if file.open(QFile.ReadOnly | QFile.Text):
stream = QTextStream(file)
return stream.readAll()
return ""
class AppContext:
def __init__(self):
self.config_manager = ConfigManager()
self.logger = setup_logger("nexusware")
self.api_client = APIClient(base_url=self.config_manager.get("api_base_url",
"http://127.0.0.1:8000/api/v1"))
self.users_api = UsersAPI(self.api_client)
self.auth_service = AuthenticationService(self.users_api)
self.offline_manager = OfflineManager("offline_data.db")
self.update_manager = UpdateManager(self.config_manager)

def initialize_app(self):
app = QApplication(sys.argv)
app.setApplicationName("NexusWare WMS")
app.setOrganizationName("NexusWare")
app.setOrganizationDomain(self.config_manager.get("organization_domain", "nexusware.com"))

QDir.addSearchPath("icons", self.config_manager.get("icons_path", "resources/icons"))
QDir.addSearchPath("styles", self.config_manager.get("styles_path", "resources/styles"))
QDir.addSearchPath("templates", self.config_manager.get("templates_path",
"resources/templates"))
QDir.addSearchPath("translations", self.config_manager.get("translations_path",
"resources/translations"))

app_icon = QIcon("icons:app_icon.png")
app.setWindowIcon(app_icon)
self.apply_appearance_settings(app)

language = self.config_manager.get("language", "English")
translator = QTranslator()
if language != "English":
if translator.load(f"translations:{language.lower()}.qm"):
app.installTranslator(translator)

return app

def apply_appearance_settings(self, app):
theme = self.config_manager.get("theme", "light")
stylesheet = self.load_stylesheet(f"styles:{theme}_theme.qss")
app.setStyleSheet(stylesheet)

font_family = self.config_manager.get("font", "Arial")
font_size_name = self.config_manager.get("font_size", "Medium")
font = QFont(font_family)

if font_size_name == "Small":
font.setPointSize(8)
elif font_size_name == "Medium":
font.setPointSize(10)
elif font_size_name == "Large":
font.setPointSize(12)

app.setFont(font)

def load_stylesheet(self, filename):
file = QFile(filename)
if file.open(QFile.ReadOnly | QFile.Text):
stream = QTextStream(file)
return stream.readAll()
return ""

def create_and_show_main_window(self):
user_permissions = self.users_api.get_current_user_permissions()
main_window = MainWindow(api_client=self.api_client,
config_manager=self.config_manager,
permission_manager=user_permissions)
main_window.show()
return main_window


def apply_appearance_settings(app, config_manager):
# Apply theme
theme = config_manager.get("theme", "light")
stylesheet = load_stylesheet(f"resources/styles/{theme}_theme.qss")
app.setStyleSheet(stylesheet)
def main():
app_context = AppContext()
app_context.logger.info("Starting NexusWare WMS")

# Apply font
font_family = config_manager.get("font", "Arial")
font_size_name = config_manager.get("font_size", "Medium")
font = QFont(font_family)
app = app_context.initialize_app()

if font_size_name == "Small":
font.setPointSize(8)
elif font_size_name == "Medium":
font.setPointSize(10)
elif font_size_name == "Large":
font.setPointSize(12)
sys.excepthook = global_exception_handler(app_context)

app.setFont(font)
app_context.offline_manager.clear_all_actions()

if app_context.config_manager.get("auto_update", True) and app_context.update_manager.check_for_updates():
app_context.update_manager.perform_update()

def main():
# Set up logging
logger = setup_logger("nexusware")
logger.info("Starting NexusWare WMS")

# Initialize the application
app = QApplication(sys.argv)
app.setApplicationName("NexusWare WMS")
app.setOrganizationName("NexusWare")
app.setOrganizationDomain("nexusware.com")

# Set application icon
app_icon = QIcon("resources/icons/app_icon.png")
app.setWindowIcon(app_icon)

# Load configuration
config_manager = ConfigManager()

# Apply appearance settings
apply_appearance_settings(app, config_manager)

# Load language
language = config_manager.get("language", "English")
translator = QTranslator()
if language != "English":
if translator.load(f"resources/translations/{language.lower()}.qm"):
app.installTranslator(translator)

# Initialize API client
api_client = APIClient(base_url=config_manager.get("api_base_url", "http://127.0.0.1:8000/api/v1"))

# Initialize services
users_api = UsersAPI(api_client)
auth_service = AuthenticationService(users_api)
offline_manager = OfflineManager("offline_data.db")

# TODO: Implement the UpdateManager class correctly
offline_manager.clear_all_actions()
update_manager = UpdateManager(config_manager)

# Check for updates
if config_manager.get("auto_update", True) and update_manager.check_for_updates():
update_manager.perform_update()

# Show login dialog
login_dialog = LoginDialog(auth_service)
login_dialog = LoginDialog(app_context.auth_service)
if login_dialog.exec() != LoginDialog.Accepted:
sys.exit(0)

# Set up main window
user_permissions = users_api.get_current_user_permissions()
main_window = MainWindow(api_client=api_client, config_manager=config_manager, permission_manager=user_permissions)

def handle_auth_error():
QMessageBox.warning(None, "Authentication Error", "Your session has expired. Please log in again.")
main_window.close()
if login_dialog.exec() == LoginDialog.Accepted:
main_window.show()
else:
sys.exit(0)

# Show main window
main_window.show()

# Start the event loop with error handling
try:
sys.exit(app.exec())
main_window = app_context.create_and_show_main_window() # noqa
except HTTPError as e:
if e.response.status_code == 403:
handle_auth_error()
else:
raise
print(e)
QMessageBox.critical(None, "Error", str(e))

sys.exit(app.exec())


if __name__ == "__main__":
Expand Down
21 changes: 15 additions & 6 deletions desktop_app/src/services/authentication.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
from public_api.api import UsersAPI
from public_api.shared_schemas import (
UserCreate, UserUpdate, UserSanitizedWithRole, Token, Message, User
UserCreate, UserUpdate, UserSanitized, Token, Message
)


class AuthenticationService:
def __init__(self, users_api: UsersAPI):
self.users_api = users_api

def login(self, email: str, password: str) -> Token:
return self.users_api.login(email, password)
def login(self, username: str, password: str) -> Token:
return self.users_api.login(username, password)

def register(self, user_data: UserCreate) -> User:
def login_2fa(self, username: str, password: str, two_factor_code: str) -> Token:
return self.users_api.login_2fa(username, password, two_factor_code)

def register(self, user_data: UserCreate) -> UserSanitized:
return self.users_api.register(user_data)

def reset_password(self, email: str) -> Message:
return self.users_api.reset_password(email)

def get_current_user(self) -> UserSanitizedWithRole:
def refresh_token(self) -> Token:
return self.users_api.refresh_token()

def get_current_user(self) -> UserSanitized:
return self.users_api.get_current_user()

def update_current_user(self, user_data: UserUpdate) -> User:
def update_current_user(self, user_data: UserUpdate) -> UserSanitized:
return self.users_api.update_current_user(user_data)

def change_password(self, current_password: str, new_password: str) -> Message:
return self.users_api.change_password(current_password, new_password)
1 change: 1 addition & 0 deletions desktop_app/src/services/document_management/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .report_exporter import ReportExporter, format_report_data
Loading
Loading