Skip to content
This repository was archived by the owner on Jun 30, 2024. It is now read-only.

Commit 1cf0bca

Browse files
committed
Merge branch 'copy_assignment'
2 parents 56855ff + 819baec commit 1cf0bca

File tree

4 files changed

+149
-23
lines changed

4 files changed

+149
-23
lines changed

controllers/admin.py

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -546,24 +546,22 @@ def admin():
546546
if row.user_id not in instructordict:
547547
studentdict[row.user_id]= name
548548

549-
#Not rebuilding
550-
if not request.vars.projectname or not request.vars.startdate:
551-
course = db(db.courses.course_name == auth.user.course_name).select().first()
552-
curr_start_date = course.term_start_date.strftime("%m/%d/%Y")
553-
return dict(sectionInfo=sectionsList,startDate=date,
554-
coursename=auth.user.course_name, course_id=auth.user.course_name,
555-
instructors=instructordict, students=studentdict,
556-
curr_start_date=curr_start_date, confirm=True,
557-
build_info=my_build, master_build=master_build, my_vers=my_vers,
558-
mst_vers=mst_vers,
559-
course=sidQuery,
560-
)
561-
562-
return dict(sectionInfo=sectionsList, startDate=date.isoformat(), coursename=auth.user.course_name,
563-
instructors=instructordict, students=studentdict, confirm=False,
564-
task_name=uuid, course_url=course_url, course_id=auth.user.course_name,
549+
course = db(db.courses.course_name == auth.user.course_name).select().first()
550+
instructor_course_list = db( (db.course_instructor.instructor == auth.user.id) &
551+
(db.courses.id == db.course_instructor.course) &
552+
(db.courses.base_course == course.base_course) &
553+
(db.courses.course_name != course.course_name)).select(db.courses.course_name, db.courses.id)
554+
555+
curr_start_date = course.term_start_date.strftime("%m/%d/%Y")
556+
return dict(sectionInfo=sectionsList,startDate=date,
557+
coursename=auth.user.course_name, course_id=auth.user.course_name,
558+
instructors=instructordict, students=studentdict,
559+
curr_start_date=curr_start_date, confirm=True,
560+
build_info=my_build, master_build=master_build, my_vers=my_vers,
561+
mst_vers=mst_vers,
565562
course=sidQuery,
566-
)
563+
instructor_course_list=instructor_course_list
564+
)
567565

568566
# Called in admin.js from courseStudents to populate the list of students
569567
# eBookConfig.getCourseStudentsURL
@@ -1648,6 +1646,50 @@ def reorder_assignment_questions():
16481646

16491647
return json.dumps("Reordered in DB")
16501648

1649+
@auth.requires(lambda: verifyInstructorStatus(auth.user.course_name, auth.user), requires_login=True)
1650+
def copy_assignment():
1651+
"""
1652+
vars:
1653+
- oldassignment id or todo (-1 for all assignments)
1654+
- course
1655+
"""
1656+
1657+
if not verifyInstructorStatus(request.vars['course'], auth.user):
1658+
return "Error: Not Authorized"
1659+
else:
1660+
if request.vars.oldassignment == '-1':
1661+
assignments = db((db.assignments.course == db.courses.id) &
1662+
(db.courses.course_name == request.vars['course'])).select()
1663+
for a in assignments:
1664+
print("A = {}".format(a))
1665+
res = _copy_one_assignment(request.vars['course'], a.assignments['id'])
1666+
if res != "success":
1667+
break
1668+
else:
1669+
res = _copy_one_assignment(request.vars['course'], request.vars['oldassignment'])
1670+
return res
1671+
1672+
def _copy_one_assignment(course, oldid):
1673+
old_course = db(db.courses.course_name == course).select().first()
1674+
this_course = db(db.courses.course_name == auth.user.course_name).select().first()
1675+
old_assignment = db(db.assignments.id == int(oldid)).select().first()
1676+
due_delta = old_assignment.duedate.date() - old_course.term_start_date
1677+
due_date = this_course.term_start_date + due_delta
1678+
newassign_id = db.assignments.insert(course=auth.user.course_id, name=old_assignment.name,
1679+
duedate=due_date, description=old_assignment.description,
1680+
points=old_assignment.points,
1681+
threshold_pct=old_assignment.threshold_pct)
1682+
1683+
old_questions = db(db.assignment_questions.assignment_id == old_assignment.id).select()
1684+
for q in old_questions:
1685+
dq = q.as_dict()
1686+
dq['assignment_id'] = newassign_id
1687+
del dq['id']
1688+
db.assignment_questions.insert(**dq)
1689+
1690+
return "success"
1691+
1692+
16511693

16521694
@auth.requires(lambda: verifyInstructorStatus(auth.user.course_name, auth.user), requires_login=True)
16531695
def courselog():

static/js/admin.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,3 +1857,18 @@ function toggle_release_grades() {
18571857
set_release_button();
18581858
}
18591859
}
1860+
1861+
1862+
function copyAssignments() {
1863+
let selectedCourse = document.getElementById("courseSelection").value;
1864+
data = {oldassignment: -1,
1865+
course: selectedCourse
1866+
};
1867+
$.post("/runestone/admin/copy_assignment", data, function(mess, stat, w) {
1868+
if(mess == "success") {
1869+
alert('Done')
1870+
} else {
1871+
alert('Copy Failed')
1872+
}
1873+
});
1874+
}

tests/test_admin.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import json
2-
2+
import pytest
33

44
def test_add_assignment(test_assignment, test_user_1, runestone_db_tools):
55
my_ass = test_assignment('test_assignment', test_user_1.course)
@@ -175,3 +175,42 @@ def test_get_assignment(test_assignment, test_user_1, test_client):
175175
res = json.loads(res)
176176
assert res
177177
assert res['questions_data']
178+
179+
@pytest.mark.parametrize('assign_id',[ (-1), (0) ])
180+
def test_copy_assignment(assign_id, test_assignment, test_client, test_user_1, runestone_db_tools):
181+
test_user_1.make_instructor()
182+
test_user_1.login()
183+
course1_id = test_user_1.course.course_id
184+
my_ass = test_assignment('test_assignment', test_user_1.course)
185+
# Should provide the following to addq_to_assignment
186+
# -- assignment (an integer)
187+
# -- question == div_id
188+
# -- points
189+
# -- autograde one of ['manual', 'all_or_nothing', 'pct_correct', 'interact']
190+
# -- which_to_grade one of ['first_answer', 'last_answer', 'best_answer']
191+
# -- reading_assignment (boolean, true if it's a page to visit rather than a directive to interact with)
192+
my_ass.addq_to_assignment(question='subc_b_fitb',points=10)
193+
print(my_ass.questions())
194+
db = runestone_db_tools.db
195+
my_ass.save_assignment()
196+
197+
course_3 = runestone_db_tools.create_course('test_course_3', base_course='test_course_1')
198+
test_user_1.make_instructor(course_3.course_id)
199+
db(db.auth_user.id == test_user_1.user_id).update(course_id=course_3.course_id, course_name='test_course_3')
200+
db.commit()
201+
test_user_1.logout()
202+
test_user_1.login()
203+
if assign_id == 0:
204+
assign_id = my_ass.assignment_id
205+
res = test_client.validate('admin/copy_assignment', data=dict(
206+
oldassignment=assign_id,
207+
course='test_course_1'
208+
))
209+
assert res == "success"
210+
211+
rows = db(db.assignments.name == 'test_assignment').count()
212+
assert rows == 2
213+
214+
row = db(db.assignments.name == 'test_assignment').select(orderby=~db.assignments.id).first()
215+
rows = db(db.assignment_questions.assignment_id == row.id).count()
216+
assert rows == 1

views/admin/admin.html

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ <h4 style="text-align: center" class="list-group-item-heading">Add TA</h4>
2020
<a id="courselog" href="/{{=request.application}}/admin/courselog" class="list-group-item">
2121
<h4 style="text-align: center" class="list-group-item-heading">Download Log</h4>
2222
</a>
23+
24+
<a id="CopyAssignmentsTab" data-toggle="tab" href="#CopyAssignments" class="list-group-item">
25+
<h4 style="text-align: center" class="list-group-item-heading">Copy Assignments</h4>
26+
</a>
2327
<a id="DeleteTab" data-toggle="tab" href="#Delete" class="list-group-item">
2428
<h4 style="text-align: center" class="list-group-item-heading">Delete</h4>
2529
</a>
@@ -196,12 +200,29 @@ <h3 style="text-align: center">This action CANNOT BE UNDONE</h3>
196200
<form action="/{{=request.application}}/admin/deletecourse" method="post"
197201
onsubmit="return confirm('Are you sure you want to delete this course? This will remove the course for all students and all custom assignments you have made will be deleted. This cannot be undone.')">
198202
<button type="submit" style="width: 33%; align-self: center;">Really Delete</button>
199-
</form>
203+
</form>
200204
</div>
201205
<h3 style="text-align: center">This will delete all sections under this course. Students will not be able to access course materials.</h3>
202206
</div>
207+
</div>
208+
209+
210+
<div id="CopyAssignments" class="tab-pane fade">
211+
<div class="col-md-8">
212+
<h2 style="text-align: center">Copy Assignments</h2>
213+
<label for="courseSelection">Select the course you want to copy assignments from.</label>
214+
<div class="col-sm-3">
215+
<select id="courseSelection" class="form-control">
216+
{{ for course in instructor_course_list: }}
217+
<option value="{{=course.course_name}}">{{=course.course_name}}</option>
218+
{{ pass }}
219+
</select>
220+
<button onclick="copyAssignments();" class="btn btn-primary">Copy</button>
221+
</div>
203222
</div>
204-
</div>
223+
</div>
224+
225+
</div>
205226

206227
<script>
207228
$(".dashboardnav").removeClass("active");
@@ -214,22 +235,31 @@ <h3 style="text-align: center">This will delete all sections under this course.
214235
$('#studentsTab').css('background-color','transparent');
215236
$('#AddInstructorTab').css('background-color','transparent');
216237
$('#DeleteTab').css('background-color','transparent');
217-
238+
$('#CopyAssignmentsTab').css('background-color', 'transparent');
218239
});
219240

220241
$('#AddInstructorTab').click(function(){
221242
$('#AddInstructorTab').css('background-color', 'gainsboro');
222243
$('#sectionsTab').css('background-color','transparent');
223244
$('#studentsTab').css('background-color','transparent');
224245
$('#DeleteTab').css('background-color','transparent');
225-
246+
$('#CopyAssignmentsTab').css('background-color', 'transparent');
226247
});
227248

228249
$('#DeleteTab').click(function(){
229250
$('#DeleteTab').css('background-color', 'gainsboro');
230251
$('#sectionsTab').css('background-color','transparent');
231252
$('#studentsTab').css('background-color','transparent');
232253
$('#AddInstructorTab').css('background-color','transparent');
254+
$('#CopyAssignmentsTab').css('background-color', 'transparent');
255+
});
256+
257+
$('#CopyAssignmentsTab').click(function(){
258+
$('#CopyAssignmentsTab').css('background-color', 'gainsboro');
259+
$('#DeleteTab').css('background-color', 'transparent');
260+
$('#sectionsTab').css('background-color','transparent');
261+
$('#studentsTab').css('background-color','transparent');
262+
$('#AddInstructorTab').css('background-color','transparent');
233263

234264
});
235265

@@ -238,7 +268,7 @@ <h3 style="text-align: center">This will delete all sections under this course.
238268
$('#sectionsTab').css('background-color','transparent');
239269
$('#DeleteTab').css('background-color','transparent');
240270
$('#AddInstructorTab').css('background-color','transparent');
241-
271+
$('#CopyAssignmentsTab').css('background-color', 'transparent');
242272
});
243273

244274
</script>

0 commit comments

Comments
 (0)