Repository: ucfopen/canvasapi

Issue:
There's no method to enroll a user into a section
**What resource needs additional coverage?**
Enrollments

**What endpoints need to be covered?**
POST /api/v1/sections/:section_id/enrollments
https://canvas.instructure.com/doc/api/enrollments.html#method.enrollments_api.create


Discussion:
davidfokkema: Any progress on this? The requested change was addressed, AFAICT. Would love to be able to automatically enroll my students in sections without having me to click a 100 names in the web interface, 😅.

Patch:
diff --git a/canvasapi/section.py b/canvasapi/section.py
index 05fdb4f..8046b64 100644
--- a/canvasapi/section.py
+++ b/canvasapi/section.py
@@ -1,7 +1,9 @@
 from canvasapi.canvas_object import CanvasObject
+from canvasapi.enrollment import Enrollment
 from canvasapi.paginated_list import PaginatedList
 from canvasapi.progress import Progress
 from canvasapi.submission import GroupedSubmission, Submission
+from canvasapi.user import User
 from canvasapi.util import combine_kwargs, normalize_bool, obj_or_id
 
 
@@ -80,6 +82,28 @@ class Section(CanvasObject):
 
         return self
 
+    def enroll_user(self, user, **kwargs):
+        """
+        Create a new user enrollment for a course or a section.
+
+        :calls: `POST /api/v1/section/:section_id/enrollments \
+        <https://canvas.instructure.com/doc/api/enrollments.html#method.enrollments_api.create>`_
+
+        :param user: The object or ID of the user to enroll in this course.
+        :type user: :class:`canvasapi.user.User` or int
+        :rtype: :class:`canvasapi.enrollment.Enrollment`
+        """
+
+        kwargs["enrollment[user_id]"] = obj_or_id(user, "user", (User,))
+
+        response = self._requester.request(
+            "POST",
+            "sections/{}/enrollments".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return Enrollment(self._requester, response.json())
+
     def get_assignment_override(self, assignment, **kwargs):
         """
         Return override for the specified assignment for this section.


Test patch:
diff --git a/tests/fixtures/section.json b/tests/fixtures/section.json
index a4eb936..82c5cde 100644
--- a/tests/fixtures/section.json
+++ b/tests/fixtures/section.json
@@ -230,5 +230,16 @@
 			"url": "https://canvas.example.edu/api/v1/progress/3"
 		},
 		"status_code": 200
+	},
+	"enroll_user": {
+		"method": "POST",
+		"endpoint": "sections/1/enrollments",
+		"data": {
+			"id": 1,
+			"course_id": 1,
+			"user_id": 1,
+			"type": "TeacherEnrollment"
+		},
+		"status_code": 200
 	}
 }
diff --git a/tests/test_section.py b/tests/test_section.py
index ce3f0ee..cc088d1 100644
--- a/tests/test_section.py
+++ b/tests/test_section.py
@@ -18,9 +18,14 @@ class TestSection(unittest.TestCase):
         self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
 
         with requests_mock.Mocker() as m:
-            register_uris({"section": ["get_by_id"]}, m)
+            requires = {
+                "section": ["get_by_id"],
+                "user": ["get_by_id"],
+            }
+            register_uris(requires, m)
 
             self.section = self.canvas.get_section(1)
+            self.user = self.canvas.get_user(1)
 
     # __str__()
     def test__str__(self, m):
@@ -121,3 +126,27 @@ class TestSection(unittest.TestCase):
         self.assertTrue(progress.context_type == "Course")
         progress = progress.query()
         self.assertTrue(progress.context_type == "Course")
+
+    def test_enroll_user(self, m):
+        requires = {"section": ["enroll_user"], "user": ["get_by_id"]}
+        register_uris(requires, m)
+
+        enrollment_type = "TeacherEnrollment"
+
+        # by user ID
+        enrollment_by_id = self.section.enroll_user(
+            1, enrollment={"type": enrollment_type}
+        )
+
+        self.assertIsInstance(enrollment_by_id, Enrollment)
+        self.assertTrue(hasattr(enrollment_by_id, "type"))
+        self.assertEqual(enrollment_by_id.type, enrollment_type)
+
+        # by user object
+        enrollment_by_obj = self.section.enroll_user(
+            self.user, enrollment={"type": enrollment_type}
+        )
+
+        self.assertIsInstance(enrollment_by_obj, Enrollment)
+        self.assertTrue(hasattr(enrollment_by_obj, "type"))
+        self.assertEqual(enrollment_by_obj.type, enrollment_type)