Skip to content
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

[WIP] Add a feedback dialog box #93

Closed
wants to merge 30 commits into from

Conversation

siddhantwahal
Copy link

@siddhantwahal siddhantwahal commented Jun 14, 2019

This PR adds a Feedback/Bugs dialog box to apptools. This dialog box allows the client-user to quickly report bugs or request features. Messages are sent directly to the developer team's slack channel.

Files:

  • feedbackbot/model.py: Logic for the client-user interface.
  • feedbackbot/view.py: Client-user side GUI
  • feedbackbot/utils.py: Some helpful functions.
  • feedbackbot/tests: Unit tests.
  • examples/example.py: An example of how the developer-user, i.e., a developer who wishes to integrate this menu item in their app, would go about doing so.

To-do list:

  • Add Readme
  • Flake8 compliance
  • Add unit tests
  • Improve UX by:
    • Present error/success message in a new pop-up dialog box
    • Pressing OK on pop-up should close the entire feedback window
    • Long messages don't wrap inside the Description field, nor can you press enter to start a new paragraph
  • Handle edge cases like non-empty image bytestring, invalid channels, invalid tokens etc.
  • HTTP requests should be blocking (slack client uses aiohttp)
  • Add logging
  • Create simpler example instead of using ets-example
  • Maybe have an ImageTrait instead of relying on utility functions to do conversions

feedback_dialog_screenshot

@cfarrow cfarrow self-requested a review June 14, 2019 15:06
@codecov-io
Copy link

codecov-io commented Jun 14, 2019

Codecov Report

❗ No coverage uploaded for pull request base (master@30cd14d). Click here to learn what that means.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff            @@
##             master      #93   +/-   ##
=========================================
  Coverage          ?   40.51%           
=========================================
  Files             ?      240           
  Lines             ?     8912           
  Branches          ?     1158           
=========================================
  Hits              ?     3611           
  Misses            ?     5155           
  Partials          ?      146

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 30cd14d...d651bf8. Read the comment docs.

@cfarrow
Copy link
Contributor

cfarrow commented Jun 14, 2019

This is an intern project. I'll be reviewing and guiding the work.

Copy link
Contributor

@corranwebster corranwebster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've given some feedback, some nit-picky some fairly important.

Other points:

  • this is a replacement for/improvement upon apptools.logging.agent so you need to think what should be done with that code. Either this should replace it, or you should remove that code as part of this PR (which breaks backwards compatibility, but I think that's OK). Following the naming of that package (eg. QualityAgent rather than Feedback) may be a good idea.
  • this PR is totally dependent on Slack and it's API, but that is just one of the possible channels that we might want to use to solicit user feedback, depending on the needs of our clients. Also there is a high likelihood that Slack's API will change or go away or cost money over the course of years and we don't want to have to re-write a quality agent a 4th (or 5th or 6th time). At a minimum we should be able to support e-mail delivery of feedback in the future; and being able to send to a generic RESTful endpoint woudl also make sense. Think about architecting this so that Slack is just one possible delivery layer, even if you don't implement anything else just yet. This would mean rather than having a send function you have an abstract base class which has an abstract send method and which can take a feedback message object; concrete subclasses will format it and send it as appropriate for that delivery mechanism. Users of your code can then implement as desired.
  • it would be nice to have a screenshot of the view as part of the PR.

Other than that, basically looks OK, and pretty good for a first shot at a TraitsUI interface.


"""

first_name = Str(msg_meta=True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please give a comment for all trait definitions, eg. #: The first name of the user.

A secondary comment is that it's probably easier to have just one name field unless there is some compelling reason to split it.


# Send message.
response = client.files_upload(
channels=self.channels,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: indentation.

Please run flake8 (or equivalent) over your code.


except slack.errors.SlackApiError as error:

print(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use print, instead use logging. Standard procedure for logging in a library like this is to have at the top of the file a

import logging

logger = logging.getLogger(__name__)

and then here you would use logger.error and/or logger.exception to record the error.


except aiohttp.client_exceptions.ClientConnectorError as error:

print('Message not sent.')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here about logging.


else:

print('Message sent successfully!')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here, but this would be logger.info.

apptools/feedback/validating_dialog_with_feedback.py Outdated Show resolved Hide resolved
#: Main body of the feedback message.
description = Str(msg_meta=True)

#: The target slack channel that the bot will post to, must start with #.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Channels" and "tokens" are not part of the concept of a "message". They are part of the concept of a (particular) delivery system and so should be factored out into their own class (see main review comment).

msg = Str

#: The screenshot pixel data in raw bytes. Note that RGB[A] ordering is assumed.
img_bytes = Bytes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a numpy array (ie. image = Array(shape=(None, None, 3)) for an RGB image, (None, None, 4) for RGBA). It is easy to convert to a Pillow Image from an array, and you don't need to store auxiliary information like the height and width.

You are using Chaco and/or Enable here, so numpy is already a dependency.

apptools/feedback/feedbackbot/utils.py Show resolved Hide resolved
apptools/feedback/feedbackbot/view.py Show resolved Hide resolved
@corranwebster
Copy link
Contributor

And one more thing - this PR doesn't add a feedback menu item, it provides a feedback dialog. You might want to update the title of the PR. Adding a menu item is a secondary thing and can be part of a second PR, if needed.

Copy link
Contributor

@cfarrow cfarrow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a first round of comments. I'll generate more as I mess around with it.

#: The screenshot height in pixels.
img_h = Int

@on_trait_change('+msg_meta')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this it makes more sense to use a Property instead of a trait-change handler.

plugin can be used in their application.
"""

from PyQt4.QtGui import QPixmap
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please import from pyface.qt.QtGui so this is more portable.

@@ -0,0 +1,468 @@
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please simplify this as much as possible and move it into examples.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already here: https://github.com/enthought/ets-examples/blob/master/snippets/validating_dialog.py

It shouldn't be in this at all, I think.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant just the menu item code. It would be good to have bare-bones example that holds the invocation sequence of the dialog.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh - I see it now. Yep, I agree.

@siddhantwahal
Copy link
Author

@corranwebster, @cfarrow: Thanks for the feedback! I'll be pushing changes today.

@cfarrow
Copy link
Contributor

cfarrow commented Jun 21, 2019

@siddhantwahal I did not get a notification when you sent that. Sorry about that. I'll try to review soon.

@siddhantwahal
Copy link
Author

siddhantwahal commented Jun 21, 2019

@siddhantwahal I did not get a notification when you sent that. Sorry about that. I'll try to review soon.

@cfarrow No worries! I'm not totally finished making changes. I'll comment here and ping you on slack when it's ready for review.

@cfarrow
Copy link
Contributor

cfarrow commented Jun 21, 2019

Sounds good. (I got that notification. 😄)

@siddhantwahal siddhantwahal changed the title [WIP] Add a feedback menu item [WIP] Add a feedback dialog box Jun 21, 2019
@siddhantwahal
Copy link
Author

@cfarrow I'm think I'm done with most of the big changes. The only thing remaining is a README.

I'm not sure if my approach to testing is right (I Mocked almost everything out), so any feedback there would be helpful.

@cfarrow
Copy link
Contributor

cfarrow commented Jun 24, 2019

Ok. I'll take a look today or tomorrow.

Copy link
Contributor

@cfarrow cfarrow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things are looking good. I have another round of comments to consider.

@@ -0,0 +1,96 @@
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is better placed in /examples/feedback instead of apptools/feedback/examples.

return img_array


def initiate_feedback_dialog_(control, token, channels):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the trailing _? If it's for the sake of avoiding name conflict in the example, it is not necessary. (Let's talk in person if it's not clear why.)

#: 3D numpy array to hold three channel (RGB) screenshot pixel data.
img_data = Array(shape=(None, None, 3), dtype='uint8', required=True)

# FIXME: Not sure if this the right way to go about initiating a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The right way to do this would be
compressed_img_buf = Instance(io.BytesIO, args=()).

The args= part says what to use when initializing the type (io.BytesIO).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(That said, this works. I've never seen it done this way before.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon closer inspection, this is only used by the send method. It's probably best to not make it a trait and create the buffer as needed inside of send.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved it out assuming that it would reduce time spent in the send function. Some more experiments revealed that while time spent in send is reduced (1.57s to 1.5s), the difference is barely noticeable by the user, so now it's back in. (Note to self: time everything)

token = Str(required=True)

#: The final message that gets posted to Slack.
msg = Property(Str, depends_on='msg_meta')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this should be depends_on='+msg_meta'. It would be good to make this consistent with the syntax in the view.

from apptools.feedback.feedbackbot.model import FeedbackMessage


class TestFeedbackMessage(unittest.TestCase, UnittestTools):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are essentially testing traits. It would be better to test the logic in the send method. This can be done by mocking slack.WebClient.files_upload and checking that the right information is passed.

Copy link
Contributor

@cfarrow cfarrow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One minor nit.



# View for the example app. The feedbackbot module provides a helper function
# `initiate_feedback_dialog_` that launches the feedback dialog box. To include
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this still has the trailing underscore.

apptools/feedback/feedbackbot/utils.py Show resolved Hide resolved
apptools/feedback/feedbackbot/view.py Show resolved Hide resolved
@cfarrow
Copy link
Contributor

cfarrow commented Jun 28, 2019

This is close to finished. There still the comment about apptools.logging.agent to consider. This is relatively simple, and should be useful as-is. I would rather not tie the completion of this that previous code. Corran, are you opposed to merging this without regard to the logging code.

@cfarrow
Copy link
Contributor

cfarrow commented Jul 24, 2019

@corran, what is your opinion about the logging stuff ?(See the comment above.)

@mdickinson
Copy link
Member

@cfarrow @corran Can either of you comment on the current status of this PR? Is it still considered work-in-progress?

@cfarrow
Copy link
Contributor

cfarrow commented Oct 11, 2019

I'm in favor of merging it because it is usable as-is. There are concerns about the logging code which has a similar intent, but is constructed completely differently.

@kitchoi
Copy link
Contributor

kitchoi commented Jan 6, 2021

I expected to see changes to CI build settings as there are additional dependencies on slack here, but those changes are absent. Then I realized that the tests are actually not run on CI, probably due to the missing __init__.py files?

With that, I am not sure this can be merged as-is.

@kitchoi
Copy link
Contributor

kitchoi commented Jan 6, 2021

Once the feature is merged and released, it will be part of a published public API and refactoring will be more constrained. Just the fact a feature is live and published demands maintenance. For apptools release 5.0, we spent a lot of time discussing whether various subpackages can be removed, and when we cannot be sure, they are kept, we flake8-them, migrate tests from nose to unittest, update setup.py for their (optional) dependencies, add documentation etc. etc.

If this feature is gaining enough traction, let's make sure it is tested on CI and maybe flag the API as provisional. If there are not enough traction, we can also consider tagging the commit to persist it on the git repo instead of merging. If interest arises again, we can continue the efforts from there.

This issue may be related: #137

@kitchoi
Copy link
Contributor

kitchoi commented Jan 13, 2021

This PR needs some changes to ensure the added tests are exercised on CI.
@siddhantwahal @corranwebster @mdickinson @cfarrow Does any of you want to advocate for this PR to be fixed for it to be merged?
Does any of you want to advocate tagging this PR in the git repo for future resurrections?

@mdickinson
Copy link
Member

Does any of you want to advocate for this PR to be fixed for it to be merged?

I'm not super-keen. We can fix the CI issue, but I also think there are some opportunities to rethink the design. In particular, I'd like to see the notification mechanism (Slack in this case) better separated from the dialog and screenshot stuff. It's a really interesting PR as a proof of concept, and yes, maybe worth an experimental/xxx tag, but I don't think it's ready for apptools master without a bit more work.

@kitchoi
Copy link
Contributor

kitchoi commented Jan 14, 2021

Thank you @mdickinson. Agree to what you said.

If no one wants to advocate the otherwise, I will close and tag this PR in about a week time (I will try to remember).

@kitchoi
Copy link
Contributor

kitchoi commented Jan 22, 2021

I have now tagged the last commit as experimental/featurebot referencing this PR.

Closing.

@kitchoi kitchoi closed this Jan 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants