Compare commits
No commits in common. "master" and "1.0.0" have entirely different histories.
67 changed files with 188 additions and 1460 deletions
|
@ -9,7 +9,7 @@ This run with the [PyQt6](https://www.riverbankcomputing.com/software/pyqt/) and
|
|||
|
||||
To install the projet, you will need to install :
|
||||
|
||||
- Python >= 3.11 (preferably in a virtual environment with `python3 -m venv ./.venv/`)
|
||||
- Python >= 3.11 (preferably in a virtual environment)
|
||||
- Python dependencies (`python3 -m pip install -r ./requirements.txt`)
|
||||
|
||||
Or download one of the build in the [releases page](https://github.com/Faraphel/M1-Recherche/releases).
|
||||
|
|
Binary file not shown.
|
@ -1,43 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="en">
|
||||
<context>
|
||||
<name>LanguageSelection</name>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\LanguageSelection.py" line="69" />
|
||||
<source>SELECT YOUR LANGUAGE</source>
|
||||
<translation>Select your language</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\LanguageSelection.py" line="70" />
|
||||
<source>START</source>
|
||||
<translation>Start</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<context>
|
||||
<name>SurveyEngine</name>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\SurveyEngine.py" line="177" />
|
||||
<source>WARNING</source>
|
||||
<translation>Warning</translation>
|
||||
<location filename="../../source/widget/SurveyEngine.py" line="177"/>
|
||||
<source>WARNING</source>
|
||||
<translation>Warning</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
</context>
|
||||
<context>
|
||||
<name>SurveyNavigation</name>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\SurveyNavigation.py" line="19" />
|
||||
<source>ABANDON</source>
|
||||
<translation>Abandon</translation>
|
||||
<location filename="../../source/widget/SurveyNavigation.py" line="19"/>
|
||||
<source>ABANDON</source>
|
||||
<translation>Abandon</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\SurveyNavigation.py" line="29" />
|
||||
<source>SKIP</source>
|
||||
<translation>Skip</translation>
|
||||
<location filename="../../source/widget/SurveyNavigation.py" line="29"/>
|
||||
<source>SKIP</source>
|
||||
<translation>Skip</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\SurveyNavigation.py" line="38" />
|
||||
<source>NEXT</source>
|
||||
<translation>Next</translation>
|
||||
<location filename="../../source/widget/SurveyNavigation.py" line="38"/>
|
||||
<source>NEXT</source>
|
||||
<translation>Next</translation>
|
||||
</message>
|
||||
</context>
|
||||
</context>
|
||||
<context>
|
||||
<name>SurveyWindow</name>
|
||||
<message>
|
||||
<location filename="../../source/widget/SurveyWindow.py" line="16"/>
|
||||
<source>SURVEY</source>
|
||||
<translation>Survey</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
Binary file not shown.
|
@ -1,43 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="es">
|
||||
<context>
|
||||
<name>LanguageSelection</name>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\LanguageSelection.py" line="69" />
|
||||
<source>SELECT YOUR LANGUAGE</source>
|
||||
<translation>Seleccione su idioma</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\LanguageSelection.py" line="70" />
|
||||
<source>START</source>
|
||||
<translation>Comenzar</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SurveyEngine</name>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\SurveyEngine.py" line="177" />
|
||||
<source>WARNING</source>
|
||||
<translation>Atención</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SurveyNavigation</name>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\SurveyNavigation.py" line="19" />
|
||||
<source>ABANDON</source>
|
||||
<translation>Abandonar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\SurveyNavigation.py" line="29" />
|
||||
<source>SKIP</source>
|
||||
<translation>Pasar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\SurveyNavigation.py" line="38" />
|
||||
<source>NEXT</source>
|
||||
<translation>Siguiente</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
Binary file not shown.
|
@ -1,43 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="fr_FR">
|
||||
<context>
|
||||
<name>LanguageSelection</name>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\LanguageSelection.py" line="69" />
|
||||
<source>SELECT YOUR LANGUAGE</source>
|
||||
<translation>Sélectionner votre langue</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\LanguageSelection.py" line="70" />
|
||||
<source>START</source>
|
||||
<translation>Démarrer</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<context>
|
||||
<name>SurveyEngine</name>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\SurveyEngine.py" line="177" />
|
||||
<source>WARNING</source>
|
||||
<translation>Attention</translation>
|
||||
<location filename="../../source/widget/SurveyEngine.py" line="177"/>
|
||||
<source>WARNING</source>
|
||||
<translation>Avertissement</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
</context>
|
||||
<context>
|
||||
<name>SurveyNavigation</name>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\SurveyNavigation.py" line="19" />
|
||||
<source>ABANDON</source>
|
||||
<translation>Abandonner</translation>
|
||||
<location filename="../../source/widget/SurveyNavigation.py" line="19"/>
|
||||
<source>ABANDON</source>
|
||||
<translation>Abandonner</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\SurveyNavigation.py" line="29" />
|
||||
<source>SKIP</source>
|
||||
<translation>Passer</translation>
|
||||
<location filename="../../source/widget/SurveyNavigation.py" line="29"/>
|
||||
<source>SKIP</source>
|
||||
<translation>Passer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="..\..\source\ui\SurveyNavigation.py" line="38" />
|
||||
<source>NEXT</source>
|
||||
<translation>Suivant</translation>
|
||||
<location filename="../../source/widget/SurveyNavigation.py" line="38"/>
|
||||
<source>NEXT</source>
|
||||
<translation>Suivant</translation>
|
||||
</message>
|
||||
</context>
|
||||
</context>
|
||||
<context>
|
||||
<name>SurveyWindow</name>
|
||||
<message>
|
||||
<location filename="../../source/widget/SurveyWindow.py" line="16"/>
|
||||
<source>SURVEY</source>
|
||||
<translation>Sondage</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
BIN
assets/language/sp.qm
Normal file
BIN
assets/language/sp.qm
Normal file
Binary file not shown.
38
assets/language/sp.ts
Normal file
38
assets/language/sp.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="es">
|
||||
<context>
|
||||
<name>SurveyEngine</name>
|
||||
<message>
|
||||
<location filename="../../source/widget/SurveyEngine.py" line="177"/>
|
||||
<source>WARNING</source>
|
||||
<translation>Atención</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SurveyNavigation</name>
|
||||
<message>
|
||||
<location filename="../../source/widget/SurveyNavigation.py" line="19"/>
|
||||
<source>ABANDON</source>
|
||||
<translation>Abandonar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../source/widget/SurveyNavigation.py" line="29"/>
|
||||
<source>SKIP</source>
|
||||
<translation>Pasar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../source/widget/SurveyNavigation.py" line="38"/>
|
||||
<source>NEXT</source>
|
||||
<translation>Siguiente</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SurveyWindow</name>
|
||||
<message>
|
||||
<location filename="../../source/widget/SurveyWindow.py" line="16"/>
|
||||
<source>SURVEY</source>
|
||||
<translation>Encuesta</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
17
main.py
17
main.py
|
@ -1,14 +1,29 @@
|
|||
import sys
|
||||
|
||||
from PyQt6.QtCore import QTranslator, QLocale
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
|
||||
from source.ui import SurveyWindow
|
||||
from source import translate
|
||||
from source import assets_path
|
||||
from source.widget import SurveyWindow
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# create the application
|
||||
application = QApplication(sys.argv)
|
||||
|
||||
# get the user language
|
||||
local = QLocale()
|
||||
language_code: str = local.languageToCode(local.language(), QLocale.LanguageCodeType.ISO639)
|
||||
|
||||
# apply the language on the translator
|
||||
translate.set_language(language_code)
|
||||
|
||||
# load the translator to support multiple languages
|
||||
translator = QTranslator()
|
||||
application.installTranslator(translator)
|
||||
translator.load(str(assets_path / f"language/{language_code}.qm"))
|
||||
|
||||
# create the window
|
||||
window = SurveyWindow("./surveys.json")
|
||||
window.show()
|
||||
|
|
|
@ -4,6 +4,4 @@ PyQt6-WebEngine
|
|||
nextcord~=2.6.0
|
||||
requests~=2.31.0
|
||||
|
||||
cx_freeze
|
||||
matplotlib~=3.8.2
|
||||
numpy~=1.26.3
|
||||
cx_freeze
|
21
setup.py
21
setup.py
|
@ -22,25 +22,16 @@ setup(
|
|||
"build_exe": {
|
||||
"include_msvcr": True,
|
||||
"include_files": [
|
||||
("./tools/", "./tools/"),
|
||||
("./assets/", "./assets/"),
|
||||
("./README.md", "./README.md"),
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
executables=[
|
||||
Executable(
|
||||
"main.py",
|
||||
base=base,
|
||||
target_name=__appname__,
|
||||
icon=__icon_ico__
|
||||
),
|
||||
Executable(
|
||||
"tools/web_replay/main.py",
|
||||
base=base,
|
||||
target_name="tools/web_replay/main",
|
||||
icon=__icon_ico__
|
||||
),
|
||||
]
|
||||
executables=[Executable(
|
||||
"main.py",
|
||||
base=base,
|
||||
target_name=__appname__,
|
||||
icon=__icon_ico__
|
||||
)]
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@ from pathlib import Path
|
|||
assets_path = Path("./assets/")
|
||||
|
||||
__appname__ = "Survey Engine"
|
||||
__version__ = (1,0,2)
|
||||
__version__ = (1,0,0)
|
||||
__str_version__ = ".".join(map(str, __version__))
|
||||
__author__ = "Faraphel"
|
||||
__mail__ = "rc60650@hotmail.com"
|
||||
|
|
|
@ -5,7 +5,6 @@ from typing import Optional
|
|||
import nextcord
|
||||
import requests
|
||||
from PyQt6.QtCore import pyqtSignal
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
|
||||
result_path = Path("./results/")
|
||||
|
||||
|
@ -60,7 +59,6 @@ def upload_discord(
|
|||
|
||||
except Exception as exc:
|
||||
if signal_warning is not None:
|
||||
application = QApplication.instance()
|
||||
signal_warning.emit(application.tr("COULD NOT UPLOAD THE DATA")) # NOQA: emit exist
|
||||
signal_warning.emit("COULD NOT UPLOAD THE DATA") # NOQA: emit exist
|
||||
else:
|
||||
raise exc
|
||||
|
|
|
@ -4,7 +4,7 @@ from PyQt6.QtCore import Qt, pyqtSignal
|
|||
from PyQt6.QtGui import QFont
|
||||
from PyQt6.QtWidgets import QVBoxLayout, QLabel, QSpinBox
|
||||
|
||||
from source import translate, ui
|
||||
from source import translate, widget
|
||||
from source.survey.base import BaseSurvey
|
||||
|
||||
|
||||
|
@ -47,7 +47,7 @@ class IntegerQuestion(BaseSurvey):
|
|||
self._layout.addWidget(self.entry_response)
|
||||
|
||||
# navigation
|
||||
self.navigation = ui.SurveyNavigation(signals=signals)
|
||||
self.navigation = widget.SurveyNavigation(signals=signals)
|
||||
self._layout.addWidget(self.navigation)
|
||||
self.navigation.show_forward()
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from PyQt6.QtCore import Qt, pyqtSignal
|
|||
from PyQt6.QtGui import QFont
|
||||
from PyQt6.QtWidgets import QVBoxLayout, QLabel
|
||||
|
||||
from source import translate, ui
|
||||
from source import translate, widget
|
||||
from source.survey.base import BaseSurvey
|
||||
|
||||
|
||||
|
@ -43,7 +43,7 @@ class Text(BaseSurvey):
|
|||
self.label_description.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
# navigation
|
||||
self.navigation = ui.SurveyNavigation(signals=signals)
|
||||
self.navigation = widget.SurveyNavigation(signals=signals)
|
||||
self._layout.addWidget(self.navigation)
|
||||
|
||||
self.navigation.show_forward() # always show forward
|
||||
|
|
|
@ -4,7 +4,7 @@ from PyQt6.QtCore import Qt, pyqtSignal
|
|||
from PyQt6.QtGui import QFont
|
||||
from PyQt6.QtWidgets import QVBoxLayout, QLabel, QTextEdit
|
||||
|
||||
from source import translate, ui
|
||||
from source import translate, widget
|
||||
from source.survey.base import BaseSurvey
|
||||
|
||||
|
||||
|
@ -34,7 +34,7 @@ class TextQuestion(BaseSurvey):
|
|||
self._layout.addWidget(self.entry_response)
|
||||
|
||||
# navigation
|
||||
self.navigation = ui.SurveyNavigation(signals=signals)
|
||||
self.navigation = widget.SurveyNavigation(signals=signals)
|
||||
self._layout.addWidget(self.navigation)
|
||||
self.navigation.show_forward()
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QUrl, QEvent, QObject, QPointF
|
|||
from PyQt6.QtGui import QFont, QMouseEvent, QResizeEvent, QKeyEvent
|
||||
from PyQt6.QtWidgets import QLabel, QVBoxLayout, QSizePolicy
|
||||
|
||||
from source import translate, ui
|
||||
from source import translate, widget
|
||||
from source.survey.base import BaseSurvey
|
||||
|
||||
|
||||
|
@ -14,20 +14,13 @@ class WebMission(BaseSurvey):
|
|||
self,
|
||||
title: translate.Translatable,
|
||||
url: str,
|
||||
|
||||
initial_js: Optional[str] = None,
|
||||
start_check_js: Optional[str] = None,
|
||||
check_js: Optional[str] = None,
|
||||
|
||||
check_condition: Optional[str] = None,
|
||||
skip_time: Optional[float] = None,
|
||||
signals: dict[str, pyqtSignal] = None
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self.initial_js = initial_js if initial_js is not None else ""
|
||||
self.start_check_js = start_check_js if start_check_js is not None else "true"
|
||||
self.check_js = check_js if check_js is not None else "true"
|
||||
|
||||
self.check_condition = check_condition
|
||||
self.default_url = url
|
||||
self.skip_time = skip_time
|
||||
self.signals = signals if signals is not None else {}
|
||||
|
@ -54,38 +47,48 @@ class WebMission(BaseSurvey):
|
|||
self.label_title.setFont(font_title)
|
||||
|
||||
# web page
|
||||
self.browser = ui.Browser()
|
||||
self.browser = widget.Browser()
|
||||
self._layout.addWidget(self.browser)
|
||||
|
||||
self.browser.web.focusProxy().installEventFilter(self) # capture the event in eventFilter
|
||||
self.browser.web.urlChanged.connect(self._on_url_changed) # NOQA: connect exist
|
||||
|
||||
self.browser.web.setUrl(QUrl(self.default_url))
|
||||
self.browser.web.loadFinished.connect(self._initialise_js) # NOQA: connect exist
|
||||
|
||||
self.browser.web.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
|
||||
# setup the timer for the check
|
||||
self.timer_check = None
|
||||
if self.check_condition is not None:
|
||||
self.timer_check = QTimer()
|
||||
self.timer_check.setInterval(100)
|
||||
self.timer_check.timeout.connect(self.check) # NOQA: connect exist
|
||||
|
||||
# setup the timer for skipping the mission
|
||||
self.timer_skip = None
|
||||
if self.skip_time is not None:
|
||||
self.timer_skip = QTimer()
|
||||
self.timer_skip.setInterval(self.skip_time * 1000)
|
||||
self.timer_skip.timeout.connect(self._on_time_skip) # NOQA: connect exist
|
||||
|
||||
# setup the events
|
||||
self.browser.web.page().scrollPositionChanged.connect(self._on_scroll_position_changed)
|
||||
|
||||
# navigation
|
||||
self.navigation = ui.SurveyNavigation(signals=signals)
|
||||
self.navigation = widget.SurveyNavigation(signals=signals)
|
||||
self._layout.addWidget(self.navigation)
|
||||
|
||||
# initialize the start time
|
||||
self.start_time = time.time()
|
||||
|
||||
# initialise the timers
|
||||
self.timer_start_check: Optional[QTimer] = None
|
||||
self.timer_check: Optional[QTimer] = None
|
||||
self.timer_skip: Optional[QTimer] = None
|
||||
# check timer
|
||||
if self.timer_check is not None:
|
||||
# enable the timer
|
||||
self.timer_check.start()
|
||||
else:
|
||||
self._success() # call directly the success method
|
||||
|
||||
# skip timer
|
||||
if self.skip_time is not None:
|
||||
self.timer_skip = QTimer()
|
||||
self.timer_skip.setInterval(self.skip_time * 1000)
|
||||
self.timer_skip.timeout.connect(self._allow_time_skip) # NOQA: connect exist
|
||||
|
||||
if self.timer_skip is not None:
|
||||
# enable the timer for skipping the question
|
||||
self.timer_skip.start()
|
||||
|
||||
@classmethod
|
||||
|
@ -93,11 +96,7 @@ class WebMission(BaseSurvey):
|
|||
return cls(
|
||||
title=data["title"],
|
||||
url=data.get("url"),
|
||||
|
||||
initial_js=data.get("initial_js"),
|
||||
start_check_js=data.get("start_check_js"),
|
||||
check_js=data.get("check_js"),
|
||||
|
||||
check_condition=data.get("check"),
|
||||
skip_time=data.get("skip_time"),
|
||||
|
||||
signals=signals
|
||||
|
@ -206,80 +205,30 @@ class WebMission(BaseSurvey):
|
|||
url=self.browser.web.url().toString()
|
||||
)
|
||||
|
||||
def _allow_time_skip(self):
|
||||
def _on_time_skip(self):
|
||||
# when the timer to allow skip have run out
|
||||
if "skip" in self.signals:
|
||||
self.navigation.show_skip()
|
||||
|
||||
# condition
|
||||
|
||||
def _preprocess_check(self, check: str) -> str:
|
||||
"""
|
||||
Preprocess a check
|
||||
"""
|
||||
|
||||
check = check.replace("#LANGUAGE_CODE#", translate.get_language())
|
||||
check = check.replace("#START_TIME#", str(self.start_time))
|
||||
|
||||
return check
|
||||
|
||||
def _initialise_js(self, ok: bool):
|
||||
# prevent the event from being call a second time
|
||||
self.browser.web.loadFinished.disconnect(self._initialise_js)
|
||||
|
||||
def callback(result: bool):
|
||||
self.timer_start_check = QTimer()
|
||||
self.timer_start_check.setInterval(100)
|
||||
self.timer_start_check.timeout.connect(self._start_check) # NOQA: connect exist
|
||||
self.timer_start_check.start()
|
||||
|
||||
# run the initial command
|
||||
self.browser.web.page().runJavaScript(
|
||||
self._preprocess_check(self.initial_js),
|
||||
resultCallback=callback
|
||||
)
|
||||
|
||||
def _start_check(self) -> None:
|
||||
"""
|
||||
Check if the real checking condition should start
|
||||
"""
|
||||
|
||||
# if the check evaluated to True, enable the normal check
|
||||
def callback(result: bool):
|
||||
if result:
|
||||
# stop this start check timer
|
||||
self.timer_start_check.stop()
|
||||
|
||||
# create a new timer for the normal check
|
||||
self.timer_check = QTimer()
|
||||
self.timer_check.setInterval(100)
|
||||
self.timer_check.timeout.connect(self._check) # NOQA: connect exist
|
||||
self.timer_check.start()
|
||||
|
||||
# run the check
|
||||
self.browser.web.page().runJavaScript(
|
||||
self._preprocess_check(self.start_check_js),
|
||||
resultCallback=callback
|
||||
)
|
||||
|
||||
def _check(self) -> None:
|
||||
def check(self) -> None:
|
||||
"""
|
||||
Check if the checking condition have been completed
|
||||
"""
|
||||
|
||||
# if the check evaluated to True, call the success method
|
||||
def callback(result: bool):
|
||||
def check_callback(result: bool):
|
||||
if result:
|
||||
# stop this check timer
|
||||
self.timer_check.stop()
|
||||
# mark the test as successful
|
||||
self._success()
|
||||
|
||||
# run the check
|
||||
self.browser.web.page().runJavaScript(
|
||||
self._preprocess_check(self.check_js),
|
||||
resultCallback=callback
|
||||
)
|
||||
page = self.browser.web.page()
|
||||
|
||||
# preprocess the condition
|
||||
condition = self.check_condition
|
||||
condition = condition.replace("#LANGUAGE_CODE#", translate.get_language())
|
||||
|
||||
# run the condition
|
||||
page.runJavaScript(condition, resultCallback=check_callback)
|
||||
|
||||
# data collection
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ from PyQt6.QtCore import Qt, pyqtSignal
|
|||
from PyQt6.QtGui import QFont
|
||||
from PyQt6.QtWidgets import QFrame, QVBoxLayout, QLabel, QButtonGroup, QLineEdit, QAbstractButton
|
||||
|
||||
from source import translate, ui
|
||||
from source import translate, widget
|
||||
from source.survey.base import BaseSurvey
|
||||
|
||||
|
||||
|
@ -54,7 +54,7 @@ class BaseChoiceQuestion(BaseSurvey):
|
|||
self.label_question.setFont(font_title)
|
||||
|
||||
# prepare navigation
|
||||
self.navigation = ui.SurveyNavigation(signals=signals)
|
||||
self.navigation = widget.SurveyNavigation(signals=signals)
|
||||
|
||||
# responses
|
||||
self.frame_responses = QFrame()
|
||||
|
@ -102,10 +102,6 @@ class BaseChoiceQuestion(BaseSurvey):
|
|||
# add the navigation
|
||||
self._layout.addWidget(self.navigation)
|
||||
|
||||
if not self.are_buttons_exclusive():
|
||||
# if the buttons are not exclusive, allow to select no options
|
||||
self.navigation.show_forward()
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: dict[str, Any], signals: dict[str, pyqtSignal]) -> "BaseChoiceQuestion":
|
||||
return cls(
|
||||
|
@ -118,7 +114,7 @@ class BaseChoiceQuestion(BaseSurvey):
|
|||
collected_data = {
|
||||
"choices": {
|
||||
choice_id: {
|
||||
"checked": choice_data["button"].isChecked(),
|
||||
"checked": choice_data["button"].isEnabled(),
|
||||
"details": entry.text() if (entry := choice_data["entry"]) is not None else None
|
||||
}
|
||||
for choice_id, choice_data in self.buttons_responses.items()
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
import typing
|
||||
from typing import Callable
|
||||
|
||||
from PyQt6.QtCore import Qt, QLocale
|
||||
from PyQt6.QtWidgets import QWidget, QLabel, QVBoxLayout, QComboBox, QPushButton
|
||||
|
||||
from source import assets_path, translate, ui
|
||||
|
||||
|
||||
class LanguageSelection(QWidget):
|
||||
def __init__(self, parent: QWidget, after: Callable):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.after = after
|
||||
|
||||
# layout
|
||||
self._layout = QVBoxLayout()
|
||||
self.setLayout(self._layout)
|
||||
|
||||
# title
|
||||
self.title = QLabel()
|
||||
self._layout.addWidget(self.title)
|
||||
self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
# language selection
|
||||
self.select_language = QComboBox()
|
||||
self._layout.addWidget(self.select_language)
|
||||
|
||||
language_default = QLocale().language() # get the default locale
|
||||
|
||||
for i, language_file in enumerate((assets_path / "language").glob("*.qm")):
|
||||
# add every translated language to the selection
|
||||
|
||||
language_code: str = language_file.stem
|
||||
language = QLocale.codeToLanguage(language_code, QLocale.LanguageCodeType.ISO639)
|
||||
|
||||
self.select_language.addItem(language.name, language_code)
|
||||
|
||||
# if the added language is the default one, select it directly
|
||||
if language == language_default:
|
||||
self.select_language.setCurrentIndex(i)
|
||||
|
||||
self.select_language.currentIndexChanged.connect(self.refresh_language) # NOQA: connect exist
|
||||
|
||||
# start button
|
||||
self.button_start = QPushButton()
|
||||
self._layout.addWidget(self.button_start)
|
||||
self.button_start.clicked.connect(self.start) # NOQA: connect exist
|
||||
|
||||
# refresh the texts
|
||||
self.refresh_language()
|
||||
|
||||
super().show()
|
||||
|
||||
def refresh_language(self):
|
||||
language_code = self.select_language.currentData()
|
||||
|
||||
# load the correct translation in the window
|
||||
window = typing.cast(ui.SurveyWindow, self.window())
|
||||
window.translator.load(str(assets_path / f"language/{language_code}.qm"))
|
||||
|
||||
# apply the language on the custom translator
|
||||
translate.set_language(language_code)
|
||||
|
||||
# refresh the texts
|
||||
self.retranslate()
|
||||
|
||||
def retranslate(self):
|
||||
self.title.setText(self.tr("SELECT YOUR LANGUAGE"))
|
||||
self.button_start.setText(self.tr("START"))
|
||||
|
||||
def start(self) -> None:
|
||||
# call the after event
|
||||
self.after()
|
||||
|
||||
# delete the language selection
|
||||
self.deleteLater()
|
|
@ -1,34 +0,0 @@
|
|||
from pathlib import Path
|
||||
|
||||
from PyQt6.QtCore import QTranslator
|
||||
from PyQt6.QtGui import QIcon
|
||||
from PyQt6.QtWidgets import QMainWindow, QApplication
|
||||
|
||||
from source import ui, assets_path, __icon_png__, __appname__
|
||||
|
||||
icon_path = assets_path / "icon.png"
|
||||
|
||||
|
||||
class SurveyWindow(QMainWindow):
|
||||
def __init__(self, survey_path: Path | str):
|
||||
super().__init__()
|
||||
|
||||
self.translator = QTranslator()
|
||||
QApplication.instance().installTranslator(self.translator)
|
||||
|
||||
# window style
|
||||
self.setWindowIcon(QIcon(__icon_png__))
|
||||
self.setWindowTitle(__appname__)
|
||||
|
||||
# start by asking the user his language
|
||||
self.language_selection = ui.LanguageSelection(
|
||||
parent=self,
|
||||
# after the language is selected, start the survey
|
||||
after=lambda: self.setCentralWidget(ui.SurveyEngine.from_file(survey_path))
|
||||
)
|
||||
self.setCentralWidget(self.language_selection)
|
||||
|
||||
def quit(self):
|
||||
# quit the application by closing and deleting the window
|
||||
self.window().close()
|
||||
self.window().deleteLater()
|
|
@ -77,7 +77,7 @@ class Browser(QWidget):
|
|||
# update the progress bar
|
||||
self.progress.setValue(value)
|
||||
|
||||
def _load_finished(self, ok: bool):
|
||||
def _load_finished(self):
|
||||
# update the progress bar
|
||||
self.progress.hide()
|
||||
# refresh the navigation buttons
|
|
@ -8,7 +8,7 @@ from typing import Optional
|
|||
from PyQt6.QtCore import pyqtSignal
|
||||
from PyQt6.QtWidgets import QVBoxLayout, QProgressBar, QWidget, QMessageBox
|
||||
|
||||
from source import translate, ui, save
|
||||
from source import translate, widget, save
|
||||
from source.survey.base import BaseSurvey
|
||||
from source.survey import Empty, survey_get
|
||||
from source.utils import compress
|
||||
|
@ -93,7 +93,7 @@ class SurveyEngine(QWidget):
|
|||
|
||||
def _on_signal_abandon(self):
|
||||
# on abandon, quit the survey
|
||||
window = typing.cast(ui.SurveyWindow, self.window())
|
||||
window = typing.cast(widget.SurveyWindow, self.window())
|
||||
window.quit()
|
||||
|
||||
def _on_signal_skip(self):
|
||||
|
@ -166,7 +166,7 @@ class SurveyEngine(QWidget):
|
|||
)
|
||||
|
||||
# finally, close the window
|
||||
window = typing.cast(ui.SurveyWindow, self.window())
|
||||
window = typing.cast(widget.SurveyWindow, self.window())
|
||||
window.quit()
|
||||
|
||||
# signals
|
||||
|
@ -175,5 +175,5 @@ class SurveyEngine(QWidget):
|
|||
QMessageBox.warning(
|
||||
self,
|
||||
self.tr("WARNING"),
|
||||
message,
|
||||
self.tr(message),
|
||||
)
|
23
source/widget/SurveyWindow.py
Normal file
23
source/widget/SurveyWindow.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
from pathlib import Path
|
||||
|
||||
from PyQt6.QtGui import QIcon
|
||||
from PyQt6.QtWidgets import QMainWindow
|
||||
|
||||
from source import widget, assets_path, __icon_png__
|
||||
|
||||
icon_path = assets_path / "icon.png"
|
||||
|
||||
|
||||
class SurveyWindow(QMainWindow):
|
||||
def __init__(self, survey_path: Path | str):
|
||||
super().__init__()
|
||||
|
||||
self.setWindowIcon(QIcon(__icon_png__))
|
||||
self.setWindowTitle(self.tr("SURVEY"))
|
||||
|
||||
self.setCentralWidget(widget.SurveyEngine.from_file(survey_path))
|
||||
|
||||
def quit(self):
|
||||
# quit the application by closing and deleting the window
|
||||
self.window().close()
|
||||
self.window().deleteLater()
|
|
@ -1,5 +1,4 @@
|
|||
from .Browser import Browser
|
||||
from .LanguageSelection import LanguageSelection
|
||||
from .SurveyEngine import SurveyEngine
|
||||
from .SurveyWindow import SurveyWindow
|
||||
from .SurveyNavigation import SurveyNavigation
|
|
@ -1,21 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
x = list(map(extract.age.extract, datas))
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Âge des personnes sondées")
|
||||
|
||||
# bar chart
|
||||
bins = np.arange(min(x), max(x), 1)
|
||||
axes.hist(x, bins=bins, edgecolor='black')
|
||||
axes.set_xlabel("Âge")
|
||||
axes.set_ylabel("Quantité")
|
||||
|
||||
return figure
|
|
@ -1,42 +0,0 @@
|
|||
from collections import defaultdict
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from tools.statistics import extract
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
ages_completion: dict[int, int] = defaultdict(lambda: 0)
|
||||
ages_count: dict[int, int] = defaultdict(lambda: 0)
|
||||
|
||||
for data in datas:
|
||||
age = extract.age.extract(data)
|
||||
ages_count[age] += 1
|
||||
|
||||
for survey, survey_data in data["surveys"].items():
|
||||
# only scan survey mission
|
||||
if not survey.startswith("mission-"):
|
||||
continue
|
||||
|
||||
if extract.mission_completed.extract(data, survey):
|
||||
ages_completion[age] += 1
|
||||
|
||||
x = list(ages_completion.keys())
|
||||
y = (
|
||||
np.array(list(ages_completion.values()))
|
||||
/ np.array(list(ages_count.values()))
|
||||
)
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Nombre moyen de mission complété par âge")
|
||||
|
||||
# bar chart
|
||||
bins = np.arange(min(x), max(x), 1)
|
||||
axes.hist(x, bins, weights=y, edgecolor="black")
|
||||
axes.set_xlabel("Âge")
|
||||
axes.set_ylabel("Complétion")
|
||||
|
||||
return figure
|
|
@ -1,41 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
experience_completion: dict[str, int] = dict.fromkeys(ressource.experience.choices, 0)
|
||||
experience_count: dict[str, int] = dict.fromkeys(ressource.experience.choices, 0)
|
||||
|
||||
for data in datas:
|
||||
experience = extract.experience.extract(data)
|
||||
experience_count[experience] += 1
|
||||
|
||||
for survey, survey_data in data["surveys"].items():
|
||||
# only scan survey mission
|
||||
if not survey.startswith("mission-"):
|
||||
continue
|
||||
|
||||
if extract.mission_completed.extract(data, survey):
|
||||
experience_completion[experience] += 1
|
||||
|
||||
x = list(experience_completion.keys())
|
||||
y = (
|
||||
np.array(list(experience_completion.values()))
|
||||
/ np.array(list(experience_count.values()))
|
||||
)
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Nombre moyen de mission complété par opinion")
|
||||
|
||||
# bar chart
|
||||
axes.bar(x, y, color=ressource.experience.colors, edgecolor='black')
|
||||
axes.set_xticks(x)
|
||||
axes.set_xticklabels(ressource.experience.labels)
|
||||
axes.set_xlabel("Expérience")
|
||||
axes.set_ylabel("Complétion")
|
||||
|
||||
return figure
|
|
@ -1,41 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
languages_completion: dict[str, int] = dict.fromkeys(ressource.language.choices, 0)
|
||||
languages_count: dict[str, int] = dict.fromkeys(ressource.language.choices, 0)
|
||||
|
||||
for data in datas:
|
||||
language = extract.language.extract(data)
|
||||
languages_count[language] += 1
|
||||
|
||||
for survey, survey_data in data["surveys"].items():
|
||||
# only scan survey mission
|
||||
if not survey.startswith("mission-"):
|
||||
continue
|
||||
|
||||
if extract.mission_completed.extract(data, survey):
|
||||
languages_completion[language] += 1
|
||||
|
||||
x = list(languages_completion.keys())
|
||||
y = (
|
||||
np.array(list(languages_completion.values()))
|
||||
/ np.array(list(languages_count.values()))
|
||||
)
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Nombre moyen de mission complété par langue")
|
||||
|
||||
# bar chart
|
||||
axes.bar(x, y, color=ressource.language.colors, edgecolor='black')
|
||||
axes.set_xticks(x)
|
||||
axes.set_xticklabels(ressource.language.labels)
|
||||
axes.set_xlabel("Langue")
|
||||
axes.set_ylabel("Complétion")
|
||||
|
||||
return figure
|
|
@ -1,36 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
completions: dict[str] = dict.fromkeys(ressource.mission.choices, 0)
|
||||
|
||||
# TODO : couleur par mission
|
||||
|
||||
for data in datas:
|
||||
for survey in data["surveys"].keys():
|
||||
# only scan survey mission
|
||||
if not survey.startswith("mission-"):
|
||||
continue
|
||||
|
||||
if extract.mission_completed.extract(data, survey):
|
||||
completions[survey] += 1
|
||||
|
||||
x = list(completions.keys())
|
||||
y = list(completions.values())
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Nombre de mission total complété")
|
||||
|
||||
# bar chart
|
||||
axes.bar(x, y, color=ressource.mission.colors, edgecolor='black')
|
||||
axes.set_xlabel("Mission")
|
||||
axes.set_ylabel("Complétion")
|
||||
axes.set_xticks(x)
|
||||
axes.set_xticklabels(ressource.mission.labels, rotation=45, ha="right")
|
||||
figure.tight_layout()
|
||||
|
||||
return figure
|
|
@ -1,45 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
usage_completion: dict[str, int] = dict.fromkeys(ressource.usage.choices, 0)
|
||||
usage_count: dict[str, int] = dict.fromkeys(ressource.usage.choices, 0)
|
||||
|
||||
for data in datas:
|
||||
usage = next(filter(
|
||||
lambda it: it[1]["checked"],
|
||||
data["surveys"]["question-usage-steam"]["choices"].items()
|
||||
))[0]
|
||||
|
||||
usage_count[usage] += 1
|
||||
|
||||
for survey in data["surveys"].keys():
|
||||
# only scan survey mission
|
||||
if not survey.startswith("mission-"):
|
||||
continue
|
||||
|
||||
if extract.mission_completed.extract(data, survey):
|
||||
usage_completion[usage] += 1
|
||||
|
||||
x = list(usage_completion.keys())
|
||||
y = (
|
||||
np.array(list(usage_completion.values()))
|
||||
/ np.array(list(usage_count.values()))
|
||||
)
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Nombre moyen de mission complété par habitude d'utilisation")
|
||||
|
||||
# bar chart
|
||||
axes.bar(x, y, color=ressource.usage.colors, edgecolor='black')
|
||||
axes.set_xticks(x)
|
||||
axes.set_xticklabels(ressource.usage.labels)
|
||||
axes.set_xlabel("Usage")
|
||||
axes.set_ylabel("Complétion")
|
||||
|
||||
return figure
|
|
@ -1,43 +0,0 @@
|
|||
from collections import defaultdict
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from tools.statistics import extract
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
ages_duration: dict[int, int] = defaultdict(lambda: 0)
|
||||
ages_count: dict[int, int] = defaultdict(lambda: 0)
|
||||
|
||||
# TODO: affichage en minutes ?
|
||||
|
||||
for data in datas:
|
||||
age = extract.age.extract(data)
|
||||
ages_count[age] += 1
|
||||
|
||||
for survey in data["surveys"].keys():
|
||||
# only scan survey mission
|
||||
if not survey.startswith("mission-"):
|
||||
continue
|
||||
|
||||
ages_duration[age] += extract.mission_duration.extract(data, survey)
|
||||
|
||||
x = list(ages_duration.keys())
|
||||
y = (
|
||||
np.array(list(ages_duration.values()))
|
||||
/ np.array(list(ages_count.values()))
|
||||
)
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Temps moyen passé par âge")
|
||||
|
||||
# bar chart
|
||||
bins = np.arange(min(x), max(x), 1)
|
||||
axes.hist(x, bins, weights=y, edgecolor="black")
|
||||
axes.set_xlabel("Âge")
|
||||
axes.set_ylabel("Durée")
|
||||
|
||||
return figure
|
|
@ -1,41 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
experience_duration: dict[str, int] = dict.fromkeys(ressource.experience.choices, 0)
|
||||
experience_count: dict[str, int] = dict.fromkeys(ressource.experience.choices, 0)
|
||||
|
||||
for data in datas:
|
||||
experience = extract.experience.extract(data)
|
||||
experience_count[experience] += 1
|
||||
|
||||
for survey, survey_data in data["surveys"].items():
|
||||
# only scan survey mission
|
||||
if not survey.startswith("mission-"):
|
||||
continue
|
||||
|
||||
if extract.mission_completed.extract(data, survey):
|
||||
experience_duration[experience] += extract.mission_duration.extract(data, survey)
|
||||
|
||||
x = list(experience_duration.keys())
|
||||
y = (
|
||||
np.array(list(experience_duration.values()))
|
||||
/ np.array(list(experience_count.values()))
|
||||
)
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Temps moyen passé par opinion")
|
||||
|
||||
# bar chart
|
||||
axes.bar(x, y, color=ressource.experience.colors, edgecolor='black')
|
||||
axes.set_xticks(x)
|
||||
axes.set_xticklabels(ressource.experience.labels)
|
||||
axes.set_xlabel("Expérience")
|
||||
axes.set_ylabel("Durée")
|
||||
|
||||
return figure
|
|
@ -1,40 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
languages_duration: dict[str, int] = dict.fromkeys(ressource.language.choices, 0)
|
||||
languages_count: dict[str, int] = dict.fromkeys(ressource.language.choices, 0)
|
||||
|
||||
for data in datas:
|
||||
language = extract.language.extract(data)
|
||||
languages_count[language] += 1
|
||||
|
||||
for survey in data["surveys"].keys():
|
||||
# only scan survey mission
|
||||
if not survey.startswith("mission-"):
|
||||
continue
|
||||
|
||||
languages_duration[language] += extract.mission_duration.extract(data, survey)
|
||||
|
||||
x = list(languages_duration.keys())
|
||||
y = (
|
||||
np.array(list(languages_duration.values()))
|
||||
/ np.array(list(languages_count.values()))
|
||||
)
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Temps moyen passé par langue")
|
||||
|
||||
# bar chart
|
||||
axes.bar(x, y, color=ressource.language.colors, edgecolor='black')
|
||||
axes.set_xticks(x)
|
||||
axes.set_xticklabels(ressource.language.labels)
|
||||
axes.set_xlabel("Langue")
|
||||
axes.set_ylabel("Durée")
|
||||
|
||||
return figure
|
|
@ -1,37 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
durations: dict[str] = dict.fromkeys(ressource.mission.choices, 0)
|
||||
|
||||
# TODO : marqué en rouge la durée d'abandon ?
|
||||
# TODO : couleur par mission
|
||||
|
||||
for data in datas:
|
||||
for survey in data["surveys"].keys():
|
||||
# only scan survey mission
|
||||
if not survey.startswith("mission-"):
|
||||
continue
|
||||
|
||||
durations[survey] += extract.mission_duration.extract(data, survey)
|
||||
|
||||
x = list(durations.keys())
|
||||
y = np.array(list(durations.values())) / len(datas)
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Temps total passé par mission")
|
||||
|
||||
# bar chart
|
||||
axes.bar(x, y, color=ressource.mission.colors, edgecolor='black')
|
||||
axes.set_xlabel("Mission")
|
||||
axes.set_ylabel("Durée")
|
||||
axes.set_xticks(x)
|
||||
axes.set_xticklabels(ressource.mission.labels, rotation=45, ha="right")
|
||||
figure.tight_layout()
|
||||
|
||||
return figure
|
|
@ -1,40 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
usage_completion: dict[str, int] = dict.fromkeys(ressource.usage.choices, 0)
|
||||
usage_count: dict[str, int] = dict.fromkeys(ressource.usage.choices, 0)
|
||||
|
||||
for data in datas:
|
||||
usage = extract.usage.extract(data)
|
||||
usage_count[usage] += 1
|
||||
|
||||
for survey in data["surveys"].keys():
|
||||
# only scan survey mission
|
||||
if not survey.startswith("mission-"):
|
||||
continue
|
||||
|
||||
usage_completion[usage] += extract.mission_duration.extract(data, survey)
|
||||
|
||||
x = list(usage_completion.keys())
|
||||
y = (
|
||||
np.array(list(usage_completion.values()))
|
||||
/ np.array(list(usage_count.values()))
|
||||
)
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Temps moyen passé par habitude d'utilisation")
|
||||
|
||||
# bar chart
|
||||
axes.bar(x, y, color=ressource.usage.colors, edgecolor='black')
|
||||
axes.set_xticks(x)
|
||||
axes.set_xticklabels(ressource.usage.labels)
|
||||
axes.set_xlabel("Usage")
|
||||
axes.set_ylabel("Durée")
|
||||
|
||||
return figure
|
|
@ -1,30 +0,0 @@
|
|||
from collections import Counter
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
experiences: dict[str, int] = dict.fromkeys(ressource.experience.choices, 0)
|
||||
for data in datas:
|
||||
experience = extract.experience.extract(data)
|
||||
experiences[experience] += 1
|
||||
|
||||
counter = Counter(experiences)
|
||||
x = list(counter.keys())
|
||||
y = list(counter.values())
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Opinion des personnes sondées")
|
||||
|
||||
# bar chart
|
||||
axes.bar(x, y, color=ressource.experience.colors, edgecolor='black')
|
||||
axes.set_xticks(x)
|
||||
axes.set_xticklabels(ressource.experience.labels)
|
||||
axes.set_xlabel("Expérience")
|
||||
axes.set_ylabel("Quantité")
|
||||
|
||||
return figure
|
|
@ -1,28 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
missions = dict.fromkeys(ressource.mission.choices, 0)
|
||||
|
||||
for data in datas:
|
||||
missions[extract.hardest_mission.extract(data)] += 1
|
||||
|
||||
x = list(missions.keys())
|
||||
y = list(missions.values())
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Mission la plus difficile des personnes sondées")
|
||||
|
||||
# bar chart
|
||||
axes.bar(x, y, color=ressource.mission.colors, edgecolor='black')
|
||||
axes.set_xlabel("Mission")
|
||||
axes.set_ylabel("Quantité")
|
||||
axes.set_xticks(x)
|
||||
axes.set_xticklabels(ressource.mission.labels, rotation=45, ha="right")
|
||||
figure.tight_layout()
|
||||
|
||||
return figure
|
|
@ -1,26 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
languages = dict.fromkeys(ressource.language.choices, 0)
|
||||
for language in map(extract.language.extract, datas):
|
||||
languages[language] += 1
|
||||
|
||||
x = list(languages.keys())
|
||||
y = list(languages.values())
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Langue des personnes sondées")
|
||||
|
||||
# bar chart
|
||||
axes.bar(x, y, color=ressource.language.colors, edgecolor='black')
|
||||
axes.set_xticks(x)
|
||||
axes.set_xticklabels(ressource.language.labels)
|
||||
axes.set_xlabel("Langue")
|
||||
axes.set_ylabel("Quantité")
|
||||
|
||||
return figure
|
|
@ -1,26 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
|
||||
from tools.statistics import extract, ressource
|
||||
|
||||
|
||||
def analyse(datas: list[dict]) -> plt.Figure:
|
||||
usages: dict[str, int] = dict.fromkeys(ressource.usage.choices, 0)
|
||||
for usage in map(extract.usage.extract, datas):
|
||||
usages[usage] += 1
|
||||
|
||||
x = list(usages.keys())
|
||||
y = list(usages.values())
|
||||
|
||||
# prepare plotting
|
||||
figure: plt.Figure = plt.figure()
|
||||
axes = figure.add_subplot(1, 1, 1)
|
||||
axes.set_title("Habitude d'utilisation des personnes sondées")
|
||||
|
||||
# bar chart
|
||||
axes.bar(x, y, color=ressource.usage.colors, edgecolor='black')
|
||||
axes.set_xticks(x)
|
||||
axes.set_xticklabels(ressource.usage.labels)
|
||||
axes.set_xlabel("Usage")
|
||||
axes.set_ylabel("Quantité")
|
||||
|
||||
return figure
|
|
@ -1,7 +0,0 @@
|
|||
from . import usage
|
||||
from . import age
|
||||
from . import mission_duration
|
||||
from . import mission_completed
|
||||
from . import experience
|
||||
from . import hardest_mission
|
||||
from . import language
|
|
@ -1,2 +0,0 @@
|
|||
def extract(data: dict) -> int:
|
||||
return data["surveys"]["question-age"]["value"]
|
|
@ -1,5 +0,0 @@
|
|||
def extract(data: dict) -> str:
|
||||
return next(filter(
|
||||
lambda it: it[1]["checked"],
|
||||
data["surveys"]["question-experience"]["choices"].items()
|
||||
))[0]
|
|
@ -1,5 +0,0 @@
|
|||
def extract(data: dict) -> str:
|
||||
return next(filter(
|
||||
lambda it: it[1]["checked"],
|
||||
data["surveys"]["question-hardest-mission"]["choices"].items()
|
||||
))[0]
|
|
@ -1,2 +0,0 @@
|
|||
def extract(data: dict) -> str:
|
||||
return data["language"]
|
|
@ -1,9 +0,0 @@
|
|||
def extract(data: dict, mission: str) -> bool:
|
||||
events = data["surveys"][mission]["event"]
|
||||
|
||||
try:
|
||||
checks = next(filter(lambda event: event["type"] == "check", events))
|
||||
except StopIteration:
|
||||
return False
|
||||
else:
|
||||
return True
|
|
@ -1,2 +0,0 @@
|
|||
def extract(data: dict, mission: str) -> float:
|
||||
return data["surveys"][mission]["event"][-1]["time"]
|
|
@ -1,5 +0,0 @@
|
|||
def extract(data: dict) -> str:
|
||||
return next(filter(
|
||||
lambda it: it[1]["checked"],
|
||||
data["surveys"]["question-usage-steam"]["choices"].items()
|
||||
))[0]
|
|
@ -1,70 +0,0 @@
|
|||
from pathlib import Path
|
||||
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
from tools.statistics.analyse import (age, usage, completion_per_mission, duration_per_mission, completion_per_age,
|
||||
completion_per_usage, duration_per_age, duration_per_usage,
|
||||
completion_per_experience, duration_per_experience, experience, hardest_mission,
|
||||
language, duration_per_language, completion_per_language)
|
||||
|
||||
|
||||
plt.rcParams['text.usetex'] = True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from source.utils import compress
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use("pgf")
|
||||
matplotlib.rcParams.update({
|
||||
"pgf.texsystem": "pdflatex",
|
||||
'font.family': 'serif',
|
||||
'font.size': 11,
|
||||
'text.usetex': True,
|
||||
'pgf.rcfonts': False,
|
||||
})
|
||||
|
||||
sondage_path = Path(r"./sondage/")
|
||||
graph_path = Path(r"./graph/")
|
||||
graph_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# read every peoples survey data
|
||||
datas_all = [
|
||||
compress.uncompress_data(file.read_bytes()) # decompress the data
|
||||
for file in sondage_path.rglob("*.rsl")
|
||||
]
|
||||
|
||||
# keep only the datas before the steam new year update
|
||||
datas_steam_version_1 = list(filter(lambda data: data["time"] < 1704409200, datas_all))
|
||||
|
||||
# keep only the datas after the steam new year update
|
||||
datas_steam_version_2 = list(filter(lambda data: data["time"] >= 1704409200, datas_all))
|
||||
|
||||
# regroup all the datas
|
||||
datasets = {
|
||||
"all": datas_all,
|
||||
"version_1": datas_steam_version_1,
|
||||
"version_2": datas_steam_version_2,
|
||||
}
|
||||
|
||||
for datas_name, datas in datasets.items():
|
||||
directory = graph_path / datas_name
|
||||
directory.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
age.analyse(datas).savefig(directory / "age.svg")
|
||||
usage.analyse(datas).savefig(directory / "usage.svg")
|
||||
experience.analyse(datas).savefig(directory / "experience.svg")
|
||||
hardest_mission.analyse(datas).savefig(directory / "hardest_mission.svg")
|
||||
language.analyse(datas).savefig(directory / "language.svg")
|
||||
|
||||
completion_per_mission.analyse(datas).savefig(directory / "completion_per_mission.svg")
|
||||
completion_per_age.analyse(datas).savefig(directory / "completion_per_age.svg")
|
||||
completion_per_usage.analyse(datas).savefig(directory / "completion_per_usage.svg")
|
||||
completion_per_experience.analyse(datas).savefig(directory / "completion_per_experience.svg")
|
||||
completion_per_language.analyse(datas).savefig(directory / "completion_per_language.svg")
|
||||
|
||||
duration_per_mission.analyse(datas).savefig(directory / "duration_per_mission.svg")
|
||||
duration_per_age.analyse(datas).savefig(directory / "duration_per_age.svg")
|
||||
duration_per_usage.analyse(datas).savefig(directory / "duration_per_usage.svg")
|
||||
duration_per_experience.analyse(datas).savefig(directory / "duration_per_experience.svg")
|
||||
duration_per_language.analyse(datas).savefig(directory / "duration_per_language.svg")
|
|
@ -1,4 +0,0 @@
|
|||
from . import experience
|
||||
from . import mission
|
||||
from . import usage
|
||||
from . import language
|
|
@ -1,3 +0,0 @@
|
|||
choices = ["yes", "mixed", "no"]
|
||||
labels = ["Oui", "Mitigé", "Non"]
|
||||
colors = ["g", "y", "r"]
|
|
@ -1,3 +0,0 @@
|
|||
choices = ["fr", "en", "es"]
|
||||
labels = ["Français", "Anglais", "Espagnol"]
|
||||
colors = ["b", "g", "r"]
|
|
@ -1,36 +0,0 @@
|
|||
choices = [
|
||||
"mission-language",
|
||||
"mission-price",
|
||||
"mission-community-hub",
|
||||
"mission-game-page",
|
||||
"mission-game-dlc",
|
||||
"mission-actuality-new",
|
||||
"mission-profile",
|
||||
"mission-game-discussion",
|
||||
"mission-gift-card",
|
||||
"mission-workshop",
|
||||
]
|
||||
labels = [
|
||||
"Changement de Langue",
|
||||
"Filtre de Prix",
|
||||
"Hub de la Communauté",
|
||||
"Page de Jeu",
|
||||
"Page de DLC",
|
||||
"Page d'Actualité",
|
||||
"Page de Profil",
|
||||
"Page de Discussion",
|
||||
"Carte Cadeaux",
|
||||
"Workshop",
|
||||
]
|
||||
colors = [
|
||||
"tab:blue",
|
||||
"tab:orange",
|
||||
"tab:green",
|
||||
"tab:red",
|
||||
"tab:olive",
|
||||
"tab:purple",
|
||||
"tab:brown",
|
||||
"tab:pink",
|
||||
"tab:cyan",
|
||||
"tab:gray",
|
||||
]
|
|
@ -1,3 +0,0 @@
|
|||
choices = ["always", "often", "sometime", "rarely", "never"]
|
||||
labels = ["Toujours", "Souvent", "Parfois", "Rarement", "Jamais"]
|
||||
colors = ["g", "c", "y", "m", "r"]
|
|
@ -1,20 +0,0 @@
|
|||
# Web-Replay
|
||||
|
||||
Allow you to replay the `web-mission` tasks that a user did while taking a survey.
|
||||
|
||||
# Installation
|
||||
|
||||
The installation is the same as the Survey-Engine. Refer to their installation step instead.
|
||||
|
||||
# Run
|
||||
|
||||
You must open a shell in the root directory (the `Survey-Engine` directory).
|
||||
You can run the tool simply by using the command `python3 ./tools/web_replay/main.py`.
|
||||
|
||||
You will need to select a replay file `.rsl` and the mission you want to replay.
|
||||
|
||||
# Warning
|
||||
|
||||
The replay is in early development. Some features are missing and the replay is not very precise.
|
||||
For now, key inputs don't seem to work correctly, so they are written on the bottom right instead.
|
||||
Mouse movement are not perfect and mouse click don't seem to be recognised either.
|
|
@ -1,17 +0,0 @@
|
|||
import sys
|
||||
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
|
||||
from ui import ReplayWindow
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# create the application
|
||||
application = QApplication(sys.argv)
|
||||
|
||||
# create the window
|
||||
window = ReplayWindow("surveys.json")
|
||||
window.show()
|
||||
|
||||
# start the application
|
||||
application.exec()
|
|
@ -1,63 +0,0 @@
|
|||
from PyQt6.QtCore import pyqtSignal
|
||||
from PyQt6.QtWidgets import QWidget, QLineEdit, QPushButton, QVBoxLayout, QFrame, QHBoxLayout, QComboBox, QFileDialog
|
||||
|
||||
|
||||
class ReplayConfiguration(QWidget):
|
||||
signal_start_replay = pyqtSignal([str, str])
|
||||
|
||||
def __init__(self, missions: list[str]):
|
||||
super().__init__()
|
||||
|
||||
# layout
|
||||
layout = QVBoxLayout()
|
||||
self.setLayout(layout)
|
||||
|
||||
# replay path
|
||||
self.frame_path = QFrame()
|
||||
layout.addWidget(self.frame_path)
|
||||
|
||||
layout_path = QHBoxLayout()
|
||||
self.frame_path.setLayout(layout_path)
|
||||
|
||||
self.entry_path = QLineEdit()
|
||||
layout_path.addWidget(self.entry_path)
|
||||
|
||||
self.button_path = QPushButton()
|
||||
layout_path.addWidget(self.button_path)
|
||||
self.button_path.setText("...")
|
||||
self.button_path.clicked.connect(self.select_replay) # NOQA: connect exist
|
||||
|
||||
# mission
|
||||
self.frame_mission = QFrame()
|
||||
layout.addWidget(self.frame_mission)
|
||||
|
||||
layout_mission = QHBoxLayout()
|
||||
self.frame_mission.setLayout(layout_mission)
|
||||
|
||||
self.listbox_mission = QComboBox()
|
||||
layout_mission.addWidget(self.listbox_mission)
|
||||
|
||||
for mission in missions:
|
||||
self.listbox_mission.addItem(mission)
|
||||
|
||||
# button
|
||||
self.button_confirm = QPushButton()
|
||||
layout.addWidget(self.button_confirm)
|
||||
self.button_confirm.setText("Replay")
|
||||
self.button_confirm.clicked.connect(self._start_replay) # NOQA: connect exist
|
||||
|
||||
def select_replay(self):
|
||||
# prompt the user for a file
|
||||
file, raw_filetype = QFileDialog.getOpenFileName(
|
||||
caption="Select the file to replay.",
|
||||
filter="Replay (*.rsl)"
|
||||
)
|
||||
|
||||
# if no file were selected, ignore
|
||||
if not file:
|
||||
return
|
||||
|
||||
self.entry_path.setText(file)
|
||||
|
||||
def _start_replay(self):
|
||||
self.signal_start_replay.emit(self.entry_path.text(), self.listbox_mission.currentText()) # NOQA: emit
|
|
@ -1,194 +0,0 @@
|
|||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
|
||||
from PyQt6.QtCore import Qt, QUrl, QPointF, QTimer
|
||||
from PyQt6.QtGui import QKeyEvent, QMouseEvent
|
||||
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QApplication, QLabel
|
||||
|
||||
from source.utils import compress
|
||||
from . import ReplayWebEngineView, ReplayNavigation
|
||||
|
||||
|
||||
class ReplayEngine(QWidget):
|
||||
"""
|
||||
This widget allow to replay some event that occurred on a web page
|
||||
"""
|
||||
|
||||
def __init__(self, replay_path: Path | str, mission: str):
|
||||
super().__init__()
|
||||
|
||||
# load the replay
|
||||
with open(replay_path, "rb") as file:
|
||||
replay_data = compress.uncompress_data(file.read())
|
||||
|
||||
self.start_time = datetime.fromtimestamp(replay_data["time"])
|
||||
self.replay_events = replay_data["surveys"][mission]["event"]
|
||||
self.replay_index: int = 0
|
||||
self.replay_time: float = 0
|
||||
|
||||
# layout
|
||||
self._layout = QVBoxLayout()
|
||||
self.setLayout(self._layout)
|
||||
|
||||
# cursor
|
||||
self.cursor = QLabel(self)
|
||||
self.cursor.setFixedSize(20, 20)
|
||||
self.cursor.setStyleSheet("background-color: red; border-radius: 10px;")
|
||||
|
||||
# web
|
||||
self.web = ReplayWebEngineView(self.start_time)
|
||||
self._layout.addWidget(self.web, 1)
|
||||
|
||||
# information
|
||||
self.navigation = ReplayNavigation()
|
||||
self._layout.addWidget(self.navigation)
|
||||
|
||||
# event timer
|
||||
self.timer = QTimer()
|
||||
|
||||
def run_event(self, event: dict, callback: Callable):
|
||||
# TODO: check if click are done correctly, check position, if event are correct, ...
|
||||
|
||||
match event["type"]:
|
||||
case "success":
|
||||
# success event
|
||||
print(f"success ! ({event['time']}s)")
|
||||
|
||||
callback()
|
||||
|
||||
case "url":
|
||||
# changing url event
|
||||
self.web.setUrl(QUrl(event["url"]))
|
||||
|
||||
# callback
|
||||
self.web.loadFinished.connect(lambda ok: callback()) # NOQA: connect exist
|
||||
|
||||
case "resize":
|
||||
# changing widget size event
|
||||
w, h = event["size"]
|
||||
zoom_factor: float = self.web.page().contentsSize().width() / w
|
||||
self.web.setZoomFactor(zoom_factor)
|
||||
|
||||
# callback
|
||||
callback()
|
||||
|
||||
case "keyboard_press":
|
||||
# keyboard key pressed event
|
||||
qevent = QKeyEvent(
|
||||
QKeyEvent.Type.KeyPress,
|
||||
event["key"],
|
||||
Qt.KeyboardModifier.NoModifier
|
||||
)
|
||||
qevent.custom = True
|
||||
QApplication.postEvent(self.web.focusProxy(), qevent)
|
||||
|
||||
# callback
|
||||
callback()
|
||||
|
||||
case "keyboard_release":
|
||||
# keyboard key released event
|
||||
qevent = QKeyEvent(
|
||||
QKeyEvent.Type.KeyRelease,
|
||||
event["key"],
|
||||
Qt.KeyboardModifier.NoModifier
|
||||
)
|
||||
qevent.custom = True
|
||||
QApplication.postEvent(self.web.focusProxy(), qevent)
|
||||
|
||||
# callback
|
||||
callback()
|
||||
|
||||
case "mouse_press":
|
||||
# mouse pressed event
|
||||
qevent = QMouseEvent(
|
||||
QMouseEvent.Type.KeyPress,
|
||||
QPointF(*event["position"]) / self.web.zoomFactor(),
|
||||
Qt.MouseButton(event["button"]),
|
||||
Qt.MouseButton.NoButton,
|
||||
Qt.KeyboardModifier.NoModifier
|
||||
)
|
||||
qevent.custom = True
|
||||
QApplication.postEvent(self.web.focusProxy(), qevent)
|
||||
|
||||
# callback
|
||||
callback()
|
||||
|
||||
case "mouse_release":
|
||||
# mouse pressed event
|
||||
qevent = QMouseEvent(
|
||||
QMouseEvent.Type.KeyRelease,
|
||||
QPointF(*event["position"]) / self.web.zoomFactor(),
|
||||
Qt.MouseButton(event["button"]),
|
||||
Qt.MouseButton.NoButton,
|
||||
Qt.KeyboardModifier.NoModifier
|
||||
)
|
||||
qevent.custom = True
|
||||
QApplication.postEvent(self.web.focusProxy(), qevent)
|
||||
|
||||
# callback
|
||||
callback()
|
||||
|
||||
case "mouse_move":
|
||||
# mouse moved event
|
||||
qevent = QMouseEvent(
|
||||
QMouseEvent.Type.MouseMove,
|
||||
QPointF(*event["position"]) / self.web.zoomFactor(),
|
||||
Qt.MouseButton.NoButton,
|
||||
Qt.MouseButton.NoButton,
|
||||
Qt.KeyboardModifier.NoModifier
|
||||
)
|
||||
qevent.custom = True
|
||||
QApplication.postEvent(self.web.focusProxy(), qevent)
|
||||
|
||||
# move the cursor
|
||||
self.cursor.move(QPointF(*event["position"]).toPoint())
|
||||
self.cursor.raise_()
|
||||
|
||||
# callback
|
||||
callback()
|
||||
|
||||
case "scroll":
|
||||
# scroll event
|
||||
x, y = event["position"]
|
||||
self.web.page().runJavaScript(
|
||||
f"window.scrollTo({x}, {y});",
|
||||
resultCallback=lambda result: callback()
|
||||
)
|
||||
|
||||
def next(self):
|
||||
# get event information
|
||||
if self.replay_index >= len(self.replay_events):
|
||||
return
|
||||
|
||||
event = self.replay_events[self.replay_index]
|
||||
self.replay_time = event["time"]
|
||||
self.replay_index = self.replay_index + 1
|
||||
|
||||
# set text
|
||||
self.navigation.set_description(f"{event}")
|
||||
|
||||
# run the event
|
||||
self.run_event(
|
||||
event,
|
||||
self._next_callback
|
||||
)
|
||||
|
||||
def _next_callback(self):
|
||||
# prevent the web loading to call this function again
|
||||
try:
|
||||
self.web.loadFinished.disconnect(self._next_callback) # NOQA: disconnect exist
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
# if there are still events after this one
|
||||
if self.replay_index < len(self.replay_events):
|
||||
# next event
|
||||
next_event: dict = self.replay_events[self.replay_index]
|
||||
next_time: float = next_event["time"]
|
||||
|
||||
# prepare the timer to play the event at the corresponding time
|
||||
self.timer.singleShot(
|
||||
round((next_time - self.replay_time) / 200),
|
||||
self.next
|
||||
)
|
|
@ -1,19 +0,0 @@
|
|||
from PyQt6.QtCore import Qt
|
||||
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel
|
||||
|
||||
|
||||
class ReplayNavigation(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# layout
|
||||
layout = QVBoxLayout()
|
||||
self.setLayout(layout)
|
||||
|
||||
# information of the replay
|
||||
self._description = QLabel()
|
||||
layout.addWidget(self._description)
|
||||
self._description.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
def set_description(self, text: str):
|
||||
self._description.setText(text)
|
|
@ -1,66 +0,0 @@
|
|||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from PyQt6.QtCore import QObject, QEvent, QUrl
|
||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||
|
||||
|
||||
class ReplayWebEngineView(QWebEngineView):
|
||||
def __init__(self, start_time: datetime):
|
||||
super().__init__()
|
||||
|
||||
self.start_time = start_time
|
||||
self._last_url: Optional[QUrl] = None
|
||||
|
||||
self.loadFinished.connect(self._initialize_proxy_event) # NOQA: connect exist
|
||||
|
||||
# event filter
|
||||
|
||||
def setUrl(self, url: QUrl, archive: bool = True) -> None:
|
||||
if archive:
|
||||
# get the archive.org link corresponding to that time
|
||||
archive_time: str = self.start_time.strftime("%Y%m%d%H%M%S")
|
||||
url = QUrl(f"https://web.archive.org/web/{archive_time}/{url.toString()}")
|
||||
|
||||
self._last_url = url
|
||||
|
||||
# call the super function with the archive url instead
|
||||
super().setUrl(url)
|
||||
|
||||
# clean the archive header popup that will appear
|
||||
self.loadFinished.connect(self._on_load_finished) # NOQA: connect exist
|
||||
|
||||
def eventFilter(self, obj: QObject, event: QEvent) -> bool:
|
||||
match event.type():
|
||||
# allow scroll events (they are created automatically)
|
||||
case event.Type.Scroll:
|
||||
pass
|
||||
|
||||
# allow timed events
|
||||
case event.Type.Timer:
|
||||
pass
|
||||
|
||||
# ignore all other events
|
||||
case _:
|
||||
if not getattr(event, "custom", False):
|
||||
return True
|
||||
|
||||
return super().eventFilter(obj, event)
|
||||
|
||||
# events
|
||||
|
||||
def _initialize_proxy_event(self):
|
||||
# make self.eventFilter intercept all focusProxy events
|
||||
self.focusProxy().installEventFilter(self)
|
||||
|
||||
def _on_load_finished(self, ok: bool):
|
||||
# prevent the event from being enabled another time
|
||||
self.loadFinished.disconnect(self._on_load_finished) # NOQA: disconnect exist
|
||||
|
||||
if ok:
|
||||
# hide archive.org header to avoid mouse movement being shifted
|
||||
self.page().runJavaScript("document.getElementById('wm-ipp-base').style.display = 'none';")
|
||||
self._initialize_proxy_event()
|
||||
|
||||
else:
|
||||
self.setUrl(self._last_url, archive=False)
|
|
@ -1,38 +0,0 @@
|
|||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt6.QtWidgets import QMainWindow
|
||||
|
||||
from tools.web_replay.ui import ReplayConfiguration, ReplayEngine
|
||||
|
||||
|
||||
class ReplayWindow(QMainWindow):
|
||||
def __init__(self, survey_path: Path | str):
|
||||
super().__init__()
|
||||
|
||||
# get the survey configuration
|
||||
with open(survey_path, encoding="utf-8") as file:
|
||||
survey_configuration: dict = json.load(file)
|
||||
|
||||
# get all the missions available in the survey that can be replayed
|
||||
missions: list[str] = [
|
||||
mission_id
|
||||
for mission_id, mission in survey_configuration["surveys"].items()
|
||||
if mission["type"] == "mission-web"
|
||||
]
|
||||
|
||||
# decoration
|
||||
self.setWindowTitle("Survey Engine - Web Replay")
|
||||
|
||||
# show the configuration
|
||||
self.replay_engine = None
|
||||
self.replay_configuration = ReplayConfiguration(missions=missions)
|
||||
self.setCentralWidget(self.replay_configuration)
|
||||
self.replay_configuration.signal_start_replay.connect(self.start_replay)
|
||||
|
||||
def start_replay(self, replay_path: str, mission: str):
|
||||
# start the replay
|
||||
self.replay_engine = ReplayEngine(replay_path, mission)
|
||||
self.setCentralWidget(self.replay_engine)
|
||||
self.showFullScreen()
|
||||
self.replay_engine.next()
|
|
@ -1,5 +0,0 @@
|
|||
from .ReplayWebEngineView import ReplayWebEngineView
|
||||
from .ReplayNavigation import ReplayNavigation
|
||||
from .ReplayEngine import ReplayEngine
|
||||
from .ReplayConfiguration import ReplayConfiguration
|
||||
from .ReplayWindow import ReplayWindow
|
Loading…
Reference in a new issue