479
543
flag, weeks = self.context.getDueDateFilter(self.person)
546
def getLinkedActivityInfo(self, activity):
547
source = getSourceObj(activity.source)
548
insecure_activity = proxy.removeSecurityProxy(activity)
549
insecure_source = proxy.removeSecurityProxy(source)
551
if interfaces.IActivity.providedBy(insecure_source):
552
short, long, best_score = self.getActivityAttrs(source)
553
elif interfaces.IWorksheet.providedBy(insecure_source):
555
short = activity.label or long
557
short = short[:5].strip()
560
short = long = best_score = ''
563
'linked_source': source,
568
'hash': insecure_activity.__name__,
573
def getActivityInfo(self, activity):
574
insecure_activity = proxy.removeSecurityProxy(activity)
576
if interfaces.ILinkedColumnActivity.providedBy(insecure_activity):
577
return self.getLinkedActivityInfo(activity)
579
short, long, best_score = self.getActivityAttrs(activity)
582
ICommentScoreSystem.providedBy(insecure_activity.scoresystem) or
583
interfaces.ILinkedActivity.providedBy(insecure_activity))
585
if interfaces.ILinkedActivity.providedBy(insecure_activity):
586
updateGrades = '%s/updateGrades.html' % (
587
absoluteURL(insecure_activity, self.request))
592
'linked_source': None,
593
'scorable': scorable,
597
'hash': insecure_activity.__name__,
599
'updateGrades': updateGrades,
603
def filtered_activity_info(self):
605
for activity in self.getFilteredActivities():
606
info = self.getActivityInfo(activity)
482
610
def getActivityAttrs(self, activity):
483
shortTitle = activity.label or activity.title
611
longTitle = activity.title
612
shortTitle = activity.label or longTitle
484
613
shortTitle = shortTitle.replace(' ', '')
485
614
if len(shortTitle) > 5:
486
615
shortTitle = shortTitle[:5].strip()
487
longTitle = activity.title
488
if ICommentScoreSystem.providedBy(activity.scoresystem):
616
ss = proxy.removeSecurityProxy(activity.scoresystem)
617
if ICommentScoreSystem.providedBy(ss):
491
bestScore = activity.scoresystem.getBestScore()
620
bestScore = ss.getBestScore()
492
621
return shortTitle, longTitle, bestScore
494
623
def activities(self):
495
624
"""Get a list of all activities."""
496
625
self.person = IPerson(self.request.principal)
498
for activity in self.getFilteredActivities():
499
if interfaces.ILinkedColumnActivity.providedBy(activity):
501
source = getSourceObj(activity.source)
502
if interfaces.IActivity.providedBy(source):
503
shortTitle, longTitle, bestScore = \
504
self.getActivityAttrs(source)
505
elif interfaces.IWorksheet.providedBy(source):
506
shortTitle = activity.label or source.title
507
if len(shortTitle) > 5:
508
shortTitle = shortTitle[:5].strip()
509
longTitle = source.title
512
shortTitle = longTitle = bestScore = ''
515
ICommentScoreSystem.providedBy(activity.scoresystem) or
516
interfaces.ILinkedActivity.providedBy(activity))
517
shortTitle, longTitle, bestScore = \
518
self.getActivityAttrs(activity)
520
'scorable': scorable,
521
'shortTitle': shortTitle,
522
'longTitle': longTitle,
524
'hash': activity.__name__,
627
deployed = proxy.removeSecurityProxy(self.context).context.deployed
628
for activity_info in self.filtered_activity_info:
629
result = dict(activity_info)
631
'canDelete': not deployed,
632
'moveLeft': not deployed,
633
'moveRight': not deployed,
526
635
results.append(result)
637
results[0]['moveLeft'] = False
638
results[-1]['moveRight'] = False
529
641
def scorableActivities(self):
567
679
gradebook = proxy.removeSecurityProxy(self.context)
568
680
worksheet = gradebook.getCurrentWorksheet(self.person)
569
681
section = ISection(worksheet)
570
activities = [(activity.__name__, activity)
571
for activity in self.getFilteredActivities()]
572
683
journal_data = interfaces.ISectionJournalData(section, None)
574
for student in self.context.students:
685
for student_info in self.students_info:
576
for act_hash, activity in activities:
577
value = self.getStudentActivityValue(student, activity)
578
if interfaces.ILinkedColumnActivity.providedBy(activity):
580
sourceObj = getSourceObj(activity.source)
581
if value and interfaces.IWorksheet.providedBy(sourceObj):
687
for activity_info in self.filtered_activity_info:
688
activity = activity_info['object']
689
value = self.getStudentActivityValue(student_info, activity)
690
source = activity_info['linked_source']
691
if source is not None:
692
if value and interfaces.IWorksheet.providedBy(source):
582
693
value = '%.1f' % value
585
ICommentScoreSystem.providedBy(activity.scoresystem) or
586
interfaces.ILinkedActivity.providedBy(activity))
589
'activity': act_hash,
590
'editable': editable,
695
'activity': activity_info['hash'],
696
'editable': activity_info['scorable'],
593
699
grades.append(grade)
595
total, average = gradebook.getWorksheetTotalAverage(worksheet,
701
total, raw_average = gradebook.getWorksheetTotalAverage(
702
worksheet, student_info['object'])
598
704
total = "%.1f" % total
600
if average is UNSCORED:
706
if raw_average is UNSCORED:
601
707
average = _('N/A')
603
average = convertAverage(average, self.average_scoresystem)
709
average = convertAverage(raw_average, self.average_scoresystem)
605
711
absences = tardies = 0
606
712
if (journal_data and not (self.absences_hide and self.tardies_hide)):
607
for meeting in journal_data.recordedMeetings(student):
608
grade = journal_data.getGrade(student, meeting)
713
# XXX: opt: perm checks may breed here
714
meetings = journal_data.recordedMeetings(student_info['object'])
715
for meeting in meetings:
716
grade = journal_data.getGrade(
717
student_info['object'], meeting)
609
718
if grade == ABSENT:
611
720
elif grade == TARDY:
615
{'student': {'title': student.title,
616
'id': student.username,
617
'url': absoluteURL(student, self.request),
618
'gradeurl': absoluteURL(self.context, self.request) +
619
('/%s' % student.username),
724
{'student': student_info,
621
725
'grades': grades,
622
726
'absences': unicode(absences),
623
727
'tardies': unicode(tardies),
625
729
'average': average,
730
'raw_average': raw_average,
629
734
key, reverse = self.sortKey
630
735
self.collator = ICollator(self.request.locale)
736
def generateStudentKey(row):
737
return self.collator.key(row['student']['title'])
631
738
def generateKey(row):
740
return generateStudentKey(row)
742
return (float(row['total']), generateStudentKey(row))
743
elif key == 'average':
744
if row['raw_average'] is UNSCORED:
745
return ('', generateStudentKey(row))
747
return (row['average'], generateStudentKey(row))
748
elif key in ['absences', 'tardies']:
749
if journal_data is None:
750
return (0, generateStudentKey(row))
752
return (int(row[key]), generateStudentKey(row))
753
else: # sorting by activity
633
754
grades = dict([(unicode(grade['activity']), grade['value'])
634
755
for grade in row['grades']])
635
756
if not grades.get(key, ''):
636
return (1, self.collator.key(row['student']['title']))
757
return (1, generateStudentKey(row))
638
return (0, grades.get(key), self.collator.key(row['student']['title']))
639
return self.collator.key(row['student']['title'])
759
return (0, grades.get(key), generateStudentKey(row))
640
760
return sorted(rows, key=generateKey, reverse=reverse)
652
772
results.append(result)
777
gradebook = proxy.removeSecurityProxy(self.context)
778
return gradebook.context.deployed
781
class FlourishGradebookOverview(GradebookOverview,
782
flourish.page.WideContainerPage):
783
"""flourish Gradebook Overview/Table"""
786
page_class = 'page grid'
789
def journal_present(self):
790
section = ISection(proxy.removeSecurityProxy(self.context))
791
return interfaces.ISectionJournalData(section, None) is not None
793
def handleYearChange(self):
794
if 'currentYear' in self.request:
795
currentSection = ISection(proxy.removeSecurityProxy(self.context))
796
currentYear = ISchoolYear(ITerm(currentSection))
797
requestYearId = self.request['currentYear']
798
if requestYearId != currentYear.__name__:
799
for section in self.getUserSections():
800
year = ISchoolYear(ITerm(section))
801
if year.__name__ == requestYearId:
806
url = absoluteURL(newSection, self.request)
811
self.request.response.redirect(url)
815
def handlePreferencesChange(self):
816
if not self.isTeacher:
818
gradebook = proxy.removeSecurityProxy(self.context)
819
columnPreferences = gradebook.getColumnPreferences(self.person)
820
show = self.request.get('show')
821
hide = self.request.get('hide')
823
column_keys_dict = dict(getColumnKeys(gradebook))
824
if show not in column_keys_dict and hide not in column_keys_dict:
827
prefs = columnPreferences.setdefault(show, {})
828
prefs['hide'] = False
830
prefs = columnPreferences.setdefault(hide, {})
832
gradebook.setColumnPreferences(self.person, columnPreferences)
833
elif 'scoresystem' in self.request:
834
vocab = queryUtility(IVocabularyFactory,
835
'schooltool.requirement.discretescoresystems')(None)
836
scoresystem = self.request.get('scoresystem', '')
838
name = vocab.getTermByToken(scoresystem).value.__name__
841
columnPreferences.setdefault('average', {})['scoresystem'] = name
842
gradebook.setColumnPreferences(self.person, columnPreferences)
844
def handleMoveActivity(self):
845
if not self.isTeacher or self.deployed:
847
if 'move_left' in self.request:
848
name, change = self.request['move_left'], -1
849
elif 'move_right' in self.request:
850
name, change = self.request['move_right'], 1
853
worksheet = proxy.removeSecurityProxy(self.context).context
854
keys = worksheet.keys()
856
new_pos = keys.index(name) + change
857
if new_pos >= 0 and new_pos < len(keys):
858
worksheet.changePosition(name, new_pos)
860
def handleDeleteActivity(self):
861
if not self.isTeacher or self.deployed:
863
if 'delete' in self.request:
864
name = self.request['delete']
865
worksheet = proxy.removeSecurityProxy(self.context).context
866
if name in worksheet.keys():
870
def scoresystems(self):
871
gradebook = proxy.removeSecurityProxy(self.context)
872
columnPreferences = gradebook.getColumnPreferences(self.person)
873
vocab = queryUtility(IVocabularyFactory,
874
'schooltool.requirement.discretescoresystems')(None)
875
current = columnPreferences.get('average', {}).get('scoresystem', '')
877
'title': _('No score system'),
878
'url': '?scoresystem',
879
'current': not current,
883
'title': term.value.title,
884
'url': '?scoresystem=%s' % term.token,
885
'current': term.value.__name__ == current,
890
"""Handle change of current year."""
891
self.person = IPerson(self.request.principal)
892
if self.handleYearChange():
895
"""Handle change of column preferences."""
896
self.handlePreferencesChange()
898
"""Handle change of column order."""
899
self.handleMoveActivity()
901
"""Handle removal of column."""
902
self.handleDeleteActivity()
904
"""Everything else handled by old skin method."""
905
GradebookOverview.update(self)
908
class FlourishGradebookYearNavigation(flourish.page.RefineLinksViewlet):
909
"""flourish Gradebook Overview year navigation viewlet."""
912
class FlourishGradebookYearNavigationViewlet(flourish.viewlet.Viewlet,
914
template = InlineViewPageTemplate('''
916
tal:attributes="action string:${context/@@absolute_url}">
917
<select name="currentYear" class="navigator"
918
onchange="this.form.submit()">
919
<tal:block repeat="year view/getYears">
921
tal:attributes="value year/form_id;
922
selected year/selected"
923
tal:content="year/title" />
931
return IPerson(self.request.principal)
934
currentSection = ISection(proxy.removeSecurityProxy(self.context))
935
currentYear = ISchoolYear(ITerm(currentSection))
937
for section in self.getUserSections():
938
year = ISchoolYear(ITerm(section))
939
if year not in years:
941
return [{'title': year.title,
942
'form_id': year.__name__,
943
'selected': year is currentYear and 'selected' or None}
946
def render(self, *args, **kw):
947
return self.template(*args, **kw)
950
class FlourishGradebookTermNavigation(flourish.page.RefineLinksViewlet):
951
"""flourish Gradebook Overview term navigation viewlet."""
954
class FlourishGradebookTermNavigationViewlet(flourish.viewlet.Viewlet,
956
template = InlineViewPageTemplate('''
958
tal:attributes="action string:${context/@@absolute_url}">
959
<select name="currentTerm" class="navigator"
960
onchange="this.form.submit()">
961
<tal:block repeat="term view/getTerms">
963
tal:attributes="value term/form_id;
964
selected term/selected"
965
tal:content="term/title" />
973
return IPerson(self.request.principal)
976
currentSection = ISection(proxy.removeSecurityProxy(self.context))
977
currentTerm = ITerm(currentSection)
978
currentYear = ISchoolYear(currentTerm)
980
for section in self.getUserSections():
981
term = ITerm(section)
982
if term not in terms and ISchoolYear(term) == currentYear:
984
return [{'title': term.title,
985
'form_id': self.getTermId(term),
986
'selected': term is currentTerm and 'selected' or None}
989
def render(self, *args, **kw):
990
return self.template(*args, **kw)
993
class FlourishGradebookSectionNavigation(flourish.page.RefineLinksViewlet):
994
"""flourish Gradebook Overview section navigation viewlet."""
997
class FlourishGradebookSectionNavigationViewlet(flourish.viewlet.Viewlet,
999
template = InlineViewPageTemplate('''
1001
tal:attributes="action string:${context/@@absolute_url}">
1002
<select name="currentSection" class="navigator"
1003
onchange="this.form.submit()">
1004
<tal:block repeat="section view/getSections">
1006
tal:attributes="value section/form_id;
1007
selected section/selected;"
1008
tal:content="section/title" />
1016
return IPerson(self.request.principal)
1018
def getSections(self):
1019
currentSection = ISection(proxy.removeSecurityProxy(self.context))
1020
currentTerm = ITerm(currentSection)
1021
for section in self.getUserSections():
1022
term = ITerm(section)
1023
if term != currentTerm:
1025
url = absoluteURL(section, self.request)
1033
'title': section.title,
1034
'form_id': self.getSectionId(section),
1035
'selected': section==currentSection and 'selected' or None,
1038
def render(self, *args, **kw):
1039
return self.template(*args, **kw)
1042
class FlourishGradebookOverviewLinks(flourish.page.RefineLinksViewlet):
1043
"""flourish Gradebook Overview add links viewlet."""
1046
class ActivityAddLink(flourish.page.LinkViewlet):
1050
worksheet = proxy.removeSecurityProxy(self.context).context
1051
if worksheet.deployed:
1053
return _("Activity")
1056
class FlourishGradebookSettingsLinks(flourish.page.RefineLinksViewlet):
1057
"""flourish Gradebook Settings links viewlet."""
1060
class GradebookTertiaryNavigationManager(flourish.page.TertiaryNavigationManager):
1062
template = InlineViewPageTemplate("""
1063
<ul tal:attributes="class view/list_class">
1064
<li tal:repeat="item view/items"
1065
tal:attributes="class item/class"
1066
tal:content="structure item/viewlet">
1074
gradebook = proxy.removeSecurityProxy(self.context)
1075
current = gradebook.context.__name__
1076
for worksheet in gradebook.worksheets:
1077
url = '%s/gradebook' % absoluteURL(worksheet, self.request)
1079
'class': worksheet.__name__ == current and 'active' or None,
1080
'viewlet': u'<a href="%s">%s</a>' % (url, worksheet.title[:15]),
656
1085
class GradeActivity(object):
657
1086
"""Grading a single activity"""
815
1248
return self.context.getCurrentWorksheet(self.person)
1251
class FlourishMyGradesView(MyGradesView, flourish.page.Page):
1252
"""Flourish student view of own grades."""
1256
def handleYearChange(self):
1257
if 'currentYear' in self.request:
1258
currentSection = ISection(proxy.removeSecurityProxy(self.context))
1259
currentYear = ISchoolYear(ITerm(currentSection))
1260
requestYearId = self.request['currentYear']
1261
if requestYearId != currentYear.__name__:
1262
for section in self.getUserSections():
1263
year = ISchoolYear(ITerm(section))
1264
if year.__name__ == requestYearId:
1265
newSection = section
1269
url = absoluteURL(newSection, self.request)
1274
self.request.response.redirect(url)
1279
"""Handle change of year."""
1280
self.person = IPerson(self.request.principal)
1281
if self.handleYearChange():
1284
"""Everything else handled by old skin method."""
1285
MyGradesView.update(self)
1288
class FlourishMyGradesYearNavigation(flourish.page.RefineLinksViewlet):
1289
"""flourish MyGrades Overview year navigation viewlet."""
1292
class FlourishMyGradesYearNavigationViewlet(
1293
FlourishGradebookYearNavigationViewlet):
1298
class FlourishMyGradesTermNavigation(flourish.page.RefineLinksViewlet):
1299
"""flourish MyGrades Overview term navigation viewlet."""
1302
class FlourishMyGradesTermNavigationViewlet(
1303
FlourishGradebookTermNavigationViewlet):
1308
class FlourishMyGradesSectionNavigation(flourish.page.RefineLinksViewlet):
1309
"""flourish MyGrades Overview section navigation viewlet."""
1312
class FlourishMyGradesSectionNavigationViewlet(
1313
FlourishGradebookSectionNavigationViewlet):
1318
class MyGradesTertiaryNavigationManager(flourish.page.TertiaryNavigationManager):
1320
template = InlineViewPageTemplate("""
1321
<ul tal:attributes="class view/list_class">
1322
<li tal:repeat="item view/items"
1323
tal:attributes="class item/class"
1324
tal:content="structure item/viewlet">
1332
gradebook = proxy.removeSecurityProxy(self.context)
1333
current = gradebook.context.__name__
1334
for worksheet in gradebook.worksheets:
1335
url = '%s/mygrades' % absoluteURL(worksheet, self.request)
1337
'class': worksheet.__name__ == current and 'active' or None,
1338
'viewlet': u'<a href="%s">%s</a>' % (url, worksheet.title[:15]),
818
1343
class LinkedActivityGradesUpdater(object):
819
1344
"""Functionality to update grades from a linked activity"""
1197
1745
return activity.due_date < cutoff
1748
class FlourishStudentGradebookView(flourish.page.Page):
1749
"""A flourish view of the student gradebook."""
1753
return self.context.student.title
1757
gradebook = proxy.removeSecurityProxy(self.context.gradebook)
1758
return _(u'${section} grades for ${worksheet}',
1759
mapping={'section': ISection(gradebook).title,
1760
'worksheet': gradebook.context.title})
1763
gradebook = proxy.removeSecurityProxy(self.context.gradebook)
1764
worksheet = proxy.removeSecurityProxy(gradebook.context)
1765
student = self.context.student
1766
column_keys_dict = dict(getColumnKeys(gradebook))
1768
person = IPerson(self.request.principal, None)
1770
columnPreferences = {}
1772
columnPreferences = gradebook.getColumnPreferences(person)
1774
prefs = columnPreferences.get('average', {})
1775
self.average_hide = prefs.get('hide', False)
1776
self.average_label = prefs.get('label', '')
1777
if len(self.average_label) == 0:
1778
self.average_label = column_keys_dict['average']
1779
scoresystems = IScoreSystemContainer(ISchoolToolApplication(None))
1780
self.average_scoresystem = scoresystems.get(
1781
prefs.get('scoresystem', ''))
1785
for activity in gradebook.getWorksheetActivities(worksheet):
1786
activity = proxy.removeSecurityProxy(activity)
1787
score = gradebook.getScore(student, activity)
1790
ss = score.scoreSystem
1791
if ICommentScoreSystem.providedBy(ss):
1794
'paragraphs': buildHTMLParagraphs(score.value),
1796
elif IValuesScoreSystem.providedBy(ss):
1797
s_min, s_max = getScoreSystemDiscreteValues(ss)
1799
if IDiscreteValuesScoreSystem.providedBy(ss):
1800
value = score.scoreSystem.getNumericalValue(score.value)
1803
count += s_max - s_min
1806
'value': '%s / %s' % (value, ss.getBestScore()),
1812
'value': score.value,
1821
title = activity.title
1822
if activity.description:
1823
title += ' - %s' % activity.description
1829
self.table.append(row)
1832
total, average = gradebook.getWorksheetTotalAverage(worksheet,
1834
self.average = convertAverage(average, self.average_scoresystem)
1200
1839
class GradebookCSVView(BrowserView):
1202
1841
def __call__(self):