Repository: spyder-ide/spyder

Issue:
"Help > Report Issue" goes to a broken link if not logged into GitHub
Spyder>Help>Report Issue... loads a page with "Ooops!!! 500" error.
I think because I was not logged into GitHub.
After i logged into GitHub Help>Report Issue... started working and brought me here.

To a beginner trying to report a problem (me) solution was not obvious/disconcerting.

The URL was:
https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fspyder-ide%2Fspyder%2Fissues%2Fnew%3Fbody%3D%253C%2521---%2520%252A%252APLEASE%2520READ%253A%252A%252A%2520Before%2520submitting%2520here%252C%2520please%2520carefully%2520consult%2520our%2520%252ATroubleshooting%2520Guide%252A%253A%2520https%253A%2F%2Fgithub.com%2Fspyder-ide%2Fspyder%2Fwiki%2FTroubleshooting-Guide-and-FAQ%2520and%2520search%2520the%2520issues%2520page%2520for%2520your%2520error%2Fproblem%252C%2520as%2520most%2520posted%2520bugs%2520are%2520duplicates%2520or%2520easy%2520fixes.%250A%250AIf%2520you%2520don%2527t%2520find%2520anything%252C%2520please%2520provide%2520a%2520detailed%2520step-by-step%2520description%2520%2528in%2520English%2529%2520of%2520the%2520problem%2520and%2520what%2520led%2520up%2520to%2520it%2520below.%2520Issue%2520reports%2520without%2520a%2520clear%2520way%2520to%2520reproduce%2520them%2520will%2520be%2520closed.%2520Thanks%2521%2520---%253E%250A%250A%2523%2523%2520Problem%2520Description%250A%250A%2523%2523%2523%2520What%2520steps%2520will%2520reproduce%2520the%2520problem%253F%250A%250A%250A%250A%2523%2523%2520Package%2520Versions%250A%250A%252A%2520Spyder%2520version%253A%25203.2.8%2520%250A%252A%2520Python%2520version%253A%25203.6.4%250A%252A%2520Qt%2520version%253A%25205.6.2%250A%252A%2520PyQt5%2520version%253A%25205.6%250A%252A%2520Operating%2520system%253A%2520Windows%25207%250A%250A%2523%2523%2523%2520Dependencies%250A%250A%2560%2560%2560%250Apyflakes%2520%253E%253D0.6.0%2520%253A%2520%25201.6.0%2520%2528OK%2529%250D%250Apycodestyle%2520%253E%253D2.3%253A%2520%25202.3.1%2520%2528OK%2529%250D%250Apygments%2520%253E%253D2.0%2520%2520%2520%253A%2520%25202.2.0%2520%2528OK%2529%250D%250Apandas%2520%253E%253D0.13.1%2520%2520%253A%2520%25200.22.0%2520%2528OK%2529%250D%250Anumpy%2520%253E%253D1.7%2520%2520%2520%2520%2520%2520%253A%2520%25201.14.0%2520%2528OK%2529%250D%250Asphinx%2520%253E%253D0.6.6%2520%2520%2520%253A%2520%25201.6.6%2520%2528OK%2529%250D%250Arope%2520%253E%253D0.9.4%2520%2520%2520%2520%2520%253A%2520%25200.10.7%2520%2528OK%2529%250D%250Ajedi%2520%253E%253D0.9.0%2520%2520%2520%2520%2520%253A%2520%25200.11.1%2520%2528OK%2529%250D%250Anbconvert%2520%253E%253D4.0%2520%2520%253A%2520%25205.3.1%2520%2528OK%2529%250D%250Asympy%2520%253E%253D0.7.3%2520%2520%2520%2520%253A%2520%25201.1.1%2520%2528OK%2529%250D%250Acython%2520%253E%253D0.21%2520%2520%2520%2520%253A%2520%25200.27.3%2520%2528OK%2529%250D%250Aqtconsole%2520%253E%253D4.2.0%253A%2520%25204.3.1%2520%2528OK%2529%250D%250AIPython%2520%253E%253D4.0%2520%2520%2520%2520%253A%2520%25206.2.1%2520%2528OK%2529%250D%250Apylint%2520%253E%253D0.25%2520%2520%2520%2520%253A%2520%25201.8.2%2520%2528OK%2529%250D%250A%2560%2560%2560%250A

## Package Versions
* Spyder version: 3.2.8 
* Python version: 3.6.4
* Qt version: 5.6.2
* PyQt5 version: 5.6
* Operating system: Windows 7

### Dependencies

```
pyflakes >=0.6.0 :  1.6.0 (OK)
pycodestyle >=2.3:  2.3.1 (OK)
pygments >=2.0   :  2.2.0 (OK)
pandas >=0.13.1  :  0.22.0 (OK)
numpy >=1.7      :  1.14.0 (OK)
sphinx >=0.6.6   :  1.6.6 (OK)
rope >=0.9.4     :  0.10.7 (OK)
jedi >=0.9.0     :  0.11.1 (OK)
nbconvert >=4.0  :  5.3.1 (OK)
sympy >=0.7.3    :  1.1.1 (OK)
cython >=0.21    :  0.27.3 (OK)
qtconsole >=4.2.0:  4.3.1 (OK)
IPython >=4.0    :  6.2.1 (OK)
pylint >=0.25    :  1.8.2 (OK)
```



Discussion:
Thanks for reporting. We're in the process of improving how users report issues to Github, so we'll fix this as part of that.
@ccordoba12 The trouble here is that due to having to pass the full URL as a parameter, even more things are percent encoded, making the length even longer, in addition to the marginal additional length of the actual URL content. 

The solutions are basically what I outlined in #6870 . The best way to really make a big dent in the length is to greatly cut down on the spaces and special characters in the deps section (e.g. min versions, since with the OK they are redundant, extra spaces before and after : , parens around OK, etc.) and cut any deps if possible that have never posed a troubleshooting issue and are unlikely to (``psutil`` on macOS/Linux, ``sympy``? ``nbconvert``? ``pygments`` ?). This will have far more bang for the buck in cutting down on characters than removing any more from the existing text, which has already been cut to the bone (though any more we can remove won't hurt); just doing the above suggestions will cut down the total length by around 1/3 while sacrificing little to no valuable information. 

The alternative is making the menu option use the new error report dialog as well, but that adds extra complexity and time for the user which can likely be avoided.
Yeah, my idea is to use the new report error dialog to handle the *Report issue...* action too.

Resolving patch:
diff --git a/spyder/app/mainwindow.py b/spyder/app/mainwindow.py
--- a/spyder/app/mainwindow.py
+++ b/spyder/app/mainwindow.py
@@ -2365,34 +2365,6 @@ def render_issue(self, description='', traceback=''):
         if versions['revision']:
             revision = versions['revision']
 
-        # Store and format the reminder message for the troubleshooting guide
-        reminder_message_full = (
-            "<!--- **PLEASE READ:** Before submitting here, please carefully "
-            "consult our *Troubleshooting Guide*: {0!s} and search the "
-            "issues page for your error/problem, as most posted bugs are "
-            "duplicates or easy fixes.\n\n"
-            "If you don't find anything, please provide a detailed step-by-"
-            "step description (in English) of the problem and what led up to "
-            "it below. Issue reports without a clear way to reproduce them "
-            "will be closed. Thanks! --->"
-            ).format(__trouble_url__)
-
-        reminder_message_short = (
-            "<!--- PLEASE READ: Complete the following checklist. "
-            "Issues without it may be closed. --->"
-            )
-
-        bug_checklist = (
-            "* [ ] Searched issues page for similar reports\n"
-            "* [ ] Read and followed relevant sections of the"
-            "[Troubleshooting Guide]({0!s})\n"
-            "* [ ] Reproduced after updating (`conda update spyder`)\n"
-            "* [ ] Tried basic troubleshooting\n"
-            "    * [ ] Restarted Spyder\n"
-            "    * [ ] Ran `spyder --reset`\n"
-            "    * [ ] Reinstalled latest Anaconda\n"
-            ).format(__trouble_url_short__)
-
         # Make a description header in case no description is supplied
         if not description:
             description = "### What steps reproduce the problem?"
@@ -2403,13 +2375,9 @@ def render_issue(self, description='', traceback=''):
                              "```python-traceback\n"
                              "{}\n"
                              "```".format(traceback))
-            reminder_message = reminder_message_full
         else:
             error_section = ''
-            reminder_message = reminder_message_short + "\n\n" + bug_checklist
         issue_template = """\
-{reminder_message}
-
 ## Description
 
 {description}
@@ -2429,8 +2397,7 @@ def render_issue(self, description='', traceback=''):
 ```
 {dependencies}
 ```
-""".format(reminder_message=reminder_message,
-           description=description,
+""".format(description=description,
            error_section=error_section,
            spyder_version=versions['spyder'],
            commit=revision,
@@ -2445,29 +2412,27 @@ def render_issue(self, description='', traceback=''):
         return issue_template
 
     @Slot()
-    def report_issue(self, body=None, title=None):
+    def report_issue(self, body=None, title=None, open_webpage=False):
         """Report a Spyder issue to github, generating body text if needed."""
-        if PY3:
-            from urllib.parse import quote
-        else:
-            from urllib import quote     # analysis:ignore
-
         if body is None:
-            body = self.render_issue()
-
-        url = QUrl(__project_url__ + '/issues/new')
-        if PYQT5:
-            from qtpy.QtCore import QUrlQuery
-            query = QUrlQuery()
-            query.addQueryItem("body", quote(body))
-            if title:
-                query.addQueryItem("title", quote(title))
-            url.setQuery(query)
+            from spyder.widgets.reporterror import SpyderErrorDialog
+            report_dlg = SpyderErrorDialog(self, is_report=True)
+            report_dlg.show()
         else:
-            url.addEncodedQueryItem("body", quote(body))
-            if title:
-                url.addEncodedQueryItem("title", quote(title))
-        QDesktopServices.openUrl(url)
+            if open_webpage:
+                if PY3:
+                    from urllib.parse import quote
+                else:
+                    from urllib import quote     # analysis:ignore
+                from qtpy.QtCore import QUrlQuery
+
+                url = QUrl(__project_url__ + '/issues/new')
+                query = QUrlQuery()
+                query.addQueryItem("body", quote(body))
+                if title:
+                    query.addQueryItem("title", quote(title))
+                url.setQuery(query)
+                QDesktopServices.openUrl(url)
 
     @Slot()
     def trouble_guide(self):
diff --git a/spyder/widgets/reporterror.py b/spyder/widgets/reporterror.py
--- a/spyder/widgets/reporterror.py
+++ b/spyder/widgets/reporterror.py
@@ -19,7 +19,7 @@
 
 # Local imports
 from spyder import __project_url__, __trouble_url__
-from spyder.config.base import _
+from spyder.config.base import _, DEV
 from spyder.config.gui import get_font
 from spyder.utils import icon_manager as ima
 from spyder.utils.qthelpers import restore_keyevent
@@ -121,8 +121,10 @@ def __init__(self, parent=None):
 class SpyderErrorDialog(QDialog):
     """Custom error dialog for error reporting."""
 
-    def __init__(self, parent=None):
+    def __init__(self, parent=None, is_report=False):
         QDialog.__init__(self, parent)
+        self.is_report = is_report
+
         self.setWindowTitle(_("Issue reporter"))
         self.setModal(True)
 
@@ -130,14 +132,21 @@ def __init__(self, parent=None):
         self.error_traceback = ""
 
         # Dialog main label
+        if self.is_report:
+            title = _("Please fill the following information")
+        else:
+            title = _("Spyder has encountered an internal problem!")
         main_label = QLabel(
-            _("""<h3>Spyder has encountered an internal problem!</h3>
-              Before reporting it, <i>please</i> consult our comprehensive 
-              <b><a href=\"{0!s}\">Troubleshooting Guide</a></b> 
-              which should help solve most issues, and search for 
-              <b><a href=\"{1!s}\">known bugs</a></b> matching your error 
-              message or problem description for a quicker solution.
-              """).format(__trouble_url__, __project_url__))
+            _("<h3>{title}</h3>"
+              "Before reporting this problem, <i>please</i> consult our "
+              "comprehensive "
+              "<b><a href=\"{trouble_url}\">Troubleshooting Guide</a></b> "
+              "which should help solve most issues, and search for "
+              "<b><a href=\"{project_url}\">known bugs</a></b> "
+              "matching your error message or problem description for a "
+              "quicker solution."
+              ).format(title=title, trouble_url=__trouble_url__,
+                          project_url=__project_url__))
         main_label.setOpenExternalLinks(True)
         main_label.setWordWrap(True)
         main_label.setAlignment(Qt.AlignJustify)
@@ -184,6 +193,8 @@ def __init__(self, parent=None):
         # Checkbox to dismiss future errors
         self.dismiss_box = QCheckBox(_("Hide all future errors during this "
                                        "session"))
+        if self.is_report:
+            self.dismiss_box.hide()
 
         # Dialog buttons
         gh_icon = ima.icon('github')
@@ -193,8 +204,12 @@ def __init__(self, parent=None):
 
         self.details_btn = QPushButton(_('Show details'))
         self.details_btn.clicked.connect(self._show_details)
+        if self.is_report:
+            self.details_btn.hide()
 
         self.close_btn = QPushButton(_('Close'))
+        if self.is_report:
+            self.close_btn.clicked.connect(self.reject)
 
         # Buttons layout
         buttons_layout = QHBoxLayout()
@@ -231,8 +246,16 @@ def __init__(self, parent=None):
 
     def _submit_to_github(self):
         """Action to take when pressing the submit button."""
+        # Get reference to the main window
         if self.parent() is not None:
-            main = self.parent().main
+            if getattr(self.parent(), 'main', False):
+                # This covers the case when the dialog is attached
+                # to the internal console
+                main = self.parent().main
+            else:
+                # Else the dialog is attached to the main window
+                # directly
+                main = self.parent()
         else:
             main = None
 
@@ -249,11 +272,10 @@ def _submit_to_github(self):
             issue_text = description
 
         try:
-            if main is not None:
-                org = 'spyder-ide'
-            else:
-                # For testing
+            if DEV or main is None:
                 org = 'ccordoba12'
+            else:
+                org = 'spyder-ide'
             github_backend = GithubBackend(org, 'spyder')
             github_report = github_backend.send_report(title, issue_text)
             if github_report:
@@ -274,7 +296,8 @@ def _submit_to_github(self):
                     " \n<!---   *** BEFORE SUBMITTING: PASTE CLIPBOARD HERE "
                     "TO COMPLETE YOUR REPORT ***   ---!>\n")
                 if main is not None:
-                    main.report_issue(body=issue_body, title=title)
+                    main.report_issue(body=issue_body, title=title,
+                                      open_webpage=True)
                 else:
                     pass
 


Test patch:
diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py
--- a/spyder/app/tests/test_mainwindow.py
+++ b/spyder/app/tests/test_mainwindow.py
@@ -1019,7 +1019,7 @@ def test_issue_4066(main_window, qtbot):
     qtbot.waitUntil(lambda: nsb.editor.model.rowCount() == 0, timeout=EVAL_TIMEOUT)
 
     # Close editor
-    ok_widget = obj_editor.bbox.button(obj_editor.bbox.Ok)
+    ok_widget = obj_editor.btn_close
     qtbot.mouseClick(ok_widget, Qt.LeftButton)
 
     # Wait for the segfault
@@ -1566,30 +1566,19 @@ def test_report_issue_url(monkeypatch):
     attr_to_patch = ('spyder.app.mainwindow.QDesktopServices')
     monkeypatch.setattr(attr_to_patch, MockQDesktopServices)
 
-    # Test when body=None, i.e. when Help > Report Issue is chosen
-    target_url = QUrl(target_url_base + '?body=' + body_autogenerated)
-    MainWindow.report_issue(mockMainWindow_instance, body=None, title=None)
-    assert MockQDesktopServices.openUrl.call_count == 1
-    MockQDesktopServices.openUrl.assert_called_once_with(target_url)
-
-    # Test with body=None and title != None
-    target_url = QUrl(target_url_base + '?body=' + body_autogenerated
-                      + "&title=" + title)
-    MainWindow.report_issue(mockMainWindow_instance, body=None, title=title)
-    assert MockQDesktopServices.openUrl.call_count == 2
-    mockQDesktopServices_instance.openUrl.called_with(target_url)
-
     # Test when body != None, i.e. when auto-submitting error to Github
     target_url = QUrl(target_url_base + '?body=' + body)
-    MainWindow.report_issue(mockMainWindow_instance, body=body, title=None)
-    assert MockQDesktopServices.openUrl.call_count == 3
+    MainWindow.report_issue(mockMainWindow_instance, body=body, title=None,
+                            open_webpage=True)
+    assert MockQDesktopServices.openUrl.call_count == 1
     mockQDesktopServices_instance.openUrl.called_with(target_url)
 
     # Test when body != None and title != None
     target_url = QUrl(target_url_base + '?body=' + body
                       + "&title=" + title)
-    MainWindow.report_issue(mockMainWindow_instance, body=body, title=title)
-    assert MockQDesktopServices.openUrl.call_count == 4
+    MainWindow.report_issue(mockMainWindow_instance, body=body, title=title,
+                            open_webpage=True)
+    assert MockQDesktopServices.openUrl.call_count == 2
     mockQDesktopServices_instance.openUrl.called_with(target_url)