Skip to content
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
7 changes: 5 additions & 2 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,20 @@ jobs:
build:

runs-on: ubuntu-latest

env:
DISPLAY: ":99.0"
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install Qt GUI dependencies
uses: tlambert03/setup-qt-libs@v1
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest numpy
pip install flake8 pytest numpy pytest-qt pytest-xvfb
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
Expand Down
7 changes: 6 additions & 1 deletion ast_monitor/html_request_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,10 @@ def translate_path(self, path):
current_file_directory = os.path.dirname(os.path.abspath(__file__))
custom_folder = os.path.join(current_file_directory, 'map', 'dist')
translated_path = os.path.join(custom_folder, parsed_path.lstrip('/'))
print(f"Translated path: {translated_path}") # Debugging line to print the translated path
if self.server.logging:
print(f"Translated path: {translated_path}") # Debugging line to print the translated path
return translated_path

def log_message(self, format, *args):
if self.server.logging:
return super().log_message(format, *args)
25 changes: 17 additions & 8 deletions ast_monitor/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class AST(QMainWindow, Ui_MainWindow):
path to the file that contains GPS data
"""

def __init__(self, hr_data_path: str, gps_data_path: str, route_data_path=None) -> None:
def __init__(self, hr_data_path: str, gps_data_path: str, route_data_path=None, server_port: int=8000, logging :bool=True) -> None:
"""
Initialization method for AST class.\n
Args:
Expand All @@ -58,12 +58,14 @@ def __init__(self, hr_data_path: str, gps_data_path: str, route_data_path=None)
rr = RouteReader(route_data_path)
self.route = rr.read()

self.server_port = server_port
self.logging = logging
self.basic_data = BasicData(hr_data_path, gps_data_path)
self.initialize_GUI()
self.map_initialized = False
self.goals_processor = None

self.server_thread = HttpServerThread()
self.server_thread = HttpServerThread(self.server_port,self.logging)
self.server_thread.start()

def on_load_finished(self):
Expand Down Expand Up @@ -94,7 +96,7 @@ def initialize_GUI(self) -> None:
self.channel.registerObject("MainWindow", self)
self.view.page().setWebChannel(self.channel)

self.view.setUrl(QUrl("http://localhost:8000"))
self.view.setUrl(QUrl(f"http://localhost:{self.server_port}"))
self.view.loadFinished.connect(self.on_load_finished)

self.vb_map.addWidget(self.view)
Expand Down Expand Up @@ -490,26 +492,33 @@ def convert_time_to_hours_minutes_seconds(self, time: int) -> str:

return time

class CustomTCPServer(socketserver.TCPServer):
logging = True

def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True, logging=True):
super().__init__(server_address, RequestHandlerClass, bind_and_activate)
self.logging = logging

class HttpServerThread(QThread):
signal = pyqtSignal(str)

def __init__(self):
def __init__(self, server_port: int, logging=True):
"""
Initialization method for HttpServerThread class. The class is used for running a simple HTTP server that
renders the Vue.js map component.
"""
super().__init__()
self.running = True
self.server_port = server_port
self.logging = logging

def run(self):
"""
Method that runs the HTTP server.
"""
PORT = 8000
"""
Handler = CustomHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
self.signal.emit(f"Serving at http://localhost:{PORT}")
with CustomTCPServer(("", self.server_port), Handler,self.logging) as httpd:
self.signal.emit(f"Serving at http://localhost:{self.server_port}")
while self.running:
httpd.handle_request()

Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ python = "^3.9"
matplotlib = "*"
geopy = "*"
tcxreader = "^0.4.2"
sport-activities-features = "^0.4.2"
sport-activities-features = "^0.4.2, < 0.5.0"
openant = "^1.2.0"
PyQt6 = "6.4.2"
PyQt6-WebEngine = "^6.5.0"
pyqt-feedback-flow = "^0.3.4"

[tool.poetry.dev-dependencies]
pytest = "^7.2.2"
pytest-qt = "*"
Sphinx = "^5.0"
sphinx-rtd-theme = "^1.0.0"
sphinxcontrib-bibtex = "^2.4.1"
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ matplotlib
geopy
pyqt-feedback-flow
tcxreader
sport-activities-features
sport-activities-features==0.4.5
PyQt6-WebEngine
69 changes: 69 additions & 0 deletions tests/test_gui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import sys,os,random
import pytest

try:
from ast_monitor.model import AST
except ModuleNotFoundError:
sys.path.append('../')
from ast_monitor.model import AST



@pytest.fixture
def widget(qtbot):
hr_data = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'sensor_data', 'hr.txt')
gps_data = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'sensor_data', 'gps.txt')
route_data = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'development', 'routes', 'route.json')
random_port = random.randint(8000, 9000)
window = AST(hr_data, gps_data, route_data,server_port=random_port,logging=False)
qtbot.addWidget(window)
return window


def test_window_title(qtbot,widget):
assert widget.windowTitle() == "TrainingFeedback"
widget.server_thread.stop()

def test_buttons(qtbot,widget):
assert widget.btn_shutdown is not None
assert widget.btn_start_tracking is not None
assert widget.btn_stop_tracking is not None
assert widget.btn_move_left is not None
assert widget.btn_move_right is not None
assert widget.btn_load_training is not None
assert widget.btn_start_training is not None
widget.server_thread.stop()

def test_menu_navigation(qtbot,widget):
assert widget.stackedWidget.count() == 4 # Currently has 4 pages
assert widget.stackedWidget.currentIndex() == 2 # Start on Intervals, index should be 2
widget.btn_move_left.click() # Map, index should now be 1
assert widget.stackedWidget.currentIndex() == 1
widget.btn_move_left.click() # Basic data, index should now be 0
assert widget.stackedWidget.currentIndex() == 0
widget.btn_move_right.click()
widget.btn_move_right.click() # Intervals, index should now be 2
assert widget.stackedWidget.currentIndex() == 2
widget.btn_move_right.click() # Training, index should now be 3
assert widget.stackedWidget.currentIndex() == 3
widget.server_thread.stop()

def test_start_stop_tracking(qtbot,widget):
assert widget.widget_start_stop.currentIndex() == 0 # Shows start icon, is stopped
widget.btn_start_tracking.click()
assert widget.widget_start_stop.currentIndex() == 1 # Shows stop icon, is running
widget.btn_stop_tracking.click()
assert widget.widget_start_stop.currentIndex() == 0 # Shows start icon, is stopped
widget.server_thread.stop()

def test_load_training(qtbot,widget):
widget.btn_move_right.click() # Move to Training page
widget.btn_load_training.click() # Load training
assert widget.lbl_training_type.text() == "Interval"
assert widget.lbl_training_speed_duration.text() == "1 min"
assert widget.lbl_training_speed_hr.text() == "155"
assert widget.lbl_training_rest_duration.text() == "1 min"
assert widget.lbl_training_rest_hr.text() == "90"
assert widget.lbl_training_repetitions.text() == "2"
widget.server_thread.stop()

Loading