Compare commits
21 commits
Author | SHA1 | Date | |
---|---|---|---|
1abd1327ef | |||
9967f1dff7 | |||
a878d142bd | |||
7a62e19b7f | |||
cb2d7244e8 | |||
fe79db36f0 | |||
0e7ddd4c53 | |||
3d120dab2d | |||
e4f00742e2 | |||
207434484e | |||
eff680be38 | |||
40ff08a13d | |||
a166d64241 | |||
7e1467e7f1 | |||
ca21da9f7a | |||
12617346d7 | |||
30dd5410f0 | |||
e7d43371bb | |||
10e921ea23 | |||
1ffcf5cdb8 | |||
4a8a80a206 |
67 changed files with 1460 additions and 188 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 :
|
To install the projet, you will need to install :
|
||||||
|
|
||||||
- Python >= 3.11 (preferably in a virtual environment)
|
- Python >= 3.11 (preferably in a virtual environment with `python3 -m venv ./.venv/`)
|
||||||
- Python dependencies (`python3 -m pip install -r ./requirements.txt`)
|
- 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).
|
Or download one of the build in the [releases page](https://github.com/Faraphel/M1-Recherche/releases).
|
||||||
|
|
Binary file not shown.
|
@ -1,10 +1,23 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!DOCTYPE TS>
|
<!DOCTYPE TS>
|
||||||
<TS version="2.1" language="en">
|
<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>
|
<name>SurveyEngine</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../source/widget/SurveyEngine.py" line="177"/>
|
<location filename="..\..\source\ui\SurveyEngine.py" line="177" />
|
||||||
<source>WARNING</source>
|
<source>WARNING</source>
|
||||||
<translation>Warning</translation>
|
<translation>Warning</translation>
|
||||||
</message>
|
</message>
|
||||||
|
@ -12,27 +25,19 @@
|
||||||
<context>
|
<context>
|
||||||
<name>SurveyNavigation</name>
|
<name>SurveyNavigation</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../source/widget/SurveyNavigation.py" line="19"/>
|
<location filename="..\..\source\ui\SurveyNavigation.py" line="19" />
|
||||||
<source>ABANDON</source>
|
<source>ABANDON</source>
|
||||||
<translation>Abandon</translation>
|
<translation>Abandon</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../source/widget/SurveyNavigation.py" line="29"/>
|
<location filename="..\..\source\ui\SurveyNavigation.py" line="29" />
|
||||||
<source>SKIP</source>
|
<source>SKIP</source>
|
||||||
<translation>Skip</translation>
|
<translation>Skip</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../source/widget/SurveyNavigation.py" line="38"/>
|
<location filename="..\..\source\ui\SurveyNavigation.py" line="38" />
|
||||||
<source>NEXT</source>
|
<source>NEXT</source>
|
||||||
<translation>Next</translation>
|
<translation>Next</translation>
|
||||||
</message>
|
</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>
|
</TS>
|
||||||
|
|
BIN
assets/language/es.qm
Normal file
BIN
assets/language/es.qm
Normal file
Binary file not shown.
43
assets/language/es.ts
Normal file
43
assets/language/es.ts
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<?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,38 +1,43 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!DOCTYPE TS>
|
<!DOCTYPE TS>
|
||||||
<TS version="2.1" language="fr_FR">
|
<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>
|
<name>SurveyEngine</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../source/widget/SurveyEngine.py" line="177"/>
|
<location filename="..\..\source\ui\SurveyEngine.py" line="177" />
|
||||||
<source>WARNING</source>
|
<source>WARNING</source>
|
||||||
<translation>Avertissement</translation>
|
<translation>Attention</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>SurveyNavigation</name>
|
<name>SurveyNavigation</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../source/widget/SurveyNavigation.py" line="19"/>
|
<location filename="..\..\source\ui\SurveyNavigation.py" line="19" />
|
||||||
<source>ABANDON</source>
|
<source>ABANDON</source>
|
||||||
<translation>Abandonner</translation>
|
<translation>Abandonner</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../source/widget/SurveyNavigation.py" line="29"/>
|
<location filename="..\..\source\ui\SurveyNavigation.py" line="29" />
|
||||||
<source>SKIP</source>
|
<source>SKIP</source>
|
||||||
<translation>Passer</translation>
|
<translation>Passer</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../../source/widget/SurveyNavigation.py" line="38"/>
|
<location filename="..\..\source\ui\SurveyNavigation.py" line="38" />
|
||||||
<source>NEXT</source>
|
<source>NEXT</source>
|
||||||
<translation>Suivant</translation>
|
<translation>Suivant</translation>
|
||||||
</message>
|
</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>
|
</TS>
|
||||||
|
|
Binary file not shown.
|
@ -1,38 +0,0 @@
|
||||||
<?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,29 +1,14 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from PyQt6.QtCore import QTranslator, QLocale
|
|
||||||
from PyQt6.QtWidgets import QApplication
|
from PyQt6.QtWidgets import QApplication
|
||||||
|
|
||||||
from source import translate
|
from source.ui import SurveyWindow
|
||||||
from source import assets_path
|
|
||||||
from source.widget import SurveyWindow
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# create the application
|
# create the application
|
||||||
application = QApplication(sys.argv)
|
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
|
# create the window
|
||||||
window = SurveyWindow("./surveys.json")
|
window = SurveyWindow("./surveys.json")
|
||||||
window.show()
|
window.show()
|
||||||
|
|
|
@ -5,3 +5,5 @@ nextcord~=2.6.0
|
||||||
requests~=2.31.0
|
requests~=2.31.0
|
||||||
|
|
||||||
cx_freeze
|
cx_freeze
|
||||||
|
matplotlib~=3.8.2
|
||||||
|
numpy~=1.26.3
|
13
setup.py
13
setup.py
|
@ -22,16 +22,25 @@ setup(
|
||||||
"build_exe": {
|
"build_exe": {
|
||||||
"include_msvcr": True,
|
"include_msvcr": True,
|
||||||
"include_files": [
|
"include_files": [
|
||||||
|
("./tools/", "./tools/"),
|
||||||
("./assets/", "./assets/"),
|
("./assets/", "./assets/"),
|
||||||
("./README.md", "./README.md"),
|
("./README.md", "./README.md"),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
executables=[Executable(
|
executables=[
|
||||||
|
Executable(
|
||||||
"main.py",
|
"main.py",
|
||||||
base=base,
|
base=base,
|
||||||
target_name=__appname__,
|
target_name=__appname__,
|
||||||
icon=__icon_ico__
|
icon=__icon_ico__
|
||||||
)]
|
),
|
||||||
|
Executable(
|
||||||
|
"tools/web_replay/main.py",
|
||||||
|
base=base,
|
||||||
|
target_name="tools/web_replay/main",
|
||||||
|
icon=__icon_ico__
|
||||||
|
),
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,7 @@ from pathlib import Path
|
||||||
assets_path = Path("./assets/")
|
assets_path = Path("./assets/")
|
||||||
|
|
||||||
__appname__ = "Survey Engine"
|
__appname__ = "Survey Engine"
|
||||||
__version__ = (1,0,0)
|
__version__ = (1,0,2)
|
||||||
__str_version__ = ".".join(map(str, __version__))
|
__str_version__ = ".".join(map(str, __version__))
|
||||||
__author__ = "Faraphel"
|
__author__ = "Faraphel"
|
||||||
__mail__ = "rc60650@hotmail.com"
|
__mail__ = "rc60650@hotmail.com"
|
||||||
|
|
|
@ -5,6 +5,7 @@ from typing import Optional
|
||||||
import nextcord
|
import nextcord
|
||||||
import requests
|
import requests
|
||||||
from PyQt6.QtCore import pyqtSignal
|
from PyQt6.QtCore import pyqtSignal
|
||||||
|
from PyQt6.QtWidgets import QApplication
|
||||||
|
|
||||||
result_path = Path("./results/")
|
result_path = Path("./results/")
|
||||||
|
|
||||||
|
@ -59,6 +60,7 @@ def upload_discord(
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
if signal_warning is not None:
|
if signal_warning is not None:
|
||||||
signal_warning.emit("COULD NOT UPLOAD THE DATA") # NOQA: emit exist
|
application = QApplication.instance()
|
||||||
|
signal_warning.emit(application.tr("COULD NOT UPLOAD THE DATA")) # NOQA: emit exist
|
||||||
else:
|
else:
|
||||||
raise exc
|
raise exc
|
||||||
|
|
|
@ -4,7 +4,7 @@ from PyQt6.QtCore import Qt, pyqtSignal
|
||||||
from PyQt6.QtGui import QFont
|
from PyQt6.QtGui import QFont
|
||||||
from PyQt6.QtWidgets import QVBoxLayout, QLabel, QSpinBox
|
from PyQt6.QtWidgets import QVBoxLayout, QLabel, QSpinBox
|
||||||
|
|
||||||
from source import translate, widget
|
from source import translate, ui
|
||||||
from source.survey.base import BaseSurvey
|
from source.survey.base import BaseSurvey
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ class IntegerQuestion(BaseSurvey):
|
||||||
self._layout.addWidget(self.entry_response)
|
self._layout.addWidget(self.entry_response)
|
||||||
|
|
||||||
# navigation
|
# navigation
|
||||||
self.navigation = widget.SurveyNavigation(signals=signals)
|
self.navigation = ui.SurveyNavigation(signals=signals)
|
||||||
self._layout.addWidget(self.navigation)
|
self._layout.addWidget(self.navigation)
|
||||||
self.navigation.show_forward()
|
self.navigation.show_forward()
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from PyQt6.QtCore import Qt, pyqtSignal
|
||||||
from PyQt6.QtGui import QFont
|
from PyQt6.QtGui import QFont
|
||||||
from PyQt6.QtWidgets import QVBoxLayout, QLabel
|
from PyQt6.QtWidgets import QVBoxLayout, QLabel
|
||||||
|
|
||||||
from source import translate, widget
|
from source import translate, ui
|
||||||
from source.survey.base import BaseSurvey
|
from source.survey.base import BaseSurvey
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class Text(BaseSurvey):
|
||||||
self.label_description.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.label_description.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
# navigation
|
# navigation
|
||||||
self.navigation = widget.SurveyNavigation(signals=signals)
|
self.navigation = ui.SurveyNavigation(signals=signals)
|
||||||
self._layout.addWidget(self.navigation)
|
self._layout.addWidget(self.navigation)
|
||||||
|
|
||||||
self.navigation.show_forward() # always show forward
|
self.navigation.show_forward() # always show forward
|
||||||
|
|
|
@ -4,7 +4,7 @@ from PyQt6.QtCore import Qt, pyqtSignal
|
||||||
from PyQt6.QtGui import QFont
|
from PyQt6.QtGui import QFont
|
||||||
from PyQt6.QtWidgets import QVBoxLayout, QLabel, QTextEdit
|
from PyQt6.QtWidgets import QVBoxLayout, QLabel, QTextEdit
|
||||||
|
|
||||||
from source import translate, widget
|
from source import translate, ui
|
||||||
from source.survey.base import BaseSurvey
|
from source.survey.base import BaseSurvey
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ class TextQuestion(BaseSurvey):
|
||||||
self._layout.addWidget(self.entry_response)
|
self._layout.addWidget(self.entry_response)
|
||||||
|
|
||||||
# navigation
|
# navigation
|
||||||
self.navigation = widget.SurveyNavigation(signals=signals)
|
self.navigation = ui.SurveyNavigation(signals=signals)
|
||||||
self._layout.addWidget(self.navigation)
|
self._layout.addWidget(self.navigation)
|
||||||
self.navigation.show_forward()
|
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.QtGui import QFont, QMouseEvent, QResizeEvent, QKeyEvent
|
||||||
from PyQt6.QtWidgets import QLabel, QVBoxLayout, QSizePolicy
|
from PyQt6.QtWidgets import QLabel, QVBoxLayout, QSizePolicy
|
||||||
|
|
||||||
from source import translate, widget
|
from source import translate, ui
|
||||||
from source.survey.base import BaseSurvey
|
from source.survey.base import BaseSurvey
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,13 +14,20 @@ class WebMission(BaseSurvey):
|
||||||
self,
|
self,
|
||||||
title: translate.Translatable,
|
title: translate.Translatable,
|
||||||
url: str,
|
url: str,
|
||||||
check_condition: Optional[str] = None,
|
|
||||||
|
initial_js: Optional[str] = None,
|
||||||
|
start_check_js: Optional[str] = None,
|
||||||
|
check_js: Optional[str] = None,
|
||||||
|
|
||||||
skip_time: Optional[float] = None,
|
skip_time: Optional[float] = None,
|
||||||
signals: dict[str, pyqtSignal] = None
|
signals: dict[str, pyqtSignal] = None
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.check_condition = check_condition
|
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.default_url = url
|
self.default_url = url
|
||||||
self.skip_time = skip_time
|
self.skip_time = skip_time
|
||||||
self.signals = signals if signals is not None else {}
|
self.signals = signals if signals is not None else {}
|
||||||
|
@ -47,48 +54,38 @@ class WebMission(BaseSurvey):
|
||||||
self.label_title.setFont(font_title)
|
self.label_title.setFont(font_title)
|
||||||
|
|
||||||
# web page
|
# web page
|
||||||
self.browser = widget.Browser()
|
self.browser = ui.Browser()
|
||||||
self._layout.addWidget(self.browser)
|
self._layout.addWidget(self.browser)
|
||||||
|
|
||||||
self.browser.web.focusProxy().installEventFilter(self) # capture the event in eventFilter
|
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.urlChanged.connect(self._on_url_changed) # NOQA: connect exist
|
||||||
|
|
||||||
self.browser.web.setUrl(QUrl(self.default_url))
|
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)
|
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
|
# setup the events
|
||||||
self.browser.web.page().scrollPositionChanged.connect(self._on_scroll_position_changed)
|
self.browser.web.page().scrollPositionChanged.connect(self._on_scroll_position_changed)
|
||||||
|
|
||||||
# navigation
|
# navigation
|
||||||
self.navigation = widget.SurveyNavigation(signals=signals)
|
self.navigation = ui.SurveyNavigation(signals=signals)
|
||||||
self._layout.addWidget(self.navigation)
|
self._layout.addWidget(self.navigation)
|
||||||
|
|
||||||
# initialize the start time
|
# initialize the start time
|
||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
|
|
||||||
# check timer
|
# initialise the timers
|
||||||
if self.timer_check is not None:
|
self.timer_start_check: Optional[QTimer] = None
|
||||||
# enable the timer
|
self.timer_check: Optional[QTimer] = None
|
||||||
self.timer_check.start()
|
self.timer_skip: Optional[QTimer] = None
|
||||||
else:
|
|
||||||
self._success() # call directly the success method
|
|
||||||
|
|
||||||
# skip timer
|
# skip timer
|
||||||
if self.timer_skip is not None:
|
if self.skip_time is not None:
|
||||||
# enable the timer for skipping the question
|
self.timer_skip = QTimer()
|
||||||
|
self.timer_skip.setInterval(self.skip_time * 1000)
|
||||||
|
self.timer_skip.timeout.connect(self._allow_time_skip) # NOQA: connect exist
|
||||||
|
|
||||||
self.timer_skip.start()
|
self.timer_skip.start()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -96,7 +93,11 @@ class WebMission(BaseSurvey):
|
||||||
return cls(
|
return cls(
|
||||||
title=data["title"],
|
title=data["title"],
|
||||||
url=data.get("url"),
|
url=data.get("url"),
|
||||||
check_condition=data.get("check"),
|
|
||||||
|
initial_js=data.get("initial_js"),
|
||||||
|
start_check_js=data.get("start_check_js"),
|
||||||
|
check_js=data.get("check_js"),
|
||||||
|
|
||||||
skip_time=data.get("skip_time"),
|
skip_time=data.get("skip_time"),
|
||||||
|
|
||||||
signals=signals
|
signals=signals
|
||||||
|
@ -205,30 +206,80 @@ class WebMission(BaseSurvey):
|
||||||
url=self.browser.web.url().toString()
|
url=self.browser.web.url().toString()
|
||||||
)
|
)
|
||||||
|
|
||||||
def _on_time_skip(self):
|
def _allow_time_skip(self):
|
||||||
# when the timer to allow skip have run out
|
# when the timer to allow skip have run out
|
||||||
if "skip" in self.signals:
|
if "skip" in self.signals:
|
||||||
self.navigation.show_skip()
|
self.navigation.show_skip()
|
||||||
|
|
||||||
# condition
|
# condition
|
||||||
|
|
||||||
def check(self) -> None:
|
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:
|
||||||
"""
|
"""
|
||||||
Check if the checking condition have been completed
|
Check if the checking condition have been completed
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def check_callback(result: bool):
|
# if the check evaluated to True, call the success method
|
||||||
|
def callback(result: bool):
|
||||||
if result:
|
if result:
|
||||||
|
# stop this check timer
|
||||||
|
self.timer_check.stop()
|
||||||
|
# mark the test as successful
|
||||||
self._success()
|
self._success()
|
||||||
|
|
||||||
page = self.browser.web.page()
|
# run the check
|
||||||
|
self.browser.web.page().runJavaScript(
|
||||||
# preprocess the condition
|
self._preprocess_check(self.check_js),
|
||||||
condition = self.check_condition
|
resultCallback=callback
|
||||||
condition = condition.replace("#LANGUAGE_CODE#", translate.get_language())
|
)
|
||||||
|
|
||||||
# run the condition
|
|
||||||
page.runJavaScript(condition, resultCallback=check_callback)
|
|
||||||
|
|
||||||
# data collection
|
# data collection
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from PyQt6.QtCore import Qt, pyqtSignal
|
||||||
from PyQt6.QtGui import QFont
|
from PyQt6.QtGui import QFont
|
||||||
from PyQt6.QtWidgets import QFrame, QVBoxLayout, QLabel, QButtonGroup, QLineEdit, QAbstractButton
|
from PyQt6.QtWidgets import QFrame, QVBoxLayout, QLabel, QButtonGroup, QLineEdit, QAbstractButton
|
||||||
|
|
||||||
from source import translate, widget
|
from source import translate, ui
|
||||||
from source.survey.base import BaseSurvey
|
from source.survey.base import BaseSurvey
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ class BaseChoiceQuestion(BaseSurvey):
|
||||||
self.label_question.setFont(font_title)
|
self.label_question.setFont(font_title)
|
||||||
|
|
||||||
# prepare navigation
|
# prepare navigation
|
||||||
self.navigation = widget.SurveyNavigation(signals=signals)
|
self.navigation = ui.SurveyNavigation(signals=signals)
|
||||||
|
|
||||||
# responses
|
# responses
|
||||||
self.frame_responses = QFrame()
|
self.frame_responses = QFrame()
|
||||||
|
@ -102,6 +102,10 @@ class BaseChoiceQuestion(BaseSurvey):
|
||||||
# add the navigation
|
# add the navigation
|
||||||
self._layout.addWidget(self.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
|
@classmethod
|
||||||
def from_dict(cls, data: dict[str, Any], signals: dict[str, pyqtSignal]) -> "BaseChoiceQuestion":
|
def from_dict(cls, data: dict[str, Any], signals: dict[str, pyqtSignal]) -> "BaseChoiceQuestion":
|
||||||
return cls(
|
return cls(
|
||||||
|
@ -114,7 +118,7 @@ class BaseChoiceQuestion(BaseSurvey):
|
||||||
collected_data = {
|
collected_data = {
|
||||||
"choices": {
|
"choices": {
|
||||||
choice_id: {
|
choice_id: {
|
||||||
"checked": choice_data["button"].isEnabled(),
|
"checked": choice_data["button"].isChecked(),
|
||||||
"details": entry.text() if (entry := choice_data["entry"]) is not None else None
|
"details": entry.text() if (entry := choice_data["entry"]) is not None else None
|
||||||
}
|
}
|
||||||
for choice_id, choice_data in self.buttons_responses.items()
|
for choice_id, choice_data in self.buttons_responses.items()
|
||||||
|
|
|
@ -77,7 +77,7 @@ class Browser(QWidget):
|
||||||
# update the progress bar
|
# update the progress bar
|
||||||
self.progress.setValue(value)
|
self.progress.setValue(value)
|
||||||
|
|
||||||
def _load_finished(self):
|
def _load_finished(self, ok: bool):
|
||||||
# update the progress bar
|
# update the progress bar
|
||||||
self.progress.hide()
|
self.progress.hide()
|
||||||
# refresh the navigation buttons
|
# refresh the navigation buttons
|
77
source/ui/LanguageSelection.py
Normal file
77
source/ui/LanguageSelection.py
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
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()
|
|
@ -8,7 +8,7 @@ from typing import Optional
|
||||||
from PyQt6.QtCore import pyqtSignal
|
from PyQt6.QtCore import pyqtSignal
|
||||||
from PyQt6.QtWidgets import QVBoxLayout, QProgressBar, QWidget, QMessageBox
|
from PyQt6.QtWidgets import QVBoxLayout, QProgressBar, QWidget, QMessageBox
|
||||||
|
|
||||||
from source import translate, widget, save
|
from source import translate, ui, save
|
||||||
from source.survey.base import BaseSurvey
|
from source.survey.base import BaseSurvey
|
||||||
from source.survey import Empty, survey_get
|
from source.survey import Empty, survey_get
|
||||||
from source.utils import compress
|
from source.utils import compress
|
||||||
|
@ -93,7 +93,7 @@ class SurveyEngine(QWidget):
|
||||||
|
|
||||||
def _on_signal_abandon(self):
|
def _on_signal_abandon(self):
|
||||||
# on abandon, quit the survey
|
# on abandon, quit the survey
|
||||||
window = typing.cast(widget.SurveyWindow, self.window())
|
window = typing.cast(ui.SurveyWindow, self.window())
|
||||||
window.quit()
|
window.quit()
|
||||||
|
|
||||||
def _on_signal_skip(self):
|
def _on_signal_skip(self):
|
||||||
|
@ -166,7 +166,7 @@ class SurveyEngine(QWidget):
|
||||||
)
|
)
|
||||||
|
|
||||||
# finally, close the window
|
# finally, close the window
|
||||||
window = typing.cast(widget.SurveyWindow, self.window())
|
window = typing.cast(ui.SurveyWindow, self.window())
|
||||||
window.quit()
|
window.quit()
|
||||||
|
|
||||||
# signals
|
# signals
|
||||||
|
@ -175,5 +175,5 @@ class SurveyEngine(QWidget):
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(
|
||||||
self,
|
self,
|
||||||
self.tr("WARNING"),
|
self.tr("WARNING"),
|
||||||
self.tr(message),
|
message,
|
||||||
)
|
)
|
34
source/ui/SurveyWindow.py
Normal file
34
source/ui/SurveyWindow.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
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()
|
|
@ -1,4 +1,5 @@
|
||||||
from .Browser import Browser
|
from .Browser import Browser
|
||||||
|
from .LanguageSelection import LanguageSelection
|
||||||
from .SurveyEngine import SurveyEngine
|
from .SurveyEngine import SurveyEngine
|
||||||
from .SurveyWindow import SurveyWindow
|
from .SurveyWindow import SurveyWindow
|
||||||
from .SurveyNavigation import SurveyNavigation
|
from .SurveyNavigation import SurveyNavigation
|
|
@ -1,23 +0,0 @@
|
||||||
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()
|
|
0
tools/__init__.py
Normal file
0
tools/__init__.py
Normal file
0
tools/statistics/__init__.py
Normal file
0
tools/statistics/__init__.py
Normal file
0
tools/statistics/analyse/__init__.py
Normal file
0
tools/statistics/analyse/__init__.py
Normal file
21
tools/statistics/analyse/age.py
Normal file
21
tools/statistics/analyse/age.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
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
|
42
tools/statistics/analyse/completion_per_age.py
Normal file
42
tools/statistics/analyse/completion_per_age.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
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
|
41
tools/statistics/analyse/completion_per_experience.py
Normal file
41
tools/statistics/analyse/completion_per_experience.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
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
|
41
tools/statistics/analyse/completion_per_language.py
Normal file
41
tools/statistics/analyse/completion_per_language.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
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
|
36
tools/statistics/analyse/completion_per_mission.py
Normal file
36
tools/statistics/analyse/completion_per_mission.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
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
|
45
tools/statistics/analyse/completion_per_usage.py
Normal file
45
tools/statistics/analyse/completion_per_usage.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
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
|
43
tools/statistics/analyse/duration_per_age.py
Normal file
43
tools/statistics/analyse/duration_per_age.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
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
|
41
tools/statistics/analyse/duration_per_experience.py
Normal file
41
tools/statistics/analyse/duration_per_experience.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
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
|
40
tools/statistics/analyse/duration_per_language.py
Normal file
40
tools/statistics/analyse/duration_per_language.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
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
|
37
tools/statistics/analyse/duration_per_mission.py
Normal file
37
tools/statistics/analyse/duration_per_mission.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
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
|
40
tools/statistics/analyse/duration_per_usage.py
Normal file
40
tools/statistics/analyse/duration_per_usage.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
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
|
30
tools/statistics/analyse/experience.py
Normal file
30
tools/statistics/analyse/experience.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
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
|
28
tools/statistics/analyse/hardest_mission.py
Normal file
28
tools/statistics/analyse/hardest_mission.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
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
|
26
tools/statistics/analyse/language.py
Normal file
26
tools/statistics/analyse/language.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
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
|
26
tools/statistics/analyse/usage.py
Normal file
26
tools/statistics/analyse/usage.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
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
|
7
tools/statistics/extract/__init__.py
Normal file
7
tools/statistics/extract/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from . import usage
|
||||||
|
from . import age
|
||||||
|
from . import mission_duration
|
||||||
|
from . import mission_completed
|
||||||
|
from . import experience
|
||||||
|
from . import hardest_mission
|
||||||
|
from . import language
|
2
tools/statistics/extract/age.py
Normal file
2
tools/statistics/extract/age.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
def extract(data: dict) -> int:
|
||||||
|
return data["surveys"]["question-age"]["value"]
|
5
tools/statistics/extract/experience.py
Normal file
5
tools/statistics/extract/experience.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
def extract(data: dict) -> str:
|
||||||
|
return next(filter(
|
||||||
|
lambda it: it[1]["checked"],
|
||||||
|
data["surveys"]["question-experience"]["choices"].items()
|
||||||
|
))[0]
|
5
tools/statistics/extract/hardest_mission.py
Normal file
5
tools/statistics/extract/hardest_mission.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
def extract(data: dict) -> str:
|
||||||
|
return next(filter(
|
||||||
|
lambda it: it[1]["checked"],
|
||||||
|
data["surveys"]["question-hardest-mission"]["choices"].items()
|
||||||
|
))[0]
|
2
tools/statistics/extract/language.py
Normal file
2
tools/statistics/extract/language.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
def extract(data: dict) -> str:
|
||||||
|
return data["language"]
|
9
tools/statistics/extract/mission_completed.py
Normal file
9
tools/statistics/extract/mission_completed.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
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
|
2
tools/statistics/extract/mission_duration.py
Normal file
2
tools/statistics/extract/mission_duration.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
def extract(data: dict, mission: str) -> float:
|
||||||
|
return data["surveys"][mission]["event"][-1]["time"]
|
5
tools/statistics/extract/usage.py
Normal file
5
tools/statistics/extract/usage.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
def extract(data: dict) -> str:
|
||||||
|
return next(filter(
|
||||||
|
lambda it: it[1]["checked"],
|
||||||
|
data["surveys"]["question-usage-steam"]["choices"].items()
|
||||||
|
))[0]
|
70
tools/statistics/main.py
Normal file
70
tools/statistics/main.py
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
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")
|
4
tools/statistics/ressource/__init__.py
Normal file
4
tools/statistics/ressource/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
from . import experience
|
||||||
|
from . import mission
|
||||||
|
from . import usage
|
||||||
|
from . import language
|
3
tools/statistics/ressource/experience.py
Normal file
3
tools/statistics/ressource/experience.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
choices = ["yes", "mixed", "no"]
|
||||||
|
labels = ["Oui", "Mitigé", "Non"]
|
||||||
|
colors = ["g", "y", "r"]
|
3
tools/statistics/ressource/language.py
Normal file
3
tools/statistics/ressource/language.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
choices = ["fr", "en", "es"]
|
||||||
|
labels = ["Français", "Anglais", "Espagnol"]
|
||||||
|
colors = ["b", "g", "r"]
|
36
tools/statistics/ressource/mission.py
Normal file
36
tools/statistics/ressource/mission.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
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",
|
||||||
|
]
|
3
tools/statistics/ressource/usage.py
Normal file
3
tools/statistics/ressource/usage.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
choices = ["always", "often", "sometime", "rarely", "never"]
|
||||||
|
labels = ["Toujours", "Souvent", "Parfois", "Rarement", "Jamais"]
|
||||||
|
colors = ["g", "c", "y", "m", "r"]
|
20
tools/web_replay/README.md
Normal file
20
tools/web_replay/README.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# 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.
|
0
tools/web_replay/__init__.py
Normal file
0
tools/web_replay/__init__.py
Normal file
17
tools/web_replay/main.py
Normal file
17
tools/web_replay/main.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
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()
|
63
tools/web_replay/ui/ReplayConfiguration.py
Normal file
63
tools/web_replay/ui/ReplayConfiguration.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
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
|
194
tools/web_replay/ui/ReplayEngine.py
Normal file
194
tools/web_replay/ui/ReplayEngine.py
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
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
|
||||||
|
)
|
19
tools/web_replay/ui/ReplayNavigation.py
Normal file
19
tools/web_replay/ui/ReplayNavigation.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
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)
|
66
tools/web_replay/ui/ReplayWebEngineView.py
Normal file
66
tools/web_replay/ui/ReplayWebEngineView.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
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)
|
38
tools/web_replay/ui/ReplayWindow.py
Normal file
38
tools/web_replay/ui/ReplayWindow.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
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()
|
5
tools/web_replay/ui/__init__.py
Normal file
5
tools/web_replay/ui/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
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