-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add mechanism to ask user to fill survey (#1125)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a survey mechanism in PartSeg 0.15.3 to allow users to participate in surveys supporting PartSeg development. - Added documentation for the new survey mechanism. - **Documentation** - Updated documentation to include details about the new survey feature in the incomplete documentation section. - Included `graphviz` in the build configuration for documentation generation. - **Chores** - Updated the manifest file to exclude `survey_url.txt` during package distribution. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Loading branch information
Showing
9 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,3 +44,4 @@ exclude dist/**.swp | |
exclude cliff.toml | ||
exclude runtime.txt | ||
exclude .sourcery.yaml | ||
exclude survey_url.txt |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
PartSeg surveys | ||
=============== | ||
|
||
Since the partseg 0.15.3 release there is a new feature that allows | ||
to ask users for fill a survey to help in the PartSeg development. | ||
|
||
How it works | ||
------------ | ||
|
||
Whole logic is implemented in ``PartSeg._launcher.check_survey`` module. | ||
|
||
.. image:: images/survey.png | ||
:width: 600 | ||
:alt: Survey message | ||
|
||
1. Check if ignore file exist and is last modified less than 16 hours ago. If yes, do nothing. | ||
2. Fetch data from https://raw.githubusercontent.com/4DNucleome/PartSeg/develop/survey_url.txt. | ||
If file do not exists or is empty, do nothing. | ||
Save fetched data as url to form. | ||
3. Check if ignore file exist and its content is equal to fetched url. If yes, do nothing. | ||
4. Display message to user with information that there is a survey available. | ||
|
||
* If user click "Open survey" button, open browser with fetched url. | ||
* If user click "Ignore" button, save fetched url to ignore file. | ||
* If user click "Close" button, touch the ignore file to prevent showing the message again for 16 hours. | ||
|
||
|
||
.. graphviz:: | ||
|
||
digraph flow { | ||
"start" -> "check if already asked" | ||
|
||
"check if already asked" -> "fetch if exist active survey" [label=No]; | ||
"check if already asked" -> "end" [label=Yes]; | ||
"fetch if exist active survey" -> "end" [label=No]; | ||
"fetch if exist active survey" -> "check if user decided to ignore this survey" [label=Yes] | ||
"check if user decided to ignore this survey" -> "end" [label=Yes] | ||
"check if user decided to ignore this survey" -> "show dialog with question" [label=No] | ||
"show dialog with question" -> "end" [label=Close] | ||
"show dialog with question" -> "open browser" [label="Open survey"] | ||
"show dialog with question" -> "Save to not ask about this survey" [label="Ignore"] | ||
"open browser" -> "end" | ||
"Save to not ask about this survey" -> "end" | ||
start [shape=Mdiamond]; | ||
|
||
end [shape=Msquare]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import os | ||
import time | ||
import urllib.error | ||
import urllib.request | ||
import webbrowser | ||
from contextlib import suppress | ||
from pathlib import Path | ||
|
||
from qtpy.QtCore import Qt, QThread | ||
from qtpy.QtWidgets import QMessageBox, QWidget | ||
from superqt import ensure_main_thread | ||
|
||
from PartSeg import state_store | ||
|
||
IGNORE_DAYS = 21 | ||
IGNORE_FILE = "ignore_survey.txt" | ||
|
||
IGNORE_FILE_PATH = Path(state_store.save_folder) / ".." / IGNORE_FILE | ||
|
||
|
||
class SurveyMessageBox(QMessageBox): | ||
def __init__(self, survey_url: str, parent: QWidget = None): | ||
super().__init__(parent) | ||
self.setWindowTitle("PartSeg survey") | ||
self.setIcon(QMessageBox.Icon.Information) | ||
self.setTextFormat(Qt.TextFormat.RichText) | ||
self.setText( | ||
f'Please fill the survey <a href="{survey_url}">{survey_url}</a> to <b>help us</b> improve PartSeg' | ||
) | ||
self._open_btn = self.addButton("Open survey", QMessageBox.ButtonRole.AcceptRole) | ||
self._close_btn = self.addButton("Close", QMessageBox.ButtonRole.RejectRole) | ||
self._ignore_btn = self.addButton("Ignore", QMessageBox.ButtonRole.DestructiveRole) | ||
self.setEscapeButton(self._ignore_btn) | ||
self.survey_url = survey_url | ||
|
||
self._open_btn.clicked.connect(self._open_survey) | ||
self._ignore_btn.clicked.connect(self._ignore_survey) | ||
|
||
def _open_survey(self): | ||
webbrowser.open(self.survey_url) | ||
|
||
def _ignore_survey(self): | ||
with IGNORE_FILE_PATH.open("w") as f_p: | ||
f_p.write(self.survey_url) | ||
|
||
def exec_(self): | ||
result = super().exec_() | ||
IGNORE_FILE_PATH.touch() | ||
return result | ||
|
||
|
||
class CheckSurveyThread(QThread): | ||
"""Thread to check if there is new PartSeg release. Checks base on newest version available on pypi_ | ||
.. _PYPI: https://pypi.org/project/PartSeg/ | ||
""" | ||
|
||
def __init__(self, survey_file_url="https://raw.githubusercontent.com/4DNucleome/PartSeg/develop/survey_url.txt"): | ||
super().__init__() | ||
self.survey_file_url = survey_file_url | ||
self.survey_url = "" | ||
self.finished.connect(self.show_version_info) | ||
|
||
def run(self): | ||
"""This function perform check if there is any active survey.""" | ||
if IGNORE_FILE_PATH.exists() and (time.time() - os.path.getmtime(IGNORE_FILE_PATH)) < 60 * 60 * 16: | ||
return | ||
with suppress(urllib.error.URLError), urllib.request.urlopen(self.survey_file_url) as r: # nosec # noqa: S310 | ||
self.survey_url = r.read().decode("utf-8").strip() | ||
|
||
@ensure_main_thread | ||
def show_version_info(self): | ||
if os.path.exists(IGNORE_FILE_PATH): | ||
with open(IGNORE_FILE_PATH) as f_p: | ||
old_survey_link = f_p.read().strip() | ||
if old_survey_link == self.survey_url: | ||
return | ||
|
||
if self.survey_url: | ||
SurveyMessageBox(self.survey_url).exec_() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
from io import BytesIO | ||
from unittest.mock import MagicMock, Mock | ||
|
||
import pytest | ||
|
||
from PartSeg._launcher.check_survey import CheckSurveyThread, SurveyMessageBox | ||
|
||
|
||
@pytest.fixture(autouse=True) | ||
def _mock_file_path(monkeypatch, tmp_path): | ||
monkeypatch.setattr("PartSeg._launcher.check_survey.IGNORE_FILE_PATH", tmp_path / "file1.txt") | ||
|
||
|
||
@pytest.mark.usefixtures("qtbot") | ||
def test_check_survey_thread(monkeypatch, tmp_path): | ||
urlopen_mock = Mock(return_value=BytesIO(b"some data")) | ||
monkeypatch.setattr("urllib.request.urlopen", urlopen_mock) | ||
thr = CheckSurveyThread("test_url") | ||
|
||
thr.run() | ||
assert urlopen_mock.call_count == 1 | ||
assert thr.survey_url == "some data" | ||
|
||
(tmp_path / "file1.txt").touch() | ||
thr2 = CheckSurveyThread("test_url") | ||
thr2.run() | ||
|
||
assert urlopen_mock.call_count == 1 | ||
assert thr2.survey_url == "" | ||
|
||
|
||
@pytest.mark.usefixtures("qtbot") | ||
def test_thread_ignore_file_exist(monkeypatch, tmp_path): | ||
message_mock = Mock() | ||
monkeypatch.setattr("PartSeg._launcher.check_survey.SurveyMessageBox", message_mock) | ||
with (tmp_path / "file1.txt").open("w") as f_p: | ||
f_p.write("test_url") | ||
|
||
thr = CheckSurveyThread("test_url2") | ||
thr.survey_url = "test_url" | ||
|
||
thr.show_version_info() | ||
assert message_mock.call_count == 0 | ||
|
||
|
||
@pytest.mark.usefixtures("qtbot") | ||
def test_call_survey_message_box(monkeypatch): | ||
message_mock = MagicMock() | ||
monkeypatch.setattr("PartSeg._launcher.check_survey.SurveyMessageBox", message_mock) | ||
thr = CheckSurveyThread("test_url") | ||
thr.survey_url = "test_url" | ||
thr.show_version_info() | ||
assert message_mock.call_count == 1 | ||
|
||
|
||
def test_survey_message_box(qtbot, monkeypatch, tmp_path): | ||
web_open = Mock() | ||
monkeypatch.setattr("PartSeg._launcher.check_survey.webbrowser.open", web_open) | ||
monkeypatch.setattr("PartSeg._launcher.check_survey.QMessageBox.exec_", Mock(return_value=0)) | ||
msg = SurveyMessageBox("test_url") | ||
qtbot.addWidget(msg) | ||
|
||
msg._open_btn.click() | ||
web_open.assert_called_once_with("test_url") | ||
|
||
msg.exec_() | ||
assert (tmp_path / "file1.txt").exists() | ||
assert (tmp_path / "file1.txt").read_text() == "" | ||
|
||
msg._ignore_btn.click() | ||
|
||
assert (tmp_path / "file1.txt").exists() | ||
assert (tmp_path / "file1.txt").read_text() == "test_url" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
https://forms.gle/CCnS7ccyPQUfKRgCA |