Skip to content

Commit f22d73d

Browse files
ensure create_courseware adds the depts (#3220)
Co-authored-by: Dan Subak <dsubak@users.noreply.github.com>
1 parent 57472be commit f22d73d

1 file changed

Lines changed: 160 additions & 126 deletions

File tree

courses/management/commands/create_courseware.py

Lines changed: 160 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,42 @@ def _create_departments(self, departments: List[str]) -> models.QuerySet: # noq
8585

8686
return Department.objects.filter(name__in=departments).all()
8787

88+
def _get_or_create_departments(
89+
self,
90+
department_names: list[str],
91+
*,
92+
create_if_missing: bool,
93+
) -> models.QuerySet:
94+
"""
95+
Retrieves or creates Department objects based on the provided names.
96+
97+
Args:
98+
department_names (List[str]): List of department names.
99+
create_if_missing (bool): If True, creates departments that don't exist.
100+
If False, exits with an error if any departments don't exist.
101+
102+
Returns:
103+
models.QuerySet: Query set containing all of the departments specified
104+
in the list of department names.
105+
"""
106+
if not department_names:
107+
self._department_must_be_defined_error()
108+
109+
existing_depts = Department.objects.filter(name__in=department_names)
110+
existing_names = {dept.name for dept in existing_depts}
111+
missing_names = [
112+
name for name in department_names if name not in existing_names
113+
]
114+
115+
if missing_names:
116+
if create_if_missing:
117+
for dept_name in missing_names:
118+
Department.objects.create(name=dept_name)
119+
else:
120+
self._departments_do_not_exist_error()
121+
122+
return Department.objects.filter(name__in=department_names)
123+
88124
def _department_must_be_defined_error(self):
89125
"""
90126
Outputs an error message indicating that departments must be
@@ -169,6 +205,121 @@ def _create_course_run(self, course, **kwargs):
169205
self.style.SUCCESS(f"Created course run {course_run.id}: {course_run}")
170206
)
171207

208+
def _handle_program(self, add_depts: models.QuerySet, **kwargs):
209+
"""Handle creation of a program."""
210+
self._check_if_courseware_object_readable_id_exists(
211+
Program, kwargs["courseware_id"]
212+
)
213+
214+
new_program = Program.objects.create(
215+
readable_id=kwargs["courseware_id"],
216+
title=kwargs["title"],
217+
live=kwargs["live"],
218+
)
219+
220+
self._add_departments_to_courseware_object(new_program, add_depts)
221+
222+
self._successfully_created_courseware_object_message(new_program)
223+
224+
if kwargs["related"] is not None and len(kwargs["related"]) > 0:
225+
for readable_id in kwargs["related"]:
226+
try:
227+
related_program = Program.objects.filter(
228+
readable_id=readable_id
229+
).get()
230+
new_program.add_related_program(related_program)
231+
232+
self.stdout.write(
233+
self.style.SUCCESS(f"Added relationship for {readable_id}.")
234+
)
235+
except Exception: # noqa: BLE001, PERF203
236+
self.stderr.write(
237+
self.style.ERROR(
238+
f"Can't add relationship for {readable_id}: program not found."
239+
)
240+
)
241+
242+
self.stdout.write(
243+
self.style.SUCCESS(
244+
f"Added {len(new_program.related_programs)} program relationships."
245+
)
246+
)
247+
248+
def _handle_course(self, add_depts: models.QuerySet, **kwargs):
249+
"""Handle creation of a course."""
250+
self._check_if_courseware_object_readable_id_exists(
251+
Course, kwargs["courseware_id"]
252+
)
253+
254+
new_course = Course.objects.create(
255+
title=kwargs["title"],
256+
readable_id=kwargs["courseware_id"],
257+
live=kwargs["live"],
258+
)
259+
260+
self._add_departments_to_courseware_object(new_course, add_depts)
261+
262+
self._successfully_created_courseware_object_message(new_course)
263+
264+
if "create_run" in kwargs and kwargs["create_run"] is not None:
265+
self._create_course_run(new_course, **kwargs)
266+
267+
if kwargs["live"] or kwargs["force"]:
268+
if kwargs["force"]:
269+
self.stderr.write(
270+
self.style.ERROR(
271+
f"WARNING: creating a requirement for {new_course.readable_id} anyway since you specified --force. This will probably break the Django Admin until you set the course to Live."
272+
)
273+
)
274+
275+
new_req = None
276+
277+
if "program" in kwargs and kwargs["program"] is not None:
278+
try:
279+
program = Program.objects.filter(pk=kwargs["program"]).first()
280+
except: # noqa: E722
281+
program = Program.objects.filter(readable_id=kwargs["program"]).first()
282+
283+
self._add_departments_to_courseware_object(program, add_depts)
284+
285+
if program is not None and kwargs["required"]:
286+
new_req = program.add_requirement(new_course)
287+
elif program is not None and kwargs["elective"]:
288+
new_req = program.add_elective(new_course)
289+
290+
if new_req is not None:
291+
self.stdout.write(
292+
self.style.SUCCESS(
293+
f"Added {new_course.readable_id} to {program.readable_id}'s {new_req.get_parent().title} requirements."
294+
)
295+
)
296+
else:
297+
self.stdout.write(
298+
f"Live flag not specified for {new_course.readable_id}, ignoring any requirements flags"
299+
)
300+
301+
def _handle_courserun(self, **kwargs):
302+
"""Handle creation of a course run."""
303+
if not Course.objects.filter(readable_id=kwargs["courseware_id"]).exists():
304+
self.stderr.write(
305+
self.style.ERROR(
306+
f"Course with ID {kwargs['courseware_id']} doesn't exist."
307+
)
308+
)
309+
exit(-1) # noqa: PLR1722
310+
311+
if "create_run" not in kwargs or kwargs["create_run"] is None:
312+
self.stderr.write(
313+
self.style.ERROR(
314+
"You must specify the run tag with either --run-tag or --create-run when creating a course run."
315+
)
316+
)
317+
exit(-1) # noqa: PLR1722
318+
319+
course = Course.objects.filter(readable_id=kwargs["courseware_id"]).get()
320+
321+
self._create_course_run(course, **kwargs)
322+
172323
def add_arguments(self, parser) -> None:
173324
parser.add_argument(
174325
"type",
@@ -297,7 +448,7 @@ def add_arguments(self, parser) -> None:
297448
action="store_true",
298449
)
299450

300-
def handle(self, *args, **kwargs): # pylint: disable=unused-argument # noqa: C901, PLR0915
451+
def handle(self, *_args, **kwargs):
301452
if not (
302453
kwargs["force"]
303454
or kwargs["courseware_id"].startswith("course")
@@ -310,134 +461,17 @@ def handle(self, *args, **kwargs): # pylint: disable=unused-argument # noqa: C
310461
)
311462
exit(-1) # noqa: PLR1722
312463

313-
if kwargs["type"] == "program":
314-
if kwargs["depts"] and len(kwargs["depts"]) > 0:
315-
add_depts = Department.objects.filter(name__in=kwargs["depts"]).all()
316-
else:
317-
self._department_must_be_defined_error()
318-
319-
if kwargs["create_depts"]:
320-
add_depts = self._create_departments(kwargs["depts"])
321-
elif not add_depts:
322-
self._departments_do_not_exist_error()
323-
324-
self._check_if_courseware_object_readable_id_exists(
325-
Program, kwargs["courseware_id"]
326-
)
327-
328-
new_program = Program.objects.create(
329-
readable_id=kwargs["courseware_id"],
330-
title=kwargs["title"],
331-
live=kwargs["live"],
464+
# Validate/create departments for programs and courses (not needed for courseruns)
465+
if kwargs["type"] in ["program", "course"]:
466+
add_depts = self._get_or_create_departments(
467+
kwargs["depts"], create_if_missing=kwargs["create_depts"]
332468
)
333469

334-
self._add_departments_to_courseware_object(new_program, add_depts)
335-
336-
self._successfully_created_courseware_object_message(new_program)
337-
338-
if kwargs["related"] is not None and len(kwargs["related"]) > 0:
339-
for readable_id in kwargs["related"]:
340-
try:
341-
related_program = Program.objects.filter(
342-
readable_id=readable_id
343-
).get()
344-
new_program.add_related_program(related_program)
345-
346-
self.stdout.write(
347-
self.style.SUCCESS(f"Added relationship for {readable_id}.")
348-
)
349-
except Exception: # noqa: BLE001, PERF203
350-
self.stderr.write(
351-
self.style.ERROR(
352-
f"Can't add relationship for {readable_id}: program not found."
353-
)
354-
)
355-
356-
self.stdout.write(
357-
self.style.SUCCESS(
358-
f"Added {len(new_program.related_programs)} program relationships."
359-
)
360-
)
470+
if kwargs["type"] == "program":
471+
self._handle_program(add_depts, **kwargs)
361472
elif kwargs["type"] == "course":
362-
self._check_if_courseware_object_readable_id_exists(
363-
Course, kwargs["courseware_id"]
364-
)
365-
366-
new_course = Course.objects.create(
367-
title=kwargs["title"],
368-
readable_id=kwargs["courseware_id"],
369-
live=kwargs["live"],
370-
)
371-
372-
if kwargs["depts"] and len(kwargs["depts"]) > 0:
373-
add_depts = Department.objects.filter(name__in=kwargs["depts"]).all()
374-
else:
375-
self._department_must_be_defined_error()
376-
377-
if kwargs["create_depts"]:
378-
add_depts = self._create_departments(kwargs["depts"])
379-
if "add_depts" not in locals() or not add_depts:
380-
self._departments_do_not_exist_error()
381-
382-
self._successfully_created_courseware_object_message(new_course)
383-
384-
if "create_run" in kwargs and kwargs["create_run"] is not None:
385-
self._create_course_run(new_course, **kwargs)
386-
387-
if kwargs["live"] or kwargs["force"]:
388-
if kwargs["force"]:
389-
self.stderr.write(
390-
self.style.ERROR(
391-
f"WARNING: creating a requirement for {new_course.readable_id} anyway since you specified --force. This will probably break the Django Admin until you set the course to Live."
392-
)
393-
)
394-
395-
new_req = None
396-
397-
if "program" in kwargs and kwargs["program"] is not None:
398-
try:
399-
program = Program.objects.filter(pk=kwargs["program"]).first()
400-
except: # noqa: E722
401-
program = Program.objects.filter(
402-
readable_id=kwargs["program"]
403-
).first()
404-
405-
self._add_departments_to_courseware_object(program, add_depts)
406-
407-
if program is not None and kwargs["required"]:
408-
new_req = program.add_requirement(new_course)
409-
elif program is not None and kwargs["elective"]:
410-
new_req = program.add_elective(new_course)
411-
412-
if new_req is not None:
413-
self.stdout.write(
414-
self.style.SUCCESS(
415-
f"Added {new_course.readable_id} to {program.readable_id}'s {new_req.get_parent().title} requirements."
416-
)
417-
)
418-
else:
419-
self.stdout.write(
420-
f"Live flag not specified for {new_course.readable_id}, ignoring any requirements flags"
421-
)
473+
self._handle_course(add_depts, **kwargs)
422474
elif kwargs["type"] == "courserun":
423-
if not Course.objects.filter(readable_id=kwargs["courseware_id"]).exists():
424-
self.stderr.write(
425-
self.style.ERROR(
426-
f"Course with ID {kwargs['courseware_id']} doesn't exist."
427-
)
428-
)
429-
exit(-1) # noqa: PLR1722
430-
431-
if "create_run" not in kwargs or kwargs["create_run"] is None:
432-
self.stderr.write(
433-
self.style.ERROR(
434-
"You must specify the run tag with either --run-tag or --create-run when creating a course run."
435-
)
436-
)
437-
exit(-1) # noqa: PLR1722
438-
439-
course = Course.objects.filter(readable_id=kwargs["courseware_id"]).get()
440-
441-
self._create_course_run(course, **kwargs)
475+
self._handle_courserun(**kwargs)
442476
else:
443477
self.stderr.write(self.style.ERROR(f"Not sure what {kwargs['type']} is."))

0 commit comments

Comments
 (0)