-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add action to print conversation #1584
Conversation
@cfm I've drafted adding the "Print Conversation" action to the conversation menu, but we'll still have plenty to work on together tomorrow!
|
b098919
to
0cb323e
Compare
🍐 From offline conversation: @cfm and I considered the following options to deal with the print dialog:
We decided to go with the last option (:green_heart:) after seriously considering two of the previous ones (:yellow_circle:). I'll have a go at implementing it to see what it would look like and de-risk it a bit before reaching out to @tina-ux. |
status = "<i>Waiting for printer status to be known...</i>" | ||
self.body.setText("<br /><br />".join([self._body, status])) | ||
self.adjustSize() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it wants to call a self.set_status()
method like:
class ConfirmationDialog(ModalDialog):
# [...]
def set_status(status: str) -> None:
# TODO: handle case of *removing* the status
self.body.setText("<br /><br />").join([self._body, status]))
self.adjustSize()
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. There are a few things wrong with this "append a string" approach. E.g. self.adjustSize()
doesn't have the desired effect (making the last line visible). So there are a few things to revisit.
Intuitively, I think that showing and hiding widgets in a layout will be easier conceprually and probably solve the visibility issues without need for explicit handling. To be confirmed.
No matter how we do that auxiliary method to show/hise/select the adequate widget/copy may well make sense indeed 👍
I'm planning to look into that when writing tests for the dialogs.
d1e8266
to
107f685
Compare
securedrop_client/gui/conversation/print/confirmation_dialog.py
Outdated
Show resolved
Hide resolved
Bringing this note to the foreground:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've verified that the dialog sequence is correct by looking at the sd-log
output. 🍏
# sd-log
tail -f ~/QubesIncomingLogs/sd-devices/syslog.log | grep securedrop_export
There is still cleanup to do, but I think we're getting there.
Major insight: The connection between the Printer
and the export.Service
is asynchronous which allows the GUI to remain responsive, so we want to keep that. But that means that nothing prevents, technically, several conversations to be printed concurrently. (We also want that, form a journalist perspective, it's a nice, reasonable, and likely expected feature.)
Where that currently grinds to a halt is that there is no way to attribute response signals to any one printing job (e.g. export.Service.print_succeeded
). Long story short, IMO, we need that.
Jumping to solution mode for a moment: that could be achieved by allowing an opaque identifier to be (optionally) included with any request to the export.Service
, and making sure the service includes such opaque identifier in its response in a way that can be parsed by the entity that made the request and received the response. With that, we unlock printing conversations with a familiar UX. 🎁 (cc: @rocodes @cfm Let's put my solution proposal aside and think about this together when it's the right time?)
d61aa3f
to
35b682d
Compare
I've rebased the branch, updated the PR descriptiom, and made |
The new directory structure will make extension easier.
Besides being the dedicated tool to assert on Qt signal emissions, QSignalSpy can track when signals are emitted from other signals, which the mocking strategy misses. Example: some_signal.connect(some_other_signal) # verify that some_other_signal is emitted See https://doc.qt.io/qt-5.15/qsignalspy.html
f6b8cad
to
217d38d
Compare
By default, Qt picks the a Qt.QueuedConnection automatically whenever needed. Explicit queued connections also happen to make testing harder. See https://doc.qt.io/qt-5/qt.html#ConnectionType-enum
I'll use an expand and contract pattern to refactor the export.Service tests, and keeping this file distinct while it is used as a reference will be more convenient and minimize the change sets. Important: this file is still fully operational and will be kept as such until all deprecated methods have been removed from export.Export (soon to be renamed export.Service).
217d38d
to
be2237f
Compare
(rebased, cleaned up first half of the branch, forgot signing one commit, added remaining work in progress at the end) |
Refactor tests to: - distinguish testing of GUI API, and CLI API internals - improve readability
d3621a7
to
10ce1af
Compare
Decorate static methods (from what didn't need to be class methods) Tests were minially modified: patching static methods must be done unsing unittest.mock.patch.object to preserve the independence of the unit tests. Assigning to the class attributes directly does modify the class for the entire test session. (Not desirable.)
I am leaving the deprecated tests as untouched as possible to make easier to review the changes.
f6dfc90
to
1874fb6
Compare
This will ensure that one one instance of the export service queries the sd-devices CLI. That was already the case, but nothing was done to suggest it. Additionally, this will allow to simplify the export service injection by making possible to retrieve it like a logger. Note: I haven't stopped injecting the export_service into the main.Window because I couldn't figure out how to prevent what seems to be a fixture scope error that affects only half of the integration tests. That seems like something that can be resolved, so I don't consider it to be a sign of export.Service being a bad idea.
1874fb6
to
fdca857
Compare
82b03f9
to
2b28efe
Compare
Note, this PR will be closed from #1621. Please refer to it for details. |
Goals
Fixes #1435
Description
Overview
One action (
QAction
) called "Print conversation" generates a conversation transcript and enqueues it for printing. It displays a confirmation dialog, which is responsible for keeping the user updated on the printing queue readiness, until the user intent to print is captured. All "Print conversation" actions remains disabled until the printing job is has been enqueued, or the operation failed. If relevant, the action displays an error dialog to inform the user that the printing job wasn't successfully enqueued. The only option at that point for the user is to acknowledge the failure and try again.Implementation and technical vision
Considerations
The printing queue lives in a different qube (
sd-devices
) than the application (sd-app
). The application qube can initiate communication withsd-devices
, butsd-devices
does only respond with a restricted set of text strings by design. (Because USB devices are attached to itsd-devices
is less trusted thansd-app
(which contains the most sensitive information), so no arbitrary content must be written tosd-app
fromsd-devices
.) As a consequence, we've decided that the application code would poll the status of the printer.A CLI interface is available to send commands from
sd-app
tosd-devices
. Currently, only one printer can be connected tosd-devices
at any single time. As a consequence, there is no point in querying/polling the status of the printer from more than one place in the application code. Any response fromsd-devices
will be true for the entire application (e.g. no matter which conversation the user intends to print). Care must then be taken to ensure that we don't overload the CLI unnecessarily.Technically, printing and writing to a (removable, USB) disk is handled by the same code in
sd-devices
. Both operations are usually referred to as "exporting" data fromsd-app
.Export service
sd-devices
, for all practical purposes it might as well be a printer from the point of view of the application. Where that leads to simpler naming, the term is used in application code.export.Service
now exposes a printer API. Later on, it will expose a disk API as well. Everything other than those APIs must be private by that point.Printer.Status
type (enum
)printer_status_changed
printer_status
that returns aPrinter.Status
valueenqueue_printing_job_on(signal)
printing_job_succeeded
printing_job_failed
printing_job_finished
(either succeeded or failed)export.Service
still exposes the legacy USB disk API as of this PR.Action
QAction
instance, because that's the right thing to do by the framework.accepted
,rejected
,finished
signals).Confirmation dialog
export.Service
. It does not interact in any way with the job queue interface (commands must be emitted by an action, not a presentational component like a dialog).QAbstractButton
), because that's the right thing to do by the framework (aka any modern accessibility API).Error dialog
Test plan
TK