Repository: ucfopen/canvasapi

Issue:
Course.list_multiple_submissions mismanages grouped response
The `Course.list_multiple_submissions` method returns a paginated list of `Submission` instances.  However, printing any of these instances using its default string conversion fails:

```python
submissions = course.list_multiple_submissions(student_ids='all', assignment_ids=(123, 456))
for submission in submissions:
    print(submission)
```

> `AttributeError: 'Submission' object has no attribute 'id'`

If instead I print each submission’s `__class__` and attribute dictionary, I see something rather strange.  The class is indeed `canvasapi.submission.Submission`.  However, the attributes are not those that a `Submission` should have.  Ignoring the double-underscore internal attributes, each `submission` instance has:

- `_requester`
- `attributes`
- `integration_id`
- `section_id`
- `set_attributes`
- `sis_user_id`
- `submissions`
- `to_json`
- `user_id`

The `sis_user_id` and `user_id` attributes tell me that this is some sort of person-identifying structure along the lines of a `User` or `Enrollment` object.  But it does not have the complete set of attributes for either of those.  The value of the `submissions` attribute is a list of dicts; I believe that each of these dicts corresponds to one `Submission` object.

The `Course.list_multiple_submissions` method produces a `GET /api/v1/courses/:course_id/students/submissions` request, and [the documentation for that request](https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.for_students) shows that its response can take one of two forms depending on the `grouped` parameter.  I can see that the `Course.list_multiple_submissions` implementation always hard-codes `grouped=False`, but the response coming back looks exactly like the Canvas API documentation example of a *grouped* response, complete with those `submissions` attributes giving lists of `Submission` objects.

So it seems that the `grouped=False` aspect of the request is not working as intended.  I don’t know why; is this a Canvas bug or a `canvasapi` bug?  Either way, the current behavior certainly is not working as intended.  `canvasapi` should either request a non-grouped response in a way that works, or else it should process the grouped response in a way that respects the actual structure and meaning of the returned data.

For now I am working around this myself by building new `Submission` instances out of the dicts in each of the `submissions` attributes, like this:

```python
groups = course.list_multiple_submissions(student_ids='all', assignment_ids=assignmentIds)
submissions = [Submission(course._requester, raw) for raw in chain.from_iterable(group.submissions for group in groups)]
```

But clearly that is not how the API is intended to be used.

Discussion:

Patch:
diff --git a/canvasapi/course.py b/canvasapi/course.py
index 8cd5329..7b87e2d 100644
--- a/canvasapi/course.py
+++ b/canvasapi/course.py
@@ -1,4 +1,5 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
+from warnings import warn
 
 from six import python_2_unicode_compatible
 
@@ -1134,12 +1135,15 @@ class Course(CanvasObject):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.submission.Submission`
         """
+        if 'grouped' in kwargs:
+            warn('The `grouped` parameter must be empty. Removing kwarg `grouped`.')
+            del kwargs['grouped']
+
         return PaginatedList(
             Submission,
             self._requester,
             'GET',
             'courses/%s/students/submissions' % (self.id),
-            grouped=False,
             _kwargs=combine_kwargs(**kwargs)
         )
 
diff --git a/canvasapi/section.py b/canvasapi/section.py
index 5d74533..f9f86ca 100644
--- a/canvasapi/section.py
+++ b/canvasapi/section.py
@@ -1,4 +1,5 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
+from warnings import warn
 
 from six import python_2_unicode_compatible
 
@@ -157,12 +158,15 @@ class Section(CanvasObject):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.submission.Submission`
         """
+        if 'grouped' in kwargs:
+            warn('The `grouped` parameter must be empty. Removing kwarg `grouped`.')
+            del kwargs['grouped']
+
         return PaginatedList(
             Submission,
             self._requester,
             'GET',
             'sections/%s/students/submissions' % (self.id),
-            grouped=False,
             _kwargs=combine_kwargs(**kwargs)
         )
 


Test patch:
diff --git a/tests/test_course.py b/tests/test_course.py
index 09b5e4c..b2bc030 100644
--- a/tests/test_course.py
+++ b/tests/test_course.py
@@ -1,7 +1,8 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
+import os
 import unittest
 import uuid
-import os
+import warnings
 
 import requests_mock
 from six import text_type
@@ -647,6 +648,25 @@ class TestCourse(unittest.TestCase):
         self.assertEqual(len(submission_list), 2)
         self.assertIsInstance(submission_list[0], Submission)
 
+    def test_list_multiple_submissions_grouped_param(self, m):
+        register_uris({'course': ['list_multiple_submissions']}, m)
+
+        with warnings.catch_warnings(record=True) as warning_list:
+            warnings.simplefilter('always')
+            submissions = self.course.list_multiple_submissions(grouped=True)
+            submission_list = [submission for submission in submissions]
+
+            # Ensure using the `grouped` param raises a warning
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, UserWarning)
+            self.assertEqual(
+                text_type(warning_list[-1].message),
+                'The `grouped` parameter must be empty. Removing kwarg `grouped`.'
+            )
+
+            self.assertEqual(len(submission_list), 2)
+            self.assertIsInstance(submission_list[0], Submission)
+
     # get_submission()
     def test_get_submission(self, m):
         register_uris({'course': ['get_submission']}, m)
diff --git a/tests/test_section.py b/tests/test_section.py
index c26ba21..b3313fa 100644
--- a/tests/test_section.py
+++ b/tests/test_section.py
@@ -1,7 +1,9 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
 import unittest
+import warnings
 
 import requests_mock
+from six import text_type
 
 from canvasapi import Canvas
 from canvasapi.enrollment import Enrollment
@@ -104,6 +106,25 @@ class TestSection(unittest.TestCase):
         self.assertEqual(len(submission_list), 2)
         self.assertIsInstance(submission_list[0], Submission)
 
+    def test_list_multiple_submissions_grouped_param(self, m):
+        register_uris({'section': ['list_multiple_submissions']}, m)
+
+        with warnings.catch_warnings(record=True) as warning_list:
+            warnings.simplefilter('always')
+            submissions = self.section.list_multiple_submissions(grouped=True)
+            submission_list = [submission for submission in submissions]
+
+            # Ensure using the `grouped` param raises a warning
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, UserWarning)
+            self.assertEqual(
+                text_type(warning_list[-1].message),
+                'The `grouped` parameter must be empty. Removing kwarg `grouped`.'
+            )
+
+            self.assertEqual(len(submission_list), 2)
+            self.assertIsInstance(submission_list[0], Submission)
+
     # get_submission()
     def test_get_submission(self, m):
         register_uris({'section': ['get_submission']}, m)