Repository: ucfopen/canvasapi

Issue:
Course Analytics summary data returns a json, should return a PaginatedList
# Describe the bug

The methods to return analytics data only return a json value, but Canvas returns this as a paginated list for some of these methods.

# To Reproduce

Steps to reproduce the behavior:

Call methods like `course.get_course_level_student_summary_data()` on a course with many users. This method returns json so it's not possible to get the next set of data, only the first.

# Expected behavior

Expected that these methods return a PaginatedList so you can loop to the next record.

# Environment information

<!--(please complete the following information)-->

- Python version (`python --version`) 3.8.5
- CanvasAPI version (`pip show canvasapi`) 2.0.0

# Additional context

Review the [other analytics](https://canvas.instructure.com/doc/api/analytics.html) (and possibly other) methods like
```
get_course_level_participation_data
get_course_level_assignment_data
get_user_in_a_course_level_assignment_data
get_user_in_a_course_level_messaging_data
```

to see if these also need a Paginatedlist. 

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/ucfopen/canvasapi/pull/506?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ucfopen) Report
> Merging [#506](https://codecov.io/gh/ucfopen/canvasapi/pull/506?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ucfopen) (f01bfb9) into [develop](https://codecov.io/gh/ucfopen/canvasapi/commit/72ac81b0e8e3240886144a57054df21afa4a3a2f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ucfopen) (72ac81b) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff            @@
##           develop      #506   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           68        68           
  Lines         3611      3613    +2     
=========================================
+ Hits          3611      3613    +2     
```


| [Impacted Files](https://codecov.io/gh/ucfopen/canvasapi/pull/506?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ucfopen) | Coverage Δ | |
|---|---|---|
| [canvasapi/course.py](https://codecov.io/gh/ucfopen/canvasapi/pull/506/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ucfopen#diff-Y2FudmFzYXBpL2NvdXJzZS5weQ==) | `100.00% <100.00%> (ø)` | |

:mega: Codecov can now indicate which changes are the most critical in Pull Requests. [Learn more](https://about.codecov.io/product/feature/runtime-insights/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ucfopen)


Patch:
diff --git a/AUTHORS.md b/AUTHORS.md
index 4b8eafa..eaf346e 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -26,6 +26,7 @@ Patches and Suggestions
 - Bruce Spang [@brucespang](https://github.com/brucespang)
 - Catherine Abbruzzese [@cat0698](https://github.com/cat0698))
 - Cameron Cuff [@ctcuff](https://github.com/ctcuff)
+- Craig Thompson [@craigdsthompson](https://github.com/craigdsthompson)
 - Dalton Durst [@UniversalSuperBox](https://github.com/UniversalSuperBox)
 - Damian Sweeney [@damianfs](https://github.com/damianfs)
 - Daniel Brinkman [@DanBrink91](https://github.com/DanBrink91)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2fca249..3798213 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@
 ### Breaking Changes
 
 - Update `QuizSubmission.get_submission_events` to return a `PaginatedList`. (Thanks, [@stevenbell](https://github.com/stevenbell))
+- Update `Course.get_course_level_student_summary_data` to returns a `PaginatedList` of `CourseStudentSummary` items instead of a dictionary. (Thanks, [@craigdsthompson](https://github.com/craigdsthompson))
 - Remove unnecessary `id` parameter from `delete`, `reorder_question_group`, and `update` methods in `QuizGroup` class. (Thanks, [@kailukaitisBrendan](https://github.com/kailukaitisBrendan))
 
 ## [2.2.0] - 2021-03-25
diff --git a/canvasapi/course.py b/canvasapi/course.py
index 11842f9..26d71b5 100644
--- a/canvasapi/course.py
+++ b/canvasapi/course.py
@@ -1052,17 +1052,19 @@ class Course(CanvasObject):
         :calls: `GET /api/v1/courses/:course_id/analytics/student_summaries \
         <https://canvas.instructure.com/doc/api/analytics.html#method.analytics_api.course_student_summaries>`_
 
-        :rtype: dict
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.course.CourseStudentSummary`
         """
 
-        response = self._requester.request(
+        return PaginatedList(
+            CourseStudentSummary,
+            self._requester,
             "GET",
             "courses/{}/analytics/student_summaries".format(self.id),
+            {"course_id": self.id},
             _kwargs=combine_kwargs(**kwargs),
         )
 
-        return response.json()
-
     def get_custom_columns(self, **kwargs):
         """
         List of all the custom gradebook columns for a course.
@@ -2687,6 +2689,11 @@ class CourseNickname(CanvasObject):
         return CourseNickname(self._requester, response.json())
 
 
+class CourseStudentSummary(CanvasObject):
+    def __str__(self):
+        return "Course Student Summary {}".format(self.id)
+
+
 class LatePolicy(CanvasObject):
     def __str__(self):
         return "Late Policy {}".format(self.id)
diff --git a/docs/course-ref.rst b/docs/course-ref.rst
index 3d43f67..02e77cf 100644
--- a/docs/course-ref.rst
+++ b/docs/course-ref.rst
@@ -12,6 +12,13 @@ CourseNickname
 .. autoclass:: canvasapi.course.CourseNickname
     :members:
 
+====================
+CourseStudentSummary
+====================
+
+.. autoclass:: canvasapi.course.CourseStudentSummary
+    :members:
+
 ==========
 LatePolicy
 ==========


Test patch:
diff --git a/tests/test_course.py b/tests/test_course.py
index 915d8f5..284e24e 100644
--- a/tests/test_course.py
+++ b/tests/test_course.py
@@ -1027,8 +1027,7 @@ class TestCourse(unittest.TestCase):
         register_uris({"course": ["get_course_level_student_summary_data"]}, m)
 
         response = self.course.get_course_level_student_summary_data()
-
-        self.assertIsInstance(response, list)
+        self.assertIsInstance(response, PaginatedList)
 
     # get_user_in_a_course_level_participation_data()
     def test_get_user_in_a_course_level_participation_data(self, m):
@@ -1813,6 +1812,27 @@ class TestCourseNickname(unittest.TestCase):
         self.assertTrue(hasattr(deleted_nick, "nickname"))
 
 
+@requests_mock.Mocker()
+class TestCourseStudentSummary(unittest.TestCase):
+    def setUp(self):
+        self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
+
+        with requests_mock.Mocker() as m:
+            register_uris(
+                {"course": ["get_by_id", "get_course_level_student_summary_data"]}, m
+            )
+
+            self.course = self.canvas.get_course(1)
+            self.course_student_summary = (
+                self.course.get_course_level_student_summary_data()[0]
+            )
+
+    # __str__()
+    def test__str__(self, m):
+        string = str(self.course_student_summary)
+        self.assertIsInstance(string, str)
+
+
 @requests_mock.Mocker()
 class TestLatePolicy(unittest.TestCase):
     def setUp(self):