From c16dc006aaafbfa3136df55fbf959b88e3e2a5c9 Mon Sep 17 00:00:00 2001 From: Adrian Fish Date: Wed, 20 Apr 2022 18:35:13 +0100 Subject: [PATCH] SAK-40437 GradebookNG GradingService refactored and centralized (#9990) * SAK-40437 Moved the grading service into gradebookng https://sakaiproject.atlassian.net/browse/SAK-40437 This commit merges the 3 grading interfaces into one, GradingService. It also adds a load of tests and adds a lot of typing to the various collections used. * List -> Collection removed System.out.println * Cleaned up poms and typed authz api * hibernate mappings change --- assignment/impl/pom.xml | 8 +- .../impl/AssignmentEventObserver.java | 8 +- .../impl/AssignmentGradeInfoProvider.java | 70 +- .../impl/AssignmentServiceImpl.java | 77 +- .../impl/AssignmentServiceTest.java | 8 +- .../impl/AssignmentTestConfiguration.java | 20 +- .../impl/src/webapp/WEB-INF/components.xml | 8 +- assignment/tool/pom.xml | 4 +- .../AssignmentEntityProvider.java | 48 +- .../assignment/tool/AssignmentAction.java | 267 +- .../assignment/tool/AssignmentToolUtils.java | 303 +- .../src/webapp/WEB-INF/applicationContext.xml | 7 +- assignment/tool/src/webapp/js/assignments.js | 2 +- .../tool/src/webapp/js/instructorNewEdit.js | 36 +- .../instructor_new_edit/grading_section.vm | 362 +- basiclti/basiclti-blis/pom.xml | 4 +- .../org/sakaiproject/lti13/LTI13Servlet.java | 17 +- basiclti/basiclti-common/pom.xml | 8 +- .../basiclti/util/SakaiBLTIUtil.java | 43 +- .../org/sakaiproject/lti13/LineItemUtil.java | 46 +- basiclti/basiclti-portlet/pom.xml | 9 +- .../sakaiproject/portlets/IMSBLTIPortlet.java | 51 +- basiclti/basiclti-tool/pom.xml | 8 +- .../sakaiproject/blti/tool/LTIAdminTool.java | 2 +- content-review/impl/compilatio/pom.xml | 4 - content-review/impl/turnitin/pom.xml | 8 +- .../turnitin/TurnitinReviewServiceImpl.java | 24 +- .../src/test/resources/hibernate.properties | 2 +- .../pack/src/webapp/WEB-INF/turnitin.xml | 3 +- edu-services/gradebook-service/api/pom.xml | 41 - .../service/gradebook/shared/Assignment.java | 187 - .../gradebook/shared/CategoryDefinition.java | 127 - .../gradebook/shared/CommentDefinition.java | 61 - .../ConflictingSpreadsheetNameException.java | 29 - .../service/gradebook/shared/CourseGrade.java | 132 - .../shared/ExternalAssignmentProvider.java | 84 - .../gradebook/shared/GradeDefinition.java | 130 - .../shared/GradeMappingDefinition.java | 122 - .../shared/GradebookArchiveService.java | 32 - .../shared/GradebookConfiguration.java | 53 - .../GradebookExternalAssessmentService.java | 351 -- .../shared/GradebookFrameworkService.java | 110 - .../shared/GradebookInformation.java | 125 - .../shared/GradebookPermissionService.java | 296 - .../gradebook/shared/GradebookService.java | 889 --- .../gradebook/shared/GradingEventStatus.java | 25 - .../shared/GradingScaleDefinition.java | 100 - .../MultipleAssignmentSavingException.java | 24 - .../shared/PermissionDefinition.java | 104 - .../shared/UnknownUserException.java | 36 - .../tool/gradebook/facades/Authn.java | 40 - .../tool/gradebook/facades/Authz.java | 167 - .../gradebook-service/hibernate/pom.xml | 41 - .../tool/gradebook/Category.hbm.xml | 48 - .../tool/gradebook/Comment.hbm.xml | 29 - .../tool/gradebook/GradableObject.hbm.xml | 71 - .../tool/gradebook/GradeMapping.hbm.xml | 44 - .../tool/gradebook/GradeRecord.hbm.xml | 56 - .../tool/gradebook/Gradebook.hbm.xml | 80 - .../tool/gradebook/GradebookProperty.hbm.xml | 20 - .../tool/gradebook/GradingEvent.hbm.xml | 37 - .../tool/gradebook/GradingScale.hbm.xml | 39 - .../LetterGradePercenteMapping.hbm.xml | 28 - .../tool/gradebook/Permission.hbm.xml | 28 - .../tool/gradebook/Spreadsheet.hbm.xml | 28 - .../tool/gradebook/AbstractGradeRecord.java | 138 - .../sakaiproject/tool/gradebook/Category.java | 493 -- .../sakaiproject/tool/gradebook/Comment.java | 145 - .../tool/gradebook/CommonGradeRecord.java | 97 - .../tool/gradebook/CourseGrade.java | 113 - .../tool/gradebook/CourseGradeRecord.java | 275 - .../CourseGradesToSpreadsheetConverter.java | 42 - .../tool/gradebook/GradableObject.java | 367 -- .../tool/gradebook/GradeMapping.java | 258 - .../tool/gradebook/Gradebook.java | 143 - .../tool/gradebook/GradebookArchive.java | 128 - .../tool/gradebook/GradebookAssignment.java | 548 -- .../tool/gradebook/GradebookProperty.java | 74 - .../tool/gradebook/GradingScale.java | 128 - .../gradebook/LetterGradePercentMapping.java | 164 - .../tool/gradebook/Permission.java | 100 - .../tool/gradebook/Spreadsheet.java | 144 - edu-services/gradebook-service/impl/pom.xml | 127 - .../gradebook/BaseHibernateManager.java | 1433 ----- .../gradebook/GradebookDefinition.java | 84 - ...radebookExternalAssessmentServiceImpl.java | 1003 --- .../GradebookFrameworkServiceImpl.java | 445 -- .../GradebookPermissionServiceImpl.java | 1395 ----- .../GradebookServiceHibernateImpl.java | 3860 ------------ .../facades/sakai2impl/AuthnSakai2Impl.java | 49 - .../facades/sakai2impl/AuthzSakai2Impl.java | 129 - .../facades/sections/AuthzSectionsImpl.java | 685 --- edu-services/gradebook-service/pom.xml | 23 - .../gradebook-service/sakai-pack/pom.xml | 53 - .../sakai_gradebook_post_schemaupdate.sql | 20 - .../sakai_gradebook_post_schemaupdate.sql | 20 - .../sakai_gradebook_post_schemaupdate.sql | 23 - .../src/webapp/WEB-INF/components.xml | 148 - edu-services/pom.xml | 1 - .../api/coursemanagement/CourseSection.java | 2 +- .../section/sakai/SectionAwarenessImpl.java | 2 +- .../section/sakai/CourseSectionImpl.java | 2 +- gradebookng/api/pom.xml | 57 + .../api}/AssessmentNotFoundException.java | 4 +- .../sakaiproject/grading/api/Assignment.java | 138 + .../AssignmentHasIllegalPointsException.java | 6 +- .../grading/api/CategoryDefinition.java | 125 + .../grading/api}/CategoryScoreData.java | 16 +- .../grading/api/CommentDefinition.java | 61 + .../ConflictingAssignmentNameException.java | 4 +- .../ConflictingCategoryNameException.java | 6 +- .../api}/ConflictingExternalIdException.java | 4 +- .../grading/api/CourseGradeTransferBean.java | 59 + .../grading/api}/DoubleComparator.java | 38 +- .../api/ExternalAssignmentProvider.java | 84 + .../ExternalAssignmentProviderCompat.java | 18 +- .../grading/api/GradeDefinition.java | 26 +- .../grading/api/GradeMappingDefinition.java | 122 + .../sakaiproject/grading/api/GradeType.java | 60 +- .../grading/api}/GradebookException.java | 10 +- .../api}/GradebookExistsException.java | 4 +- .../grading/api}/GradebookHelper.java | 24 +- .../grading/api/GradebookInformation.java | 126 + .../grading/api}/GraderPermission.java | 50 +- .../grading/api/GradingAuthz.java | 150 + .../grading/api/GradingCategoryType.java | 60 + .../grading/api/GradingConstants.java | 63 + .../grading/api/GradingEventStatus.java | 10 + .../grading/api}/GradingEvents.java | 4 +- .../grading/api/GradingPermission.java | 50 + .../grading/api/GradingPermissionService.java | 334 + .../api/GradingPersistenceManager.java | 124 + .../grading/api/GradingScaleDefinition.java | 75 + .../grading/api/GradingSecurityException.java | 34 +- .../grading/api/GradingService.java | 1191 ++++ .../api}/InvalidCategoryException.java | 2 +- .../grading/api}/InvalidGradeException.java | 10 +- .../api}/InvalidGradeItemNameException.java | 10 +- .../grading/api/PermissionDefinition.java | 39 + .../sakaiproject/grading/api}/SortType.java | 22 +- .../StaleObjectModificationException.java | 2 +- ...nmappableCourseGradeOverrideException.java | 8 +- .../api/model/AbstractGradeRecord.java | 94 + .../api/model}/AssignmentGradeRecord.java | 212 +- .../grading/api/model/Category.java | 352 ++ .../grading/api/model/Comment.java | 89 + .../grading/api/model/CourseGrade.java | 110 + .../grading/api/model/CourseGradeRecord.java | 244 + .../grading/api/model/GradableObject.java | 274 + .../grading/api/model/GradeMapping.java | 237 + .../api/model}/GradePointsMapping.java | 35 +- .../grading/api/model/Gradebook.java | 164 + .../api/model/GradebookAssignment.java | 580 ++ .../grading/api/model/GradebookProperty.java | 65 + .../grading/api/model}/GradingEvent.java | 69 +- .../grading/api/model/GradingScale.java | 121 + .../api/model}/LetterGradeMapping.java | 30 +- .../api/model/LetterGradePercentMapping.java | 137 + .../model}/LetterGradePlusMinusMapping.java | 25 +- .../api/model}/PassNotPassMapping.java | 27 +- .../grading/api/model/Permission.java | 58 + .../grading/api/model/Spreadsheet.java | 85 + .../AssignmentGradeRecordRepository.java | 23 + .../api/repository/CategoryRepository.java | 16 + .../api/repository/CommentRepository.java | 23 + .../CourseGradeRecordRepository.java | 19 + .../api/repository/CourseGradeRepository.java | 13 + .../repository/GradeMappingRepository.java | 12 + .../GradebookAssignmentRepository.java | 25 + .../GradebookPropertyRepository.java | 11 + .../api/repository/GradebookRepository.java | 14 + .../repository/GradingEventRepository.java | 16 + .../repository/GradingScaleRepository.java | 16 + .../LetterGradePercentMappingRepository.java | 14 + .../api/repository/PermissionRepository.java | 21 + gradebookng/impl/pom.xml | 124 + .../grading/impl/GradebookDefinition.java | 44 + .../grading/impl/GradingAuthzImpl.java | 685 +++ .../impl/GradingPermissionServiceImpl.java | 1230 ++++ .../impl/GradingPersistenceManagerImpl.java | 383 ++ .../grading/impl/GradingServiceImpl.java | 5444 +++++++++++++++++ .../impl}/VersionedExternalizable.java | 106 +- .../main/java/org/sakaiproject/grading/impl/] | 51 + .../AssignmentGradeRecordRepositoryImpl.java | 161 + .../repository/CategoryRepositoryImpl.java | 73 + .../repository/CommentRepositoryImpl.java | 75 + .../CourseGradeRecordRepositoryImpl.java | 100 + .../repository/CourseGradeRepositoryImpl.java | 41 + .../GradeMappingRepositoryImpl.java | 33 + .../GradebookAssignmentRepositoryImpl.java | 186 + .../GradebookPropertyRepositoryImpl.java | 26 + .../repository/GradebookRepositoryImpl.java | 41 + .../GradingEventRepositoryImpl.java | 84 + .../GradingScaleRepositoryImpl.java | 53 + ...tterGradePercentMappingRepositoryImpl.java | 41 + .../repository/PermissionRepositoryImpl.java | 148 + .../src/main/webapp/WEB-INF/components.xml | 115 + .../impl/test/GradingServiceTests.java | 693 +++ .../impl/test/GradingTestConfiguration.java | 69 + .../src/test/resources/hibernate.properties | 20 + gradebookng/pom.xml | 15 +- gradebookng/tool/pom.xml | 11 +- .../business/CourseGradeComparator.java | 8 +- .../gradebookng/business/GbCategoryType.java | 61 - .../business/GradebookNgBusinessService.java | 291 +- .../business/LetterGradeComparator.java | 2 +- .../business/model/GbCourseGrade.java | 57 - .../business/model/GbGradeComparisonItem.java | 2 +- .../business/model/GbGradeInfo.java | 2 +- .../business/model/GbGradeLog.java | 4 +- .../business/model/GbStudentGradeInfo.java | 3 +- .../business/util/CourseGradeFormatter.java | 22 +- .../business/util/EventHelper.java | 12 +- .../business/util/FormatHelper.java | 2 +- .../business/util/ImportGradesHelper.java | 2 +- .../framework/GradebookNgContextObserver.java | 69 - .../framework/GradebookNgEntityProducer.java | 37 +- .../rest/GradebookNgEntityProvider.java | 14 +- .../tool/actions/ExcuseGradeAction.java | 10 +- .../tool/actions/GradeUpdateAction.java | 16 +- .../tool/actions/MoveAssignmentAction.java | 2 +- .../tool/chart/AssignmentGradeChart.java | 20 +- .../tool/chart/CourseGradeChart.java | 14 +- .../tool/component/GbGradeTable.html | 2 +- .../tool/model/GbGradeTableData.java | 10 +- .../tool/model/GbGradebookData.java | 74 +- .../gradebookng/tool/model/GbModalWindow.java | 2 +- .../gradebookng/tool/model/GbSettings.java | 2 +- .../tool/model/GradebookUiSettings.java | 2 +- .../tool/model/ImportWizardModel.java | 2 +- .../gradebookng/tool/pages/GradebookPage.java | 18 +- .../tool/pages/PermissionsPage.java | 14 +- .../gradebookng/tool/pages/SettingsPage.java | 28 +- .../tool/panels/AddOrEditGradeItemPanel.java | 22 +- .../AddOrEditGradeItemPanelContent.java | 36 +- .../panels/AssignmentStatisticsPanel.java | 2 +- .../gradebookng/tool/panels/BasePanel.java | 4 +- .../tool/panels/BulkEditItemsPanel.java | 6 +- .../panels/CourseGradeOverrideLogPanel.java | 4 +- .../tool/panels/CourseGradeOverridePanel.java | 16 +- .../panels/CourseGradeStatisticsPanel.java | 2 +- .../tool/panels/DeleteItemPanel.java | 2 +- .../tool/panels/EditGradeCommentPanel.java | 2 +- .../tool/panels/GradeLogPanel.java | 2 +- .../tool/panels/GradeSummaryTablePanel.java | 44 +- .../InstructorGradeSummaryGradesPanel.java | 32 +- .../tool/panels/SettingsCategoryPanel.java | 44 +- .../tool/panels/SettingsGradeEntryPanel.java | 10 +- .../panels/SettingsGradeReleasePanel.java | 30 +- .../panels/SettingsGradingSchemaPanel.java | 10 +- .../panels/SortGradeItemsByCategoryPanel.java | 4 +- .../SortGradeItemsByGradeItemPanel.java | 4 +- .../StudentAssignmentStatisticsPanel.java | 2 +- .../panels/StudentCompareGradesPanel.java | 12 +- .../StudentCourseGradeStatisticsPanel.java | 8 +- .../StudentGradeSummaryGradesPanel.java | 42 +- .../panels/ToggleGradeItemsToolbarPanel.java | 2 +- .../tool/panels/UpdateUngradedItemsPanel.java | 25 +- .../tool/panels/ZeroUngradedItemsPanel.java | 2 +- .../importExport/CreateGradeItemStep.java | 2 +- .../tool/panels/importExport/ExportPanel.java | 22 +- .../GradeImportConfirmationStep.java | 18 +- .../tool/stats/CourseGradeStatistics.java | 8 +- .../business/util/TestImportGradesHelper.java | 4 +- .../src/webapp/WEB-INF/applicationContext.xml | 17 +- .../webapp/scripts/gradebook-gbgrade-table.js | 2 + kernel/kernel-test/pom.xml | 66 + .../test/SakaiTestConfiguration.java | 131 + kernel/pom.xml | 1 + .../service/Assignment2Entity.java | 10 +- .../service/ScormEntity.java | 8 +- .../webapp/WEB-INF/opt-applicationContext.xml | 4 +- lessonbuilder/tool/pom.xml | 4 +- .../service/GradebookIfc.java | 20 +- .../service/LessonBuilderEntityProducer.java | 2 +- .../tool/beans/SimplePageBean.java | 2 +- .../src/webapp/WEB-INF/applicationContext.xml | 2 +- master/pom.xml | 12 +- msgcntr/messageforums-app/pom.xml | 4 +- .../messageforums/DiscussionForumTool.java | 163 +- .../ui/MessageForumStatisticsBean.java | 129 +- msgcntr/messageforums-component-impl/pom.xml | 8 +- .../entity/TopicEntityProviderImpl.java | 11 +- .../MsgcntrTestConfiguration.java | 20 +- .../api/grading/GradebookServiceAPI.java | 6 - samigo/samigo-app/pom.xml | 4 +- .../bean/author/AssessmentSettingsBean.java | 46 +- .../PublishedAssessmentSettingsBean.java | 60 +- .../bean/author/RestoreAssessmentsBean.java | 25 +- .../assessment/ui/bean/qti/XMLImportBean.java | 7 +- .../ConfirmPublishAssessmentListener.java | 7 +- .../author/PublishAssessmentListener.java | 11 +- .../author/RemoveAssessmentListener.java | 7 +- .../RemovePublishedAssessmentListener.java | 8 +- .../author/RepublishAssessmentListener.java | 152 +- .../author/SavePublishedSettingsListener.java | 265 +- .../src/webapp/jsf/author/authorSettings.jsp | 6 +- .../webapp/jsf/author/publishedSettings.jsp | 6 +- samigo/samigo-services/pom.xml | 8 +- .../spring/integrationContext.xml | 2 +- .../facade/AssessmentFacadeQueries.java | 12 +- .../AssessmentGradingFacadeQueries.java | 7 +- .../PublishedAssessmentFacadeQueries.java | 15 +- .../helper/ifc/GradebookServiceHelper.java | 23 +- .../AssessmentGradeInfoProvider.java | 20 +- .../GradebookServiceHelperImpl.java | 136 +- .../assessment/services/GradingService.java | 12 +- .../gradebook/GradebookServiceHelper.java | 4 - .../impl/grading/GradebookServiceImpl.java | 17 - .../jsf/backingbean/LocalSectionModel.java | 21 + site-manage/datemanager/impl/pom.xml | 9 +- .../impl/DateManagerServiceImpl.java | 20 +- .../impl/src/webapp/WEB-INF/components.xml | 2 +- site-manage/site-manage-impl/impl/pom.xml | 9 +- .../impl/job/SeedSitesAndUsersJob.java | 12 +- .../impl/src/webapp/WEB-INF/components.xml | 2 +- webapi/pom.xml | 4 +- .../webapi/beans/GradeRestBean.java | 2 +- .../webapi/controllers/GradesController.java | 16 +- webservices/cxf/pom.xml | 8 +- .../webservices/AbstractWebService.java | 15 +- .../sakaiproject/webservices/Assignments.java | 636 +- .../webservices/SakaiGradebook.java | 76 +- .../cxf/src/resources/applicationContext.xml | 4 +- .../MockingAbstractWebService.java | 6 +- 325 files changed, 18320 insertions(+), 19876 deletions(-) delete mode 100644 edu-services/gradebook-service/api/pom.xml delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/Assignment.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CategoryDefinition.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CommentDefinition.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingSpreadsheetNameException.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CourseGrade.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ExternalAssignmentProvider.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradeDefinition.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradeMappingDefinition.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookArchiveService.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookConfiguration.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookExternalAssessmentService.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookFrameworkService.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookInformation.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookPermissionService.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookService.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradingEventStatus.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradingScaleDefinition.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/MultipleAssignmentSavingException.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/PermissionDefinition.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/UnknownUserException.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/tool/gradebook/facades/Authn.java delete mode 100644 edu-services/gradebook-service/api/src/java/org/sakaiproject/tool/gradebook/facades/Authz.java delete mode 100644 edu-services/gradebook-service/hibernate/pom.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Category.hbm.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Comment.hbm.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradableObject.hbm.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradeMapping.hbm.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradeRecord.hbm.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Gradebook.hbm.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradebookProperty.hbm.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradingEvent.hbm.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradingScale.hbm.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/LetterGradePercenteMapping.hbm.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Permission.hbm.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Spreadsheet.hbm.xml delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/AbstractGradeRecord.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Category.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Comment.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CommonGradeRecord.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CourseGrade.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CourseGradeRecord.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CourseGradesToSpreadsheetConverter.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradableObject.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradeMapping.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Gradebook.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradebookArchive.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradebookAssignment.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradebookProperty.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradingScale.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/LetterGradePercentMapping.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Permission.java delete mode 100644 edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Spreadsheet.java delete mode 100644 edu-services/gradebook-service/impl/pom.xml delete mode 100644 edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/BaseHibernateManager.java delete mode 100644 edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookDefinition.java delete mode 100644 edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookExternalAssessmentServiceImpl.java delete mode 100644 edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookFrameworkServiceImpl.java delete mode 100644 edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookPermissionServiceImpl.java delete mode 100644 edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookServiceHibernateImpl.java delete mode 100644 edu-services/gradebook-service/impl/src/java/org/sakaiproject/tool/gradebook/facades/sakai2impl/AuthnSakai2Impl.java delete mode 100644 edu-services/gradebook-service/impl/src/java/org/sakaiproject/tool/gradebook/facades/sakai2impl/AuthzSakai2Impl.java delete mode 100644 edu-services/gradebook-service/impl/src/java/org/sakaiproject/tool/gradebook/facades/sections/AuthzSectionsImpl.java delete mode 100644 edu-services/gradebook-service/pom.xml delete mode 100644 edu-services/gradebook-service/sakai-pack/pom.xml delete mode 100644 edu-services/gradebook-service/sakai-pack/src/resources/hsqldb/sakai_gradebook_post_schemaupdate.sql delete mode 100644 edu-services/gradebook-service/sakai-pack/src/resources/mysql/sakai_gradebook_post_schemaupdate.sql delete mode 100644 edu-services/gradebook-service/sakai-pack/src/resources/oracle/sakai_gradebook_post_schemaupdate.sql delete mode 100644 edu-services/gradebook-service/sakai-pack/src/webapp/WEB-INF/components.xml create mode 100644 gradebookng/api/pom.xml rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/AssessmentNotFoundException.java (90%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/Assignment.java rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/AssignmentHasIllegalPointsException.java (95%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryDefinition.java rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/CategoryScoreData.java (74%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/CommentDefinition.java rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/ConflictingAssignmentNameException.java (89%) rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/ConflictingCategoryNameException.java (86%) rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/ConflictingExternalIdException.java (89%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/CourseGradeTransferBean.java rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/DoubleComparator.java (58%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/ExternalAssignmentProvider.java rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/ExternalAssignmentProviderCompat.java (82%) rename edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookNotFoundException.java => gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeDefinition.java (61%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeMappingDefinition.java rename edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradingType.java => gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeType.java (54%) rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/GradebookException.java (83%) rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/GradebookExistsException.java (89%) rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/GradebookHelper.java (70%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookInformation.java rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/GraderPermission.java (56%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingAuthz.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingCategoryType.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingConstants.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingEventStatus.java rename {edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/GradingEvents.java (94%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingPermission.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingPermissionService.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingPersistenceManager.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingScaleDefinition.java rename edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookSecurityException.java => gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingSecurityException.java (59%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingService.java rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/InvalidCategoryException.java (95%) rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/InvalidGradeException.java (84%) rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/InvalidGradeItemNameException.java (79%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/PermissionDefinition.java rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/SortType.java (74%) rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/StaleObjectModificationException.java (95%) rename {edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/exception => gradebookng/api/src/main/java/org/sakaiproject/grading/api}/UnmappableCourseGradeOverrideException.java (75%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/AbstractGradeRecord.java rename {edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook => gradebookng/api/src/main/java/org/sakaiproject/grading/api/model}/AssignmentGradeRecord.java (53%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Category.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Comment.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/CourseGrade.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/CourseGradeRecord.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradableObject.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradeMapping.java rename {edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook => gradebookng/api/src/main/java/org/sakaiproject/grading/api/model}/GradePointsMapping.java (83%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Gradebook.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradebookAssignment.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradebookProperty.java rename {edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook => gradebookng/api/src/main/java/org/sakaiproject/grading/api/model}/GradingEvent.java (51%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradingScale.java rename {edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook => gradebookng/api/src/main/java/org/sakaiproject/grading/api/model}/LetterGradeMapping.java (79%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/LetterGradePercentMapping.java rename {edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook => gradebookng/api/src/main/java/org/sakaiproject/grading/api/model}/LetterGradePlusMinusMapping.java (87%) rename {edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook => gradebookng/api/src/main/java/org/sakaiproject/grading/api/model}/PassNotPassMapping.java (79%) create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Permission.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Spreadsheet.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/AssignmentGradeRecordRepository.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CategoryRepository.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CommentRepository.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CourseGradeRecordRepository.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CourseGradeRepository.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradeMappingRepository.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradebookAssignmentRepository.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradebookPropertyRepository.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradebookRepository.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradingEventRepository.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradingScaleRepository.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/LetterGradePercentMappingRepository.java create mode 100644 gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/PermissionRepository.java create mode 100644 gradebookng/impl/pom.xml create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradebookDefinition.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingAuthzImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingPermissionServiceImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingPersistenceManagerImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingServiceImpl.java rename {edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook => gradebookng/impl/src/main/java/org/sakaiproject/grading/impl}/VersionedExternalizable.java (53%) create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/] create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/AssignmentGradeRecordRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CategoryRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CommentRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CourseGradeRecordRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CourseGradeRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradeMappingRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradebookAssignmentRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradebookPropertyRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradebookRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradingEventRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradingScaleRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/LetterGradePercentMappingRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/PermissionRepositoryImpl.java create mode 100644 gradebookng/impl/src/main/webapp/WEB-INF/components.xml create mode 100644 gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java create mode 100644 gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingTestConfiguration.java create mode 100644 gradebookng/impl/src/test/resources/hibernate.properties delete mode 100644 gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GbCategoryType.java delete mode 100644 gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbCourseGrade.java delete mode 100644 gradebookng/tool/src/java/org/sakaiproject/gradebookng/framework/GradebookNgContextObserver.java create mode 100644 kernel/kernel-test/pom.xml create mode 100644 kernel/kernel-test/src/main/java/org/sakaiproject/test/SakaiTestConfiguration.java diff --git a/assignment/impl/pom.xml b/assignment/impl/pom.xml index 3fef46216d4b..2d26585fa1c5 100644 --- a/assignment/impl/pom.xml +++ b/assignment/impl/pom.xml @@ -45,8 +45,12 @@ sakai-assignment-api - org.sakaiproject.edu-services.gradebook - gradebook-service-api + org.sakaiproject.grading + sakai-grading-api + + + org.sakaiproject.edu-services.sections + sections-api org.sakaiproject.announcement diff --git a/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentEventObserver.java b/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentEventObserver.java index 738fe71c7b4e..8eac2c3e162c 100644 --- a/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentEventObserver.java +++ b/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentEventObserver.java @@ -30,8 +30,8 @@ import org.sakaiproject.event.api.EventTrackingService; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.PermissionException; -import org.sakaiproject.service.gradebook.shared.AssessmentNotFoundException; -import org.sakaiproject.service.gradebook.shared.GradebookService; +import org.sakaiproject.grading.api.AssessmentNotFoundException; +import org.sakaiproject.grading.api.GradingService; import org.sakaiproject.user.api.User; import org.sakaiproject.user.api.UserDirectoryService; import org.sakaiproject.user.api.UserNotDefinedException; @@ -44,7 +44,7 @@ public class AssignmentEventObserver implements Observer { @Setter private AssignmentService assignmentService; @Setter private EventTrackingService eventTrackingService; - @Setter private GradebookService gradebookService; + @Setter private GradingService gradingService; @Setter private UserDirectoryService userDirectoryService; public void init() { @@ -75,7 +75,7 @@ public void update(Observable o, Object arg) { Optional submission = Optional.empty(); // Assignments stores the gradebook item name and not the id :(, so we need to look it up try { - org.sakaiproject.service.gradebook.shared.Assignment gradebookAssignment = gradebookService.getAssignmentByNameOrId(event.getContext(), itemId); + org.sakaiproject.grading.api.Assignment gradebookAssignment = gradingService.getAssignmentByNameOrId(event.getContext(), itemId); assignment = Optional.ofNullable(assignmentService.getAssignmentForGradebookLink(event.getContext(), gradebookAssignment.getName())); if (assignment.isPresent()) { final Assignment a = assignment.get(); diff --git a/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentGradeInfoProvider.java b/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentGradeInfoProvider.java index 41c500321575..c3c9b699eb18 100644 --- a/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentGradeInfoProvider.java +++ b/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentGradeInfoProvider.java @@ -36,26 +36,29 @@ import org.sakaiproject.authz.api.AuthzGroupService; import org.sakaiproject.authz.api.SecurityAdvisor; import org.sakaiproject.authz.api.SecurityService; +import org.sakaiproject.grading.api.ExternalAssignmentProvider; +import org.sakaiproject.grading.api.ExternalAssignmentProviderCompat; +import org.sakaiproject.grading.api.GradingService; import org.sakaiproject.entity.api.Entity; import org.sakaiproject.entity.api.EntityManager; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.PermissionException; -import org.sakaiproject.service.gradebook.shared.ExternalAssignmentProvider; -import org.sakaiproject.service.gradebook.shared.ExternalAssignmentProviderCompat; -import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService; import org.sakaiproject.site.api.SiteService; import org.sakaiproject.tool.api.SessionManager; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; @Slf4j +@Getter +@Setter public class AssignmentGradeInfoProvider implements ExternalAssignmentProvider, ExternalAssignmentProviderCompat { - // Sakai Service Beans private AssignmentService assignmentService; private SiteService siteService; - private GradebookExternalAssessmentService geaService; + private GradingService gradingService; private AuthzGroupService authzGroupService; private EntityManager entityManager; private SecurityService securityService; @@ -63,12 +66,12 @@ public class AssignmentGradeInfoProvider implements ExternalAssignmentProvider, public void init() { log.info("INIT and register AssignmentGradeInfoProvider"); - geaService.registerExternalAssignmentProvider(this); + gradingService.registerExternalAssignmentProvider(this); } public void destroy() { log.info("DESTROY and unregister AssignmentGradeInfoProvider"); - geaService.unregisterExternalAssignmentProvider(getAppKey()); + gradingService.unregisterExternalAssignmentProvider(getAppKey()); } public String getAppKey() { @@ -171,58 +174,5 @@ public Map> getAllExternalAssignments(String gradebookUid, } return allExternals; } - - public void setGradebookExternalAssessmentService(GradebookExternalAssessmentService geaService) { - this.geaService = geaService; - } - - public GradebookExternalAssessmentService getGradebookExternalAssessmentService() { - return geaService; - } - - public void setAssignmentService(AssignmentService assignmentService) { - this.assignmentService = assignmentService; - } - - public AssignmentService getAssignmentService() { - return assignmentService; - } - - public void setSiteService(SiteService siteService) { - this.siteService = siteService; - } - - public SiteService getSiteService() { - return siteService; - } - - public void setAuthzGroupService(AuthzGroupService authzGroupService) { - this.authzGroupService = authzGroupService; - } - - public AuthzGroupService getAuthzGroupService() { - return authzGroupService; - } - - public void setEntityManager(EntityManager entityManager) { - this.entityManager = entityManager; - } - - public EntityManager getEntityManager() { - return entityManager; - } - - public void setSecurityService(SecurityService securityService) { - this.securityService = securityService; - } - - public SecurityService getSecurityService() { - return securityService; - } - - public void setSessionManager(SessionManager sessionManager) { - this.sessionManager = sessionManager; - } - } diff --git a/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentServiceImpl.java b/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentServiceImpl.java index 38c5d5515d95..7509c26af36c 100644 --- a/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentServiceImpl.java +++ b/assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentServiceImpl.java @@ -141,6 +141,10 @@ import org.sakaiproject.exception.PermissionException; import org.sakaiproject.exception.ServerOverloadException; import org.sakaiproject.exception.TypeException; +import org.sakaiproject.grading.api.AssessmentNotFoundException; +import org.sakaiproject.grading.api.CategoryDefinition; +import org.sakaiproject.grading.api.GradebookInformation; +import org.sakaiproject.grading.api.GradingService; import org.sakaiproject.messaging.api.Message; import org.sakaiproject.messaging.api.MessageMedium; import org.sakaiproject.messaging.api.UserMessagingService; @@ -148,13 +152,6 @@ import org.sakaiproject.rubrics.logic.RubricsService; import org.sakaiproject.rubrics.logic.model.ToolItemRubricAssociation; import org.sakaiproject.search.api.SearchService; -import org.sakaiproject.service.gradebook.shared.AssessmentNotFoundException; -import org.sakaiproject.service.gradebook.shared.CategoryDefinition; -import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService; -import org.sakaiproject.service.gradebook.shared.GradebookFrameworkService; -import org.sakaiproject.service.gradebook.shared.GradebookInformation; -import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException; -import org.sakaiproject.service.gradebook.shared.GradebookService; import org.sakaiproject.site.api.Group; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SiteService; @@ -225,10 +222,8 @@ public class AssignmentServiceImpl implements AssignmentService, EntityTransferr @Setter private EventTrackingService eventTrackingService; @Setter private FormattedText formattedText; @Setter private FunctionManager functionManager; - @Setter private GradebookExternalAssessmentService gradebookExternalAssessmentService; - @Setter private GradebookFrameworkService gradebookFrameworkService; - @Setter private GradebookService gradebookService; @Setter private GradeSheetExporter gradeSheetExporter; + @Setter private GradingService gradingService; @Setter private LearningResourceStoreService learningResourceStoreService; @Setter private LinkMigrationHelper linkMigrationHelper; @Setter private TransactionTemplate transactionTemplate; @@ -326,9 +321,8 @@ public String archive(String siteId, Document doc, Stack stack, String stack.peek().appendChild(element); stack.push(element); - Collection assignments = getAssignmentsForContext(siteId); int assignmentsArchived = 0; - for (Assignment assignment : assignments) { + for (Assignment assignment : getAssignmentsForContext(siteId)) { String xml = assignmentRepository.toXML(assignment); try { @@ -1701,8 +1695,7 @@ public Map> getSubmittableAssignmentsForContext(String } // TODO this called getAccessibleAssignments need to implement - Collection assignments = getAssignmentsForContext(context); - for (Assignment assignment : assignments) { + for (Assignment assignment : getAssignmentsForContext(context)) { Set userIds = new HashSet<>(); if (assignment.getTypeOfAccess() == GROUP) { for (String groupRef : assignment.getGroups()) { @@ -3213,14 +3206,8 @@ private void removeAssociatedGradebookItem(Assignment assignment) { String context = assignment.getContext(); String associatedGradebookAssignment = assignment.getProperties().get(AssignmentConstants.PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT); if (StringUtils.isNotBlank(associatedGradebookAssignment)) { - try { - boolean isExternalAssignmentDefined = gradebookExternalAssessmentService.isExternalAssignmentDefined(context, associatedGradebookAssignment); - if (isExternalAssignmentDefined) { - gradebookExternalAssessmentService.removeExternalAssessment(context, associatedGradebookAssignment); - } - } catch (GradebookNotFoundException gnfe) { - // this may occur if no gradebook tool exists in the site - log.debug("Attempted to remove associated gradebook item, {}", gnfe.getMessage()); + if (gradingService.isExternalAssignmentDefined(context, associatedGradebookAssignment)) { + gradingService.removeExternalAssignment(context, associatedGradebookAssignment); } } } @@ -4076,8 +4063,7 @@ public Optional> getTransferOptions() { @Transactional public void updateEntityReferences(String toContext, Map transversalMap) { if (transversalMap != null && !transversalMap.isEmpty()) { - Collection assignments = getAssignmentsForContext(toContext); - for (Assignment assignment : assignments) { + for (Assignment assignment : getAssignmentsForContext(toContext)) { try { String msgBody = assignment.getInstructions(); StringBuffer msgBodyPreMigrate = new StringBuffer(msgBody); @@ -4111,9 +4097,8 @@ public void updateEntityReferences(String toContext, Map transve public Map transferCopyEntities(String fromContext, String toContext, List ids, List transferOptions) { Map transversalMap = new HashMap<>(); - Collection assignments = getAssignmentsForContext(fromContext); - for (Assignment oAssignment : assignments) { + for (Assignment oAssignment : getAssignmentsForContext(fromContext)) { String oAssignmentId = oAssignment.getId(); String nAssignmentId = null; @@ -4282,28 +4267,25 @@ public Map transferCopyEntities(String fromContext, String toCon nProperties.put(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK, GRADEBOOK_INTEGRATION_NO); } else { // see if the old assignment's associated gradebook item is an internal gradebook entry or externally defined - boolean isExternalAssignmentDefined = gradebookExternalAssessmentService.isExternalAssignmentDefined(oAssignment.getContext(), associatedGradebookAssignment); + boolean isExternalAssignmentDefined = gradingService.isExternalAssignmentDefined(oAssignment.getContext(), associatedGradebookAssignment); if (isExternalAssignmentDefined) { if (!nAssignment.getDraft()) { String gbUid = nAssignment.getContext(); - if (!gradebookFrameworkService.isGradebookDefined(gbUid)) { - gradebookFrameworkService.addGradebook(gbUid, gbUid); - } // This assignment has been published, make sure the associated gb item is available - org.sakaiproject.service.gradebook.shared.Assignment gbAssignment - = gradebookService.getAssignmentByNameOrId( + org.sakaiproject.grading.api.Assignment gbAssignment + = gradingService.getAssignmentByNameOrId( nAssignment.getContext(), associatedGradebookAssignment); if (gbAssignment == null) { // The associated gb item hasn't been created here yet. - gbAssignment = gradebookService.getExternalAssignment( + gbAssignment = gradingService.getExternalAssignment( oAssignment.getContext(), associatedGradebookAssignment); Optional categoryId = createCategoryForGbAssignmentIfNecessary( gbAssignment, oAssignment.getContext(), nAssignment.getContext()); - gradebookExternalAssessmentService.addExternalAssessment(nAssignment.getContext() + gradingService.addExternalAssessment(nAssignment.getContext() , nAssignmentRef, null, nAssignment.getTitle() , nAssignment.getMaxGradePoint() / (double) nAssignment.getScaleFactor() , Date.from(nAssignment.getDueDate()), this.getToolTitle() @@ -4321,14 +4303,14 @@ public Map transferCopyEntities(String fromContext, String toCon } else { // If this is an internal gradebook item then it should be associated with the assignment try { - org.sakaiproject.service.gradebook.shared.Assignment gbAssignment - = gradebookService.getAssignmentByNameOrId( + org.sakaiproject.grading.api.Assignment gbAssignment + = gradingService.getAssignmentByNameOrId( nAssignment.getContext(), associatedGradebookAssignment); if (gbAssignment == null) { if (!nAssignment.getDraft()) { // The target gb item doesn't exist and we're in publish mode, so copy it over. - gbAssignment = gradebookService.getAssignmentByNameOrId( + gbAssignment = gradingService.getAssignmentByNameOrId( oAssignment.getContext(), associatedGradebookAssignment); gbAssignment.setId(null); @@ -4339,7 +4321,7 @@ public Map transferCopyEntities(String fromContext, String toCon gbAssignment.setCategoryId(categoryId.get()); } - gradebookService.addAssignment(nAssignment.getContext(), gbAssignment); + gradingService.addAssignment(nAssignment.getContext(), gbAssignment); nProperties.put(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT, gbAssignment.getName()); } else { nProperties.put(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT, AssignmentReferenceReckoner.reckoner().assignment(nAssignment).reckon().getReference()); @@ -4472,8 +4454,7 @@ public Map transferCopyEntities(String fromContext, String toCon try { if (cleanup) { - Collection assignments = getAssignmentsForContext(toContext); - for (Assignment assignment : assignments) { + for (Assignment assignment : getAssignmentsForContext(toContext)) { String assignmentId = assignment.getId(); try { @@ -4894,14 +4875,14 @@ private String getReviewError(ContentReviewResult reviewResult){ } private Optional createCategoryForGbAssignmentIfNecessary( - org.sakaiproject.service.gradebook.shared.Assignment gbAssignment, String fromGradebookId + org.sakaiproject.grading.api.Assignment gbAssignment, String fromGradebookId , String toGradebookId) { String categoryName = gbAssignment.getCategoryName(); if (!StringUtils.isBlank(categoryName)) { List toCategoryDefinitions - = gradebookService.getCategoryDefinitions(toGradebookId); + = gradingService.getCategoryDefinitions(toGradebookId); if (toCategoryDefinitions == null) { toCategoryDefinitions = new ArrayList<>(); } @@ -4909,32 +4890,32 @@ private Optional createCategoryForGbAssignmentIfNecessary( if (!toCategoryDefinitions.stream().anyMatch(cd -> cd.getName().equals(categoryName))) { // The category doesn't exist yet CategoryDefinition fromCategoryDefinition - = gradebookService.getCategoryDefinitions(fromGradebookId) + = gradingService.getCategoryDefinitions(fromGradebookId) .stream() .filter(cd -> cd.getName().equals(categoryName)) .findAny().get(); CategoryDefinition toCategoryDefinition = new CategoryDefinition(); toCategoryDefinition.setName(fromCategoryDefinition.getName()); toCategoryDefinition.setAssignmentList( - Arrays.asList(new org.sakaiproject.service.gradebook.shared.Assignment[] { gbAssignment })); + Arrays.asList(new org.sakaiproject.grading.api.Assignment[] { gbAssignment })); toCategoryDefinition.setExtraCredit(fromCategoryDefinition.getExtraCredit()); toCategoryDefinition.setWeight(fromCategoryDefinition.getWeight()); toCategoryDefinition.setDropHighest(fromCategoryDefinition.getDropHighest()); toCategoryDefinition.setDropLowest(fromCategoryDefinition.getDropLowest()); toCategoryDefinition.setKeepHighest(fromCategoryDefinition.getKeepHighest()); - GradebookInformation toGbInformation = gradebookService.getGradebookInformation(toGradebookId); - GradebookInformation fromGbInformation = gradebookService.getGradebookInformation(fromGradebookId); + GradebookInformation toGbInformation = gradingService.getGradebookInformation(toGradebookId); + GradebookInformation fromGbInformation = gradingService.getGradebookInformation(fromGradebookId); toGbInformation.setCategoryType(fromGbInformation.getCategoryType()); List categories = toGbInformation.getCategories(); categories.add(toCategoryDefinition); - gradebookService.updateGradebookSettings(toGradebookId, toGbInformation); + gradingService.updateGradebookSettings(toGradebookId, toGbInformation); } // A new category may have been added in the previous block. Pull them again, just to be sure. This will // ensure that any upstream caching is refreshed, too. Optional optional - = gradebookService.getCategoryDefinitions(toGradebookId) + = gradingService.getCategoryDefinitions(toGradebookId) .stream() .filter(cd -> cd.getName().equals(categoryName)).findAny(); if (optional.isPresent()) { diff --git a/assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentServiceTest.java b/assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentServiceTest.java index 19964cbd1096..94a12df73bfc 100644 --- a/assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentServiceTest.java +++ b/assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentServiceTest.java @@ -80,7 +80,7 @@ import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.IdUsedException; import org.sakaiproject.exception.PermissionException; -import org.sakaiproject.service.gradebook.shared.GradebookService; +import org.sakaiproject.grading.api.GradingService; import org.sakaiproject.site.api.Group; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SiteService; @@ -119,7 +119,7 @@ public class AssignmentServiceTest extends AbstractTransactionalJUnit4SpringCont @Autowired private AuthzGroupService authzGroupService; @Autowired private EntityManager entityManager; @Autowired private FormattedText formattedText; - @Autowired private GradebookService gradebookService; + @Autowired private GradingService gradingService; @Autowired private SecurityService securityService; @Autowired private SessionManager sessionManager; @Autowired private ServerConfigurationService serverConfigurationService; @@ -1268,9 +1268,9 @@ public void gradeUpdateFromAssignmentEventObeserver() { Event event = createMockEvent(context, gradebookId, itemId, submitterId, "25", instructorId); - org.sakaiproject.service.gradebook.shared.Assignment gradebookAssignment = mock(org.sakaiproject.service.gradebook.shared.Assignment.class); + org.sakaiproject.grading.api.Assignment gradebookAssignment = mock(org.sakaiproject.grading.api.Assignment.class); when(gradebookAssignment.getName()).thenReturn(itemId.toString()); - when(gradebookService.getAssignmentByNameOrId(context, itemId.toString())).thenReturn(gradebookAssignment); + when(gradingService.getAssignmentByNameOrId(context, itemId.toString())).thenReturn(gradebookAssignment); User mockUser = mock(User.class); when(mockUser.getId()).thenReturn(submitterId); when(userDirectoryService.getUser(submitterId)).thenReturn(mockUser); diff --git a/assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentTestConfiguration.java b/assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentTestConfiguration.java index 6e3e43378637..031b19e896a1 100644 --- a/assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentTestConfiguration.java +++ b/assignment/impl/src/test/org/sakaiproject/assignment/impl/AssignmentTestConfiguration.java @@ -49,15 +49,13 @@ import org.sakaiproject.entitybroker.DeveloperHelperService; import org.sakaiproject.event.api.EventTrackingService; import org.sakaiproject.event.api.LearningResourceStoreService; +import org.sakaiproject.grading.api.GradingService; import org.sakaiproject.hibernate.AssignableUUIDGenerator; import org.sakaiproject.messaging.api.MessagingService; import org.sakaiproject.messaging.api.UserMessagingService; import org.sakaiproject.rubrics.logic.RubricsService; import org.sakaiproject.search.api.SearchIndexBuilder; import org.sakaiproject.search.api.SearchService; -import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService; -import org.sakaiproject.service.gradebook.shared.GradebookFrameworkService; -import org.sakaiproject.service.gradebook.shared.GradebookService; import org.sakaiproject.site.api.SiteService; import org.sakaiproject.springframework.orm.hibernate.AdditionalHibernateMappings; import org.sakaiproject.tasks.api.TaskService; @@ -208,19 +206,9 @@ public FunctionManager functionManager() { return mock(FunctionManager.class); } - @Bean(name = "org_sakaiproject_service_gradebook_GradebookExternalAssessmentService") - public GradebookExternalAssessmentService gradebookExternalAssessmentService() { - return mock(GradebookExternalAssessmentService.class); - } - - @Bean(name = "org.sakaiproject.service.gradebook.GradebookFrameworkService") - public GradebookFrameworkService gradebookFrameworkService() { - return mock(GradebookFrameworkService.class); - } - - @Bean(name = "org.sakaiproject.service.gradebook.GradebookService") - public GradebookService gradebookService() { - return mock(GradebookService.class); + @Bean(name = "org.sakaiproject.grading.api.GradingService") + public GradingService gradingService() { + return mock(GradingService.class); } @Bean(name = "org.sakaiproject.assignment.impl.GradeSheetExporter") diff --git a/assignment/impl/src/webapp/WEB-INF/components.xml b/assignment/impl/src/webapp/WEB-INF/components.xml index c3c1e8931757..2c1efe13ff86 100644 --- a/assignment/impl/src/webapp/WEB-INF/components.xml +++ b/assignment/impl/src/webapp/WEB-INF/components.xml @@ -33,10 +33,8 @@ - - - + @@ -171,7 +169,7 @@ class="org.sakaiproject.assignment.impl.AssignmentGradeInfoProvider" lazy-init="false" init-method="init" destroy-method="destroy"> - + @@ -294,7 +292,7 @@ destroy-method="destroy"> - + diff --git a/assignment/tool/pom.xml b/assignment/tool/pom.xml index 0506d48e8173..c96bbc9ee473 100644 --- a/assignment/tool/pom.xml +++ b/assignment/tool/pom.xml @@ -53,8 +53,8 @@ sakai-calendar-util - org.sakaiproject.edu-services.gradebook - gradebook-service-api + org.sakaiproject.grading + sakai-grading-api org.sakaiproject.rubrics diff --git a/assignment/tool/src/java/org/sakaiproject/assignment/entityproviders/AssignmentEntityProvider.java b/assignment/tool/src/java/org/sakaiproject/assignment/entityproviders/AssignmentEntityProvider.java index fbec1ece92a0..42618239bfd2 100644 --- a/assignment/tool/src/java/org/sakaiproject/assignment/entityproviders/AssignmentEntityProvider.java +++ b/assignment/tool/src/java/org/sakaiproject/assignment/entityproviders/AssignmentEntityProvider.java @@ -63,9 +63,8 @@ import org.sakaiproject.entitybroker.util.AbstractEntityProvider; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.PermissionException; +import org.sakaiproject.grading.api.GradingService; import org.sakaiproject.rubrics.logic.RubricsConstants; -import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService; -import org.sakaiproject.service.gradebook.shared.GradebookService; import org.sakaiproject.site.api.Group; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SiteService; @@ -99,8 +98,7 @@ public class AssignmentEntityProvider extends AbstractEntityProvider implements private SessionManager sessionManager; private SiteService siteService; private AssignmentSupplementItemService assignmentSupplementItemService; - private GradebookService gradebookService; - private GradebookExternalAssessmentService gradebookExternalService; + private GradingService gradingService; private ServerConfigurationService serverConfigurationService; private UserDirectoryService userDirectoryService; private UserTimeService userTimeService; @@ -1400,31 +1398,29 @@ public SimpleAssignment(Assignment a) { this.anonymousGrading = assignmentService.assignmentUsesAnonymousGrading(a); String gradebookAssignmentProp = a.getProperties().get(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT); - if (gradebookService.isGradebookDefined(a.getContext())) { - if (StringUtils.isNotBlank(gradebookAssignmentProp)) { - // try to get internal gradebook assignment first - org.sakaiproject.service.gradebook.shared.Assignment gAssignment = gradebookService.getAssignment(a.getContext(), gradebookAssignmentProp); - if (gAssignment != null) { - // linked Gradebook item is internal - this.gradebookItemId = gAssignment.getId(); - this.gradebookItemName = gAssignment.getName(); - } else { - // If the linked assignment is not internal to Gradebook, try the external assignment service - // However, there is no API available in GradebookExternalAssessmentService of getExternalAssignment() - // We will first check whether the external assignment is defined, and then get it through GradebookService - boolean isExternalAssignmentDefined = gradebookExternalService.isExternalAssignmentDefined(a.getContext(), gradebookAssignmentProp); - if (isExternalAssignmentDefined) { - // since the gradebook item is externally defined, the item is named after the external object's title - gAssignment = gradebookService.getAssignment(a.getContext(), a.getTitle()); - if (gAssignment != null) { - this.gradebookItemId = gAssignment.getId(); - this.gradebookItemName = gAssignment.getName(); - } + if (StringUtils.isNotBlank(gradebookAssignmentProp)) { + // try to get internal gradebook assignment first + org.sakaiproject.grading.api.Assignment gAssignment = gradingService.getAssignment(a.getContext(), gradebookAssignmentProp); + if (gAssignment != null) { + // linked Gradebook item is internal + this.gradebookItemId = gAssignment.getId(); + this.gradebookItemName = gAssignment.getName(); + } else { + // If the linked assignment is not internal to Gradebook, try the external assignment service + // However, there is no API available in GradebookExternalAssessmentService of getExternalAssignment() + // We will first check whether the external assignment is defined, and then get it through GradingService + boolean isExternalAssignmentDefined = gradingService.isExternalAssignmentDefined(a.getContext(), gradebookAssignmentProp); + if (isExternalAssignmentDefined) { + // since the gradebook item is externally defined, the item is named after the external object's title + gAssignment = gradingService.getAssignment(a.getContext(), a.getTitle()); + if (gAssignment != null) { + this.gradebookItemId = gAssignment.getId(); + this.gradebookItemName = gAssignment.getName(); } } - } else { - log.warn("The property \"prop_new_assignment_add_to_gradebook\" is null for the assignment feed"); } + } else { + log.warn("The property \"prop_new_assignment_add_to_gradebook\" is null for the assignment feed"); } this.attachments = a.getAttachments().stream().map(att -> { diff --git a/assignment/tool/src/java/org/sakaiproject/assignment/tool/AssignmentAction.java b/assignment/tool/src/java/org/sakaiproject/assignment/tool/AssignmentAction.java index 221382fdfa20..b656855eb61c 100644 --- a/assignment/tool/src/java/org/sakaiproject/assignment/tool/AssignmentAction.java +++ b/assignment/tool/src/java/org/sakaiproject/assignment/tool/AssignmentAction.java @@ -210,6 +210,8 @@ import org.sakaiproject.exception.PermissionException; import org.sakaiproject.exception.SakaiException; import org.sakaiproject.exception.ServerOverloadException; +import org.sakaiproject.grading.api.CategoryDefinition; +import org.sakaiproject.grading.api.GradingService; import org.sakaiproject.javax.PagingPosition; import org.sakaiproject.message.api.MessageHeader; import org.sakaiproject.rubrics.logic.RubricsConstants; @@ -217,12 +219,8 @@ import org.sakaiproject.scoringservice.api.ScoringAgent; import org.sakaiproject.scoringservice.api.ScoringComponent; import org.sakaiproject.scoringservice.api.ScoringService; -import org.sakaiproject.service.gradebook.shared.AssessmentNotFoundException; -import org.sakaiproject.service.gradebook.shared.AssignmentHasIllegalPointsException; -import org.sakaiproject.service.gradebook.shared.CategoryDefinition; -import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService; -import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException; -import org.sakaiproject.service.gradebook.shared.GradebookService; +import org.sakaiproject.grading.api.AssessmentNotFoundException; +import org.sakaiproject.grading.api.AssignmentHasIllegalPointsException; import org.sakaiproject.site.api.Group; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SiteService; @@ -1128,8 +1126,7 @@ public class AssignmentAction extends PagedResourceActionII { private EventTrackingService eventTrackingService; private FileConversionService fileConversionService; private FormattedText formattedText; - private GradebookService gradebookService; - private GradebookExternalAssessmentService gradebookExternalAssessmentService; + private GradingService gradingService; private LearningResourceStoreService learningResourceStoreService; private NotificationService notificationService; private PreferencesService preferencesService; @@ -1173,8 +1170,7 @@ public AssignmentAction() { eventTrackingService = ComponentManager.get(EventTrackingService.class); fileConversionService = ComponentManager.get(FileConversionService.class); formattedText = ComponentManager.get(FormattedText.class); - gradebookExternalAssessmentService = (GradebookExternalAssessmentService) ComponentManager.get("org.sakaiproject.service.gradebook.GradebookExternalAssessmentService"); - gradebookService = (GradebookService) ComponentManager.get("org.sakaiproject.service.gradebook.GradebookService"); + gradingService = (GradingService) ComponentManager.get("org.sakaiproject.grading.api.GradingService"); learningResourceStoreService = ComponentManager.get(LearningResourceStoreService.class); notificationService = ComponentManager.get(NotificationService.class); preferencesService = ComponentManager.get(PreferencesService.class); @@ -3220,8 +3216,7 @@ protected void setAssignmentFormContext(SessionState state, Context context) { assignment_extension_option_into_context(context, state); // get all available assignments from Gradebook tool except for those created fromcategoryTable - boolean gradebookExists = isGradebookDefined(); - if (gradebookExists) { + if (canGrade()) { String gradebookUid = toolManager.getCurrentPlacement().getContext(); // how many gradebook assignment have been integrated with Assignment tool already @@ -3231,17 +3226,15 @@ protected void setAssignmentFormContext(SessionState state, Context context) { state.setAttribute(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK, GRADEBOOK_INTEGRATION_NO); } - context.put("withGradebook", Boolean.TRUE); - - // offer the gradebook integration choice only in the Assignments with Grading tool - boolean withGrade = (Boolean) state.getAttribute(WITH_GRADES); - if (withGrade) { - context.put("name_SendToGradebook", NEW_ASSIGNMENT_SEND_TO_GRADEBOOK); - context.put("name_Addtogradebook", NEW_ASSIGNMENT_ADD_TO_GRADEBOOK); - context.put("name_AssociateGradebookAssignment", PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT); - } + context.put("name_SendToGradebook", NEW_ASSIGNMENT_SEND_TO_GRADEBOOK); + context.put("name_Addtogradebook", NEW_ASSIGNMENT_ADD_TO_GRADEBOOK); + context.put("name_AssociateGradebookAssignment", PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT); context.put("gradebookChoice", state.getAttribute(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK)); + if (state.getAttribute(EDIT_ASSIGNMENT_ID) == null) { + // This is a new assignment. Pick add new item to gradebook radio option. + context.put("gradebookChoice", GRADEBOOK_INTEGRATION_ADD); + } context.put("gradebookChoice_no", GRADEBOOK_INTEGRATION_NO); context.put("gradebookChoice_add", GRADEBOOK_INTEGRATION_ADD); context.put("gradebookChoice_associate", GRADEBOOK_INTEGRATION_ASSOCIATE); @@ -3264,7 +3257,7 @@ protected void setAssignmentFormContext(SessionState state, Context context) { } else { // check internally maintained by gradebook try { - if (gradebookService.getAssignmentByNameOrId(gradebookUid, associateGradebookAssignment) != null) { + if (gradingService.getAssignmentByNameOrId(gradebookUid, associateGradebookAssignment) != null) { valid = true; } } catch (AssessmentNotFoundException anfe) { @@ -3435,13 +3428,12 @@ private String getContentReviewAcceptedFileTypesMessage() { private void currentAssignmentGradebookIntegrationIntoContext(Context context, SessionState state, String gradebookUid, String aTitle) { String contextString = (String) state.getAttribute(STATE_CONTEXT_STRING); // get all assignment - Collection assignments = assignmentService.getAssignmentsForContext(contextString); HashMap gAssignmentIdTitles = new HashMap<>(); HashMap gradebookAssignmentsSelectedDisabled = new HashMap<>(); HashMap gradebookAssignmentsLabel = new HashMap<>(); - for (Assignment a : assignments) { + for (Assignment a : assignmentService.getAssignmentsForContext(contextString)) { String gradebookItem = a.getProperties().get(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT); if (StringUtils.isNotBlank(gradebookItem)) { String associatedAssignmentTitles = ""; @@ -3459,52 +3451,46 @@ private void currentAssignmentGradebookIntegrationIntoContext(Context context, S } // get all assignments in Gradebook - try { - List gradebookAssignments = gradebookService.getAssignments(gradebookUid); - - // filtering out those from Samigo - for (Iterator i = gradebookAssignments.iterator(); i.hasNext(); ) { - org.sakaiproject.service.gradebook.shared.Assignment gAssignment = (org.sakaiproject.service.gradebook.shared.Assignment) i.next(); - if (!gAssignment.isExternallyMaintained() || gAssignment.isExternallyMaintained() && gAssignment.getExternalAppName().equals(assignmentService.getToolTitle())) { - - // gradebook item has been associated or not - String gaId = gAssignment.isExternallyMaintained() ? gAssignment.getExternalId() : gAssignment.getName(); - String status = ""; - if (gAssignmentIdTitles.containsKey(gaId)) { - String assignmentTitle = gAssignmentIdTitles.get(gaId); - if (aTitle != null && aTitle.equals(assignmentTitle)) { - // this gradebook item is associated with current assignment, make it selected - status = "selected"; - } - } - - // check with the state variable - if (StringUtils.equals((String) state.getAttribute(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT), gaId)) { + List gradebookAssignments = gradingService.getAssignments(gradebookUid); + + // filtering out those from Samigo + for (Iterator i = gradebookAssignments.iterator(); i.hasNext(); ) { + org.sakaiproject.grading.api.Assignment gAssignment = (org.sakaiproject.grading.api.Assignment) i.next(); + if (!gAssignment.getExternallyMaintained() || gAssignment.getExternallyMaintained() && gAssignment.getExternalAppName().equals(assignmentService.getToolTitle())) { + + // gradebook item has been associated or not + String gaId = gAssignment.getExternallyMaintained() ? gAssignment.getExternalId() : gAssignment.getName(); + String status = ""; + if (gAssignmentIdTitles.containsKey(gaId)) { + String assignmentTitle = gAssignmentIdTitles.get(gaId); + if (aTitle != null && aTitle.equals(assignmentTitle)) { + // this gradebook item is associated with current assignment, make it selected status = "selected"; } + } - gradebookAssignmentsSelectedDisabled.put(formattedText.escapeHtml(gaId), status); - - // gradebook assignment label - String label = gAssignment.getName(); - if (gAssignmentIdTitles.containsKey(gaId)) { - label += " ( " + rb.getFormattedMessage("usedGradebookAssignment", new Object[]{gAssignmentIdTitles.get(gaId)}) + " )"; - } - gradebookAssignmentsLabel.put(formattedText.escapeHtml(gaId), label); + // check with the state variable + if (StringUtils.equals((String) state.getAttribute(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT), gaId)) { + status = "selected"; } - } - // Items sorted by name - gradebookAssignmentsLabel = gradebookAssignmentsLabel.entrySet().stream() - .sorted(Entry.comparingByValue()) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue, - (e1, e2) -> e1, LinkedHashMap::new)); + gradebookAssignmentsSelectedDisabled.put(formattedText.escapeHtml(gaId), status); - } catch (GradebookNotFoundException e) { - // exception - log.debug(this + ":currentAssignmentGradebookIntegrationIntoContext " + rb.getFormattedMessage("addtogradebook.alertMessage", new Object[]{e.getMessage()})); + // gradebook assignment label + String label = gAssignment.getName(); + if (gAssignmentIdTitles.containsKey(gaId)) { + label += " ( " + rb.getFormattedMessage("usedGradebookAssignment", new Object[]{gAssignmentIdTitles.get(gaId)}) + " )"; + } + gradebookAssignmentsLabel.put(formattedText.escapeHtml(gaId), label); + } } + // Items sorted by name + gradebookAssignmentsLabel = gradebookAssignmentsLabel.entrySet().stream() + .sorted(Entry.comparingByValue()) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue, + (e1, e2) -> e1, LinkedHashMap::new)); + context.put("gradebookAssignmentsSelectedDisabled", gradebookAssignmentsSelectedDisabled); context.put("gradebookAssignmentsLabel", gradebookAssignmentsLabel); @@ -3513,7 +3499,7 @@ private void currentAssignmentGradebookIntegrationIntoContext(Context context, S } private void putGradebookCategoryInfoIntoContext(SessionState state, Context context) { - Map categoryTable = categoryTable(); + Map categoryTable = getCategoryTable(); if (categoryTable != null) { long categoryTableSize = categoryTable.size(); context.put("value_totalCategories", Long.valueOf(categoryTableSize)); @@ -3521,10 +3507,7 @@ private void putGradebookCategoryInfoIntoContext(SessionState state, Context con // selected category context.put("value_Category", state.getAttribute(NEW_ASSIGNMENT_CATEGORY)); - List categoryList = new ArrayList(); - for (Map.Entry entry : categoryTable.entrySet()) { - categoryList.add(entry.getKey()); - } + List categoryList = new ArrayList<>(categoryTable.keySet()); Collections.sort(categoryList); context.put("categoryKeys", categoryList); context.put("categoryTable", categoryTable); @@ -3632,7 +3615,7 @@ private String build_instructor_preview_assignment_context(VelocityPortlet portl context.put("value_CheckAnonymousGrading", Boolean.FALSE); // get all available assignments from Gradebook tool except for those created from - if (isGradebookDefined()) { + if (canGrade()) { context.put("gradebookChoice", state.getAttribute(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK)); context.put("associateGradebookAssignment", state.getAttribute(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT)); @@ -3735,11 +3718,9 @@ protected String build_instructor_grade_submission_context(VelocityPortlet portl boolean allowToGrade = true; if (StringUtils.isNotBlank(a.getProperties().get(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT))) { String gradebookUid = toolManager.getCurrentPlacement().getContext(); - if (gradebookService != null && gradebookService.isGradebookDefined(gradebookUid)) { - if (!gradebookService.currentUserHasGradingPerm(gradebookUid)) { - context.put("notAllowedToGradeWarning", rb.getString("not_allowed_to_grade_in_gradebook")); - allowToGrade = false; - } + if (!gradingService.currentUserHasGradingPerm(gradebookUid)) { + context.put("notAllowedToGradeWarning", rb.getString("not_allowed_to_grade_in_gradebook")); + allowToGrade = false; } } context.put("allowToGrade", allowToGrade); @@ -5333,8 +5314,6 @@ private String build_instructor_view_students_assignment_context(VelocityPortlet String search = (String) state.getAttribute(VIEW_SUBMISSION_SEARCH); Boolean searchFilterOnly = (state.getAttribute(SUBMISSIONS_SEARCH_ONLY) != null && ((Boolean) state.getAttribute(SUBMISSIONS_SEARCH_ONLY)) ? Boolean.TRUE : Boolean.FALSE); - Collection assignments = assignmentService.getAssignmentsForContext(contextString); - String accessPointUrl = serverConfigurationService.getAccessUrl() + AssignmentReferenceReckoner.reckoner().context(contextString).reckon().getReference() + "?contextString=" + contextString + @@ -5344,6 +5323,8 @@ private String build_instructor_view_students_assignment_context(VelocityPortlet "&estimate=true"; context.put("accessPointUrl", accessPointUrl); + Collection assignments = assignmentService.getAssignmentsForContext(contextString); + boolean hasAtLeastOneAnonAssigment = false; for (Assignment assignment : assignments) { if (assignmentService.assignmentUsesAnonymousGrading(assignment)) { @@ -5477,20 +5458,20 @@ private String build_instructor_report_submissions(VelocityPortlet portlet, Cont } // build_instructor_report_submissions // Is Gradebook defined for the site? - private boolean isGradebookDefined() { + private boolean canGrade() { boolean rv = false; try { String gradebookUid = toolManager.getCurrentPlacement().getContext(); - if (gradebookService.isGradebookDefined(gradebookUid) && (gradebookService.currentUserHasEditPerm(gradebookUid) || gradebookService.currentUserHasGradingPerm(gradebookUid))) { + if (gradingService.currentUserHasEditPerm(gradebookUid) || gradingService.currentUserHasGradingPerm(gradebookUid)) { rv = true; } } catch (Exception e) { - log.debug(this + "isGradebookDefined " + rb.getFormattedMessage("addtogradebook.alertMessage", new Object[]{e.getMessage()})); + log.debug(this + "canGrade " + rb.getFormattedMessage("addtogradebook.alertMessage", new Object[]{e.getMessage()})); } return rv; - } // isGradebookDefined() + } // canGrade() /** * build the instructor view to download/upload information from archive file @@ -7125,9 +7106,9 @@ private void setNewAssignmentParameters(RunData data, boolean validify) { String order = params.getString(NEW_ASSIGNMENT_ORDER); state.setAttribute(NEW_ASSIGNMENT_ORDER, order); - String contextString = toolManager.getCurrentPlacement().getContext(); + String siteId = toolManager.getCurrentPlacement().getContext(); - boolean groupAssignment = rangeAndGroups.setNewOrEditedAssignmentParameters(data, state, contextString); + boolean groupAssignment = rangeAndGroups.setNewOrEditedAssignmentParameters(data, state, siteId); if (StringUtils.isBlank(title)) { // empty assignment title @@ -7215,8 +7196,7 @@ private void setNewAssignmentParameters(RunData data, boolean validify) { state.setAttribute(NEW_ASSIGNMENT_GRADE_TYPE, gradeType.ordinal()); } - boolean sendToGradebook = gradeType == SCORE_GRADE_TYPE && params.getBoolean(NEW_ASSIGNMENT_SEND_TO_GRADEBOOK); - String grading = sendToGradebook ? params.getString(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK) : GRADEBOOK_INTEGRATION_NO; + String grading = params.getString(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK); state.setAttribute(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK, grading); state.setAttribute(NEW_ASSIGNMENT_CHECK_ANONYMOUS_GRADING, Boolean.valueOf(params.getString(NEW_ASSIGNMENT_CHECK_ANONYMOUS_GRADING))); @@ -7232,7 +7212,7 @@ private void setNewAssignmentParameters(RunData data, boolean validify) { String associateAssignment = params.getString(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT); Double droppedCategoryPoints = -1D; - if (grading != null) { + if (grading != null && gradeType != UNGRADED_GRADE_TYPE) { if (grading.equals(GRADEBOOK_INTEGRATION_ASSOCIATE)) { state.setAttribute(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT, associateAssignment); } else { @@ -7250,8 +7230,7 @@ private void setNewAssignmentParameters(RunData data, boolean validify) { addAlert(state, rb.getString("grading.associate.alert")); } else { Long thisCatRef = -1L; - String gradebookUid = toolManager.getCurrentPlacement().getContext(); - List categoryDefinitions = gradebookService.getCategoryDefinitions(gradebookUid); + List categoryDefinitions = gradingService.getCategoryDefinitions(siteId); if (catInt != -1) { thisCatRef = catInt; } else if (assignmentRef.isEmpty()) { @@ -7278,10 +7257,9 @@ private void setNewAssignmentParameters(RunData data, boolean validify) { } // check if chosen a previously associated object - Collection assignments = assignmentService.getAssignmentsForContext(contextString); String associatedAssignmentTitles = ""; // check assignments from the site - for (Assignment a : assignments) { + for (Assignment a : assignmentService.getAssignmentsForContext(siteId)) { if (assignmentId.equals(a.getId())) { continue; } @@ -7667,8 +7645,7 @@ private void setNewAssignmentParameters(RunData data, boolean validify) { private boolean sameAssignmentTitleInContext(String assignmentRef, String title, String contextString) { boolean rv = false; // in the student list view of assignments - Collection assignments = assignmentService.getAssignmentsForContext(contextString); - for (Assignment a : assignments) { + for (Assignment a : assignmentService.getAssignmentsForContext(contextString)) { if (assignmentRef == null || !assignmentRef.equals(AssignmentReferenceReckoner.reckoner().assignment(a).reckon().getReference())) { // don't do self-compare String aTitle = a.getTitle(); @@ -8222,7 +8199,10 @@ private void post_save_assignment(RunData data, String postOrSave) { Boolean checkAddHonorPledge = (Boolean) state.getAttribute(NEW_ASSIGNMENT_CHECK_ADD_HONOR_PLEDGE); - String addtoGradebook = StringUtils.isNotBlank((String) state.getAttribute(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK)) ? (String) state.getAttribute(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK) : GRADEBOOK_INTEGRATION_NO; + String addtoGradebook = GRADEBOOK_INTEGRATION_NO; + if (((Boolean) state.getAttribute(NEW_ASSIGNMENT_GRADE_ASSIGNMENT))) { + addtoGradebook = StringUtils.isNotBlank((String) state.getAttribute(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK)) ? (String) state.getAttribute(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK) : GRADEBOOK_INTEGRATION_NO; + } long category = state.getAttribute(NEW_ASSIGNMENT_CATEGORY) != null ? (Long) state.getAttribute(NEW_ASSIGNMENT_CATEGORY) : -1; @@ -8749,54 +8729,51 @@ private void addRemoveSubmissionsForNonElectronicAssignment(SessionState state, private void initIntegrateWithGradebook(SessionState state, String siteId, String aOldTitle, String oAssociateGradebookAssignment, Assignment assignment, String title, Instant dueTime, Assignment.GradeType gradeType, String gradePoints, String addtoGradebook, String associateGradebookAssignment, String range, long category) { String context = (String) state.getAttribute(STATE_CONTEXT_STRING); - boolean gradebookExists = isGradebookDefined(); String assignmentReference = AssignmentReferenceReckoner.reckoner().assignment(assignment).reckon().getReference(); // only if the gradebook is defined - if (gradebookExists) { - String gradebookUid = toolManager.getCurrentPlacement().getContext(); - String addUpdateRemoveAssignment = "remove"; - if (!addtoGradebook.equals(GRADEBOOK_INTEGRATION_NO)) { - // if integrate with Gradebook - if (addtoGradebook.equals(GRADEBOOK_INTEGRATION_ADD)) { - addUpdateRemoveAssignment = GRADEBOOK_INTEGRATION_ADD; - } else if (addtoGradebook.equals(GRADEBOOK_INTEGRATION_ASSOCIATE)) { - addUpdateRemoveAssignment = "update"; - } + String gradebookUid = toolManager.getCurrentPlacement().getContext(); + String addUpdateRemoveAssignment = "remove"; + if (!addtoGradebook.equals(GRADEBOOK_INTEGRATION_NO)) { + // if integrate with Gradebook + if (addtoGradebook.equals(GRADEBOOK_INTEGRATION_ADD)) { + addUpdateRemoveAssignment = GRADEBOOK_INTEGRATION_ADD; + } else if (addtoGradebook.equals(GRADEBOOK_INTEGRATION_ASSOCIATE)) { + addUpdateRemoveAssignment = "update"; + } - if (!"remove".equals(addUpdateRemoveAssignment) && gradeType == SCORE_GRADE_TYPE) { - try { - addAlerts(state, assignmentToolUtils.integrateGradebook(stateToMap(state), assignmentReference, associateGradebookAssignment, addUpdateRemoveAssignment, aOldTitle, title, Integer.parseInt(gradePoints), dueTime, null, null, category)); + if (!"remove".equals(addUpdateRemoveAssignment) && gradeType == SCORE_GRADE_TYPE) { + try { + addAlerts(state, assignmentToolUtils.integrateGradebook(stateToMap(state), assignmentReference, associateGradebookAssignment, addUpdateRemoveAssignment, aOldTitle, title, Integer.parseInt(gradePoints), dueTime, null, null, category)); - // add all existing grades, if any, into Gradebook - addAlerts(state, assignmentToolUtils.integrateGradebook(stateToMap(state), assignmentReference, associateGradebookAssignment, null, null, null, -1, null, null, "update", category)); + // add all existing grades, if any, into Gradebook + addAlerts(state, assignmentToolUtils.integrateGradebook(stateToMap(state), assignmentReference, associateGradebookAssignment, null, null, null, -1, null, null, "update", category)); - // if the assignment has been assoicated with a different entry in gradebook before, remove those grades from the entry in Gradebook - if (StringUtils.trimToNull(oAssociateGradebookAssignment) != null && !oAssociateGradebookAssignment.equals(associateGradebookAssignment)) { - // remove all previously associated grades, if any, into Gradebook - addAlerts(state, assignmentToolUtils.integrateGradebook(stateToMap(state), assignmentReference, oAssociateGradebookAssignment, null, null, null, -1, null, null, "remove", category)); + // if the assignment has been assoicated with a different entry in gradebook before, remove those grades from the entry in Gradebook + if (StringUtils.trimToNull(oAssociateGradebookAssignment) != null && !oAssociateGradebookAssignment.equals(associateGradebookAssignment)) { + // remove all previously associated grades, if any, into Gradebook + addAlerts(state, assignmentToolUtils.integrateGradebook(stateToMap(state), assignmentReference, oAssociateGradebookAssignment, null, null, null, -1, null, null, "remove", category)); - // if the old assoicated assignment entry in GB is an external one, but doesn't have anything assoicated with it in Assignment tool, remove it - removeNonAssociatedExternalGradebookEntry(context, assignmentReference, oAssociateGradebookAssignment, gradebookUid); - } - } catch (NumberFormatException nE) { - alertInvalidPoint(state, gradePoints, assignment.getScaleFactor()); - log.warn(this + ":initIntegrateWithGradebook " + nE.getMessage()); + // if the old assoicated assignment entry in GB is an external one, but doesn't have anything assoicated with it in Assignment tool, remove it + removeNonAssociatedExternalGradebookEntry(context, assignmentReference, oAssociateGradebookAssignment, gradebookUid); } - } else { - addAlerts(state, assignmentToolUtils.integrateGradebook(stateToMap(state), assignmentReference, associateGradebookAssignment, "remove", null, null, -1, null, null, null, category)); + } catch (NumberFormatException nE) { + alertInvalidPoint(state, gradePoints, assignment.getScaleFactor()); + log.warn(this + ":initIntegrateWithGradebook " + nE.getMessage()); } } else { - // remove all previously associated grades, if any, into Gradebook - addAlerts(state, assignmentToolUtils.integrateGradebook(stateToMap(state), assignmentReference, oAssociateGradebookAssignment, null, null, null, -1, null, null, "remove", category)); - - // need to remove the associated gradebook entry if 1) it is external and 2) no other assignment are associated with it - removeNonAssociatedExternalGradebookEntry(context, assignmentReference, oAssociateGradebookAssignment, gradebookUid); + addAlerts(state, assignmentToolUtils.integrateGradebook(stateToMap(state), assignmentReference, associateGradebookAssignment, "remove", null, null, -1, null, null, null, category)); } + } else { + // remove all previously associated grades, if any, into Gradebook + addAlerts(state, assignmentToolUtils.integrateGradebook(stateToMap(state), assignmentReference, oAssociateGradebookAssignment, null, null, null, -1, null, null, "remove", category)); + + // need to remove the associated gradebook entry if 1) it is external and 2) no other assignment are associated with it + removeNonAssociatedExternalGradebookEntry(context, assignmentReference, oAssociateGradebookAssignment, gradebookUid); } } private void removeNonAssociatedExternalGradebookEntry(String context, String assignmentReference, String associateGradebookAssignment, String gradebookUid) { - boolean isExternalAssignmentDefined = gradebookExternalAssessmentService.isExternalAssignmentDefined(gradebookUid, associateGradebookAssignment); + boolean isExternalAssignmentDefined = gradingService.isExternalAssignmentDefined(gradebookUid, associateGradebookAssignment); if (isExternalAssignmentDefined) { boolean found = false; // iterate through all assignments currently in the site, see if any is associated with this GB entry @@ -8810,7 +8787,7 @@ private void removeNonAssociatedExternalGradebookEntry(String context, String as } // so if none of the assignment in this site is associated with the entry, remove the entry if (!found) { - gradebookExternalAssessmentService.removeExternalAssessment(gradebookUid, associateGradebookAssignment); + gradingService.removeExternalAssignment(gradebookUid, associateGradebookAssignment); } } } @@ -9878,7 +9855,7 @@ private String getGradeName(final Assignment a, final String assignmentReference // add new entry to gradebook sb.append(rb.getString("grading.add")) .append(rb.getString("grading.ofcategory")) - .append(categoryTable().get(0)); + .append(getCategoryTable().get(0)); break; case GRADEBOOK_INTEGRATION_ASSOCIATE: // associated with one existing entry in Gradebook @@ -12012,7 +11989,7 @@ private void initializeAssignment(SessionState state) { // SAK-17606 state.removeAttribute(NEW_ASSIGNMENT_CHECK_ANONYMOUS_GRADING); - } // resetNewAssignment + } // initializeAssignment /** * reset the attributes for assignment @@ -12148,7 +12125,7 @@ private void resetAssignment(SessionState state) { state.removeAttribute(NEW_ASSIGNMENT_PREVIOUSLY_ASSOCIATED); state.removeAttribute(RUBRIC_ASSOCIATION); - } // resetNewAssignment + } // resetAssignment /** * construct a HashMap using integer as the key and three character string of the month as the value @@ -12209,21 +12186,17 @@ private Map submissionTypeTable() { * * @return */ - private Map categoryTable() { - boolean gradebookExists = isGradebookDefined(); + private Map getCategoryTable() { + Map catTable = new HashMap<>(); String gradebookUid = toolManager.getCurrentPlacement().getContext(); - if (gradebookExists && gradebookExternalAssessmentService.isCategoriesEnabled(gradebookUid)) { - List categoryDefinitions = gradebookService.getCategoryDefinitions(gradebookUid); - - catTable.put((long) -1, rb.getString("grading.unassigned")); - for (CategoryDefinition category : categoryDefinitions) { - catTable.put(category.getId(), category.getName()); - } + if (canGrade() && gradingService.isCategoriesEnabled(gradebookUid)) { + catTable = gradingService.getCategoryDefinitions(gradebookUid).stream() + .collect(Collectors.toMap(c -> c.getId(), c -> c.getName())); + catTable.put(-1L, rb.getString("grading.unassigned")); } return catTable; - - } // categoryTable + } /** * Sort based on the given property @@ -12484,9 +12457,8 @@ protected int sizeResources(SessionState state) { } else if (allowAddAssignment && MODE_STUDENT_VIEW.equals(view) || (!allowAddAssignment && assignmentService.allowAddSubmission((String) state.getAttribute(STATE_CONTEXT_STRING)))) { // in the student list view of assignments - Collection assignments = assignmentService.getAssignmentsForContext(contextString); Instant currentTime = Instant.now(); - for (Assignment a : assignments) { + for (Assignment a : assignmentService.getAssignmentsForContext(contextString)) { if (!a.getDeleted()) { // show not deleted assignments Instant openTime = a.getOpenDate(); @@ -12524,14 +12496,13 @@ protected int sizeResources(SessionState state) { String search = (String) state.getAttribute(VIEW_SUBMISSION_SEARCH); Boolean searchFilterOnly = (state.getAttribute(SUBMISSIONS_SEARCH_ONLY) != null && ((Boolean) state.getAttribute(SUBMISSIONS_SEARCH_ONLY)) ? Boolean.TRUE : Boolean.FALSE); - Collection assignments = assignmentService.getAssignmentsForContext(contextString); Boolean has_multiple_groups_for_user = false; List submissions = new ArrayList<>(); try { Site site = siteService.getSite(contextString); - for (Assignment a : assignments) { + for (Assignment a : assignmentService.getAssignmentsForContext(contextString)) { String aRef = AssignmentReferenceReckoner.reckoner().assignment(a).reckon().getReference(); List submitterIds = assignmentService.getSubmitterIdList(searchFilterOnly.toString(), allOrOneGroup, search, aRef, contextString); Collection dupUsers = new ArrayList<>(); @@ -14925,17 +14896,17 @@ protected void setScoringAgentProperties(Context context, Assignment assignment, gbItemName = associatedGbItem; } - org.sakaiproject.service.gradebook.shared.Assignment gbItem = null; + org.sakaiproject.grading.api.Assignment gbItem = null; try { - gbItem = gradebookService.getAssignment(gradebookUid, gbItemName); + gbItem = gradingService.getAssignment(gradebookUid, gbItemName); } catch (SecurityException se) { // the gradebook method above is overzealous about security when retrieving the gb item by name. It doesn't // allow student-role users to access the assignment via this method. So we // have to retrieve all viewable gb items and filter to get the one we want, unfortunately, if we hit an exception. // If gb item isn't released in the gb, scoring agent info will not be available. - List viewableGbItems = gradebookService.getViewableAssignmentsForCurrentUser(gradebookUid); + List viewableGbItems = gradingService.getViewableAssignmentsForCurrentUser(gradebookUid); if (viewableGbItems != null && !viewableGbItems.isEmpty()) { - for (org.sakaiproject.service.gradebook.shared.Assignment viewableGbItem : viewableGbItems) { + for (org.sakaiproject.grading.api.Assignment viewableGbItem : viewableGbItems) { if (gbItemName.equals(viewableGbItem.getName())) { gbItem = viewableGbItem; break; diff --git a/assignment/tool/src/java/org/sakaiproject/assignment/tool/AssignmentToolUtils.java b/assignment/tool/src/java/org/sakaiproject/assignment/tool/AssignmentToolUtils.java index aaab88c10a94..3ced1a53c716 100644 --- a/assignment/tool/src/java/org/sakaiproject/assignment/tool/AssignmentToolUtils.java +++ b/assignment/tool/src/java/org/sakaiproject/assignment/tool/AssignmentToolUtils.java @@ -46,14 +46,12 @@ import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.exception.PermissionException; +import org.sakaiproject.grading.api.AssessmentNotFoundException; +import org.sakaiproject.grading.api.AssignmentHasIllegalPointsException; +import org.sakaiproject.grading.api.ConflictingAssignmentNameException; +import org.sakaiproject.grading.api.GradingService; +import org.sakaiproject.grading.api.InvalidGradeItemNameException; import org.sakaiproject.lti.api.LTIService; -import org.sakaiproject.service.gradebook.shared.AssessmentNotFoundException; -import org.sakaiproject.service.gradebook.shared.AssignmentHasIllegalPointsException; -import org.sakaiproject.service.gradebook.shared.ConflictingAssignmentNameException; -import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService; -import org.sakaiproject.service.gradebook.shared.GradebookFrameworkService; -import org.sakaiproject.service.gradebook.shared.GradebookService; -import org.sakaiproject.service.gradebook.shared.InvalidGradeItemNameException; import org.sakaiproject.time.api.TimeService; import org.sakaiproject.tool.api.ToolManager; import org.sakaiproject.user.api.User; @@ -76,9 +74,7 @@ public class AssignmentToolUtils { private AssignmentService assignmentService; private UserDirectoryService userDirectoryService; - private GradebookExternalAssessmentService gradebookExternalAssessmentService; - private GradebookFrameworkService gradebookFrameworkService; - private GradebookService gradebookService; + private GradingService gradingService; private TimeService timeService; private ToolManager toolManager; private LTIService ltiService; @@ -419,15 +415,10 @@ public void gradeSubmission(AssignmentSubmission submission, String gradeOption, String aReference = AssignmentReferenceReckoner.reckoner().assignment(a).reckon().getReference(); String associateGradebookAssignment = a.getProperties().get(PROP_ASSIGNMENT_ASSOCIATE_GRADEBOOK_ASSIGNMENT); - if (!"remove".equals(gradeOption)) { - // update grade in gradebook - alerts.addAll(integrateGradebook(options, aReference, associateGradebookAssignment, null, null, null, -1, null, sReference, "update", -1)); - } else { - //remove grade from gradebook - alerts.addAll(integrateGradebook(options, aReference, associateGradebookAssignment, null, null, null, -1, null, sReference, "remove", -1)); - } + String op = gradeOption.equals("remove") ? "remove" : "update"; + alerts.addAll(integrateGradebook(options, aReference, associateGradebookAssignment, null, null, null, -1, null, sReference, op, -1)); } - } // grade_submission_option + } // gradeSubmission /** * construct time object based on various state variables @@ -472,7 +463,11 @@ private Instant getTimeFromOptions(Map options, String monthStri * @param submissionRef Any submission grade need to be updated? Do bulk update if null * @param updateRemoveSubmission "update" for update submission;"remove" for remove submission */ - public List integrateGradebook(Map options, String assignmentRef, String associateGradebookAssignment, String addUpdateRemoveAssignment, String oldAssignment_title, String newAssignment_title, int newAssignment_maxPoints, Instant newAssignment_dueTime, String submissionRef, String updateRemoveSubmission, long category) { + List integrateGradebook(Map options, String assignmentRef, String associateGradebookAssignment, + String addUpdateRemoveAssignment, String oldAssignment_title, String newAssignment_title, + int newAssignment_maxPoints, Instant newAssignment_dueTime, String submissionRef, + String updateRemoveSubmission, long category) { + associateGradebookAssignment = StringUtils.trimToNull(associateGradebookAssignment); // add or remove external grades to gradebook @@ -492,40 +487,40 @@ public List integrateGradebook(Map options, String assig if (gradebookUid == null) { gradebookUid = toolManager.getCurrentPlacement().getContext(); } - if (gradebookService.isGradebookDefined(gradebookUid) && gradebookService.currentUserHasGradingPerm(gradebookUid)) { - boolean isExternalAssignmentDefined = gradebookExternalAssessmentService.isExternalAssignmentDefined(gradebookUid, assignmentRef); - boolean isExternalAssociateAssignmentDefined = gradebookExternalAssessmentService.isExternalAssignmentDefined(gradebookUid, associateGradebookAssignment); - boolean isAssignmentDefined = gradebookService.isAssignmentDefined(gradebookUid, associateGradebookAssignment); + if (gradingService.currentUserHasGradingPerm(gradebookUid)) { + boolean isExternalAssignmentDefined = gradingService.isExternalAssignmentDefined(gradebookUid, assignmentRef); + boolean isExternalAssociateAssignmentDefined = gradingService.isExternalAssignmentDefined(gradebookUid, associateGradebookAssignment); + boolean isAssignmentDefined = gradingService.isAssignmentDefined(gradebookUid, associateGradebookAssignment); if (addUpdateRemoveAssignment != null) { Assignment a = assignmentService.getAssignment(assignmentId); // add an entry into Gradebook for newly created assignment or modified assignment, and there wasn't a correspond record in gradebook yet - if ((addUpdateRemoveAssignment.equals(GRADEBOOK_INTEGRATION_ADD) || - ("update".equals(addUpdateRemoveAssignment) && !isExternalAssignmentDefined)) && associateGradebookAssignment == null) { + if ((addUpdateRemoveAssignment.equals(GRADEBOOK_INTEGRATION_ADD) || addUpdateRemoveAssignment.equals("update")) + && associateGradebookAssignment == null) { // add assignment into gradebook try { // add assignment to gradebook - gradebookExternalAssessmentService.addExternalAssessment(gradebookUid, assignmentRef, null, newAssignment_title, newAssignment_maxPoints / (double) a.getScaleFactor(), Date.from(newAssignment_dueTime), assignmentToolTitle, null, false, category != -1 ? category : null); + gradingService.addExternalAssessment(gradebookUid, assignmentRef, null, newAssignment_title, newAssignment_maxPoints / (double) a.getScaleFactor(), Date.from(newAssignment_dueTime), assignmentToolTitle, null, false, category != -1 ? category : null); } catch (AssignmentHasIllegalPointsException e) { alerts.add(rb.getString("addtogradebook.illegalPoints")); - log.warn(this + ":integrateGradebook " + e.getMessage()); + log.warn("integrateGradebook: {}", e.toString()); } catch (ConflictingAssignmentNameException e) { // add alert prompting for change assignment title alerts.add(rb.getFormattedMessage("addtogradebook.nonUniqueTitle", "\"" + newAssignment_title + "\"")); - log.warn(this + ":integrateGradebook " + e.getMessage()); + log.warn("integrateGradebook: {}", e.toString()); } catch (InvalidGradeItemNameException e) { // add alert prompting for invalid assignment title name alerts.add(rb.getFormattedMessage("addtogradebook.titleInvalidCharacters", "\"" + newAssignment_title + "\"")); - log.warn(this + ":integrateGradebook " + e.getMessage()); + log.warn("integrateGradebook: {}", e.toString()); } catch (Exception e) { - log.warn(this + ":integrateGradebook " + e.getMessage()); + log.warn("integrateGradebook: {}", e.toString()); } } else if ("update".equals(addUpdateRemoveAssignment)) { - if (associateGradebookAssignment != null && isExternalAssociateAssignmentDefined) { + if (isExternalAssociateAssignmentDefined) { // if there is an external entry created in Gradebook based on this assignment, update it try { // update attributes if the GB assignment was created for the assignment - gradebookExternalAssessmentService.updateExternalAssessment(gradebookUid, associateGradebookAssignment, null, null, newAssignment_title, newAssignment_maxPoints / (double) a.getScaleFactor(), Date.from(newAssignment_dueTime), false); + gradingService.updateExternalAssessment(gradebookUid, associateGradebookAssignment, null, null, newAssignment_title, newAssignment_maxPoints / (double) a.getScaleFactor(), Date.from(newAssignment_dueTime), false); } catch (Exception e) { alerts.add(rb.getFormattedMessage("cannotfin_assignment", assignmentRef)); log.warn("{}", rb.getFormattedMessage("cannotfin_assignment", assignmentRef)); @@ -538,147 +533,145 @@ else if ("remove".equals(addUpdateRemoveAssignment)) { } } - if (updateRemoveSubmission != null) { - Assignment a = assignmentService.getAssignment(assignmentId); - - if (a != null) { - String propAddToGradebook = a.getProperties().get(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK); - if ("update".equals(updateRemoveSubmission) - && (StringUtils.equals(propAddToGradebook, GRADEBOOK_INTEGRATION_ADD) - || StringUtils.equals(propAddToGradebook, GRADEBOOK_INTEGRATION_ASSOCIATE)) - && a.getTypeOfGrade() == SCORE_GRADE_TYPE) { - - if (submissionRef == null) { - //Assignment scores map - Map sm = new HashMap<>(); - //Assignment comments map, though doesn't look like there's any way to update comments in bulk in the UI yet - Map cm = new HashMap<>(); - - // bulk add all grades for assignment into gradebook - for (AssignmentSubmission submission : assignmentService.getSubmissions(a)) { - if (submission.getGradeReleased()) { - String gradeString = StringUtils.trimToNull(submission.getGrade()); - String commentString = formattedText.convertFormattedTextToPlaintext(submission.getFeedbackComment()); - - String grade = gradeString != null ? displayGrade(gradeString, a.getScaleFactor()) : null; - for (AssignmentSubmissionSubmitter submitter : submission.getSubmitters()) { - String submitterId = submitter.getSubmitter(); - String submitterGrade = submitter.getGrade() != null ? displayGrade(submitter.getGrade(), a.getScaleFactor()) : null; - String gradeStringToUse = (a.getIsGroup() && submitterGrade != null) ? submitterGrade : grade; - sm.put(submitterId, gradeStringToUse); - cm.put(submitterId, commentString); - } + Assignment a = assignmentService.getAssignment(assignmentId); + + if (a != null) { + String propAddToGradebook = a.getProperties().get(NEW_ASSIGNMENT_ADD_TO_GRADEBOOK); + if ("update".equals(updateRemoveSubmission) + && (StringUtils.equals(propAddToGradebook, GRADEBOOK_INTEGRATION_ADD) + || StringUtils.equals(propAddToGradebook, GRADEBOOK_INTEGRATION_ASSOCIATE)) + && a.getTypeOfGrade() == SCORE_GRADE_TYPE) { + + if (submissionRef == null) { + //Assignment scores map + Map sm = new HashMap<>(); + //Assignment comments map, though doesn't look like there's any way to update comments in bulk in the UI yet + Map cm = new HashMap<>(); + + // bulk add all grades for assignment into gradebook + for (AssignmentSubmission submission : assignmentService.getSubmissions(a)) { + if (submission.getGradeReleased()) { + String gradeString = StringUtils.trimToNull(submission.getGrade()); + String commentString = formattedText.convertFormattedTextToPlaintext(submission.getFeedbackComment()); + + String grade = gradeString != null ? displayGrade(gradeString, a.getScaleFactor()) : null; + for (AssignmentSubmissionSubmitter submitter : submission.getSubmitters()) { + String submitterId = submitter.getSubmitter(); + String submitterGrade = submitter.getGrade() != null ? displayGrade(submitter.getGrade(), a.getScaleFactor()) : null; + String gradeStringToUse = (a.getIsGroup() && submitterGrade != null) ? submitterGrade : grade; + sm.put(submitterId, gradeStringToUse); + cm.put(submitterId, commentString); } } + } - // need to update only when there is at least one submission - if (!sm.isEmpty()) { - if (associateGradebookAssignment != null) { - if (isExternalAssociateAssignmentDefined) { - // the associated assignment is externally maintained - gradebookExternalAssessmentService.updateExternalAssessmentScoresString(gradebookUid, associateGradebookAssignment, sm); - gradebookExternalAssessmentService.updateExternalAssessmentComments(gradebookUid, associateGradebookAssignment, cm); - } else if (isAssignmentDefined) { - Long associateGradebookAssignmentId = gradebookService.getAssignment(gradebookUid, associateGradebookAssignment).getId(); - // the associated assignment is internal one, update records one by one - for (Map.Entry entry : sm.entrySet()) { - String submitterId = (String) entry.getKey(); - String grade = StringUtils.trimToNull(displayGrade((String) sm.get(submitterId), a.getScaleFactor())); - if (grade != null && gradebookService.isUserAbleToGradeItemForStudent(gradebookUid, associateGradebookAssignmentId, submitterId)) { - gradebookService.setAssignmentScoreString(gradebookUid, associateGradebookAssignmentId, submitterId, grade, ""); - String comment = StringUtils.isNotEmpty(cm.get(submitterId)) ? cm.get(submitterId) : ""; - gradebookService.setAssignmentScoreComment(gradebookUid, associateGradebookAssignmentId, submitterId, comment); - } + // need to update only when there is at least one submission + if (!sm.isEmpty()) { + if (associateGradebookAssignment != null) { + if (isExternalAssociateAssignmentDefined) { + // the associated assignment is externally maintained + gradingService.updateExternalAssessmentScoresString(gradebookUid, associateGradebookAssignment, sm); + gradingService.updateExternalAssessmentComments(gradebookUid, associateGradebookAssignment, cm); + } else if (isAssignmentDefined) { + Long associateGradebookAssignmentId = gradingService.getAssignment(gradebookUid, associateGradebookAssignment).getId(); + // the associated assignment is internal one, update records one by one + for (Map.Entry entry : sm.entrySet()) { + String submitterId = (String) entry.getKey(); + String grade = StringUtils.trimToNull(displayGrade((String) sm.get(submitterId), a.getScaleFactor())); + if (grade != null && gradingService.isUserAbleToGradeItemForStudent(gradebookUid, associateGradebookAssignmentId, submitterId)) { + gradingService.setAssignmentScoreString(gradebookUid, associateGradebookAssignmentId, submitterId, grade, ""); + String comment = StringUtils.isNotEmpty(cm.get(submitterId)) ? cm.get(submitterId) : ""; + gradingService.setAssignmentScoreComment(gradebookUid, associateGradebookAssignmentId, submitterId, comment); } } - } else if (isExternalAssignmentDefined) { - gradebookExternalAssessmentService.updateExternalAssessmentScoresString(gradebookUid, assignmentRef, sm); - gradebookExternalAssessmentService.updateExternalAssessmentComments(gradebookUid, assignmentRef, cm); } + } else if (isExternalAssignmentDefined) { + gradingService.updateExternalAssessmentScoresString(gradebookUid, assignmentRef, sm); + gradingService.updateExternalAssessmentComments(gradebookUid, assignmentRef, cm); } - } else { - // only update one submission - AssignmentSubmission aSubmission = assignmentService.getSubmission(submissionId); - if (aSubmission != null) { - int factor = aSubmission.getAssignment().getScaleFactor(); - Set submitters = aSubmission.getSubmitters(); - String gradeString = displayGrade(StringUtils.trimToNull(aSubmission.getGrade()), factor); - for (AssignmentSubmissionSubmitter submitter : submitters) { - String gradeStringToUse = (a.getIsGroup() && submitter.getGrade() != null) ? displayGrade(StringUtils.trimToNull(submitter.getGrade()), factor) : gradeString; - //Gradebook only supports plaintext strings - String commentString = formattedText.convertFormattedTextToPlaintext(aSubmission.getFeedbackComment()); - if (associateGradebookAssignment != null) { - if (gradebookExternalAssessmentService.isExternalAssignmentDefined(gradebookUid, associateGradebookAssignment)) { - // the associated assignment is externally maintained - gradebookExternalAssessmentService.updateExternalAssessmentScore(gradebookUid, associateGradebookAssignment, submitter.getSubmitter(), - (gradeStringToUse != null && aSubmission.getGradeReleased()) ? gradeStringToUse : ""); - gradebookExternalAssessmentService.updateExternalAssessmentComment(gradebookUid, associateGradebookAssignment, submitter.getSubmitter(), - (commentString != null && aSubmission.getGradeReleased()) ? commentString : ""); - } else if (gradebookService.isAssignmentDefined(gradebookUid, associateGradebookAssignment)) { - // the associated assignment is internal one, update records - final Long associateGradebookAssignmentId = gradebookService.getAssignment(gradebookUid, associateGradebookAssignment).getId(); - final String submitterId = submitter.getSubmitter(); - if (gradebookService.isUserAbleToGradeItemForStudent(gradebookUid, associateGradebookAssignmentId, submitterId)) { - gradebookService.setAssignmentScoreString(gradebookUid, associateGradebookAssignmentId, submitterId, - (gradeStringToUse != null && aSubmission.getGradeReleased()) ? gradeStringToUse : "", ""); - gradebookService.setAssignmentScoreComment(gradebookUid, associateGradebookAssignmentId, submitterId, - (commentString != null && aSubmission.getGradeReleased()) ? commentString : ""); - } - } - } else { - gradebookExternalAssessmentService.updateExternalAssessmentScore(gradebookUid, assignmentRef, submitter.getSubmitter(), + } + } else { + // only update one submission + AssignmentSubmission aSubmission = assignmentService.getSubmission(submissionId); + if (aSubmission != null) { + int factor = aSubmission.getAssignment().getScaleFactor(); + Set submitters = aSubmission.getSubmitters(); + String gradeString = displayGrade(StringUtils.trimToNull(aSubmission.getGrade()), factor); + for (AssignmentSubmissionSubmitter submitter : submitters) { + String gradeStringToUse = (a.getIsGroup() && submitter.getGrade() != null) ? displayGrade(StringUtils.trimToNull(submitter.getGrade()), factor) : gradeString; + //Gradebook only supports plaintext strings + String commentString = formattedText.convertFormattedTextToPlaintext(aSubmission.getFeedbackComment()); + if (associateGradebookAssignment != null) { + if (gradingService.isExternalAssignmentDefined(gradebookUid, associateGradebookAssignment)) { + // the associated assignment is externally maintained + gradingService.updateExternalAssessmentScore(gradebookUid, associateGradebookAssignment, submitter.getSubmitter(), (gradeStringToUse != null && aSubmission.getGradeReleased()) ? gradeStringToUse : ""); - gradebookExternalAssessmentService.updateExternalAssessmentComment(gradebookUid, assignmentRef, submitter.getSubmitter(), + gradingService.updateExternalAssessmentComment(gradebookUid, associateGradebookAssignment, submitter.getSubmitter(), (commentString != null && aSubmission.getGradeReleased()) ? commentString : ""); + } else if (gradingService.isAssignmentDefined(gradebookUid, associateGradebookAssignment)) { + // the associated assignment is internal one, update records + final Long associateGradebookAssignmentId = gradingService.getAssignment(gradebookUid, associateGradebookAssignment).getId(); + final String submitterId = submitter.getSubmitter(); + if (gradingService.isUserAbleToGradeItemForStudent(gradebookUid, associateGradebookAssignmentId, submitterId)) { + gradingService.setAssignmentScoreString(gradebookUid, associateGradebookAssignmentId, submitterId, + (gradeStringToUse != null && aSubmission.getGradeReleased()) ? gradeStringToUse : "", ""); + gradingService.setAssignmentScoreComment(gradebookUid, associateGradebookAssignmentId, submitterId, + (commentString != null && aSubmission.getGradeReleased()) ? commentString : ""); + } } + } else { + gradingService.updateExternalAssessmentScore(gradebookUid, assignmentRef, submitter.getSubmitter(), + (gradeStringToUse != null && aSubmission.getGradeReleased()) ? gradeStringToUse : ""); + gradingService.updateExternalAssessmentComment(gradebookUid, assignmentRef, submitter.getSubmitter(), + (commentString != null && aSubmission.getGradeReleased()) ? commentString : ""); } } } + } - } else if ("remove".equals(updateRemoveSubmission)) { - if (submissionRef == null) { - // remove all submission grades (when changing the associated entry in Gradebook) - Iterator submissions = assignmentService.getSubmissions(a).iterator(); - - // any score to copy over? get all the assessmentGradingData and copy over - while (submissions.hasNext()) { - AssignmentSubmission aSubmission = (AssignmentSubmission) submissions.next(); - if (StringUtils.isNotBlank(aSubmission.getGrade())) { - final List submitters = getSubmitters(aSubmission).collect(Collectors.toList()); - for (User submitter :submitters) { - if (isExternalAssociateAssignmentDefined) { - // if the old associated assignment is an external maintained one - gradebookExternalAssessmentService.updateExternalAssessmentScore(gradebookUid, associateGradebookAssignment, submitter.getId(), null); - } else if (isAssignmentDefined) { - final String submitterId = submitter.getId(); - final Long associateGradebookAssignmentId = gradebookService.getAssignment(gradebookUid, associateGradebookAssignment).getId(); - if (gradebookService.isUserAbleToGradeItemForStudent(gradebookUid, associateGradebookAssignmentId, submitterId)) { - gradebookService.setAssignmentScoreString(gradebookUid, associateGradebookAssignment, submitter.getId(), "0", assignmentToolTitle); - } - } - } - } - } - } else { - // remove only one submission grade - AssignmentSubmission aSubmission = assignmentService.getSubmission(submissionId); - if (aSubmission != null) { - final List submitters = getSubmitters(aSubmission).collect(Collectors.toList()); - for (User submitter :submitters) { + } else if ("remove".equals(updateRemoveSubmission)) { + if (submissionRef == null) { + // remove all submission grades (when changing the associated entry in Gradebook) + Iterator submissions = assignmentService.getSubmissions(a).iterator(); + + // any score to copy over? get all the assessmentGradingData and copy over + while (submissions.hasNext()) { + AssignmentSubmission aSubmission = (AssignmentSubmission) submissions.next(); + if (StringUtils.isNotBlank(aSubmission.getGrade())) { + final List submitters = getSubmitters(aSubmission).collect(Collectors.toList()); + for (User submitter :submitters) { if (isExternalAssociateAssignmentDefined) { - // external assignment - gradebookExternalAssessmentService.updateExternalAssessmentScore(gradebookUid, assignmentRef, submitter.getId(), null); + // if the old associated assignment is an external maintained one + gradingService.updateExternalAssessmentScore(gradebookUid, associateGradebookAssignment, submitter.getId(), null); } else if (isAssignmentDefined) { - // gb assignment final String submitterId = submitter.getId(); - final Long associateGradebookAssignmentId = gradebookService.getAssignment(gradebookUid, associateGradebookAssignment).getId(); - if (gradebookService.isUserAbleToGradeItemForStudent(gradebookUid, associateGradebookAssignmentId, submitterId)) { - gradebookService.setAssignmentScoreString(gradebookUid, associateGradebookAssignment, submitter.getId(), "0", ""); + final Long associateGradebookAssignmentId = gradingService.getAssignment(gradebookUid, associateGradebookAssignment).getId(); + if (gradingService.isUserAbleToGradeItemForStudent(gradebookUid, associateGradebookAssignmentId, submitterId)) { + gradingService.setAssignmentScoreString(gradebookUid, associateGradebookAssignment, submitter.getId(), "0", assignmentToolTitle); } } } } } + } else { + // remove only one submission grade + AssignmentSubmission aSubmission = assignmentService.getSubmission(submissionId); + if (aSubmission != null) { + final List submitters = getSubmitters(aSubmission).collect(Collectors.toList()); + for (User submitter :submitters) { + if (isExternalAssociateAssignmentDefined) { + // external assignment + gradingService.updateExternalAssessmentScore(gradebookUid, assignmentRef, submitter.getId(), null); + } else if (isAssignmentDefined) { + // gb assignment + final String submitterId = submitter.getId(); + final Long associateGradebookAssignmentId = gradingService.getAssignment(gradebookUid, associateGradebookAssignment).getId(); + if (gradingService.isUserAbleToGradeItemForStudent(gradebookUid, associateGradebookAssignmentId, submitterId)) { + gradingService.setAssignmentScoreString(gradebookUid, associateGradebookAssignment, submitter.getId(), "0", ""); + } + } + } + } } } } @@ -691,9 +684,9 @@ else if ("remove".equals(addUpdateRemoveAssignment)) { /** * A utility class to find a gradebook column of a particular name */ - public org.sakaiproject.service.gradebook.shared.Assignment findGradeBookColumn(String gradebookUid, String assignmentName) { + public org.sakaiproject.grading.api.Assignment findGradeBookColumn(String gradebookUid, String assignmentName) { try { - return gradebookService.getAssignmentByNameOrId(gradebookUid, assignmentName); + return gradingService.getAssignmentByNameOrId(gradebookUid, assignmentName); } catch (AssessmentNotFoundException anfe) { return null; } @@ -802,7 +795,7 @@ private String displayGrade(String grade, Integer factor) { } private void removeNonAssociatedExternalGradebookEntry(String context, String assignmentReference, String associateGradebookAssignment, String gradebookUid) { - boolean isExternalAssignmentDefined = gradebookExternalAssessmentService.isExternalAssignmentDefined(gradebookUid, associateGradebookAssignment); + boolean isExternalAssignmentDefined = gradingService.isExternalAssignmentDefined(gradebookUid, associateGradebookAssignment); if (isExternalAssignmentDefined) { boolean found = false; // iterate through all assignments currently in the site, see if any is associated with this GB entry @@ -816,7 +809,7 @@ private void removeNonAssociatedExternalGradebookEntry(String context, String as } // so if none of the assignment in this site is associated with the entry, remove the entry if (!found) { - gradebookExternalAssessmentService.removeExternalAssessment(gradebookUid, associateGradebookAssignment); + gradingService.removeExternalAssignment(gradebookUid, associateGradebookAssignment); } } } diff --git a/assignment/tool/src/webapp/WEB-INF/applicationContext.xml b/assignment/tool/src/webapp/WEB-INF/applicationContext.xml index 4377dd74a25d..cbf51cd3b94b 100644 --- a/assignment/tool/src/webapp/WEB-INF/applicationContext.xml +++ b/assignment/tool/src/webapp/WEB-INF/applicationContext.xml @@ -6,9 +6,7 @@ - - - + @@ -28,8 +26,7 @@ - - + diff --git a/assignment/tool/src/webapp/js/assignments.js b/assignment/tool/src/webapp/js/assignments.js index 7816d532ffa9..2bbdad5bc4f4 100755 --- a/assignment/tool/src/webapp/js/assignments.js +++ b/assignment/tool/src/webapp/js/assignments.js @@ -285,7 +285,7 @@ ASN.setupAssignNew = function(){ }); const isEstimate = document.getElementById('new_assignment_check_add_estimate'); - if (isEstimate.checked) { + if (isEstimate && isEstimate.checked) { document.getElementById('checkestimaterequired').classList.remove('hidden'); document.getElementById('inputtimestimate').classList.remove('hidden'); } diff --git a/assignment/tool/src/webapp/js/instructorNewEdit.js b/assignment/tool/src/webapp/js/instructorNewEdit.js index 857eff65ed58..3d73f54192bf 100644 --- a/assignment/tool/src/webapp/js/instructorNewEdit.js +++ b/assignment/tool/src/webapp/js/instructorNewEdit.js @@ -210,21 +210,6 @@ ASN_INE.disablePeerAssessment = function(disable) } }; -ASN_INE.handleGradebookRadioClick = function(radio, addToGbRadioId) -{ - var catSelect = document.getElementById("category"); - var itemSelect = document.getElementById("gradebookItemSelect"); - var isAdd = radio.id === addToGbRadioId; - if (catSelect !== null) - { - catSelect.disabled = !isAdd; - } - if (itemSelect !== null) - { - itemSelect.disabled = isAdd; - } -}; - ASN_INE.handleAssignToChangeForPeerAssessment = function() { var peerCheck = document.getElementById("usePeerAssessment"); @@ -360,3 +345,24 @@ ASN_INE_API._GET = function(url, data, onSuccess, onError, onComplete) complete: onComplete || $.noop }); }; + +window.addEventListener("DOMContentLoaded", () => { + + const gradebookList = document.getElementById("gradebookList"); + const categoryList = document.getElementById("categoryList"); + + const associate = document.getElementById("associate-gradebook-item"); + associate && associate.addEventListener("change", e => { + + gradebookList && (gradebookList.style.display = e.target.checked ? "block" : "none"); + categoryList && (categoryList.style.display = e.target.checked ? "none" : "block"); + }); + + const create = document.getElementById("create-gradebook-item"); + categoryList && (categoryList.style.display = create.hasAttribute("checked") ? "block" : "none"); + create && create.addEventListener("click", e => { + + categoryList && (categoryList.style.display = e.target.checked ? "block" : "none"); + gradebookList && (gradebookList.style.display = e.target.checked ? "none" : "block"); + }); +}); diff --git a/assignment/tool/src/webapp/vm/assignment/instructor_new_edit/grading_section.vm b/assignment/tool/src/webapp/vm/assignment/instructor_new_edit/grading_section.vm index 2e2e5f0a5e16..eed0849253b8 100644 --- a/assignment/tool/src/webapp/vm/assignment/instructor_new_edit/grading_section.vm +++ b/assignment/tool/src/webapp/vm/assignment/instructor_new_edit/grading_section.vm @@ -23,215 +23,193 @@ This section contains the options for grading this assignment, including rubrics #end ## Grading Options -#if ($withGrade) -
- +
+ +
+ +
+
+ + $tlang.getString("newassig.selectmessage") +
-
+
+ ## enable the maxfield when it is points grade type
- - $tlang.getString("newassig.selectmessage") - + +
-
- ## enable the maxfield when it is points grade type -
- - -
- - ## Rubrics -
- - -
- - #if ($!withGrade && $!withGradebook) - ## show the "Add to Gradebook" choices or not - #if(!$!allowGroupAssignmentsInGradebook) - #set ($addToGBDisplay="none") + ## Rubrics +
+ -
-
## Send to Gradebook -
+ +
+
+
+ #if (!$!noAddToGradebookChoice) +
## Add to Gradebook +
-
- #if (!$!noAddToGradebookChoice) -
## Add to Gradebook - -
- #if ($!value_totalCategories > 1) - ## show the "Category list" choices or not -
- - -
+ #if ($!value_totalCategories > 1) + ## show the "Category list" choices or not + + #end + #end - ## if there are assignment entries in Gradebook - #if (!$!gradebookAssignmentsLabel.isEmpty()) -
## Associate with Existing Gradebook Item - -
-
- - -
- #end + ## if there are assignment entries in Gradebook + #if (!$!gradebookAssignmentsLabel.isEmpty()) +
## Associate with Existing Gradebook Item +
-
+
+ + +
+ #end
- #end +
+
- #if ($allowPeerAssessment) - #set($groupAssignment = false) - #if ($value_AssignTo == $value_AssignTo_Groups && $!groupsList && !$selectedGroupsNotFound) - #set($groupAssignment = true) + #if ($allowPeerAssessment) + #set($groupAssignment = false) + #if ($value_AssignTo == $value_AssignTo_Groups && $!groupsList && !$selectedGroupsNotFound) + #set($groupAssignment = true) + #end + -#end +
diff --git a/basiclti/basiclti-blis/pom.xml b/basiclti/basiclti-blis/pom.xml index 8a689d0049f2..f47fb8cc4cdf 100644 --- a/basiclti/basiclti-blis/pom.xml +++ b/basiclti/basiclti-blis/pom.xml @@ -43,8 +43,8 @@ sakai-component-manager - org.sakaiproject.edu-services.gradebook - gradebook-service-api + org.sakaiproject.grading + sakai-grading-api javax.servlet diff --git a/basiclti/basiclti-blis/src/java/org/sakaiproject/lti13/LTI13Servlet.java b/basiclti/basiclti-blis/src/java/org/sakaiproject/lti13/LTI13Servlet.java index 79c4fd2272ec..1b83d7cf69c9 100644 --- a/basiclti/basiclti-blis/src/java/org/sakaiproject/lti13/LTI13Servlet.java +++ b/basiclti/basiclti-blis/src/java/org/sakaiproject/lti13/LTI13Servlet.java @@ -99,11 +99,10 @@ import org.sakaiproject.lti13.util.SakaiAccessToken; import org.sakaiproject.lti13.util.SakaiLineItem; -import org.sakaiproject.service.gradebook.shared.AssessmentNotFoundException; -import org.sakaiproject.service.gradebook.shared.Assignment; -import org.sakaiproject.service.gradebook.shared.CommentDefinition; -import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException; -import org.sakaiproject.service.gradebook.shared.GradebookService; +import org.sakaiproject.grading.api.AssessmentNotFoundException; +import org.sakaiproject.grading.api.Assignment; +import org.sakaiproject.grading.api.CommentDefinition; +import org.sakaiproject.grading.api.GradingService; import org.sakaiproject.site.api.Group; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.cover.SiteService; @@ -1992,8 +1991,8 @@ private void resultsForAssignment(String signed_placement, Site site, Assignment response.setContentType(Result.CONTENT_TYPE_CONTAINER); // Look up the assignment so we can find the max points - GradebookService g = (GradebookService) ComponentManager - .get("org.sakaiproject.service.gradebook.GradebookService"); + GradingService g = (GradingService) ComponentManager + .get("org.sakaiproject.grading.api.GradingService"); Session sess = SessionManager.getCurrentSession(); // Indicate "who" is reading this grade - needs to be a real user account @@ -2053,7 +2052,7 @@ private void resultsForAssignment(String signed_placement, Site site, Assignment if (commentDef != null) { result.comment = commentDef.getCommentText(); } - } catch(AssessmentNotFoundException | GradebookNotFoundException e) { + } catch(AssessmentNotFoundException e) { log.error(e.getMessage(), e); // Unexpected break; } @@ -2062,7 +2061,7 @@ private void resultsForAssignment(String signed_placement, Site site, Assignment result.resultScore = null; try { actualGrade = g.getAssignmentScoreString(context_id, a.getId(), user.getId()); - } catch(AssessmentNotFoundException | GradebookNotFoundException e) { + } catch(AssessmentNotFoundException e) { log.error(e.getMessage(), e); // Unexpected break; } diff --git a/basiclti/basiclti-common/pom.xml b/basiclti/basiclti-common/pom.xml index f744335d5de2..bbf48e7ef0ed 100644 --- a/basiclti/basiclti-common/pom.xml +++ b/basiclti/basiclti-common/pom.xml @@ -52,14 +52,14 @@ sakai-portal-util ${sakai.version} - - org.sakaiproject.edu-services.gradebook - gradebook-service-api - org.sakaiproject.assignment sakai-assignment-api + + org.sakaiproject.grading + sakai-grading-api + com.googlecode.json-simple json-simple diff --git a/basiclti/basiclti-common/src/java/org/sakaiproject/basiclti/util/SakaiBLTIUtil.java b/basiclti/basiclti-common/src/java/org/sakaiproject/basiclti/util/SakaiBLTIUtil.java index f6c725dc6649..15fdf1d87fb4 100644 --- a/basiclti/basiclti-common/src/java/org/sakaiproject/basiclti/util/SakaiBLTIUtil.java +++ b/basiclti/basiclti-common/src/java/org/sakaiproject/basiclti/util/SakaiBLTIUtil.java @@ -68,14 +68,13 @@ import org.sakaiproject.lti.api.LTIService; import org.sakaiproject.portal.util.CSSUtils; import org.sakaiproject.portal.util.ToolUtils; -import org.sakaiproject.service.gradebook.shared.AssessmentNotFoundException; +import org.sakaiproject.grading.api.AssessmentNotFoundException; // We don't import either of these to make sure we are never confused and always fully qualify -// import org.sakaiproject.service.gradebook.shared.Assignment; // We call this a "column" +// import org.sakaiproject.grading.api.Assignment; // We call this a "column" // import org.sakaiproject.assignment.api.model.Assignment // We call this an "assignment" -import org.sakaiproject.service.gradebook.shared.CommentDefinition; -import org.sakaiproject.service.gradebook.shared.ConflictingAssignmentNameException; -import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException; -import org.sakaiproject.service.gradebook.shared.GradebookService; +import org.sakaiproject.grading.api.CommentDefinition; +import org.sakaiproject.grading.api.ConflictingAssignmentNameException; +import org.sakaiproject.grading.api.GradingService; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.ToolConfiguration; import org.sakaiproject.site.cover.SiteService; @@ -2357,8 +2356,8 @@ private static Object handleGradebook(String sourcedid, HttpServletRequest reque } // Look up the gradebook column so we can find the max points - GradebookService g = (GradebookService) ComponentManager - .get("org.sakaiproject.service.gradebook.GradebookService"); + GradingService g = (GradingService) ComponentManager + .get("org.sakaiproject.grading.api.GradingService"); // Make sure the user exists in the site boolean userExistsInSite = false; @@ -2407,7 +2406,7 @@ private static Object handleGradebook(String sourcedid, HttpServletRequest reque SakaiLineItem lineItem = new SakaiLineItem(); lineItem.scoreMaximum = 100.0D; - org.sakaiproject.service.gradebook.shared.Assignment gradebookColumn = getGradebookColumn(site, user_id, title, lineItem); + org.sakaiproject.grading.api.Assignment gradebookColumn = getGradebookColumn(site, user_id, title, lineItem); if (gradebookColumn == null) { log.warn("gradebookColumn or Id is null, cannot proceed with grading in site {} for column {}", siteId, title); return "Grade failure siteId=" + siteId; @@ -2475,7 +2474,7 @@ public static Object handleGradebookLTI13(Site site, Long tool_id, Map content, SakaiLineItem lineItem) { // Look up the assignment so we can find the max points - GradebookService g = (GradebookService) ComponentManager - .get("org.sakaiproject.service.gradebook.GradebookService"); + GradingService g = (GradingService) ComponentManager + .get("org.sakaiproject.grading.api.GradingService"); if (lineItem.scoreMaximum == null) { lineItem.scoreMaximum = 100.0; @@ -182,7 +181,7 @@ public static Assignment createLineItem(String context_id, Long tool_id, Map getColumnsForToolDAO(String context_id, Long tool_id) { List retval = new ArrayList(); - GradebookService g = (GradebookService) ComponentManager - .get("org.sakaiproject.service.gradebook.GradebookService"); + GradingService g = (GradingService) ComponentManager + .get("org.sakaiproject.grading.api.GradingService"); pushAdvisor(); try { @@ -311,9 +307,6 @@ protected static List getColumnsForToolDAO(String context_id, Long t retval.add(gbColumn); } - } catch (GradebookNotFoundException e) { - log.error("Gradebook not found context_id={}", context_id); - retval = null; } catch (Throwable e) { log.error("Unexpected Throwable", e.getMessage()); retval = null; @@ -348,8 +341,8 @@ public static Assignment getColumnByKeyDAO(String context_id, Long tool_id, Long */ protected static Assignment getColumnByLabelDAO(String context_id, Long tool_id, String column_label) { - GradebookService g = (GradebookService) ComponentManager - .get("org.sakaiproject.service.gradebook.GradebookService"); + GradingService g = (GradingService) ComponentManager + .get("org.sakaiproject.grading.api.GradingService"); Assignment retval = null; pushAdvisor(); @@ -364,9 +357,6 @@ protected static Assignment getColumnByLabelDAO(String context_id, Long tool_id, break; } } - } catch (GradebookNotFoundException e) { - log.error("Gradebook not found context_id={}", context_id); - retval = null; } catch (Throwable e) { log.error("Unexpected Throwable", e.getMessage()); retval = null; @@ -388,8 +378,8 @@ protected static boolean deleteAssignmentByKeyDAO(String context_id, Long tool_i // Make sure it belongs to us Assignment a = getColumnByKeyDAO(context_id, tool_id, column_id); if ( a == null ) return false; - GradebookService g = (GradebookService) ComponentManager - .get("org.sakaiproject.service.gradebook.GradebookService"); + GradingService g = (GradingService) ComponentManager + .get("org.sakaiproject.grading.api.GradingService"); pushAdvisor(); try { @@ -425,8 +415,8 @@ public static List getLineItemsForTool(String signed_placement, S if ( tool_id == null ) { throw new RuntimeException("tool_id is required"); } - GradebookService g = (GradebookService) ComponentManager - .get("org.sakaiproject.service.gradebook.GradebookService"); + GradingService g = (GradingService) ComponentManager + .get("org.sakaiproject.grading.api.GradingService"); List retval = new ArrayList<>(); @@ -457,8 +447,6 @@ public static List getLineItemsForTool(String signed_placement, S } retval.add(item); } - } catch (GradebookNotFoundException e) { - log.error("Gradebook not found context_id={}", context_id); } catch (Throwable e) { log.error("Unexpected Throwable", e.getMessage()); } finally { diff --git a/basiclti/basiclti-portlet/pom.xml b/basiclti/basiclti-portlet/pom.xml index 55f3c55c5016..8a2c335b096c 100644 --- a/basiclti/basiclti-portlet/pom.xml +++ b/basiclti/basiclti-portlet/pom.xml @@ -61,6 +61,10 @@ org.sakaiproject.entitybroker entitybroker-utils + + org.sakaiproject.grading + sakai-grading-api + org.springframework spring-core @@ -72,11 +76,6 @@ org.springframework spring-web - - - org.sakaiproject.edu-services.gradebook - gradebook-service-api - provided com.fasterxml.jackson.core diff --git a/basiclti/basiclti-portlet/src/java/org/sakaiproject/portlets/IMSBLTIPortlet.java b/basiclti/basiclti-portlet/src/java/org/sakaiproject/portlets/IMSBLTIPortlet.java index 2c55e868fb0d..580d50f7d176 100644 --- a/basiclti/basiclti-portlet/src/java/org/sakaiproject/portlets/IMSBLTIPortlet.java +++ b/basiclti/basiclti-portlet/src/java/org/sakaiproject/portlets/IMSBLTIPortlet.java @@ -67,10 +67,9 @@ import org.sakaiproject.event.api.NotificationService; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.portlet.util.PortletHelper; -import org.sakaiproject.service.gradebook.shared.Assignment; -import org.sakaiproject.service.gradebook.shared.ConflictingAssignmentNameException; -import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException; -import org.sakaiproject.service.gradebook.shared.GradebookService; +import org.sakaiproject.grading.api.Assignment; +import org.sakaiproject.grading.api.ConflictingAssignmentNameException; +import org.sakaiproject.grading.api.GradingService; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SitePage; import org.sakaiproject.site.api.ToolConfiguration; @@ -876,10 +875,10 @@ protected boolean addGradeBookItem(ActionRequest request, String assignmentName) { try { - GradebookService g = (GradebookService) ComponentManager.get("org.sakaiproject.service.gradebook.GradebookService"); + GradingService g = (GradingService) ComponentManager.get("org.sakaiproject.grading.api.GradingService"); String gradebookUid = getContext(); - if ( ! (g.isGradebookDefined(gradebookUid) && (g.currentUserHasEditPerm(gradebookUid) || g.currentUserHasGradingPerm(gradebookUid)) && g.currentUserHasGradeAllPerm(gradebookUid) ) ) return false; + if ( ! ((g.currentUserHasEditPerm(gradebookUid) || g.currentUserHasGradingPerm(gradebookUid)) && g.currentUserHasGradeAllPerm(gradebookUid) ) ) return false; // add assignment to gradebook Assignment asn = new Assignment(); @@ -897,7 +896,7 @@ protected boolean addGradeBookItem(ActionRequest request, String assignmentName) } catch (Exception e) { - log.warn("GradebookNotFoundException (may be because GradeBook has not yet been added to the Site) {}", e.getMessage()); + log.warn("Exception (may be because GradeBook has not yet been added to the Site) {}", e.getMessage()); setErrorMessage(request, rb.getString("error.gradable.badcreate") + ":" + e.getMessage() ); log.warn("{}:addGradeItem {}", this, e.getMessage()); } @@ -908,29 +907,21 @@ protected boolean addGradeBookItem(ActionRequest request, String assignmentName) protected List getGradeBookAssignments() { List retval = new ArrayList(); - try - { - GradebookService g = (GradebookService) ComponentManager - .get("org.sakaiproject.service.gradebook.GradebookService"); - - String gradebookUid = getContext(); - if ( ! (g.isGradebookDefined(gradebookUid) && (g.currentUserHasEditPerm(gradebookUid) || g.currentUserHasGradingPerm(gradebookUid)) && g.currentUserHasGradeAllPerm(gradebookUid) ) ) return null; - List gradebookAssignments = g.getAssignments(gradebookUid); - - // filtering out anything externally provided - for (Iterator i=gradebookAssignments.iterator(); i.hasNext();) - { - org.sakaiproject.service.gradebook.shared.Assignment gAssignment = (org.sakaiproject.service.gradebook.shared.Assignment) i.next(); - if ( gAssignment.isExternallyMaintained() ) continue; - retval.add(gAssignment.getName()); - } - return retval; - } - catch (GradebookNotFoundException e) - { - log.warn("GradebookNotFoundException (may be because GradeBook has not yet been added to the Site) {}", e.getMessage()); - return null; - } + GradingService g = (GradingService) ComponentManager + .get("org.sakaiproject.grading.api.GradingService"); + + String gradebookUid = getContext(); + if ( ! ((g.currentUserHasEditPerm(gradebookUid) || g.currentUserHasGradingPerm(gradebookUid)) && g.currentUserHasGradeAllPerm(gradebookUid) ) ) return null; + List gradebookAssignments = g.getAssignments(gradebookUid); + + // filtering out anything externally provided + for (Iterator i=gradebookAssignments.iterator(); i.hasNext();) + { + org.sakaiproject.grading.api.Assignment gAssignment = (org.sakaiproject.grading.api.Assignment) i.next(); + if ( gAssignment.getExternallyMaintained() ) continue; + retval.add(gAssignment.getName()); + } + return retval; } } diff --git a/basiclti/basiclti-tool/pom.xml b/basiclti/basiclti-tool/pom.xml index b2088a9c17b2..961a5d0e227b 100644 --- a/basiclti/basiclti-tool/pom.xml +++ b/basiclti/basiclti-tool/pom.xml @@ -42,6 +42,10 @@ org.sakaiproject.kernel sakai-component-manager + + org.sakaiproject.grading + sakai-grading-api + ${sakai.velocity.groupId} sakai-velocity-tool-api @@ -54,10 +58,6 @@ org.sakaiproject.portal sakai-portal-util - - org.sakaiproject.edu-services.gradebook - gradebook-service-api - io.jsonwebtoken jjwt-api diff --git a/basiclti/basiclti-tool/src/java/org/sakaiproject/blti/tool/LTIAdminTool.java b/basiclti/basiclti-tool/src/java/org/sakaiproject/blti/tool/LTIAdminTool.java index 44a42841680d..ea9076aaed09 100644 --- a/basiclti/basiclti-tool/src/java/org/sakaiproject/blti/tool/LTIAdminTool.java +++ b/basiclti/basiclti-tool/src/java/org/sakaiproject/blti/tool/LTIAdminTool.java @@ -60,7 +60,7 @@ import static org.tsugi.basiclti.BasicLTIUtil.getObject; import static org.tsugi.basiclti.BasicLTIUtil.getString; -import org.sakaiproject.service.gradebook.shared.Assignment; +import org.sakaiproject.grading.api.Assignment; import org.sakaiproject.basiclti.util.SakaiBLTIUtil; import org.sakaiproject.cheftool.Context; import org.sakaiproject.cheftool.JetspeedRunData; diff --git a/content-review/impl/compilatio/pom.xml b/content-review/impl/compilatio/pom.xml index e0803ed73a55..4609f588203f 100644 --- a/content-review/impl/compilatio/pom.xml +++ b/content-review/impl/compilatio/pom.xml @@ -95,10 +95,6 @@ org.sakaiproject.kernel sakai-kernel-util - - org.sakaiproject.edu-services.gradebook - gradebook-service-api - org.springframework spring-test diff --git a/content-review/impl/turnitin/pom.xml b/content-review/impl/turnitin/pom.xml index 3e4a52d79199..6fb1f000de86 100644 --- a/content-review/impl/turnitin/pom.xml +++ b/content-review/impl/turnitin/pom.xml @@ -88,10 +88,10 @@ org.sakaiproject.kernel sakai-kernel-util - - org.sakaiproject.edu-services.gradebook - gradebook-service-api - + + org.sakaiproject.grading + sakai-grading-api + org.springframework spring-test diff --git a/content-review/impl/turnitin/src/main/java/org/sakaiproject/contentreview/turnitin/TurnitinReviewServiceImpl.java b/content-review/impl/turnitin/src/main/java/org/sakaiproject/contentreview/turnitin/TurnitinReviewServiceImpl.java index fc622a9e2858..cb4335d29384 100644 --- a/content-review/impl/turnitin/src/main/java/org/sakaiproject/contentreview/turnitin/TurnitinReviewServiceImpl.java +++ b/content-review/impl/turnitin/src/main/java/org/sakaiproject/contentreview/turnitin/TurnitinReviewServiceImpl.java @@ -67,10 +67,8 @@ import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.exception.TypeException; -import org.sakaiproject.service.gradebook.shared.Assignment; -import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService; -import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException; -import org.sakaiproject.service.gradebook.shared.GradebookService; +import org.sakaiproject.grading.api.Assignment; +import org.sakaiproject.grading.api.GradingService; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SiteService; import org.sakaiproject.tool.api.Session; @@ -234,10 +232,7 @@ public class TurnitinReviewServiceImpl extends BaseContentReviewService { private TurnitinContentValidator turnitinContentValidator; @Setter - private GradebookService gradebookService; - - @Setter - private GradebookExternalAssessmentService gradebookExternalAssessmentService; + private GradingService gradingService; @Setter private SecurityService securityService; @@ -575,14 +570,13 @@ public Assignment getAssociatedGbItem(Map data) { SecurityAdvisor advisor = pushAdvisor(); try { - List allGbItems = gradebookService.getAssignments(siteId); + List allGbItems = gradingService.getAssignments(siteId); for (Assignment assign : allGbItems) { // Match based on External ID / Assignment title if (taskId.equals(assign.getExternalId()) || assign.getName().equals(taskTitle)) { assignment = assign; } } - } catch (GradebookNotFoundException ignore) { } finally { popAdvisor(advisor); } @@ -602,7 +596,7 @@ public void syncGrades(Map data) { boolean isStudent = isUserStudent(data.get("siteId").toString(), sess.getUserId()); String siteId = data.get("siteId").toString(); - if (!runOnce && !isStudent && turnitinConn.getUseGradeMark() && gradebookService.isGradebookDefined(siteId)) { + if (!runOnce && !isStudent && turnitinConn.getUseGradeMark()) { log.info("Syncing Grades with Turnitin"); String taskId = data.get("taskId").toString(); @@ -670,8 +664,6 @@ public void syncGrades(Map data) { log.debug("Report list request not successful"); log.debug(document.getTextContent()); } - } catch (GradebookNotFoundException e) { - log.warn("Failed to find gradebook for site: "+ e.getMessage()); } catch (TransientSubmissionException e) { log.error(e.getMessage()); } catch (SubmissionException e) { @@ -746,17 +738,15 @@ public boolean writeGrade(Assignment assignment, Map data, HashM if (grade != null) { try { if (data.containsKey("assignment1")) { - gradebookExternalAssessmentService.updateExternalAssessmentScore(siteId, + gradingService.updateExternalAssessmentScore(siteId, assignment.getExternalId(), tiiExternalId, grade); } else { - gradebookService.setAssignmentScoreString(siteId, data.get("taskTitle").toString(), + gradingService.setAssignmentScoreString(siteId, data.get("taskTitle").toString(), tiiExternalId, grade, "SYNC"); } log.info("UPDATED GRADE (" + grade + ") FOR USER (" + tiiExternalId + ") IN ASSIGNMENT (" + assignment.getName() + ")"); success = true; - } catch (GradebookNotFoundException e) { - log.error("Error update grade GradebookNotFoundException " + e.toString()); } catch (Exception e) { log.error("Error update grade " + e.toString()); } diff --git a/content-review/impl/turnitin/src/test/resources/hibernate.properties b/content-review/impl/turnitin/src/test/resources/hibernate.properties index b7b581133e09..0d1b25253643 100644 --- a/content-review/impl/turnitin/src/test/resources/hibernate.properties +++ b/content-review/impl/turnitin/src/test/resources/hibernate.properties @@ -1,6 +1,6 @@ # Base Hibernate settings hibernate.show_sql=false -hibernate.hbm2ddl.auto=create +hibernate.hbm2ddl.auto=create-only hibernate.cache.use_second_level_cache=false # Connection definition to the HSQLDB database diff --git a/content-review/pack/src/webapp/WEB-INF/turnitin.xml b/content-review/pack/src/webapp/WEB-INF/turnitin.xml index 9dd1db9a1d2c..22864ee175e4 100644 --- a/content-review/pack/src/webapp/WEB-INF/turnitin.xml +++ b/content-review/pack/src/webapp/WEB-INF/turnitin.xml @@ -21,8 +21,7 @@ - - + diff --git a/edu-services/gradebook-service/api/pom.xml b/edu-services/gradebook-service/api/pom.xml deleted file mode 100644 index 647e5333debd..000000000000 --- a/edu-services/gradebook-service/api/pom.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - 4.0.0 - - - edu-services - org.sakaiproject.edu-services - 23-SNAPSHOT - - ../../pom.xml - - - org.sakaiproject.edu-services.gradebook - gradebook-service-api - gradebook-service-api - jar - - - shared - - - - - org.sakaiproject.kernel - sakai-component-manager - - - org.sakaiproject.kernel - sakai-kernel-api - - - org.apache.commons - commons-lang3 - - - org.sakaiproject.edu-services.sections - sections-api - provided - - - diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/Assignment.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/Assignment.java deleted file mode 100644 index 8f7946e42398..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/Assignment.java +++ /dev/null @@ -1,187 +0,0 @@ -/********************************************************************************** - * $URL$ - * $Id$ - *********************************************************************************** - * - * Copyright (c) 2006, 2007, 2008 The Sakai Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.opensource.org/licenses/ECL-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - **********************************************************************************/ - -package org.sakaiproject.service.gradebook.shared; - -import java.io.Serializable; -import java.util.Date; - -import org.apache.commons.lang3.builder.CompareToBuilder; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -/** - * JavaBean to hold data associated with a Gradebook assignment. The Course Grade is not considered an assignment. - */ -@NoArgsConstructor -@ToString -public class Assignment implements Serializable, Comparable { - private static final long serialVersionUID = 1L; - - /** - * @return Returns the name of the assignment. The assignment name is unique among currently defined assignments. However, it is not a - * safe UID for persistance, since an assignment can be renamed. Also, an assignment can be deleted and a new assignment can be - * created re-using the old name. - */ - @Getter - @Setter - private String name; - - /** - * - * @return Returns the ID of the assignment in the gradebook - */ - @Getter - @Setter - private Long id; - - /** - * the total points the assignment is worth. - */ - @Getter - @Setter - private Double points; - - /** - * the due date for the assignment, or null if none is defined. - */ - @Getter - @Setter - private Date dueDate; - - /** - * true if the assignment is maintained by some software other than the Gradebook itself. - */ - @Getter - @Setter - private boolean externallyMaintained; - - /** - * the external id, or null if the assignment is maintained by the Gradebook - */ - @Getter - @Setter - private String externalId; - - /** - * the external app name, or null if the assignment is maintained by the Gradebook - */ - @Getter - @Setter - private String externalAppName; - - /** - * the external data, or null if the assignment is maintained by the Gradebook - */ - @Getter - @Setter - private String externalData; - - /** - * true if the assignment has been released for view to students - */ - @Getter - @Setter - private boolean released; - - /** - * Note that any calls setSortOrder will not be persisted, if you want to change the sort order of an assignment you must call - * GradebookService.updateAssignmentOrder as that method properly handles the reordering of all other assignments for the gradebook. - */ - @Getter - @Setter - private Integer sortOrder; - @Getter - @Setter - private boolean counted; - @Getter - @Setter - private String categoryName; - @Getter - @Setter - private Double weight; - @Getter - @Setter - private boolean ungraded; - @Getter - @Setter - private boolean extraCredit; - @Getter - @Setter - private boolean categoryExtraCredit; - @Getter - @Setter - private boolean categoryEqualWeight; - @Getter - @Setter - private Long categoryId; - @Getter - @Setter - private Integer categoryOrder; - @Getter - @Setter - private Integer categorizedSortOrder; - - /** - * For editing. Not persisted. - */ - @Getter - @Setter - private boolean scaleGrades; - @Getter - @Setter - private String context; - @Getter - @Setter - private String gradebookId; - - - @Override - public int compareTo(final Assignment o) { - return new CompareToBuilder() - .append(this.id, o.id) - .toComparison(); - } - - @Override - public boolean equals(final Object o) { - if (!(o instanceof Assignment)) { - return false; - } - final Assignment other = (Assignment) o; - return new EqualsBuilder() - .append(this.id, other.id) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder() - .append(this.id) - .toHashCode(); - } - -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CategoryDefinition.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CategoryDefinition.java deleted file mode 100644 index e7aba16993d7..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CategoryDefinition.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.service.gradebook.shared; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.util.Comparator; -import java.util.List; - -import lombok.Getter; -import lombok.Setter; - -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - - -/** - * Provides information describing a gradebook category that may be useful - * to consumers of the shared gradebook services. Not persisted. - */ -public class CategoryDefinition implements Serializable { - - private static final long serialVersionUID = 1L; - - @Getter @Setter private Long id; - @Getter @Setter private String name; - @Getter @Setter private Double weight; - @Getter @Setter private Integer dropLowest; - @Getter @Setter private Integer dropHighest; - @Getter @Setter private Integer keepHighest; - @Getter @Setter private Boolean extraCredit; - @Getter @Setter private Boolean equalWeight; - @Getter @Setter private Integer categoryOrder; - @Getter @Setter private Boolean dropKeepEnabled; - - public static Comparator orderComparator; - - @Getter @Setter private List assignmentList; - - public CategoryDefinition() { - - } - - public CategoryDefinition(Long id, String name) { - this.id = id; - this.name = name; - } - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); - } - - static { - orderComparator = new Comparator() { - @Override - public int compare(final CategoryDefinition c1, final CategoryDefinition c2) { - if (c1.getCategoryOrder() == null && c2.getCategoryOrder() == null) { - return c1.getName().compareTo(c2.getName()); - } - if(c1.getCategoryOrder() == null) { - return -1; - } - if(c2.getCategoryOrder() == null) { - return 1; - } - return c1.getCategoryOrder().compareTo(c2.getCategoryOrder()); - } - }; - } - - /** - * Helper method to get the total points associated with a category - * - * @return the sum of all assignment totals associated with this category - */ - public Double getTotalPoints() { - BigDecimal totalPoints = new BigDecimal(0); - - if (getAssignmentList() != null) { - for (final Assignment assignment : getAssignmentList()) { - totalPoints = totalPoints.add(BigDecimal.valueOf(assignment.getPoints())); - } - } - return totalPoints.doubleValue(); - } - - public boolean isAssignmentInThisCategory(String assignmentId) { - for (Assignment thisAssignment : assignmentList) { - if (thisAssignment.getExternalId() == null) { - continue; - } - - if (thisAssignment.getExternalId().equalsIgnoreCase(assignmentId)) { - return true; - } - } - - return false; - } - - public Double getPointsForCategory() { - if (!dropKeepEnabled) { - return null; - } - - for (Assignment thisAssignment : assignmentList) { - return thisAssignment.getPoints(); - } - - return null; - } -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CommentDefinition.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CommentDefinition.java deleted file mode 100644 index df369306f7c7..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CommentDefinition.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2003-2012 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.service.gradebook.shared; - -import java.util.Date; - -/** - * - */ -public class CommentDefinition { - private String studentUid; - private String graderUid; - private Date dateRecorded; - private String commentText; - private String assignmentName; - - public String getAssignmentName() { - return assignmentName; - } - public void setAssignmentName(String assignmentName) { - this.assignmentName = assignmentName; - } - public String getCommentText() { - return commentText; - } - public void setCommentText(String commentText) { - this.commentText = commentText; - } - public Date getDateRecorded() { - return dateRecorded; - } - public void setDateRecorded(Date dateRecorded) { - this.dateRecorded = dateRecorded; - } - public String getGraderUid() { - return graderUid; - } - public void setGraderUid(String graderUid) { - this.graderUid = graderUid; - } - public String getStudentUid() { - return studentUid; - } - public void setStudentUid(String studentUid) { - this.studentUid = studentUid; - } -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingSpreadsheetNameException.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingSpreadsheetNameException.java deleted file mode 100644 index cc2101d413d9..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingSpreadsheetNameException.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006, 2008 The Sakai Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.opensource.org/licenses/ECL-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -package org.sakaiproject.service.gradebook.shared; - -/** - * Author:Louis Majanja - * Date: Aug 29, 2006 - * Time: 4:40:02 PM - */ -public class ConflictingSpreadsheetNameException extends GradebookException { - - public ConflictingSpreadsheetNameException(String message) { - super(message); - } -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CourseGrade.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CourseGrade.java deleted file mode 100644 index eadb2e1dca31..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CourseGrade.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.service.gradebook.shared; - -import java.io.Serializable; -import java.util.Date; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -/** - * Wrapper for the course grade that contains the the calculated grade (ie 46.67), the mapped grade (ie F) and any entered grade override (ie D-). - */ -public class CourseGrade implements Serializable { - - private static final long serialVersionUID = 1L; - - private Long id; - private String enteredGrade; - private Double enteredPoints; - private String calculatedGrade; - private String mappedGrade; - private Double pointsEarned; - private Double totalPointsPossible; - private Date dateRecorded; - - public CourseGrade() { - // - } - - /** - * ID of this course grade record. This will be null if the course grade is calculated, and non null if we have an override (as it then refers to the course grade assignment id). - * @return - */ - public Long getId() { - return this.id; - } - - public void setId(final Long id) { - this.id = id; - } - - public String getEnteredGrade() { - return this.enteredGrade; - } - - public void setEnteredGrade(final String enteredGrade) { - this.enteredGrade = enteredGrade; - } - - public Double getEnteredPoints() { - return this.enteredPoints; - } - - public void setEnteredPoints(final Double enteredPoints) { - this.enteredPoints = enteredPoints; - } - - public String getCalculatedGrade() { - return this.calculatedGrade; - } - - public void setCalculatedGrade(final String calculatedGrade) { - this.calculatedGrade = calculatedGrade; - } - - public String getMappedGrade() { - return this.mappedGrade; - } - - public void setMappedGrade(final String mappedGrade) { - this.mappedGrade = mappedGrade; - } - - public Double getPointsEarned() { - return this.pointsEarned; - } - - public void setPointsEarned(final Double pointsEarned) { - this.pointsEarned = pointsEarned; - } - - /** - * This value is only accurate when there are no weighted categories. - * If weighting is enabled, this value will not be what you expect. - * For this reason, this value should not be used when weighted categories are enabled. - * @return Double representing the total points possible, see caveat. - */ - public Double getTotalPointsPossible() { - return this.totalPointsPossible; - } - - public void setTotalPointsPossible(final Double totalPointsPossible) { - this.totalPointsPossible = totalPointsPossible; - } - - public Date getDateRecorded() { - return this.dateRecorded; - } - - public void setDateRecorded(final Date dateRecorded) { - this.dateRecorded = dateRecorded; - } - - /** - * Helper to get a grade override preferentially, or fallback to the standard mapped grade. - * @return - */ - public String getDisplayGrade() { - return (StringUtils.isNotBlank(getEnteredGrade()) ? getEnteredGrade() : getMappedGrade()); - } - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); - } - -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ExternalAssignmentProvider.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ExternalAssignmentProvider.java deleted file mode 100644 index b4df4b110333..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ExternalAssignmentProvider.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) 2003-2013 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.service.gradebook.shared; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * Provider model to integrate external assessment sources with the gradebook. - * Its present use is to provide advice about activity grouping and visibility - * so that learners are not shown activities in which they cannot participate. - * - * https://jira.sakaiproject.org/browse/SAK-19668 - * @since 2.9.0 - */ -public interface ExternalAssignmentProvider { - - /** - * Get the application identifier for this provider. This must - * be unique to the external tool/service for proper operation. - */ - String getAppKey(); - - /** - * Check if an assignment/assessment exists with the given identifier. - * If the externalAppName is not the tool's responsibility or if the - * id is not recognized as matching - * for this service, false is expected to be returned. - * @param externalId - */ - boolean isAssignmentDefined(String externalAppName, String externalId); - - /** - * Check if the given assignment is grouped. - * Note that this will be a prefixed ID and must be parsed. - * - * @param id The prefixed external ID as registered with Gradebook - */ - boolean isAssignmentGrouped(String id); - - /** - * Check if the given assignment is visible to the given user. The primary - * use of this check is to see if groups are in effect for the assignment - * and whether or not the user can see the item. - * - * @param id The prefixed external ID as registered with Gradebook - * @param userId The internal user ID for the user to check - */ - boolean isAssignmentVisible(String id, String userId); - - /** - * Retrieve all assignments for a gradebook that are marked as externally - * maintained and are visible to the current user. - * - * @param gradebookUid The gradebook's unique identifier - * @return A list of IDs (as for externalId) of assignments visible to the current user - */ - List getExternalAssignmentsForCurrentUser(String gradebookUid); - - /** - * Retrieve all assignments for a gradebook that are marked as externally - * maintained and are (potentially variably) visible to a set of users. - * - * @param gradebookUid The gradebook's unique identifier - * @param studentIds The collection of user IDs to search/filter for - * @return A map of Student ID to list of external IDs of assignments visible to each user - */ - Map> getAllExternalAssignments(String gradebookUid, Collection studentIds); -} - diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradeDefinition.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradeDefinition.java deleted file mode 100644 index 3bc1035e3ee7..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradeDefinition.java +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright (c) 2003-2012 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.service.gradebook.shared; - -import org.apache.commons.lang3.BooleanUtils; -import java.util.Date; - -/** - * - */ -public class GradeDefinition { - private String studentUid; - private String graderUid; - private Date dateRecorded; - private String grade; - private String gradeComment; - private int gradeEntryType; - private boolean gradeReleased; - private boolean excused; - - public String getStudentUid() { - return studentUid; - } - public void setStudentUid(String studentUid) { - this.studentUid = studentUid; - } - - public String getGraderUid() { - return graderUid; - } - public void setGraderUid(String graderUid) { - this.graderUid = graderUid; - } - - public Date getDateRecorded() { - return dateRecorded; - } - public void setDateRecorded(Date dateRecorded) { - this.dateRecorded = dateRecorded; - } - - /** - * - * @return current grade for this student in the format according to - * the gradebook's grade entry type ie %, letter, points - */ - public String getGrade() { - return grade; - } - - /** - * current grade for this student in the format according to - * the gradebook's grade entry type ie %, letter, points - * @param grade - */ - public void setGrade(String grade) { - this.grade = grade; - } - - public String getGradeComment() - { - return gradeComment; - } - - public void setGradeComment(String gradeComment) - { - this.gradeComment = gradeComment; - } - - /** - * - * @return constant equivalent to the grade entry type for the gb - - * %, letter, points, etc. lets you know what format the given grade will - * be in - */ - public int getGradeEntryType() { - return gradeEntryType; - } - - /** - * constant equivalent to the grade entry type for the gb - - * %, letter, points, etc. lets you know what format the given grade will - * be in - * @param gradeEntryType - */ - public void setGradeEntryType(int gradeEntryType) { - this.gradeEntryType = gradeEntryType; - } - - /** - * - * @return true if this grade has been released to the student - */ - public boolean isGradeReleased() { - return gradeReleased; - } - - /** - * true if this grade has been released to the student - * @param gradeReleased - */ - public void setGradeReleased(boolean gradeReleased) { - this.gradeReleased = gradeReleased; - } - - public void setExcused(Boolean excuse){ - this.excused = BooleanUtils.toBoolean(excuse); - } - - /** - * @return true if this grade is excused - */ - public boolean isExcused(){ - return excused; - } -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradeMappingDefinition.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradeMappingDefinition.java deleted file mode 100644 index 663000ee6a5b..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradeMappingDefinition.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.service.gradebook.shared; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.TreeMap; - -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -/** - * DTO to wrap the persistent GradeMapping and provides utility methods for dealing with grade mappings - */ -public class GradeMappingDefinition implements Serializable { - - private static final long serialVersionUID = 1L; - - private String id; //note that this is a Long in GradeMapping but we convert for simplicity - private String name; - private Map gradeMap; - private Map defaultBottomPercents; - - public GradeMappingDefinition(final Long id, final String name, final Map gradeMap, final Map defaultBottomPercents){ - this.id = Long.toString(id); - this.name = name; - this.gradeMap = gradeMap; - this.defaultBottomPercents = defaultBottomPercents; - } - - public String getId() { - return this.id; - } - - public void setId(final String id) { - this.id = id; - } - - public String getName() { - return this.name; - } - - public void setName(final String name) { - this.name = name; - } - - /** - * Get the current grade mappings - * - * @return - */ - public Map getGradeMap() { - return this.gradeMap; - } - - public void setGradeMap(final Map gradeMap) { - this.gradeMap = gradeMap; - } - - /** - * Get the default grade mappings - * - * @return - */ - public Map getDefaultBottomPercents() { - return this.defaultBottomPercents; - } - - public void setDefaultBottomPercents(final Map defaultBottomPercents) { - this.defaultBottomPercents = defaultBottomPercents; - } - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); - } - - /** - * Handles the sorting of a grade mapping. - * - * @param gradeMap the grademap to be sorted - * @return {@link LinkedHashMap} of sorted entries - */ - public static Map sortGradeMapping(final Map gradeMap) { - - // we only ever order by bottom percents now - final DoubleComparator doubleComparator = new DoubleComparator(gradeMap); - final Map tMap = new TreeMap<>(doubleComparator); - tMap.putAll(gradeMap); - final Map rval = new LinkedHashMap<>(tMap); - - return rval; - } - - /** - * Determines if the grade mapping is different to the defaults - * - * @return - */ - public boolean isModified() { - // TreeMap.equals uses compareTo for comparisons so cannot be used for equals in this case. Convert to HashMap. - final Map left = new HashMap<>(this.gradeMap); - final Map right = new HashMap<>(this.defaultBottomPercents); - return !left.equals(right); - } - -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookArchiveService.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookArchiveService.java deleted file mode 100644 index c0b800e4498c..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookArchiveService.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) 2003-2012 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.service.gradebook.shared; - -import org.w3c.dom.Document; - -/** - * The gradebook archive service is currently not implemented for sakai2 - * - * @author Josh Holtzman - */ -public interface GradebookArchiveService { - public String createArchive(String gradebookUid, Document doc); - public String createGradebookFromArchive(String context, Document doc); -} - - - diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookConfiguration.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookConfiguration.java deleted file mode 100644 index d05f0612e672..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookConfiguration.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) 2003-2012 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.service.gradebook.shared; - -import java.util.Collection; - -/** - * Simple configuration bean. Set up the properties you want changed and - * call init() when the GradebookService is wired up. (We can't set them - * directly by injecting the GradebookService bean because that would - * bypass transaction proxying.) - */ -public class GradebookConfiguration { - private Collection gradingScaleDefinitions; - private String defaultGradingScale; - private GradebookFrameworkService gradebookFrameworkService; - - public void init() { - if (gradebookFrameworkService != null) { - if (gradingScaleDefinitions != null) { - gradebookFrameworkService.setAvailableGradingScales(gradingScaleDefinitions); - } - if (defaultGradingScale != null) { - gradebookFrameworkService.setDefaultGradingScale(defaultGradingScale); - } - } - } - - public void setAvailableGradingScales(Collection gradingScaleDefinitions) { - this.gradingScaleDefinitions = gradingScaleDefinitions; - } - - public void setDefaultGradingScale(String defaultGradingScale) { - this.defaultGradingScale = defaultGradingScale; - } - - public void setGradebookFrameworkService(GradebookFrameworkService gradebookFrameworkService) { - this.gradebookFrameworkService = gradebookFrameworkService; - } -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookExternalAssessmentService.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookExternalAssessmentService.java deleted file mode 100644 index d571cda5f8c3..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookExternalAssessmentService.java +++ /dev/null @@ -1,351 +0,0 @@ -/** - * Copyright (c) 2003-2015 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.service.gradebook.shared; - -import java.util.Date; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.OptionalLong; - -/** - * This service is designed for use by external assessment engines. These use - * the Gradebook as a passive mirror of their own assignments and scores, - * letting Gradebook users see those assignments alongside Gradebook-managed - * assignments, and combine them when calculating a course grade. The Gradebook - * application itself will not modify externally-managed assignments and scores. - * - * WARNING: Because the Gradebook project team is not responsible for - * defining the external clients' requirements, the Gradebook service does not - * attempt to guess at their authorization needs. Our administrative and - * external-assessment methods simply follow orders and assume that the caller - * has taken the responsibility of "doing the right thing." DO NOT wrap these - * methods in an open web service! - */ - -public interface GradebookExternalAssessmentService { - /** - * @deprecated Replaced by - * {@link addExternalAssessment(String, String, String, String, Double, Date, String, Boolean)} - */ - public void addExternalAssessment(String gradebookUid, String externalId, String externalUrl, - String title, double points, Date dueDate, String externalServiceDescription, String externalData) - throws GradebookNotFoundException, ConflictingAssignmentNameException, - ConflictingExternalIdException, AssignmentHasIllegalPointsException; - - /** - * Add an externally-managed assessment to a gradebook to be treated as a - * read-only assignment. The gradebook application will not modify the - * assessment properties or create any scores for the assessment. - * Since each assignment in a given gradebook must have a unique name, - * conflicts are possible. - * @param gradebookUid - * @param externalId some unique identifier which Samigo uses for the assessment. - * The externalId is globally namespaced within the gradebook, so - * if other apps decide to put assessments into the gradebook, - * they should prefix their externalIds with a well known (and - * unique within sakai) string. - * @param externalUrl a link to go to if the instructor or student wants to look at the assessment - * in Samigo; if null, no direct link will be provided in the - * gradebook, and the user will have to navigate to the assessment - * within the other application - * @param title - * @param points this is the total amount of points available and must be greater than zero. - * It could be null if it's an ungraded item. - * @param dueDate - * @param externalServiceDescription - * @param externalData if there is some data that the external service wishes to store. - * @param ungraded - * - * - */ - public void addExternalAssessment(String gradebookUid, String externalId, String externalUrl, String title, Double points, - Date dueDate, String externalServiceDescription, String externalData, Boolean ungraded) - throws GradebookNotFoundException, ConflictingAssignmentNameException, ConflictingExternalIdException, AssignmentHasIllegalPointsException; - - /** - * This method is identical to {@link #addExternalAssessment(String, String, String, String, Double, Date, String, String, Boolean)} but - * allows you to also specify the associated Category for this assignment. If the gradebook is set up for categories and - * categoryId is null, assignment category will be unassigned - * @param gradebookUid - * @param externalId - * @param externalUrl - * @param title - * @param points - * @param dueDate - * @param externalServiceDescription - * @param externalData if there is some data that the external service wishes to store. - * @param ungraded - * @param categoryId - * @throws GradebookNotFoundException - * @throws ConflictingAssignmentNameException - * @throws ConflictingExternalIdException - * @throws AssignmentHasIllegalPointsException - * @throws InvalidCategoryException - */ - public void addExternalAssessment(String gradebookUid, String externalId, String externalUrl, String title, Double points, - Date dueDate, String externalServiceDescription, String externalData, Boolean ungraded, Long categoryId) - throws GradebookNotFoundException, ConflictingAssignmentNameException, ConflictingExternalIdException, AssignmentHasIllegalPointsException, InvalidCategoryException; - - /** - * @deprecated Replaced by - * {@link updateExternalAssessment(String, String, String, String, Double, Date, Boolean)} - */ - public void updateExternalAssessment(String gradebookUid, String externalId, String externalUrl, String externalData, - String title, double points, Date dueDate) - throws GradebookNotFoundException, AssessmentNotFoundException, ConflictingAssignmentNameException, AssignmentHasIllegalPointsException; - - /** - * Update an external assessment - * @param gradebookUid - * @param externalId - * @param externalUrl - * @param externalData - * @param title - * @param points - * @param dueDate - * @param ungraded - * @throws GradebookNotFoundException - * @throws AssessmentNotFoundException - * @throws ConflictingAssignmentNameException - * @throws AssignmentHasIllegalPointsException - */ - public void updateExternalAssessment(String gradebookUid, String externalId, String externalUrl, String externalData, - String title, Double points, Date dueDate, Boolean ungraded) - throws GradebookNotFoundException, AssessmentNotFoundException, ConflictingAssignmentNameException, AssignmentHasIllegalPointsException; - - /** - * Remove the assessment reference from the gradebook. Although Samigo - * doesn't currently delete assessments, an instructor can retract an - * assessment to keep it from students. Since such an assessment would - * presumably no longer be used to calculate final grades, Samigo should - * also remove that assessment from the gradebook. - * - * @param externalId - * the UID of the assessment - */ - public void removeExternalAssessment(String gradebookUid, String externalId) - throws GradebookNotFoundException, AssessmentNotFoundException; - - /** - * Updates an external score for an external assignment in the gradebook. - * - * @param gradebookUid - * The Uid of the gradebook - * @param externalId - * The external ID of the assignment/assessment - * @param studentUid - * The unique id of the student - * @param points - * The number of points earned on this assessment, or null if a score - * should be removed - */ - public void updateExternalAssessmentScore(String gradebookUid, String externalId, - String studentUid, String points) - throws GradebookNotFoundException, AssessmentNotFoundException; - - /** - * - * @param gradebookUid - * @param externalId - * @param studentUidsToScores - * @throws GradebookNotFoundException - * @throws AssessmentNotFoundException - * - * @deprecated Replaced by - * {@link updateExternalAssessmentScoresString(String, String, Map studentUidsToScores) - throws GradebookNotFoundException, AssessmentNotFoundException; - - /** - * Updates a set of external scores for an external assignment in the gradebook. - * - * @param gradebookUid - * The Uid of the gradebook - * @param externalId - * The external ID of the assignment/assessment - * @param studentUidsToScores - * A map whose String keys are the unique ID strings of the students and whose - * String values are points earned on this assessment or null if the score - * should be removed. - */ - public void updateExternalAssessmentScoresString(String gradebookUid, - String externalId, Map studentUidsToScores) - throws GradebookNotFoundException, AssessmentNotFoundException; - - /** - * Updates an external comment for an external assignment in the gradebook. - * - * @param gradebookUid - * The Uid of the gradebook - * @param externalId - * The external ID of the assignment/assessment - * @param studentUid - * The unique id of the student - * @param comment - * The comment to be added to this grade, or null if a comment - * should be removed - */ - public void updateExternalAssessmentComment(String gradebookUid, - String externalId, String studentUid, String comment ) - throws GradebookNotFoundException, AssessmentNotFoundException; - /** - * Updates a set of external comments for an external assignment in the gradebook. - * - * @param gradebookUid - * The Uid of the gradebook - * @param externalId - * The external ID of the assignment/assessment - * @param studentUidsToScores - * A map whose String keys are the unique ID strings of the students and whose - * String values are comments or null if the comments - * should be removed. - */ - public void updateExternalAssessmentComments(String gradebookUid, - String externalId, Map studentUidsToComments) - throws GradebookNotFoundException, AssessmentNotFoundException; - - /** - * Check to see if an assignment with the given name already exists - * in the given gradebook. This will give external assessment systems - * a chance to avoid the ConflictingAssignmentNameException. - */ - public boolean isAssignmentDefined(String gradebookUid, String assignmentTitle) - throws GradebookNotFoundException; - - /** - * Check to see if an assignment with the given external id already exists - * in the given gradebook. This will give external assessment systems - * a chance to avoid the ConflictingExternalIdException. - * - * @param gradebookUid The gradebook's unique identifier - * @param externalId The external assessment's external identifier - */ - public boolean isExternalAssignmentDefined(String gradebookUid, String externalId) - throws GradebookNotFoundException; - - /** - * Check with the appropriate external service if a specific assignment is - * available only to groups. - * - * @param gradebookUid The gradebook's unique identifier - * @param externalId The external assessment's external identifier - */ - public boolean isExternalAssignmentGrouped(String gradebookUid, String externalId) - throws GradebookNotFoundException; - - /** - * Check with the appropriate external service if a specific assignment is - * available to a specific user (i.e., the user is in an appropriate group). - * Note that this method will return true if the assignment exists in the - * gradebook and is marked as externally maintained while no provider - * recognizes it; this is to maintain a safer default (no change from the - * 2.8 release) for tools that have not implemented a provider. - * - * @param gradebookUid The gradebook's unique identifier - * @param externalId The external assessment's external identifier - * @param userId The user ID to check - */ - public boolean isExternalAssignmentVisible(String gradebookUid, String externalId, String userId) - throws GradebookNotFoundException; - - /** - * Retrieve all assignments for a gradebook that are marked as externally - * maintained and are visible to the current user. Assignments may be included - * with a null providerAppKey, indicating that the gradebook references the - * assignment, but no provider claims responsibility for it. - * - * @param gradebookUid The gradebook's unique identifier - * @return A map from the externalId of each activity to the providerAppKey - */ - public Map getExternalAssignmentsForCurrentUser(String gradebookUid) - throws GradebookNotFoundException; - - /** - * Retrieve a list of all visible, external assignments for a set of users. - * - * @param gradebookUid The gradebook's unique identifier - * @param studentIds The collection of student IDs for which to retrieve assignments - * @return A map from the student ID to all visible, external activity IDs - */ - public Map> getVisibleExternalAssignments(String gradebookUid, Collection studentIds) - throws GradebookNotFoundException; - - /** - * Register a new ExternalAssignmentProvider for handling the integration of external - * assessment sources with the sakai gradebook - * Registering more than once will overwrite the current with the new one - * - * @param provider the provider implementation object - */ - public void registerExternalAssignmentProvider(ExternalAssignmentProvider provider); - - /** - * Remove/unregister any ExternalAssignmentProvider which is currently registered, - * does nothing if they provider does not exist - * - * @param providerAppKey the unique app key for a provider - */ - public void unregisterExternalAssignmentProvider(String providerAppKey); - - /** - * Checks to see whether a gradebook with the given uid exists. - * - * @param gradebookUid - * The gradebook UID to check - * @return Whether the gradebook exists - */ - public boolean isGradebookDefined(String gradebookUid); - - /** - * Break the connection between an external assessment engine and an assessment which - * it created, giving it up to the Gradebook application to control from now on. - * - * @param gradebookUid - * @param externalId - */ - public void setExternalAssessmentToGradebookAssignment(String gradebookUid, String externalId); - - /** - * Get the category of a gradebook with the externalId given - * - * @param gradebookUId - * @param externalId - * @return - */ - public Long getExternalAssessmentCategoryId(String gradebookUId, String externalId); - - /** - * Checks to see whether a gradebook has the categories option enabled. - * - * @param gradebookUid - * The gradebook UID to check - * @return Whether the gradebook has categories enabled - */ - public boolean isCategoriesEnabled(String gradebookUid); - - /** - * Get the internal ID of an externally managed assessment (gradebook item) by it's external ID - * @param gradebookUUID the UUID of the gradebook to check - * @param externalID the external ID of the assessment (gradebook item) in question - * @return Long value of the internal ID of the assessment (gradebook item) in question - * @throws GradebookNotFoundException - * @throws AssessmentNotFoundException - */ - public OptionalLong getInternalAssessmentID(String gradebookUUID, String externalID) throws GradebookNotFoundException, AssessmentNotFoundException; -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookFrameworkService.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookFrameworkService.java deleted file mode 100644 index 1fbbceaa9e58..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookFrameworkService.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) 2003-2015 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.service.gradebook.shared; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * This service is meant to be used by the framework which manages the Gradebook - * application. The service provides various administrative functions which - * aren't handled by the application itself, including creating and removing - * gradebooks, and managing system-wide grading schemes. - * - * Because these aren't operations taken by the application, responsibility - * for security is left completely up to the client. THERE ARE NO AUTHORIZATION - * CHECKS. - * - * In other words, DO NOT provide open web service access to this service's methods! - */ -public interface GradebookFrameworkService { - /** - * Creates a new gradebook with the given UID and name - * - * @param uid - * The UID used to specify a gradebook and its associated data. - * It is the caller's responsibility to ensure that this is - * unique within gradebook storage. - * - * @param name - * The name of the gradebook, to be used for logging and other - * conveniences by the application. This should be the name of - * the site or the course. It is only used for convenience, and - * does not need to be unique. - */ - public void addGradebook(String uid, String name); - - /** - * Deletes the gradebook with the given UID, along with all its associated - * data. - */ - public void deleteGradebook(String uid) throws GradebookNotFoundException; - - /** - * Checks to see whether a gradebook with the given uid exists. - * - * @param gradebookUid - * The gradebook UID to check - * @return Whether the gradebook exists - */ - public boolean isGradebookDefined(String gradebookUid); - - /** - * @param gradingScaleDefinitions - * A collection of GradingScaleDefinition beans. - */ - public void setAvailableGradingScales(Collection gradingScaleDefinitions); - - /** - * @param uid - * The UID of the grading scale to use as the default for new gradebooks. - */ - public void setDefaultGradingScale(String uid); - - /** - * Get all of the available Grading Scales in the system. - * @return List of GradingScale - */ - public List getAvailableGradingScales(); - - /** - * Get all of the available Grading Scales in the system, as shared DTOs. - * @return List of GradingScaleDefinition - */ - public List getAvailableGradingScaleDefinitions(); - - /** - * Adds a new grade scale to an existing gradebook. - * - * @param scaleUuid - * The uuid of the scale we want to be added to the gradebook - * @param gradebookUid - * The gradebook with GradeMappings where we will add the grading scale. - * - */ - public void saveGradeMappingToGradebook(String scaleUuid, String gradebookUid); - - /** - * Update a grademapping with new values. - * - * @param gradeMappingId id of GradeMapping to update - * @param gradeMap the updated map of grades - * - */ - public void updateGradeMapping(Long gradeMappingId, Map gradeMap); - -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookInformation.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookInformation.java deleted file mode 100644 index abc9386a0381..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookInformation.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.service.gradebook.shared; - -import java.io.Serializable; -import java.util.List; -import java.util.Map; - -import lombok.Data; - -/** - * Represents the settings for the gradebook - * - */ -@Data -public class GradebookInformation implements Serializable { - private static final long serialVersionUID = 1L; - - private String selectedGradingScaleUid; - - /** - * The ID of the GradeMapping that should be used for this gradebook - */ - private String selectedGradeMappingId; - - /** - * The list of GradeMappings defined for this gradebook but as a DTO representation - */ - private List gradeMappings; - - /** - * The grading schema map currently in use for the this gradebook. For example A+ = 100 etc. - */ - private Map selectedGradingScaleBottomPercents; - - private boolean displayReleasedGradeItemsToStudents; - - private int gradeType; - - private int categoryType; - - private List categories; - - /** - * The name of the grading scale, e.g. Pass / Not Pass - */ - private String gradeScale; - - /** - * Is the course grade to be shown at all? - */ - private boolean courseGradeDisplayed; - - /** - * If the course grade is displayed, should the letter grade be displayed? - */ - private boolean courseLetterGradeDisplayed; - - /** - * If the course grade is displayed, should the total points be displayed? - */ - private boolean coursePointsDisplayed; - - /** - * If the course grade is displayed, should the percentage be displayed? - */ - private boolean courseAverageDisplayed; - - /** - * Are assignment stats to be shown to students? - */ - private boolean assignmentStatsDisplayed; - - /** - * Are course grade stats to be shown to students? - */ - private boolean courseGradeStatsDisplayed; - - - /** - * Are students allowed to compare grades with classmates? - */ - private boolean allowStudentsToCompareGrades; - - - /** - * Are student names displayed when comparing grades with classmates? - */ - private boolean comparingDisplayStudentNames; - - /** - * Are student surnames displayed when comparing grades with classmates? - */ - private boolean comparingDisplayStudentSurnames; - - /** - * Are teacher comments displayed when comparing grades with classmates? - */ - private boolean comparingDisplayTeacherComments; - - /** - * Include grades that doesn't count when comparing grades with classmates? - */ - private boolean comparingIncludeAllGrades; - - /** - * Randomize displayed data order when comparing grades with classmates? - */ - private boolean comparingRandomizeDisplayedData; - -} \ No newline at end of file diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookPermissionService.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookPermissionService.java deleted file mode 100644 index f8abcf3632ce..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookPermissionService.java +++ /dev/null @@ -1,296 +0,0 @@ -/** - * Copyright (c) 2003-2015 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.service.gradebook.shared; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import org.sakaiproject.section.api.facade.Role; - -public interface GradebookPermissionService -{ - /** - * Get all available categories for a user that the user can either view or grade. - * (For overview page) - * - * @param gradebookId Gradebook ID - * @param userId grader ID - * @param categoryList List of Category. (should be all categories for this gradebook) - * @throws IllegalArgumentException - * @return List of categories - */ - public List getCategoriesForUser(Long gradebookId, String userId, List categoryIdList) throws IllegalArgumentException; - - /** - * Returns viewable categorie id's for a user for a specific student - * @param gradebookId - * @param userId - * @param studentId - * @param categories - * @param sectionIds - * @return - * @throws IllegalArgumentException - */ - public List getCategoriesForUserForStudentView(Long gradebookId, String userId, String studentId, List categories, List sectionIds) throws IllegalArgumentException; - - /** - * Get true/false value for current user which indicats if he has permission for all - * assignments in a gradebook with category turned off or he has permission for - * assignments without category associated with in a gradebook with category - * turned on. - * (For overview page) - * - * @param gradebookId Gradebook ID - * @param userId grader ID - * @throws IllegalArgumentException - * @return boolean of true/false - */ - public boolean getPermissionForUserForAllAssignment(Long gradebookId, String userId) throws IllegalArgumentException; - - /** - * Get true/false value for current user which indicates if he has permission for - * all gb items for a given student - * @param gradebookId - * @param userId - * @param studentId - * @param sectionIds - * @return - * @throws IllegalArgumentException - */ - public boolean getPermissionForUserForAllAssignmentForStudent(Long gradebookId, String userId, String studentId, List sectionIds) throws IllegalArgumentException; - - /** - * Get students IDs that the current grader can either view or grade. - * When categoryId is null and cateType is with category - return students' map that the grader - * can grade/view any category for course sections. (this is mostly for items that have no category - * associated with them in a gradebook with category turned on) - * (For item detail page) - * - * @param gradebookId Gradebook ID - * @param userId grader ID - * @param studentIds List of student IDs - * @param cateType gradebook category type - * @param categoryId current category ID that the permission check is based on. it can be null. - * @param courseSections List of course sections for current site - * @throws IllegalArgumentException - * @return Map of student IDs with grade/view as function value - */ - public Map getStudentsForItem(Long gradebookId, String userId, List studentIds, int cateType, Long categoryId, List courseSections) throws IllegalArgumentException; - -/** - * Get students IDs that the current grader can either view or grade. - * When categoryId is null and cateType is with category - return students' map that the grader - * can grade/view any category for course sections. (this is mostly for items that have no category - * associated with them in a gradebook with category turned on) - * (For item detail page) - * - * @param gradebookUid gradebook uid - * @param userId grader ID - * @param studentIds List of student IDs - * @param cateType gradebook category type - * @param categoryId current category ID that the permission check is based on. it can be null. - * @param courseSections List of course sections for current site - * @throws IllegalArgumentException - * @return Map of student IDs with grade/view as function value - */ - public Map getStudentsForItem(String gradebookUid, String userId, List studentIds, int cateType, Long categoryId, List courseSections) throws IllegalArgumentException; - - - /** - * Get a map of itemId/permission(grade/view) of a student for a grader that he can grade - * or view for gradebook. - * (For a student's roster page) - * - * @param gradebookUid Gradebook UID - * @param userId grader ID - * @param studentId student ID - * @param courseSections List of course sections for current site - * @throws IllegalArgumentException - * @return Map of item IDs with grade/view as function value - */ - public Map getAvailableItemsForStudent(String gradebookUid, String userId, String studentId, Collection courseSections) throws IllegalArgumentException; - - /** - * Get a map of itemId/permission(grade/view) of a student for a grader that he can grade - * or view for gradebook. - * (For a student's roster page) - * - * @param gradebookId Gradebook ID - * @param userId grader ID - * @param studentId student ID - * @param courseSections List of course sections for current site - * @throws IllegalArgumentException - * @return Map of item IDs with grade/view as function value - */ - public Map getAvailableItemsForStudent(Long gradebookId, String userId, String studentId, Collection courseSections) throws IllegalArgumentException; - - /** - * Get a map of map for students whose IDs are in studentIds with id as key and another map - * as value: itemId/permission(grade/view) for a grader that he can grade - * or view for gradebook. - * (For a student's roster page) - * - * @param gradebookId Gradebook ID - * @param userId grader ID - * @param studentIds List of student IDs - * @param courseSections List of course sections for current site - * @throws IllegalArgumentException - * @return Map - */ - public Map getAvailableItemsForStudents(Long gradebookId, String userId, List studentIds, Collection courseSections) throws IllegalArgumentException; - - /** - * Get a map of map for students whose IDs are in studentIds with id as key and another map - * as value: itemId/permission(grade/view) for a grader that he can grade - * or view for gradebook. - * (For a student's roster page) - * - * @param gradebookUid Gradebook UID - * @param userId grader ID - * @param studentIds List of student IDs - * @param courseSections List of course sections for current site - * @throws IllegalArgumentException - * @return Map - */ - public Map getAvailableItemsForStudents(String gradebookUid, String userId, List studentIds, Collection courseSections) throws IllegalArgumentException; - - - /** - * Get a map with student IDs as key and view/grade as value for their course grade. - * (For course grade page) - * - * @param gradebookId Gradebook ID - * @param userId grader ID - * @param studentIds List of student IDs - * @param courseSections List of course sections for current site (Should be all course sections the current site has.) - * @throws IllegalArgumentException - * @return Map of student IDs with view/grade as function value - */ - public Map getCourseGradePermission(Long gradebookId, String userId, List studentIds, List courseSections) throws IllegalArgumentException; - - /** - * Get a map with student IDs as key and view/grade as value for their course grade. - * (For course grade page) - * - * @param gradebookUid Gradebook Uid - * @param userId grader ID - * @param studentIds List of student IDs - * @param courseSections List of course sections for current site (Should be all course sections the current site has.) - * @throws IllegalArgumentException - * @return Map of student IDs with view/grade as function value - */ - public Map getCourseGradePermission(String gradebookUid, String userId, List studentIds, List courseSections) throws IllegalArgumentException; - - - /** - * Get all Permission records associated with this user and gradebook - * @param gradebookId - * @param userId - * @return List of Permission records for user and gradebook - */ - public List getGraderPermissionsForUser(Long gradebookId, String userId); - - /** - * Get all Permission records associated with this user and gradebook - * @param gradebookUid - * @param userId - * @return List of Permission records for user and gradebook - */ - public List getGraderPermissionsForUser(String gradebookUid, String userId); - - /** - * Get all group ids associated with groups that contain at least one member - * that the user is authorized to view/grade - * @param gradebookId - * @param userId - * @param groupIds - * @return List of group ids that user has some authorization to view - */ - public List getViewableGroupsForUser(Long gradebookId, String userId, List groupIds); - - /** - * Get all group ids associated with groups that contain at least one member - * that the user is authorized to view/grade - * @param gradebookUid - * @param userId - * @param groupIds - * @return List of group ids that user has some authorization to view - */ - public List getViewableGroupsForUser(String gradebookUid, String userId, List groupIds); - - /** - * Get a unique list of students that the current user has grader permissions to view in some capacity - * @param gradebookId - * @param userId - * @param studentIds - * @param sections - * @return List of student ids - */ - public List getViewableStudentsForUser(Long gradebookId, String userId, List studentIds, List sections); - - /** - * Get a unique list of students that the current user has grader permissions to view in some capacity - * @param gradebookUid - * @param userId - * @param studentIds - * @param sections - * @return List of student ids - */ - public List getViewableStudentsForUser(String gradebookUid, String userId, List studentIds, List sections); - - /** - * Get the grader specific permissions for the given user - * - * @param gradebookUid - * @param userId - * @return - */ - public List getPermissionsForUser(final String gradebookUid, final String userId); - - /** - * Update the set of grader specific permissions for the given user - * - * @param gradebookUid - * @param userId - * @param permissionDefinitions - * @return - */ - public void updatePermissionsForUser(final String gradebookUid, final String userId, List permissionDefinitions); - - /** - * Remove all grader specific permissions for the given user - * - * @param gradebookUid - * @param userId - * @return - */ - public void clearPermissionsForUser(final String gradebookUid, final String userId); - - /** - * Get a list of permissions defined for the given user based on section and role or all sections if allowed. - * This method checks realms permissions for role/section and is independent of the - * gb_permissions_t permissions. - * - * note: If user has the grade privilege, they are given the GraderPermission.VIEW_COURSE_GRADE permission to match - * GB classic functionality. This needs to be reviewed. - * - * @param userUuid - * @param siteId - * @return list of {@link org.sakaiproject.service.gradebook.shared.PermissionDefinition PermissionDefinitions} or empty list if none - */ - public List getRealmsPermissionsForUser(String userUuid,String siteId, Role role); -} \ No newline at end of file diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookService.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookService.java deleted file mode 100644 index 8a6a5bb9e20b..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookService.java +++ /dev/null @@ -1,889 +0,0 @@ -/********************************************************************************** - * - * $Id$ - * - *********************************************************************************** - * - * Copyright (c) 2005, 2006, 2007, 2008, 2009 The Sakai Foundation, The MIT Corporation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.opensource.org/licenses/ECL-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - **********************************************************************************/ -package org.sakaiproject.service.gradebook.shared; - -import java.math.MathContext; -import java.math.RoundingMode; -import java.util.Comparator; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import org.apache.commons.lang3.StringUtils; -import org.sakaiproject.entity.api.Entity; -import org.sakaiproject.entity.api.EntityProducer; - -/** - * This is the externally exposed API of the gradebook application. - * - * This interface is principally intended for clients of application services -- that is, clients who want to "act like the Gradebook would" - * to automate what would normally be done in the UI, including any authorization checks. - * - * As a result, these methods may throw security exceptions. Call the service's authorization-check methods if you want to avoid them. - * - *

- * WARNING: For documentation of the deprecated methods, please see the service interfaces which own them. - */ -public interface GradebookService extends EntityProducer { - // Application service hooks. - - // These have been deprecated in favour of the {@link GradingType} enum - @Deprecated - public static final int GRADE_TYPE_POINTS = 1; - @Deprecated - public static final int GRADE_TYPE_PERCENTAGE = 2; - @Deprecated - public static final int GRADE_TYPE_LETTER = 3; - - public static final int CATEGORY_TYPE_NO_CATEGORY = 1; - public static final int CATEGORY_TYPE_ONLY_CATEGORY = 2; - public static final int CATEGORY_TYPE_WEIGHTED_CATEGORY = 3; - public static final String REFERENCE_ROOT = "/gbassignment"; - public static final String SAKAI_GBASSIGNMENT = "sakai:gbassignment"; - - public static final String[] validLetterGrade = { "a+", "a", "a-", "b+", "b", "b-", - "c+", "c", "c-", "d+", "d", "d-", "f" }; - - // These Strings have been kept for backwards compatibility as they are used everywhere, - // however the {@link GraderPermission} enum should be used going forward. - @Deprecated - public static final String gradePermission = GraderPermission.GRADE.toString(); - @Deprecated - public static final String viewPermission = GraderPermission.VIEW.toString(); - @Deprecated - public static final String noPermission = GraderPermission.NONE.toString(); - - public static final String enableLetterGradeString = "gradebook_enable_letter_grade"; - - public static final MathContext MATH_CONTEXT = new MathContext(10, RoundingMode.HALF_DOWN); - - /** - * An enum for defining valid/invalid information for a points possible/relative weight value for a gradebook item. See - * {@link GradebookService#isPointsPossibleValid(String, Assignment, Double)} for usage - */ - public enum PointsPossibleValidation { - /** - * The points possible/relative weight is valid - */ - VALID, - /** - * The points possible/relative weight is invalid because it is null and a value is required. - */ - INVALID_NULL_VALUE, - /** - * The points possible/relative weight is invalid because it is a value <= 0 - */ - INVALID_NUMERIC_VALUE, - /** - * The points possible/relative weight is invalid because it contains more than 2 decimal places - */ - INVALID_DECIMAL - } - - /** - * Array of chars that are not allowed at the beginning of a gb item title - */ - public static final String[] INVALID_CHARS_AT_START_OF_GB_ITEM_NAME = { "#", "*", "[" }; - - /** - * Comparator to ensure correct ordering of letter grades, catering for + and - in the grade This is duplicated in GradebookNG. If - * changing here, please change there as well. TODO combine them - */ - public static Comparator lettergradeComparator = new Comparator() { - @Override - public int compare(final String o1, final String o2) { - if (o1.toLowerCase().charAt(0) == o2.toLowerCase().charAt(0)) { - // only take the first 2 chars, to cater for GradePointsMapping as well - final String s1 = StringUtils.trim(StringUtils.left(o1, 2)); - final String s2 = StringUtils.trim(StringUtils.left(o2, 2)); - - if (s1.length() == 2 && s2.length() == 2) { - if (s1.charAt(1) == '+') { - return -1; // SAK-30094 - } else { - return 1; - } - } - if (s1.length() == 1 && s2.length() == 2) { - if (o2.charAt(1) == '+') { - return 1; // SAK-30094 - } else { - return -1; - } - } - if (s1.length() == 2 && s2.length() == 1) { - if (s1.charAt(1) == '+') { - return -1; // SAK-30094 - } else { - return 1; - } - } - return 0; - } else { - return o1.toLowerCase().compareTo(o2.toLowerCase()); - } - } - }; - - /** - * Checks to see whether a gradebook with the given uid exists. - * - * @param gradebookUid The gradebook UID to check - * @return Whether the gradebook exists - */ - public boolean isGradebookDefined(String gradebookUid); - - /** - * Check to see if the current user is allowed to grade the given item for the given student in the given gradebook. This will give - * clients a chance to avoid a security exception. - * - * @param gradebookUid - * @param assignmentId - * @param studentUid - */ - public boolean isUserAbleToGradeItemForStudent(String gradebookUid, Long assignmentId, String studentUid); - - /** - * Check to see if the current user is allowed to view the given item for the given student in the given gradebook. This will give - * clients a chance to avoid a security exception. - * - * @param gradebookUid - * @param assignmentId - * @param studentUid - * @return - */ - public boolean isUserAbleToViewItemForStudent(String gradebookUid, Long assignmentId, String studentUid); - - /** - * Check to see if current user may grade or view the given student for the given item in the given gradebook. Returns string - * representation of function per GradebookService vars (view/grade) or null if no permission - * - * @param gradebookUid - * @param assignmentId - * @param studentUid - * @return GradebookService.gradePermission, GradebookService.viewPermission, or null if no permission - */ - public String getGradeViewFunctionForUserForStudentForItem(String gradebookUid, Long assignmentId, String studentUid); - - - /** - * @return Returns a list of Assignment objects describing the assignments that are currently defined in the given gradebook. - */ - public List getAssignments(String gradebookUid) - throws GradebookNotFoundException; - - /** - * @return Returns a list of Assignment objects describing the assignments that are currently defined in the given gradebook, sorted by - * the given sort type. - */ - public List getAssignments(String gradebookUid, SortType sortBy) - throws GradebookNotFoundException; - - /** - * Get an assignment based on its id - * - * @param gradebookUid - * @param assignmentId - * @return the associated Assignment with the given assignmentId - * @throws AssessmentNotFoundException - */ - public Assignment getAssignment(String gradebookUid, Long assignmentId) - throws AssessmentNotFoundException; - - /** - * Get an assignment based on its id - * - * @param gradebookUid - * @param assignmentId - * @return the associated Assignment with the given assignmentId - * @throws AssessmentNotFoundException - */ - public Assignment getAssignmentByID(final Long gradeableObjectID) - throws AssessmentNotFoundException; - - /** - * Get an assignment based on its name. This is provided for backward compatibility only. - * - * @param gradebookUid - * @param assignmentName - * @return the associated Assignment with the given name - * @throws AssessmentNotFoundException - * - * @deprecated Use {@link #getAssignment(String,Long)} instead. - */ - @Deprecated - public Assignment getAssignment(String gradebookUid, String assignmentName) - throws AssessmentNotFoundException; - - public Assignment getExternalAssignment(final String gradebookUid, final String externalId) - throws GradebookNotFoundException; - - /** - * Get an assignment based on its name or id. This is intended as a migration path from the deprecated - * {@link #getAssignment(String,String)} to the new {@link #getAssignment(String,Long)} - * - * This method will attempt to lookup the name as provided then fall back to the ID as a Long (If it is a Long) You should use - * {@link #getAssignment(String,Long)} if you always can use the Long instead. - * - * @param gradebookUid - * @param assignmentName - * @return the associated Assignment with the given name - * @throws AssessmentNotFoundException - * - */ - public Assignment getAssignmentByNameOrId(String gradebookUid, String assignmentName) - throws AssessmentNotFoundException; - - /** - * - * @param gradebookUid - * @param assignmentId - * @param studentUid - * @return Returns a GradeDefinition for the student, respecting the grade entry type for the gradebook (ie in %, letter grade, or - * points format). Returns null if no grade - * @throws GradebookNotFoundException - * @throws AssessmentNotFoundException - */ - public GradeDefinition getGradeDefinitionForStudentForItem(String gradebookUid, - Long assignmentId, String studentUid) - throws GradebookNotFoundException, AssessmentNotFoundException; - - /** - * Get the comment (if any) currently provided for the given combination of student and assignment. - * - * @param gradebookUid - * @param assignmentId - * @param studentUid - * @return null if no comment is avaailable - * @throws GradebookNotFoundException - * @throws AssessmentNotFoundException - */ - public CommentDefinition getAssignmentScoreComment(String gradebookUid, Long assignmentId, String studentUid) - throws GradebookNotFoundException, AssessmentNotFoundException; - - /** - * - * @param gradebookUid - * @param assignmentId - * @param studentUid - * @return - * @throws GradebookNotFoundException - * @throws AssessmentNotFoundException - */ - public boolean getIsAssignmentExcused(String gradebookUid, Long assignmentId, String studentUid) - throws GradebookNotFoundException, AssessmentNotFoundException; - - /** - * Provide a student-viewable comment on the score (or lack of score) associated with the given assignment. - * - * @param gradebookUid - * @param assignmentId - * @param studentUid - * @param comment a plain text comment, or null to remove any currrent comment - * @throws GradebookNotFoundException - * @throws AssessmentNotFoundException - */ - public void setAssignmentScoreComment(String gradebookUid, Long assignmentId, String studentUid, String comment) - throws GradebookNotFoundException, AssessmentNotFoundException; - - - /** - * Check to see if an assignment with the given name already exists in the given gradebook. This will give clients a chance to avoid the - * ConflictingAssignmentNameException. - * - * This is not deprecated as we currently need the ability to check for duplciate assignment names in the given gradebook - * - */ - public boolean isAssignmentDefined(String gradebookUid, String assignmentTitle) - throws GradebookNotFoundException; - - /** - * Transfer the gradebook information and assignments from one gradebook to another - * - * @param gradebookInformation GradebookInformation to copy - * @param assignments list of Assignments to copy - * @param toGradebookUid target gradebook uid - */ - public Map transferGradebook(final GradebookInformation gradebookInformation, final List assignments, - final String toGradebookUid, final String fromContext); - - /** - * - * @param gradebookUid - * @return a {@link GradebookInformation} object that contains information about this Gradebook that may be useful to consumers outside - * the Gradebook tool - * - */ - public GradebookInformation getGradebookInformation(String gradebookUid); - - /** - * Removes an assignment from a gradebook. The assignment should not be deleted, but the assignment and all grade records associated - * with the assignment should be ignored by the application. A removed assignment should not count toward the total number of points in - * the gradebook. - * - * @param assignmentId The assignment id - */ - public void removeAssignment(Long assignmentId) throws StaleObjectModificationException; - - /** - * - * Get the categories for the given gradebook. This method cannot be used outside of the gradebook because it returns the - * org.sakaiproject.tool.gradebook.Category object. If you require info on the categories from a consumer outside the gradebook, use - * {@link #getCategoryDefinitions(String)} - * - * @param gradebookId - * @return List of categories - * @throws HibernateException - * - * @deprecated - */ - @Deprecated - public List getCategories(final Long gradebookId); - - /** - * Get the categories for the given gradebook - * - * @param gradebookUid - * @return {@link CategoryDefinition}s for the categories defined for the given gradebook. Returns an empty list if the gradebook does - * not have categories. - * @throws GradebookNotFoundException - */ - public List getCategoryDefinitions(String gradebookUid); - - /** - * remove category from gradebook - * - * @param categoryId - * @throws StaleObjectModificationException - */ - - public void removeCategory(Long categoryId) throws StaleObjectModificationException; - - /** - * Create a new Gradebook-managed assignment. - * - * @param assignmentDefinition - * @return the id of the newly created assignment - */ - public Long addAssignment(String gradebookUid, Assignment assignmentDefinition); - - /** - * Modify the definition of an existing Gradebook item. - * - * Clients should be aware that it's allowed to change the points value of an assignment even if students have already been scored on - * it. Any existing scores will not be adjusted. - * - * This method can be used to manage both internal and external gradebook items, however the title, due date and total points will not - * be edited for external gradebook items. - * - * @param assignmentId the id of the assignment that needs to be changed - * @param assignmentDefinition the new properties of the assignment - */ - public void updateAssignment(String gradebookUid, Long assignmentId, Assignment assignmentDefinition); - - /** - * - * @param gradebookUid - * @return list of gb items that the current user is authorized to view. If user has gradeAll permission, returns all gb items. If user - * has gradeSection perm with no grader permissions, returns all gb items. If user has gradeSection with grader perms, returns - * only the items that the current user is authorized to view or grade. If user does not have grading privileges but does have - * viewOwnGrades perm, will return all released gb items. - */ - public List getViewableAssignmentsForCurrentUser(String gradebookUid); - - /** - * - * @param gradebookUid - * @return list of gb items that the current user is authorized to view sorted by the provided SortType. If user has gradeAll - * permission, returns all gb items. If user has gradeSection perm with no grader permissions, returns all gb items. If user has - * gradeSection with grader perms, returns only the items that the current user is authorized to view or grade. If user does not - * have grading privileges but does have viewOwnGrades perm, will return all released gb items. - */ - public List getViewableAssignmentsForCurrentUser(String gradebookUid, SortType sortBy); - - /** - * - * @param gradebookUid - * @param assignmentId - * @return a map of studentId to view/grade function for the given gradebook and gradebook item. students who are not viewable or - * gradable will not be returned. if the current user does not have grading privileges, an empty map is returned - */ - public Map getViewableStudentsForItemForCurrentUser(String gradebookUid, Long assignmentId); - - /** - * @param userUid - * @param gradebookUid - * @param assignmentId - * @return a map of studentId to view/grade function for the given gradebook and gradebook item that the given userUid is allowed to - * view or grade. students who are not viewable or gradable will not be returned. if the given user does not have grading - * privileges, an empty map is returned - */ - public Map getViewableStudentsForItemForUser(String userUid, String gradebookUid, Long assignmentId); - - /** - * Get the Gradebook. Note that this returns Object to avoid circular dependency with sakai-gradebook-tool Consumers will need to cast - * to {@link org.sakaiproject.tool.gradebook.Gradebook} - * - */ - public Object getGradebook(String uid) throws GradebookNotFoundException; - - /** - * Check if there are students that have not submitted - * - * @param gradebookUid - * @return - */ - public boolean checkStudentsNotSubmitted(String gradebookUid); - - /** - * Check if a gradeable object with the given id exists - * - * @param gradableObjectId - * @return true if a gradable object with the given id exists and was not removed - */ - public boolean isGradableObjectDefined(Long gradableObjectId); - - /** - * Using the grader permissions, return map of section uuid to section name that includes all sections that the current user may view or - * grade - * - * @param gradebookUid - * @return - */ - public Map getViewableSectionUuidToNameMap(String gradebookUid); - - /** - * Check if the current user has the gradebook.gradeAll permission - * - * @param gradebookUid - * @return true if current user has the gradebook.gradeAll permission - */ - public boolean currentUserHasGradeAllPerm(String gradebookUid); - - /** - * Check if the given user is allowed to grade all students in this gradebook - * - * @param gradebookUid - * @param userUid - * @return true if the given user is allowed to grade all students in this gradebook - */ - public boolean isUserAllowedToGradeAll(String gradebookUid, String userUid); - - /** - * @param gradebookUid - * @return true if the current user has some form of grading privileges in the gradebook (grade all, grade section, etc) - */ - public boolean currentUserHasGradingPerm(String gradebookUid); - - /** - * - * @param gradebookUid - * @param userUid - * @return true if the given user has some form of grading privileges in the gradebook (grade all, grade section, etc) - */ - public boolean isUserAllowedToGrade(String gradebookUid, String userUid); - - /** - * @param gradebookUid - * @return true if the current user has the gradebook.editAssignments permission - */ - public boolean currentUserHasEditPerm(String gradebookUid); - - /** - * @param gradebookUid - * @return true if the current user has the gradebook.viewOwnGrades permission - */ - public boolean currentUserHasViewOwnGradesPerm(String gradebookUid); - - /** - * @param gradebookUid - * @return true if the current user has the gradebook.viewStudentNumbers permission - */ - public boolean currentUserHasViewStudentNumbersPerm(String gradebookUid); - - /** - * Get the grade records for the given list of students and the given assignment. This can only be called by an instructor or TA that - * has access, not student. - * - * See {@link #getGradeDefinitionForStudentForItem} for the method call that can be made as a student. - * - * @param gradebookUid - * @param assignmentId - * @param studentIds - * @return a list of GradeDefinition with the grade information for the given students for the given gradableObjectId - * @throws SecurityException if the current user is not authorized to view or grade a student in the passed list - */ - public List getGradesForStudentsForItem(String gradebookUid, Long assignmentId, List studentIds); - - /** - * This method gets grades for multiple gradebook items with emphasis on performance. This is particularly useful for reporting tools - * - * @param gradebookUid - * @param gradableObjectIds - * @param studentIds - * @return a Map of GradableObjectIds to a List of GradeDefinitions containing the grade information for the given students for the - * given gradableObjectIds. Comments are excluded which can be useful for performance. If a student does not have a grade on a - * gradableObject, the GradeDefinition will be omitted - * @throws SecurityException if the current user is not authorized with gradeAll in this gradebook - * @throws IllegalArgumentException if gradableObjectIds is null/empty, or if gradableObjectIds contains items that are not members of - * the gradebook with uid = gradebookUid - */ - public Map> getGradesWithoutCommentsForStudentsForItems(String gradebookUid, List gradableOjbectIds, - List studentIds); - - /** - * - * @param gradebookUuid - * @param grade - * @return true if the given grade is a valid grade given the gradebook's grade entry type. ie, if gradebook is set to grade entry by - * points, will check for valid point value. if entry by letter, will check for valid letter, etc - * @throws GradebookNotFoundException if no gradebook exists with given gradebookUid - */ - public boolean isGradeValid(String gradebookUuid, String grade) - throws GradebookNotFoundException; - - /** - * Determines if the given string contains a valid numeric grade. - * @param grade the grade as a string, expected to contain a numeric value - * @return true if the string contains a valid numeric grade - */ - public boolean isValidNumericGrade(String grade); - - /** - * - * @param gradebookUid - * @param studentIdToGradeMap - the student's username mapped to their grade that you want to validate - * @return a list of the studentIds that were associated with invalid grades given the gradebook's grade entry type. useful if - * validating a list of student/grade pairs for a single gradebook (more efficient than calling gradeIsValid repeatedly). - * returns empty list if all grades are valid - * @throws GradebookNotFoundException if no gradebook exists with given gradebookUid - */ - public List identifyStudentsWithInvalidGrades(String gradebookUid, Map studentIdToGradeMap) - throws GradebookNotFoundException; - - /** - * Save a student score and comment for a gradebook item. The input score must be valid according to the given gradebook's grade entry - * type. - * - * @param gradebookUid - * @param assignmentId - * @param studentId - * @param grade - must be in format according to gradebook's grade entry type - * @param comment - * @throws InvalidGradeException - if grade is invalid. grade and comment will not be saved - * @throws GradebookNotFoundException - * @throws AssessmentNotFoundException - * @throws SecurityException if current user is not authorized to grade student - */ - public void saveGradeAndCommentForStudent(String gradebookUid, Long assignmentId, String studentId, String grade, String comment) - throws InvalidGradeException, GradebookNotFoundException, AssessmentNotFoundException; - - /** - * Given a list of GradeDefinitions for students for a given gradebook and gradable object, will save the associated scores and - * comments. Scores must be in a format according to the gradebook's grade entry type (ie points, %, letter). - * - * @param gradebookUid - * @param assignmentId - * @param gradeDefList - * @throws InvalidGradeException if any of the grades are not valid - none will be saved - * @throws SecurityException if the user does not have access to a student in the list - no grades or comments will be saved for any - * student - * @throws GradebookNotFoundException - * @throws AssessmentNotFoundException - */ - public void saveGradesAndComments(String gradebookUid, Long assignmentId, List gradeDefList) - throws InvalidGradeException, GradebookNotFoundException, AssessmentNotFoundException; - - public void saveGradeAndExcuseForStudent(String gradebookUid, Long assignmentId, String studentId, String grade, boolean excuse) - throws InvalidGradeException, GradebookNotFoundException, AssessmentNotFoundException; - - /** - * - * @param gradebookUid - * @return the constant representation of the grade entry type (ie points, %, letter grade) - * @throws GradebookNotFoundException if no gradebook exists w/ the given uid - */ - public int getGradeEntryType(String gradebookUid) throws GradebookNotFoundException; - - /** - * Get a Map of overridden CourseGrade for students. - * - * @param gradebookUid - * @return Map of enrollment displayId as key, point as value string - * - */ - public Map getEnteredCourseGrade(String gradebookUid); - - /** - * Get student's assignment's score as string. - * @param gradebookUid - * @param assignmentId - * @param studentUid - * @return String of score - */ - public String getAssignmentScoreString(String gradebookUid, Long assignmentId, String studentUid) - throws GradebookNotFoundException, AssessmentNotFoundException; - - /** - * Get student's assignment's score as string. This is provided for backward compatibility only. - * - * @param gradebookUid - * @param assignmentName - * @param studentUid - * @return String of score - * - * @deprecated See {@link #getAssignmentScoreString(String, Long, String)} - */ - @Deprecated - public String getAssignmentScoreString(String gradebookUid, String assignmentName, String studentUid) - throws GradebookNotFoundException, AssessmentNotFoundException; - - /** - * Get student's assignment's score as string. - * - * This is intended as a migration path from the deprecated {@link #getAssignmentScoreString(String,String)} to the new - * {@link #getAssignmentScoreString(String,Long)} - * - * This method will attempt to lookup the name as provided then fallback to the ID as a Long (If it is a Long) You should use - * {@link #getAssignmentScoreString(String,Long)} if you always can use the Long instead. - * - * @param gradebookUid - * @param assignmentName - * @param studentUid - * @return String of score - */ - public String getAssignmentScoreStringByNameOrId(String gradebookUid, String assignmentName, String studentUid) - throws GradebookNotFoundException, AssessmentNotFoundException; - - /** - * Set student's score for assignment. - * - * @param gradebookUid - * @param assignmentId - * @param studentUid - * @param score - * @param clientServiceDescription - * - */ - public void setAssignmentScoreString(String gradebookUid, Long assignmentId, String studentUid, String score, - String clientServiceDescription) - throws GradebookNotFoundException, AssessmentNotFoundException; - - /** - * Set student's score for assignment. This is provided for backward compatibility only. - * - * @param gradebookUid - * @param assignmentName - * @param studentUid - * @param score - * @param clientServiceDescription - * - * @deprecated See {@link #setAssignmentScoreString(String, Long, String, String, String)} - */ - @Deprecated - public void setAssignmentScoreString(String gradebookUid, String assignmentName, String studentUid, String score, - String clientServiceDescription) - throws GradebookNotFoundException, AssessmentNotFoundException; - - - /** - * Finalize the gradebook's course grades by setting all still-unscored assignments to zero scores. - * - * @param gradebookUid - * @throws GradebookNotFoundException - */ - public void finalizeGrades(String gradebookUid) - throws GradebookNotFoundException; - - /** - * - * @param gradebookUid - * @param assignmentId - * @return the lowest possible grade allowed for the given assignmentId. For example, in a points or %-based gradebook, the lowest - * possible grade for a gradebook item is 0. In a letter-grade gb, it may be 'F' depending on the letter grade mapping. Ungraded - * items have a lowest value of null. - * @throws SecurityException if user does not have permission to view assignments in the given gradebook - * @throws AssessmentNotFoundException if there is no gradebook item with the given gradebookItemId - */ - public String getLowestPossibleGradeForGbItem(final String gradebookUid, final Long assignmentId); - - /** - * - * @param gradebookUid (non-null) - * @param assignment (non-null) the Assignment object representing the gradebook item for which you are setting the points possible (aka - * relative weight). May be a new gradebook item without an id yet. - * @param pointsPossible the points possible/relative weight you would like to validate for the gradebookItem above. - * @return {@link PointsPossibleValidation} value indicating the validity of the given points possible/relative weight or a problem code - * defining why it is invalid - */ - public PointsPossibleValidation isPointsPossibleValid(String gradebookUid, Assignment assignment, Double pointsPossible); - - /** - * Computes the Average Course Grade as a letter. - * - * @param gradebookUid - * @return - */ - public String getAverageCourseGrade(String gradebookUid); - - /** - * Update the ordering of an assignment. This can be performed on internal and external assignments. - * @param gradebookUid uid of the gradebook - * @param assignmentId id of the assignment in the gradebook - * @param order the new order for this assignment. Note it is 0 based index ordering. - * @return - */ - public void updateAssignmentOrder(final String gradebookUid, final Long assignmentId, final Integer order); - - /** - * Gets the grading events for the given student and the given assignment - * - * @param studentId - * @param assignmentId - * @return List of GradingEvent objects. - */ - @SuppressWarnings("rawtypes") - public List getGradingEvents(final String studentId, final long assignmentId); - - /** - * Calculate the category score for the given gradebook, student and category, looking up the grades. Safe to call in context of a - * student. - * - * @param gradebookId Id of the gradebook - * @param studentUuid uuid of the student - * @param categoryId id of category - * @param isInstructor will determine whether category score includes non-released items - * @param categoryType category type of the gradebook - * @param equalWeightAssignments whether category is equal-weighting regardless of points - * @return percentage and dropped items, or empty if no calculations were made - * - */ - Optional calculateCategoryScore(Long gradebookId, String studentUuid, Long categoryId, boolean includeNonReleasedItems, int categoryType, Boolean equalWeightAssignments); - - /** - * Calculate the category score for the given gradebook, category, assignments in the category and grade map. This doesn't do any - * additional grade lookups. Safe to call in context of a student. - * - * @param gradebook the gradebook. As this method is called for every student at once, this is passed in to save additional lookups by - * id. - * @param studentUuid uuid of the student - * @param category the category - * @param categoryAssignments list of assignments the student can view, and are in the category - * @param gradeMap map of assignmentId to grade, to use for the calculations - * @param includeNonReleasedItems relevant for student view - * @return percentage and dropped items, or empty if no calculations were made - */ - Optional calculateCategoryScore(Object gradebook, String studentUuid, CategoryDefinition category, - final List categoryAssignments, Map gradeMap, boolean includeNonReleasedItems); - - /** - * Get the course grade for a student - * - * @param gradebookUid - * @param userUuid uuid of the user - * @return The {@link CourseGrade} for the student - */ - CourseGrade getCourseGradeForStudent(String gradebookUid, String userUuid); - - /** - * Get the course grade for a list of students - * - * @param gradebookUid - * @param userUuids uuids of the users - * @return a Map of {@link CourseGrade} for the students. Key is the student uuid. - */ - Map getCourseGradeForStudents(String gradebookUid, List userUuids); - - /** - * Get the course grade for a list of students using the given grading schema - * - * @param gradebookUid - * @param userUuids uuids of the users - * @param schema the grading schema (bottom percents) to use in the calculation - * @return a Map of {@link CourseGrade} for the students. Key is the student uuid. - */ - Map getCourseGradeForStudents(String gradebookUid, List userUuids, Map schema); - - /** - * Get a list of CourseSections that the current user has access to in the given gradebook. This is a combination of sections and groups - * and is permission filtered. - * - * @param gradebookUid - * @return list of CourseSection objects. - */ - @SuppressWarnings("rawtypes") - List getViewableSections(String gradebookUid); - - /** - * Update the settings for this gradebook - * - * @param gradebookUid - * @param gbInfo GradebookInformation object - */ - void updateGradebookSettings(String gradebookUid, GradebookInformation gbInfo); - - /** - * Return the GradeMappings for the given gradebook. The normal getGradebook(siteId) doesn't return the GradeMapping. - * - * @param gradebookId - * @return Set of GradeMappings for the gradebook - */ - Set getGradebookGradeMappings(Long gradebookId); - - /** - * Return the GradeMappings for the given gradebook. - * @param gradebookUid - * @return Set of GradeMappings for the gradebook - */ - Set getGradebookGradeMappings(String gradebookUid); - - /** - * Allows an instructor to set a course grade override for the given student - * - * @param gradebookUid uuid of the gradebook - * @param studentUuid uuid of the student - * @param grade the new course grade - */ - void updateCourseGradeForStudent(String gradebookUid, String studentUuid, String grade, String gradeScale); - - /** - * Updates the categorized order of an assignment - * - * @param gradebookUid uuid of the gradebook - * @param categoryId id of the category - * @param assignmentId id of the assignment - * @param order new position of the assignment - */ - void updateAssignmentCategorizedOrder(final String gradebookUid, final Long categoryId, final Long assignmentId, Integer order); - - /** - * Return the grade changes made since a given time - * - * @param assignmentIds list of assignment ids to check - * @param since timestamp from which to check for changes - * @return set of changes made - */ - List getGradingEvents(final List assignmentIds, final Date since); - -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradingEventStatus.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradingEventStatus.java deleted file mode 100644 index e3c58fe81f90..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradingEventStatus.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2003-2021 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.sakaiproject.service.gradebook.shared; - -// The order of this enum is important. -// Do not modify or delete items. If changes are needed, add values only at the end -// TO-DO: Create a custom class to parse it to Integer -public enum GradingEventStatus { - GRADE_NONE, - GRADE_EXCLUDED, - GRADE_INCLUDED -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradingScaleDefinition.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradingScaleDefinition.java deleted file mode 100644 index ec559eb7356e..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradingScaleDefinition.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.service.gradebook.shared; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; - -/** - * DTO for the persistent GradingScale - */ -public class GradingScaleDefinition implements Serializable { - - private static final long serialVersionUID = 1L; - - private String uid; - private String name; - private List grades; - - /** - * This was added specifically to support CXF and is a different pattern to the original - */ - private List defaultBottomPercentsAsList; - - /** - * The original map - */ - private Map defaultBottomPercents; - - public String getUid() { - return uid; - } - public void setUid(String uid) { - this.uid = uid; - } - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public List getGrades() { - return grades; - } - public void setGrades(List grades) { - this.grades = grades; - } - public List getDefaultBottomPercentsAsList() { - return defaultBottomPercentsAsList; - } - public void setDefaultBottomPercentsAsList(List defaultBottomPercentsList) { - // Depending on how this was called, the list may - // be of Double, String, emtpy String, or null objects. Convert the strings. - List doubleScores = new ArrayList(); - for (Iterator iter = defaultBottomPercentsList.iterator(); iter.hasNext(); ) { - Object obj = iter.next(); - if (obj instanceof String) { - String str = (String)obj; - if (str.trim().length() == 0) { - obj = null; - } else { - obj = Double.valueOf((String)obj); - } - } - doubleScores.add((Double)obj); - } - this.defaultBottomPercentsAsList = doubleScores; - } - - public Map getDefaultBottomPercents() { - return defaultBottomPercents; - } - public void setDefaultBottomPercents(Map defaultBottomPercents) { - this.defaultBottomPercents = defaultBottomPercents; - } - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); - } - -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/MultipleAssignmentSavingException.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/MultipleAssignmentSavingException.java deleted file mode 100644 index bf8c9aaf711f..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/MultipleAssignmentSavingException.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2003-2009 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.service.gradebook.shared; - -public class MultipleAssignmentSavingException extends GradebookException -{ - public MultipleAssignmentSavingException(String message) - { - super(message); - } -} \ No newline at end of file diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/PermissionDefinition.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/PermissionDefinition.java deleted file mode 100644 index a2899ad9c97d..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/PermissionDefinition.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.service.gradebook.shared; - -import java.io.Serializable; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; - -/** - * DTO for the {@link org.sakaiproject.gradebook.tool.Permission} to pass to external services. Not persisted. - */ -public class PermissionDefinition implements Serializable { - - private static final long serialVersionUID = 1L; - - private Long id; - private String userId; - private String function; - private Long categoryId; - private String groupReference; - - public Long getCategoryId() { - return categoryId; - } - - public void setCategoryId(Long categoryId) { - this.categoryId = categoryId; - } - - public String getGroupReference() { - return groupReference; - } - - public void setGroupReference(String groupReference) { - this.groupReference = groupReference; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getFunction() { - return function; - } - - public void setFunction(String function) { - this.function = function; - } - - public String getUserId() { - return userId; - } - - public void setUserId(String userId) { - this.userId = userId; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof PermissionDefinition)) { - return false; - } - - PermissionDefinition other = (PermissionDefinition) o; - - return new EqualsBuilder() - // id purposely not included so that we dont get duplicate permissions - .append(userId, other.userId) - .append(function, other.function) - .append(categoryId, other.categoryId) - .append(groupReference, other.groupReference) - .isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder() - .append(userId) - .append(function) - .append(categoryId) - .append(groupReference) - .hashCode(); - } - - -} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/UnknownUserException.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/UnknownUserException.java deleted file mode 100644 index c34d3ed05187..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/UnknownUserException.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2003-2012 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.service.gradebook.shared; - -/** - * TODO Document org.sakaiproject.service.gradebook.shared.UnknownUserException - * - * @author Josh Holtzman - */ -public class UnknownUserException extends GradebookException { - - /** - * @param message - */ - public UnknownUserException(String message) { - super(message); - } - -} - - - - diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/tool/gradebook/facades/Authn.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/tool/gradebook/facades/Authn.java deleted file mode 100644 index 50dd20ddfa81..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/tool/gradebook/facades/Authn.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2003-2012 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook.facades; - -/** - * Facade to abstract external authentication services. - * Since this is an application-wide singleton pointing to an otherwise opaque service, - * we do not assume that the authenticator has access to (for example) an up-to-date - * fully constructed FacesContext. - */ -public interface Authn { - /** - * @return an ID uniquely identifying the currently authenticated user in a - * site, or null if the user has not been authenticated. - */ - public String getUserUid(); - - /** - * @param whatToAuthn the javax.servlet.http.HttpServletRequest or - * javax.portlet.PortletRequest for which authentication should be checked. Since - * they don't share an interface, a generic object is passed. - */ - public void setAuthnContext(Object whatToAuthn); -} - - diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/tool/gradebook/facades/Authz.java b/edu-services/gradebook-service/api/src/java/org/sakaiproject/tool/gradebook/facades/Authz.java deleted file mode 100644 index ffc89386e055..000000000000 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/tool/gradebook/facades/Authz.java +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook.facades; - -import java.util.List; -import java.util.Map; - -/** - * Facade to external role and authorization service. - */ -public interface Authz { - - public static final String - PERMISSION_GRADE_ALL = "gradebook.gradeAll", - PERMISSION_GRADE_SECTION = "gradebook.gradeSection", - PERMISSION_EDIT_ASSIGNMENTS = "gradebook.editAssignments", - PERMISSION_VIEW_OWN_GRADES = "gradebook.viewOwnGrades", - PERMISSION_VIEW_STUDENT_NUMBERS = "gradebook.viewStudentNumbers"; - - public boolean isUserAbleToGrade(String gradebookUid); - public boolean isUserAbleToGrade(String gradebookUid, String userUid); - public boolean isUserAbleToGradeAll(String gradebookUid); - public boolean isUserAbleToGradeAll(String gradebookUid, String userUid); - public boolean isUserAbleToEditAssessments(String gradebookUid); - public boolean isUserAbleToViewOwnGrades(String gradebookUid); - public boolean isUserAbleToViewStudentNumbers(String gradebookUid); - public boolean isUserHasGraderPermissions(String gradebookUid); - public boolean isUserHasGraderPermissions(Long gradebookId); - public boolean isUserHasGraderPermissions(Long gradebookId, String userUid); - public boolean isUserHasGraderPermissions(String gradebookUid, String userUid); - - /** - * - * @param gradebookUid - * @param itemId - * @param studentUid - * @return is user authorized to grade this gradebook item for this student? - * first checks for special grader perms. if none, uses default perms - */ - public boolean isUserAbleToGradeItemForStudent(String gradebookUid, Long itemId, String studentUid) throws IllegalArgumentException; - - /** - * - * @param gradebookUid - * @param itemId - * @param studentUid - * @return is user authorized to view this gradebook item for this student? - * first checks for special grader perms. if none, uses default perms - */ - public boolean isUserAbleToViewItemForStudent(String gradebookUid, Long itemId, String studentUid) throws IllegalArgumentException; - - /** - * @param gradebookUid - * @return all of the CourseSections for this site - */ - public List getAllSections(String gradebookUid); - - /** - * - * @param gradebookUid - * @return all CourseSections that the current user may view or grade - */ - public List getViewableSections(String gradebookUid); - - /** - * @param gradebookUid - * @param userUid - * @param categoryId - * The category id that the desired item is associated with - * @param gbCategoryType - * The category type setting for this gradebook - * @param optionalSearchString - * a substring search for student name or display UID; the exact rules are - * up to the implementation - leave null to use all students - * @param optionalSectionUid - * null if the search should be made across all sections - * @return a map of EnrollmentRecords to grade/view permission that the given user is authorized to - * view or grade for the given gradebook item - */ - public Map findMatchingEnrollmentsForItemForUser(String userUid, String gradebookUid, Long categoryId, int gbCategoryType, String optionalSearchString, String optionalSectionUid); - - /** - * - * @param gradebookUid - * @param categoryId - * The category id that the desired item is associated with - * @param gbCategoryType - * The category type setting for this gradebook - * @param optionalSearchString - * a substring search for student name or display UID; the exact rules are - * up to the implementation - leave null to use all students - * @param optionalSectionUid - * null if the search should be made across all sections - * @return a map of EnrollmentRecords to grade/view permission that the current user is authorized to - * view or grade for the given gradebook item - */ - public Map findMatchingEnrollmentsForItem(String gradebookUid, Long categoryId, int gbCategoryType, String optionalSearchString, String optionalSectionUid); - - /** - * - * @param gradebookUid - * @param gbCategoryType - * The category type setting for this gradebook - * @param optionalSearchString - * @param optionalSectionUid - * @return Map of EnrollmentRecord --> function (grade/view) for all students that the current user has permission to - * view/grade every item in the gradebook. If he/she can grade everything, GRADE function is - * returned. Otherwise, function is VIEW. May only modify course grade if he/she can grade - * everything in the gradebook for that student. If he/she can grade only a subset of the items, the - * student is not returned. - */ - public Map findMatchingEnrollmentsForViewableCourseGrade(String gradebookUid, int gbCategoryType, String optionalSearchString, String optionalSectionUid); - /** - * - * @param gradebookUid - * @param allGbItems - * List of all Assignments associated with this gradebook - * @param optionalSearchString - * a substring search for student name or display UID; the exact rules are - * up to the implementation - leave null to use all students - * @param optionalSectionUid - * null if the search should be made across all sections - * @return a map of EnrollmentRecords to a map of item id and function (grade/view) that the user is - * authorized to view/grade - */ - public Map findMatchingEnrollmentsForViewableItems(String gradebookUid, List allGbItems, String optionalSearchString, String optionalSectionUid); - - /** - * - * @param gradebookUid - * @param studentUid - * @return a list of all section memberships for the given studentUid - */ - public List findStudentSectionMemberships(String gradebookUid, String studentUid); - - /** - * - * @param gradebookUid - * @param studentUid - * @return a list of the section membership names for the give studentUid - */ - public List getStudentSectionMembershipNames(String gradebookUid, String studentUid); - - /** - * Check to see if current user may grade or view the given student for the given item in the given gradebook. - * Returns string representation of function per GradebookService vars (view/grade) or null if no permission - * @param gradebookUid - * @param itemId - * @param studentUid - * @return GradebookService.gradePermission, GradebookService.viewPermission, or null if no permission - */ - public String getGradeViewFunctionForUserForStudentForItem(String gradebookUid, Long itemId, String studentUid); -} diff --git a/edu-services/gradebook-service/hibernate/pom.xml b/edu-services/gradebook-service/hibernate/pom.xml deleted file mode 100644 index 6abb7b4bddda..000000000000 --- a/edu-services/gradebook-service/hibernate/pom.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - 4.0.0 - - edu-services - org.sakaiproject.edu-services - 23-SNAPSHOT - ../../pom.xml - - org.sakaiproject.edu-services.gradebook - gradebook-service-hibernate - gradebook-service-hibernate - jar - - shared - - - - org.apache.commons - commons-lang3 - provided - - - org.sakaiproject.edu-services.gradebook - gradebook-service-api - provided - - - org.sakaiproject.edu-services.sections - sections-api - provided - - - - - - ${basedir}/src/hibernate - - - - diff --git a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Category.hbm.xml b/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Category.hbm.xml deleted file mode 100644 index 26f412b349de..000000000000 --- a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Category.hbm.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - GB_CATEGORY_S - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - create index GB_CATEGORY_GB_INDX on GB_CATEGORY_T (GRADEBOOK_ID) - - - - - diff --git a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Comment.hbm.xml b/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Comment.hbm.xml deleted file mode 100644 index 0551aefd5810..000000000000 --- a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Comment.hbm.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - GB_COMMENT_S - - - - - - - - - - - - - - - - - - diff --git a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradableObject.hbm.xml b/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradableObject.hbm.xml deleted file mode 100644 index 30f9499a2e0e..000000000000 --- a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradableObject.hbm.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - GB_GRADABLE_OBJECT_S - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - create index GB_GRADABLE_OBJ_GB_IDX on GB_GRADABLE_OBJECT_T (GRADEBOOK_ID) - - - - - - - create index GB_GRADABLE_OBJ_ASN_IDX on GB_GRADABLE_OBJECT_T (OBJECT_TYPE_ID, GRADEBOOK_ID, NAME, REMOVED) - - - - - create index GB_GRADABLE_OBJ_CT_IDX on GB_GRADABLE_OBJECT_T (CATEGORY_ID) - - - diff --git a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradeMapping.hbm.xml b/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradeMapping.hbm.xml deleted file mode 100644 index 0045a8ea8dad..000000000000 --- a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradeMapping.hbm.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - GB_GRADE_MAPPING_S - - - - - - - - - - - - - - - - - - - - - - - - - - - create index GB_GRADE_MAP_GB_IDX on GB_GRADE_MAP_T (GRADEBOOK_ID) - - - - - - diff --git a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradeRecord.hbm.xml b/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradeRecord.hbm.xml deleted file mode 100644 index 2d7fd26d9860..000000000000 --- a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradeRecord.hbm.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - GB_GRADE_RECORD_S - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - create index GB_GRADE_RECORD_G_O_IDX on GB_GRADE_RECORD_T (GRADABLE_OBJECT_ID) - - - - - - - - create index GB_GRADE_RECORD_O_T_IDX on GB_GRADE_RECORD_T (OBJECT_TYPE_ID) - - - diff --git a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Gradebook.hbm.xml b/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Gradebook.hbm.xml deleted file mode 100644 index 9e98b1da4a17..000000000000 --- a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Gradebook.hbm.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - GB_GRADEBOOK_S - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradebookProperty.hbm.xml b/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradebookProperty.hbm.xml deleted file mode 100644 index b4bb45d97902..000000000000 --- a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradebookProperty.hbm.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - GB_PROPERTY_S - - - - - - - - - diff --git a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradingEvent.hbm.xml b/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradingEvent.hbm.xml deleted file mode 100644 index 0f1ae66d4d3a..000000000000 --- a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradingEvent.hbm.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - GB_GRADING_EVENT_S - - - - - - - - - - - - - - org.sakaiproject.service.gradebook.shared.GradingEventStatus - - - - - - - create index GB_GRADING_EVENT_T_STU_OBJ_ID on GB_GRADING_EVENT_T (STUDENT_ID, GRADABLE_OBJECT_ID); - create index GB_GRADING_EVENT_T_DATE_OBJ_ID on GB_GRADING_EVENT_T (DATE_GRADED, GRADABLE_OBJECT_ID); - - - - diff --git a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradingScale.hbm.xml b/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradingScale.hbm.xml deleted file mode 100644 index a082bf49eb06..000000000000 --- a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/GradingScale.hbm.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - GB_GRADING_SCALE_S - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/LetterGradePercenteMapping.hbm.xml b/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/LetterGradePercenteMapping.hbm.xml deleted file mode 100644 index 8c9df7ee0769..000000000000 --- a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/LetterGradePercenteMapping.hbm.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - GB_LETTER_MAPPING_S - - - - - - - - - - - - - - - - - diff --git a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Permission.hbm.xml b/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Permission.hbm.xml deleted file mode 100644 index e94d21067923..000000000000 --- a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Permission.hbm.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - GB_PERMISSION_S - - - - - - - - - - - - - - - - - diff --git a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Spreadsheet.hbm.xml b/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Spreadsheet.hbm.xml deleted file mode 100644 index 47e7496f9dbe..000000000000 --- a/edu-services/gradebook-service/hibernate/src/hibernate/org/sakaiproject/tool/gradebook/Spreadsheet.hbm.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - GB_SPREADSHEET_S - - - - - - - - - - - - - - - - - diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/AbstractGradeRecord.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/AbstractGradeRecord.java deleted file mode 100644 index 494b5231bfb2..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/AbstractGradeRecord.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook; - -import java.io.Serializable; -import java.util.Date; - -import org.apache.commons.lang3.builder.ToStringBuilder; - -/** - * AbstractGradeRecord is the abstract base class for Grade Records, which are - * records of instructors (or the application, in the case of autocalculated - * gradebooks) assigning a grade to a student for a particular GradableObject. - * - * @author Josh Holtzman - */ -public abstract class AbstractGradeRecord implements Serializable { - - private static final long serialVersionUID = 1L; - - protected Long id; - protected int version; - protected String studentId; - protected String graderId; - protected GradableObject gradableObject; - protected Date dateRecorded; - - public abstract Double getGradeAsPercentage(); - - /** - * @return Whether this is a course grade record - */ - public abstract boolean isCourseGradeRecord(); - - /** - * @return Returns the pointsEarned - */ - public abstract Double getPointsEarned(); - - /** - * @return Returns the dateRecorded. - */ - public Date getDateRecorded() { - return dateRecorded; - } - /** - * @param dateRecorded The dateRecorded to set. - */ - public void setDateRecorded(Date dateRecorded) { - this.dateRecorded = dateRecorded; - } - /** - * @return Returns the gradableObject. - */ - public GradableObject getGradableObject() { - return gradableObject; - } - /** - * @param gradableObject The gradableObject to set. - */ - public void setGradableObject(GradableObject gradableObject) { - this.gradableObject = gradableObject; - } - /** - * @return Returns the id. - */ - public Long getId() { - return id; - } - /** - * @param id The id to set. - */ - public void setId(Long id) { - this.id = id; - } - /** - * @return Returns the version. - */ - public int getVersion() { - return version; - } - /** - * @param version The version to set. - */ - public void setVersion(int version) { - this.version = version; - } - /** - * @return Returns the graderId. - */ - public String getGraderId() { - return graderId; - } - /** - * @param graderId The graderId to set. - */ - public void setGraderId(String graderId) { - this.graderId = graderId; - } - /** - * @return Returns the studentId. - */ - public String getStudentId() { - return studentId; - } - /** - * @param studentId The studentId to set. - */ - public void setStudentId(String studentId) { - this.studentId = studentId; - } - - @Override - public String toString() { - return new ToStringBuilder(this). - append("id", id). - append("studentId", studentId). - append("graderId", graderId).toString(); - } - -} - - - diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Category.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Category.java deleted file mode 100644 index 6dd69d869fbd..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Category.java +++ /dev/null @@ -1,493 +0,0 @@ -/** - * Copyright (c) 2003-2014 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.tool.gradebook; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.util.Comparator; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; -import org.sakaiproject.service.gradebook.shared.GradebookService; - -public class Category implements Serializable { - /** - * - */ - private static final long serialVersionUID = 2609776646548825870L; - private Long id; - private int version; - private Gradebook gradebook; - private String name; - private Double weight; - private Integer dropLowest; - private Integer dropHighest; - private Integer keepHighest; - - private boolean removed; - private Double averageTotalPoints; // average total points possible for this category - private Double averageScore; // average scores that students got for this category - private Double mean; // mean value of percentage for this category - private Double totalPointsEarned; // scores that students got for this category - private Double totalPointsPossible; // total points possible for this category - private List assignmentList; - private int assignmentCount; - private Boolean extraCredit = false; - private Boolean unweighted; - private Boolean equalWeightAssignments = false; - private Integer categoryOrder; - private Boolean enforcePointWeighting; - - public static final Comparator nameComparator; - public static final Comparator averageScoreComparator; - public static final Comparator weightComparator; - - public static String SORT_BY_NAME = "name"; - public static String SORT_BY_AVERAGE_SCORE = "averageScore"; - public static String SORT_BY_WEIGHT = "weight"; - - static { - nameComparator = new Comparator() { - @Override - public int compare(final Object o1, final Object o2) { - return ((Category) o1).getName().toLowerCase().compareTo(((Category) o2).getName().toLowerCase()); - } - }; - averageScoreComparator = new Comparator() { - @Override - public int compare(final Object o1, final Object o2) { - final Category one = (Category) o1; - final Category two = (Category) o2; - - if (one.getAverageScore() == null && two.getAverageScore() == null) { - return one.getName().compareTo(two.getName()); - } - - if (one.getAverageScore() == null) { - return -1; - } - if (two.getAverageScore() == null) { - return 1; - } - - final int comp = (one.getAverageScore().compareTo(two.getAverageScore())); - if (comp == 0) { - return one.getName().compareTo(two.getName()); - } else { - return comp; - } - } - }; - weightComparator = new Comparator() { - @Override - public int compare(final Object o1, final Object o2) { - final Category one = (Category) o1; - final Category two = (Category) o2; - - if (one.getWeight() == null && two.getWeight() == null) { - return one.getName().compareTo(two.getName()); - } - - if (one.getWeight() == null) { - return -1; - } - if (two.getWeight() == null) { - return 1; - } - - final int comp = (one.getWeight().compareTo(two.getWeight())); - if (comp == 0) { - return one.getName().compareTo(two.getName()); - } else { - return comp; - } - } - }; - } - - public Integer getDropHighest() { - return this.dropHighest == null ? 0 : this.dropHighest; - } - - public void setDropHighest(final Integer dropHighest) { - this.dropHighest = dropHighest; - } - - public Integer getKeepHighest() { - return this.keepHighest == null ? 0 : this.keepHighest; - } - - public void setKeepHighest(final Integer keepHighest) { - this.keepHighest = keepHighest; - } - - /* - * returns true if this category drops any scores - */ - public boolean isDropScores() { - return getDropLowest() > 0 || getDropHighest() > 0 || getKeepHighest() > 0; - } - - public Double getItemValue() { - if (isAssignmentsEqual()) { - Double returnVal = 0.0; - final List assignments = getAssignmentList(); - if (assignments != null) { - for (final Object obj : assignments) { - if (obj instanceof GradebookAssignment) { - final GradebookAssignment assignment = (GradebookAssignment) obj; - if (!GradebookAssignment.item_type_adjustment.equals(assignment.getItemType())) {// ignore adjustment items - returnVal = assignment.getPointsPossible(); - return returnVal; - } - } - } - } - // didn't find any, so return 0.0 - return returnVal; - } else { - return 0.0; - } - } - - public Integer getDropLowest() { - return this.dropLowest != null ? this.dropLowest : 0; - } - - public void setDropLowest(final Integer lowest) { - this.dropLowest = lowest; - } - - public Gradebook getGradebook() { - return this.gradebook; - } - - public void setGradebook(final Gradebook gradebook) { - this.gradebook = gradebook; - } - - public Long getId() { - return this.id; - } - - public void setId(final Long id) { - this.id = id; - } - - public String getName() { - return this.name; - } - - public void setName(final String name) { - // SAK-20071 - names over 255 chars cause DB insert failure - this.name = StringUtils.substring(name, 0, 249); - } - - public int getVersion() { - return this.version; - } - - public void setVersion(final int version) { - this.version = version; - } - - public Double getWeight() { - return this.weight; - } - - public void setWeight(final Double weight) { - this.weight = weight; - } - - public boolean isRemoved() { - return this.removed; - } - - public void setRemoved(final boolean removed) { - this.removed = removed; - } - - public Double getAverageTotalPoints() { - return this.averageTotalPoints; - } - - public void setAverageTotalPoints(final Double averageTotalPoints) { - this.averageTotalPoints = averageTotalPoints; - } - - public Double getAverageScore() { - return this.averageScore; - } - - public void setAverageScore(final Double averageScore) { - this.averageScore = averageScore; - } - - public void calculateStatistics(final List assignmentsWithStats) { - int numScored = 0; - int numOfAssignments = 0; - BigDecimal total = new BigDecimal("0"); - BigDecimal totalPossible = new BigDecimal("0"); - - for (final GradebookAssignment assign : assignmentsWithStats) { - final Double score = assign.getAverageTotal(); - - if (assign.isCounted() && !assign.getUngraded() && assign.getPointsPossible() != null - && assign.getPointsPossible().doubleValue() > 0.0) { - if (score != null) { - total = total.add(new BigDecimal(score.toString())); - if (assign.getPointsPossible() != null && !assign.isExtraCredit()) { - totalPossible = totalPossible.add(new BigDecimal(assign.getPointsPossible().toString())); - numOfAssignments++; - } - if (!assign.isExtraCredit()) { - numScored++; - } - } - } - } - - if (numScored == 0 || numOfAssignments == 0) { - this.averageScore = null; - this.averageTotalPoints = null; - this.mean = null; - this.totalPointsEarned = null; - this.totalPointsPossible = null; - } else { - final BigDecimal bdNumScored = new BigDecimal(numScored); - final BigDecimal bdNumAssign = new BigDecimal(numOfAssignments); - this.averageScore = Double.valueOf(total.divide(bdNumScored, GradebookService.MATH_CONTEXT).doubleValue()); - this.averageTotalPoints = Double.valueOf(totalPossible.divide(bdNumAssign, GradebookService.MATH_CONTEXT).doubleValue()); - final BigDecimal value = total.divide(bdNumScored, GradebookService.MATH_CONTEXT) - .divide(new BigDecimal(this.averageTotalPoints.doubleValue()), GradebookService.MATH_CONTEXT) - .multiply(new BigDecimal("100")); - this.mean = Double.valueOf(value.doubleValue()); - } - } - - public void calculateStatisticsPerStudent(final List gradeRecords, final String studentUid) { - getGradebook().getGrade_type(); - int numScored = 0; - int numOfAssignments = 0; - BigDecimal total = new BigDecimal("0"); - BigDecimal totalPossible = new BigDecimal("0"); - - if (gradeRecords == null) { - setAverageScore(null); - setAverageTotalPoints(null); - setMean(null); - setTotalPointsEarned(null); - setTotalPointsPossible(null); - return; - } - - for (final AssignmentGradeRecord gradeRecord : gradeRecords) { - if (gradeRecord != null && gradeRecord.getStudentId().equals(studentUid)) { - final GradebookAssignment assignment = gradeRecord.getAssignment(); - if (assignment.isCounted() && !assignment.getUngraded() && assignment.getPointsPossible().doubleValue() > 0.0 - && !gradeRecord.getDroppedFromGrade()) { - - final Category assignCategory = assignment.getCategory(); - if (assignCategory != null && assignCategory.getId().equals(this.id)) { - final Double score = gradeRecord.getPointsEarned(); - if (score != null) { - final BigDecimal bdScore = new BigDecimal(score.toString()); - total = total.add(bdScore); - if (assignment.getPointsPossible() != null && !assignment.isExtraCredit()) { - final BigDecimal bdPointsPossible = new BigDecimal(assignment.getPointsPossible().toString()); - totalPossible = totalPossible.add(bdPointsPossible); - numOfAssignments++; - } - if (!assignment.isExtraCredit()) { - numScored++; - } - } - } - } - } - } - - // if totalPossible is 0, this prevents a division by zero scenario likely from - // an adjustment item being the only thing graded. - if (numScored == 0 || numOfAssignments == 0 || totalPossible.doubleValue() == 0) { - this.averageScore = null; - this.averageTotalPoints = null; - this.mean = null; - this.totalPointsEarned = null; - this.totalPointsPossible = null; - } else { - final BigDecimal bdNumScored = new BigDecimal(numScored); - final BigDecimal bdNumAssign = new BigDecimal(numOfAssignments); - this.averageScore = Double.valueOf(total.divide(bdNumScored, GradebookService.MATH_CONTEXT).doubleValue()); - this.averageTotalPoints = Double.valueOf(totalPossible.divide(bdNumAssign, GradebookService.MATH_CONTEXT).doubleValue()); - final BigDecimal value = total.divide(bdNumScored, GradebookService.MATH_CONTEXT) - .divide((totalPossible.divide(bdNumAssign, GradebookService.MATH_CONTEXT)), GradebookService.MATH_CONTEXT) - .multiply(new BigDecimal("100")); - - this.mean = Double.valueOf(value.doubleValue()); - } - } - - public List getAssignmentList() { - return this.assignmentList; - } - - public void setAssignmentList(final List assignmentList) { - this.assignmentList = assignmentList; - } - - /* - * The methods below are used with the GradableObjects because all three are displayed in a dataTable together - */ - public boolean getIsCategory() { - return true; - } - - public boolean isCourseGrade() { - return false; - } - - public boolean isAssignment() { - return false; - } - - public Double getMean() { - return this.mean; - } - - public void setMean(final Double mean) { - this.mean = mean; - } - - public int getAssignmentCount() { - return this.assignmentCount; - } - - public void setAssignmentCount(final int assignmentCount) { - this.assignmentCount = assignmentCount; - } - - // these two functions are needed to keep the old API and help JSF and RSF play nicely together. Since isExtraCredit already exists and - // we can't remove it - // and JSF expects Boolean values to be "getExtraCredit", this had to be added for JSF. Also, since the external GB create item page is - // in - // RSF, you can't name it getExtraCredit and keep isExtraCredit b/c of SAK-14589 - public Boolean getIsExtraCredit() { - return isExtraCredit(); - } - - public void setIsExtraCredit(final Boolean isExtraCredit) { - setExtraCredit(isExtraCredit); - } - - public Boolean isExtraCredit() { - return this.extraCredit; - } - - public void setExtraCredit(final Boolean isExtraCredit) { - this.extraCredit = isExtraCredit; - } - - public boolean isAssignmentsEqual() { - boolean isEqual = true; - Double pointsPossible = null; - final List assignments = getAssignmentList(); - if (assignments == null) { - return isEqual; - } else { - for (final Object obj : assignments) { - if (obj instanceof GradebookAssignment) { - final GradebookAssignment assignment = (GradebookAssignment) obj; - if (pointsPossible == null) { - if (!GradebookAssignment.item_type_adjustment.equals(assignment.getItemType())) {// ignore adjustment items - pointsPossible = assignment.getPointsPossible(); - } - } else { - if (assignment.getPointsPossible() != null - && !GradebookAssignment.item_type_adjustment.equals(assignment.getItemType()) // ignore adjustment items - // that are not equal - && !pointsPossible.equals(assignment.getPointsPossible()) && !isEqualWeightAssignments()) { - isEqual = false; - return isEqual; - } - } - } - } - } - return isEqual; - } - - public Boolean isUnweighted() { - return this.unweighted; - } - - public void setUnweighted(final Boolean unweighted) { - this.unweighted = unweighted; - } - - public Boolean isEqualWeightAssignments() { - return this.equalWeightAssignments != null ? this.equalWeightAssignments : false; - } - - public void setEqualWeightAssignments(final Boolean equalWeightAssignments) { - this.equalWeightAssignments = equalWeightAssignments; - } - - public Integer getCategoryOrder() { - return this.categoryOrder; - } - - public void setCategoryOrder(final Integer categoryOrder) { - this.categoryOrder = categoryOrder; - } - - public Boolean isEnforcePointWeighting() { - return this.enforcePointWeighting; - } - - public void setEnforcePointWeighting(final Boolean enforcePointWeighting) { - this.enforcePointWeighting = enforcePointWeighting; - } - - public Double getTotalPointsEarned() { - return this.totalPointsEarned; - } - - public void setTotalPointsEarned(final Double totalPointsEarned) { - this.totalPointsEarned = totalPointsEarned; - } - - public Double getTotalPointsPossible() { - return this.totalPointsPossible; - } - - public void setTotalPointsPossible(final Double totalPointsPossible) { - this.totalPointsPossible = totalPointsPossible; - } - - /** - * Fix for Category NPE reported in SAK-14519 Since category uses "totalPointsPossible" property instead of "pointsPossible" property, - * as in Assignments - */ - public Double getPointsPossible() { - return this.totalPointsPossible; - } - - public void setPointsPossible(final Double pointsPossible) { - this.totalPointsPossible = pointsPossible; - } - -} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Comment.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Comment.java deleted file mode 100644 index 0414bb0557e6..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Comment.java +++ /dev/null @@ -1,145 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2006, 2008 The Sakai Foundation, The MIT Corporation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.opensource.org/licenses/ECL-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ - -package org.sakaiproject.tool.gradebook; - -import java.io.Serializable; -import java.util.Date; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.apache.commons.lang3.builder.ToStringBuilder; - -/** - * Comment Object - * - * - * Author:Louis Majanja - * Date: Oct 20, 2006 - * Time: 10:56:34 AM - */ -public class Comment implements Serializable { - - private Long id; - private String studentId; - private String graderId; - private int version; - private Date dateRecorded; - private String commentText; - private GradableObject gradableObject; - - - public Comment(String studentId, String comment, GradableObject gradableObject) { - this.gradableObject = gradableObject; - this.studentId = studentId; - this.commentText = comment; - } - - - public Comment() { - } - - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getStudentId() { - return studentId; - } - - public void setStudentId(String studentId) { - this.studentId = studentId; - } - - public String getGraderId() { - return graderId; - } - - public void setGraderId(String graderId) { - this.graderId = graderId; - } - - public int getVersion() { - return version; - } - - public void setVersion(int version) { - this.version = version; - } - - public Date getDateRecorded() { - return dateRecorded; - } - - public void setDateRecorded(Date dateRecorded) { - this.dateRecorded = dateRecorded; - } - - public String getCommentText() { - return commentText; - } - - public void setCommentText(String commentText) { - this.commentText = commentText; - } - - public GradableObject getGradableObject() { - return gradableObject; - } - - public void setGradableObject(GradableObject gradableObject) { - this.gradableObject = gradableObject; - } - - - @Override - public String toString() { - return new ToStringBuilder(this). - append("id", id). - append("grader", graderId). - append("comment",commentText). - append("studentid",studentId).toString(); - - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof Comment)) { - return false; - } - Comment comment = (Comment)other; - return new EqualsBuilder() - .append(gradableObject, comment.getGradableObject()) - .append(id, comment.getId()) - .append(commentText, comment.getCommentText()).isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(). - append(gradableObject). - append(id). - append(commentText). - toHashCode(); - } - -} - diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CommonGradeRecord.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CommonGradeRecord.java deleted file mode 100644 index 5c0050231753..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CommonGradeRecord.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) 2003-2009 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.tool.gradebook; - -import java.util.Date; - -/** - *

- * CommonGradebookRecordImpl uniquely identifies student grade records - * on both CL and Legacy data sources - * - * @author Jarrod Lannan - * @version $Revision:$ - */ - -public interface CommonGradeRecord -{ - - /** - * @return Returns the assignmentName. - */ - public String getAssignmentName(); - - /** - * @param assignmentName The assignmentName to set. - */ - public void setAssignmentName(String assignmentName); - - /** - * @return Returns the dateGraded. - */ - public Date getDateGraded(); - - /** - * @param dateGraded The dateGraded to set. - */ - public void setDateGraded(Date dateGraded); - - /** - * @return Returns the graderComments. - */ - public String getGraderComments(); - - /** - * @param graderComments The graderComments to set. - */ - public void setGraderComments(String graderComments); - - /** - * @return Returns the graderId. - */ - public String getGraderId(); - - /** - * @param graderId The graderId to set. - */ - public void setGraderId(String graderId); - - /** - * @return Returns the pointsEarned. - */ - public Double getPointsEarned(); - - /** - * @param pointsEarned The pointsEarned to set. - */ - public void setPointsEarned(Double pointsEarned); - - /** - * @return Returns the studentUserId. - */ - public String getStudentUserId(); - - /** - * @param studentUserId The studentUserId to set. - */ - public void setStudentUserId(String studentUserId); - -} -/********************************************************************************** - * - * $Id:$ - * - **********************************************************************************/ diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CourseGrade.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CourseGrade.java deleted file mode 100644 index bf8379db752c..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CourseGrade.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (c) 2003-2012 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook; - -import java.math.BigDecimal; -import java.util.Collection; - -import org.sakaiproject.service.gradebook.shared.GradebookService; - -/** - * A CourseGrade is a GradableObject that represents the overall course grade in a gradebook. - * - * @author Josh Holtzman - */ -public class CourseGrade extends GradableObject { - /** - * - */ - private static final long serialVersionUID = -7607255825842609208L; - - // Should only be used to fill in the DB column. - private static final String COURSE_GRADE_NAME = "Course Grade"; - - public static String SORT_BY_OVERRIDE_GRADE = "override"; - public static String SORT_BY_CALCULATED_GRADE = "autoCalc"; - public static String SORT_BY_POINTS_EARNED = "pointsEarned"; - - private Double averageScore; - - public CourseGrade() { - setName(COURSE_GRADE_NAME); - } - - /** - * @see org.sakaiproject.tool.gradebook.GradableObject#isCourseGrade() - */ - @Override - public boolean isCourseGrade() { - return true; - } - - /** - * @see org.sakaiproject.tool.gradebook.GradableObject#isAssignment() - */ - @Override - public boolean isAssignment() { - return false; - } - - /** - * @see org.sakaiproject.tool.gradebook.GradableObject#isCategory() - */ - @Override - public boolean getIsCategory() { - return false; - } - - /** - * Calculate the mean course grade (whether entered or calulated) as a percentage for all enrollments, leaving students who've - * explicitly been given non-percentage-valued manual-only course grades (such as "I" for incomplete) or null scores out of the - * calculation. - */ - public void calculateStatistics(final Collection gradeRecords, final int numEnrollments) { - // Ungraded but enrolled students count as if they have 0% in the course. - int numScored = numEnrollments - gradeRecords.size(); - BigDecimal total = new BigDecimal("0"); - BigDecimal average = new BigDecimal("0"); - - for (final CourseGradeRecord record : gradeRecords) { - final Double score = record.getGradeAsPercentage(); - - // Skip manual-only course grades. - if ((record.getEnteredGrade() != null) && (score == null)) { - continue; - } - - if (score != null && record.getPointsEarned() != null) { - average = average.add(new BigDecimal(record.getPointsEarned().toString())); - total = total.add(new BigDecimal(score.toString())); - numScored++; - } - } - if (numScored == 0) { - this.mean = null; - this.averageScore = null; - } else { - this.mean = Double.valueOf(total.divide(new BigDecimal(numScored), GradebookService.MATH_CONTEXT).doubleValue()); - this.averageScore = Double.valueOf(average.divide(new BigDecimal(numScored), GradebookService.MATH_CONTEXT).doubleValue()); - } - } - - public Double getAverageScore() { - return this.averageScore; - } - - public void setAverageScore(final Double averageScore) { - this.averageScore = averageScore; - } -} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CourseGradeRecord.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CourseGradeRecord.java deleted file mode 100644 index 6d107855c90c..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CourseGradeRecord.java +++ /dev/null @@ -1,275 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook; - -import java.math.BigDecimal; -import java.util.Comparator; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; -import org.sakaiproject.service.gradebook.shared.GradebookService; - -/** - * A CourseGradeRecord is a grade record that can be associated with a CourseGrade. - * - * @author Josh Holtzman - */ -public class CourseGradeRecord extends AbstractGradeRecord { - - private static final long serialVersionUID = 1L; - - private String enteredGrade; - private Double enteredPoints; - private Double autoCalculatedGrade; // Not persisted - private Double calculatedPointsEarned; // Not persisted - private Double totalPointsPossible; // Not persisted - - public static Comparator calcComparator; - - static { - calcComparator = new Comparator() { - @Override - public int compare(final CourseGradeRecord cgr1, final CourseGradeRecord cgr2) { - if ((cgr1 == null || cgr2 == null) || (cgr1.getGradeAsPercentage() == null && cgr2.getGradeAsPercentage() == null)) { - return 0; - } - if (cgr1.getGradeAsPercentage() == null) { - return -1; - } - if (cgr2.getGradeAsPercentage() == null) { - return 1; - } - // SAK-12017 - Commented out as getPointsEarned is no longer an accurate comparator - // due to nulls no longer being calculated in to the Course Grade - // return cgr1.getPointsEarned().compareTo(cgr2.getPointsEarned()); - // Better to use getGradeAsPercentage - return cgr1.getGradeAsPercentage().compareTo(cgr2.getGradeAsPercentage()); - } - }; - } - - public static Comparator getOverrideComparator(final GradeMapping mapping) { - return new Comparator() { - @Override - public int compare(final CourseGradeRecord cgr1, final CourseGradeRecord cgr2) { - - if (cgr1 == null && cgr2 == null) { - return 0; - } - if (cgr1 == null) { - return -1; - } - if (cgr2 == null) { - return 1; - } - - final String enteredGrade1 = StringUtils.trimToEmpty(cgr1.getEnteredGrade()); - final String enteredGrade2 = StringUtils.trimToEmpty(cgr2.getEnteredGrade()); - - // Grading scales are always defined in descending order. - final List grades = mapping.getGradingScale().getGrades(); - int gradePos1 = -1; - int gradePos2 = -1; - for (int i = 0; (i < grades.size()) && ((gradePos1 == -1) || (gradePos2 == -1)); i++) { - final String grade = grades.get(i); - if (grade.equals(enteredGrade1)) { - gradePos1 = i; - } - if (grade.equals(enteredGrade2)) { - gradePos2 = i; - } - } - return gradePos2 - gradePos1; - } - }; - - } - - /** - * The graderId and dateRecorded properties will be set explicitly by the grade manager before the database is updated. - * - * @param courseGrade - * @param studentId - */ - public CourseGradeRecord(final CourseGrade courseGrade, final String studentId) { - this.gradableObject = courseGrade; - this.studentId = studentId; - } - - /** - * Default no-arg constructor - */ - public CourseGradeRecord() { - super(); - } - - /** - * This method will fail unless this course grade was fetched "with statistics", since it relies on having the total number of points - * possible available to calculate the percentage. - * - * @see org.sakaiproject.tool.gradebook.AbstractGradeRecord#getGradeAsPercentage() - */ - @Override - public Double getGradeAsPercentage() { - if (this.enteredGrade == null) { - return this.autoCalculatedGrade; - } else { - return getCourseGrade().getGradebook().getSelectedGradeMapping().getValue(this.enteredGrade); - } - } - - /** - * Convenience method to get the correctly cast CourseGrade that this CourseGradeRecord references. - * - * @return CourseGrade referenced by this GradableObject - */ - public CourseGrade getCourseGrade() { - return (CourseGrade) super.getGradableObject(); - } - - /** - * @return Returns the enteredGrade. - */ - public String getEnteredGrade() { - return this.enteredGrade; - } - - /** - * @param enteredGrade The enteredGrade to set. - */ - public void setEnteredGrade(final String enteredGrade) { - this.enteredGrade = enteredGrade; - } - - /** - * @return Returns the enteredPoints. - */ - public Double getEnteredPoints() { - return this.enteredPoints; - } - - /** - * @param enteredPoints The enteredPoints to set. - */ - public void setEnteredPoints(final Double enteredPoints) { - this.enteredPoints = enteredPoints; - } - - /** - * @return Returns the autoCalculatedGrade. - */ - public Double getAutoCalculatedGrade() { - return this.autoCalculatedGrade; - } - - @Override - public Double getPointsEarned() { - return this.calculatedPointsEarned; - } - - /** - * @return Returns the displayGrade. - */ - public String getDisplayGrade() { - if (this.enteredGrade != null) { - return this.enteredGrade; - } else { - return getCourseGrade().getGradebook().getSelectedGradeMapping().getMappedGrade(this.autoCalculatedGrade); - } - } - - /** - * @see org.sakaiproject.tool.gradebook.AbstractGradeRecord#isCourseGradeRecord() - */ - @Override - public boolean isCourseGradeRecord() { - return true; - } - - /** - * For use by the Course Grade UI. - */ - public Double getNonNullAutoCalculatedGrade() { - Double percent = getAutoCalculatedGrade(); - if (percent == null) { - percent = Double.valueOf(0); - } - return percent; - } - - public void initNonpersistentFields(final double totalPointsPossible, final double totalPointsEarned) { - BigDecimal percentageEarned; - this.totalPointsPossible = totalPointsPossible; - this.calculatedPointsEarned = totalPointsEarned; - final BigDecimal bdTotalPointsPossible = BigDecimal.valueOf(totalPointsPossible); - final BigDecimal bdTotalPointsEarned = BigDecimal.valueOf(totalPointsEarned); - if (totalPointsPossible == 0.0) { - this.autoCalculatedGrade = null; - } else { - percentageEarned = bdTotalPointsEarned.divide(bdTotalPointsPossible, GradebookService.MATH_CONTEXT) - .multiply(new BigDecimal("100")); - this.autoCalculatedGrade = percentageEarned.doubleValue(); - } - } - - // Added by -Qu for totalPoints implementation in GB2 bugid:4371 9/2011 - public void setCalculatedPointsEarned(final double literalTotalPointsEarned) { - this.calculatedPointsEarned = literalTotalPointsEarned; - } - - // Added compatibility in GB Classic (12.x) - SAK-41295 - public void initNonpersistentFields(double totalPointsPossible, double totalPointsEarned, - double literalTotalPointsEarned) { - initNonpersistentFields(totalPointsPossible, totalPointsEarned, literalTotalPointsEarned, 0.0); - } - - public void initNonpersistentFields(final double totalPointsPossible, final double totalPointsEarned, - final double literalTotalPointsEarned, final double totalPointsExtra) { - BigDecimal percentageEarned; - BigDecimal percentageExtraEarned; - this.calculatedPointsEarned = literalTotalPointsEarned; - this.totalPointsPossible = totalPointsPossible; - final BigDecimal bdTotalPointsPossible = BigDecimal.valueOf(totalPointsPossible); - final BigDecimal bdTotalPointsEarned = BigDecimal.valueOf(totalPointsEarned); - - if (totalPointsPossible <= 0.0) { - this.autoCalculatedGrade = null; - } else { - percentageEarned = bdTotalPointsEarned.divide(bdTotalPointsPossible, GradebookService.MATH_CONTEXT) - .multiply(new BigDecimal("100")); - percentageExtraEarned = BigDecimal.valueOf(totalPointsExtra).multiply(new BigDecimal("100"), GradebookService.MATH_CONTEXT); - percentageEarned = percentageEarned.add(percentageExtraEarned, GradebookService.MATH_CONTEXT); - this.autoCalculatedGrade = percentageEarned.doubleValue(); - } - } - - public Double getCalculatedPointsEarned() { - return this.calculatedPointsEarned; - } - - public void setAutoCalculatedGrade(final Double autoCalculatedGrade) { - this.autoCalculatedGrade = autoCalculatedGrade; - } - - public Double getTotalPointsPossible() { - return this.totalPointsPossible; - } - - public void setTotalPointsPossible(final Double totalPointsPossible) { - this.totalPointsPossible = totalPointsPossible; - } -} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CourseGradesToSpreadsheetConverter.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CourseGradesToSpreadsheetConverter.java deleted file mode 100644 index 8b640ac81d86..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/CourseGradesToSpreadsheetConverter.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2003-2012 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook; - -import java.util.List; -import java.util.Map; - -import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; - -/** - * Interface to let institutions intercept course grade spreadsheet download requests. - */ -public interface CourseGradesToSpreadsheetConverter { - /** - * This method is called by the Course Grade UI after gathering filtered enrollment - * records and course grade records, and before actually formatting and downloading - * the XLS or CSV file. Customized implementations could, for example, call out to - * the course management service and change or add columns to the generic data table - * which is sent on to be formatted. - * - * @param enrollments - * @param courseGrade - * @param gradesMap a map of student UIDs to grade records - * @return a spreadsheet-like list of rows, each of which is a list of column values; - * the first row should contain header strings - */ - public List> getSpreadsheetData(List enrollments, CourseGrade courseGrade, Map gradesMap, List fields); -} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradableObject.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradableObject.java deleted file mode 100644 index 17ded285c01a..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradableObject.java +++ /dev/null @@ -1,367 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook; - -import java.io.Serializable; -import java.util.Comparator; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.apache.commons.lang3.builder.ToStringBuilder; - -/** - * A GradableObject is a component of a Gradebook for which students can be assigned a GradeRecord. - * - * @author Josh Holtzman - */ -public abstract class GradableObject implements Serializable { - - /** - * - */ - private static final long serialVersionUID = 4253182592945987136L; - protected Long id; - protected int version; - protected Gradebook gradebook; - protected String name; - protected Integer sortOrder; - protected Integer categorizedSortOrder; - - protected Double mean; // not persisted; not used in all contexts (in Overview & GradebookAssignment Grading, - // not in Roster or Student View) - - protected boolean removed; // We had trouble with foreign key constraints in the UCB pilot when - // instructors "emptied" all scores for an assignment and then tried to - // delete the assignment. Instead, we should hide the "removed" assignments - // from the app by filtering the removed assignments in the hibernate queries - - public static Comparator defaultComparator; - public static Comparator sortingComparator; - public static Comparator dateComparator; - public static Comparator meanComparator; - public static Comparator nameComparator; - public static Comparator idComparator; - public static Comparator categoryComparator; - static { - categoryComparator = new Comparator() { - @Override - @SuppressWarnings("unchecked") - public int compare(final GradebookAssignment one, final GradebookAssignment two) { - if (one.getCategory() == null && two.getCategory() == null) { - return 0; - } else if (one.getCategory() == null) { - return 1; // no cats to the end - } else if (two.getCategory() == null) { - return -1; // no cats to the end - } else { - // compare the category names the same way as the normal comparator - return Category.nameComparator.compare(one.getCategory(), two.getCategory()); - } - } - - @Override - public String toString() { - return "GradableObject.categoryComparator"; - } - }; - idComparator = new Comparator() { - @Override - public int compare(final GradableObject one, final GradableObject two) { - if (one.getId() == null && two.getId() == null) { - return 0; - } else if (one.getName() == null) { - return 1; - } else if (two.getName() == null) { - return -1; - } else { - return one.getId().compareTo(two.getId()); - } - } - - @Override - public String toString() { - return "GradableObject.idComparator"; - } - }; - nameComparator = new Comparator() { - @Override - public int compare(final GradableObject one, final GradableObject two) { - if (one.getName() == null && two.getName() == null) { - return idComparator.compare(one, two); - } else if (one.getName() == null) { - return 1; - } else if (two.getName() == null) { - return -1; - } else { - return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); - } - } - - @Override - public String toString() { - return "GradableObject.nameComparator"; - } - }; - meanComparator = new Comparator() { - @Override - public int compare(final GradableObject one, final GradableObject two) { - if (one.getMean() == null && two.getMean() == null) { - return nameComparator.compare(one, two); - } else if (one.getMean() == null) { - return 1; - } else if (two.getMean() == null) { - return -1; - } else { - return one.getMean().compareTo(two.getMean()); - } - } - - @Override - public String toString() { - return "GradableObject.meanComparator"; - } - }; - dateComparator = new Comparator() { - @Override - public int compare(final GradebookAssignment one, final GradebookAssignment two) { - if (one.getDueDate() == null && two.getDueDate() == null) { - return nameComparator.compare(one, two); - } else if (one.getDueDate() == null) { - return 1; - } else if (two.getDueDate() == null) { - return -1; - } else { - return one.getDueDate().compareTo(two.getDueDate()); - } - } - - @Override - public String toString() { - return "GradableObject.dateComparator"; - } - }; - sortingComparator = new Comparator() { - @Override - public int compare(final GradableObject one, final GradableObject two) { - if (one.getSortOrder() == null && two.getSortOrder() == null) { - if (one.getClass().equals(two.getClass()) - && one.getClass().isAssignableFrom(GradebookAssignment.class)) { - // special handling for assignments - return dateComparator.compare((GradebookAssignment) one, (GradebookAssignment) two); - } else { - return nameComparator.compare(one, two); - } - } else if (one.getSortOrder() == null) { - return 1; - } else if (two.getSortOrder() == null) { - return -1; - } else { - return one.getSortOrder().compareTo(two.getSortOrder()); - } - } - - @Override - public String toString() { - return "GradableObject.sortingComparator"; - } - }; - defaultComparator = sortingComparator; - } - - /** - * @return Whether this gradable object is a course grade - */ - public abstract boolean isCourseGrade(); - - /** - * @return Whether this gradable object is an assignment - */ - public abstract boolean isAssignment(); - - /** - * @return Whether this gradable object is a category - */ - public abstract boolean getIsCategory(); - - /** - * @return Returns the id. - */ - public Long getId() { - return this.id; - } - - /** - * @param id The id to set. - */ - public void setId(final Long id) { - this.id = id; - } - - /** - * @return Returns the gradebook. - */ - public Gradebook getGradebook() { - return this.gradebook; - } - - /** - * @param gradebook The gradebook to set. - */ - public void setGradebook(final Gradebook gradebook) { - this.gradebook = gradebook; - } - - /** - * @return Returns the mean. - */ - public Double getMean() { - return this.mean; - } - - /** - * @return Returns the mean while protecting against displaying NaN. - */ - public Double getFormattedMean() { - if (this.mean == null || this.mean.equals(Double.valueOf(Double.NaN))) { - return null; - } else { - return Double.valueOf(this.mean.doubleValue() / 100.0); - } - } - - /** - * @param mean The mean to set. - */ - public void setMean(final Double mean) { - this.mean = mean; - } - - /** - * This should really only be a field in GradebookAssignment objects, since the string describing CourseGrade needs to allow for - * localization. Unfortunately, such we keep CourseGrade and GradebookAssignment objects in the same table, and since we want - * GradebookAssignment names to be enforced as non-nullable, we're stuck with a bogus CourseGrade "name" field for now. The UI will have - * to be smart enough to disregard it. - * - * @return Returns the name. - */ - public String getName() { - return this.name; - } - - /** - * @param name The name to set. - */ - public void setName(final String name) { - this.name = name; - } - - /** - * @return Returns the version. - */ - public int getVersion() { - return this.version; - } - - /** - * @param version The version to set. - */ - public void setVersion(final int version) { - this.version = version; - } - - /** - * @return Returns the removed. - */ - public boolean isRemoved() { - return this.removed; - } - - /** - * @param removed The removed to set. - */ - public void setRemoved(final boolean removed) { - this.removed = removed; - } - - @Override - public String toString() { - return new ToStringBuilder(this) - .append("id", this.id) - .append("name", this.name) - .append("sort", this.sortOrder) - .toString(); - } - - @Override - public boolean equals(final Object other) { - if (!(other instanceof GradableObject)) { - return false; - } - final GradableObject go = (GradableObject) other; - return new EqualsBuilder() - .append(this.gradebook, go.getGradebook()) - .append(this.id, go.getId()) - .append(this.name, go.getName()).isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder() - .append(this.gradebook) - .append(this.id) - .append(this.name) - .toHashCode(); - } - - private int sortTotalItems = 1; - private int sortTruePosition = -1; - - public void assignSorting(final int sortTotalItems, final int sortTruePosition) { - // this will help correctly figure out the first/last setting and sorting - this.sortTotalItems = sortTotalItems; - this.sortTruePosition = sortTruePosition; - } - - public boolean isFirst() { - return this.sortTruePosition == 0; - } - - public boolean isLast() { - return this.sortTruePosition >= (this.sortTotalItems - 1); - } - - public int getSortPosition() { - return this.sortTruePosition; - } - - public Integer getSortOrder() { - return this.sortOrder; - } - - public void setSortOrder(final Integer sortOrder) { - this.sortOrder = sortOrder; - } - - public Integer getCategorizedSortOrder() { - return this.categorizedSortOrder; - } - - public void setCategorizedSortOrder(final Integer value) { - this.categorizedSortOrder = value; - } - -} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradeMapping.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradeMapping.java deleted file mode 100644 index 5989055bb62e..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradeMapping.java +++ /dev/null @@ -1,258 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook; - -import java.io.Serializable; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import org.apache.commons.lang3.builder.CompareToBuilder; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.sakaiproject.service.gradebook.shared.DoubleComparator; - -/** - * A GradeMapping provides a means to convert between an arbitrary set of grades - * (letter grades, pass / not pass, 4,0 scale) and numeric percentages. - * - */ -public class GradeMapping implements Serializable, Comparable { - - private static final long serialVersionUID = 1L; - - protected Long id; - protected int version; - - protected Gradebook gradebook; - protected Map gradeMap; - private GradingScale gradingScale; - - public GradeMapping() { - } - - public GradeMapping(final GradingScale gradingScale) { - setGradingScale(gradingScale); - this.gradeMap = new HashMap<>(gradingScale.getDefaultBottomPercents()); - } - - public String getName() { - return (getGradingScale() != null) ? getGradingScale().getName() : null; - } - - /** - * Sets the percentage values for this GradeMapping to their default values. - */ - public void setDefaultValues() { - this.gradeMap = new HashMap<>(getDefaultBottomPercents()); - } - - /** - * Backwards-compatible wrapper to get to grading scale. - */ - public Map getDefaultBottomPercents() { - final GradingScale scale = getGradingScale(); - if (scale != null) { - return scale.getDefaultBottomPercents(); - } else { - final Map defaultBottomPercents = new HashMap(); - final Iterator gradesIter = getGrades().iterator(); - final Iterator defaultValuesIter = getDefaultValues().iterator(); - while (gradesIter.hasNext()) { - final String grade = gradesIter.next(); - final Double value = defaultValuesIter.next(); - defaultBottomPercents.put(grade, value); - } - return defaultBottomPercents; - } - } - - /** - * - * @return An (ordered) collection of the available grade values - */ - public Collection getGrades() { - return (getGradingScale() != null) ? getGradingScale().getGrades() : Collections.emptyList(); - } - - /** - * - * @return A List of the default grade values. Only used for backward - * compatibility to pre-grading-scale mappings. - * - * @deprecated - */ - @Deprecated - public List getDefaultValues() { - throw new UnsupportedOperationException("getDefaultValues called for GradeMapping " + getName() + " in Gradebook " + getGradebook()); - } - - /** - * Gets the percentage mapped to a particular grade. - */ - public Double getValue(final String grade) { - return this.gradeMap.get(grade); - } - - /** - * Get the mapped grade based on the persistent grade mappings - * - */ - public String getMappedGrade(final Double value) { - return getMappedGrade(getGradeMap(), value); - } - - /** - * Get the mapped grade based on the passed in grade mappings. - * - * NOTE: The gradeMap MUST be sorted! - */ - public static String getMappedGrade(final Map gradeMap, final Double value) { - if(value == null) { - return null; - } - - for (final Map.Entry entry : sortGradeMapping(gradeMap).entrySet()) { - final String grade = entry.getKey(); - final Double mapVal = entry.getValue(); - - // If the value in the map is less than the value passed, then the - // map value is the letter grade for this value - if (mapVal != null && mapVal.compareTo(value) <= 0) { - return grade; - } - } - // As long as 'F' is zero, this should never happen. - return null; - } - - /** - * Handles the sorting of the grade mapping. - * - * @param gradeMap - * @return - */ - public static Map sortGradeMapping(final Map gradeMap) { - - // we only ever order by bottom percents now - final DoubleComparator doubleComparator = new DoubleComparator(gradeMap); - final Map rval = new TreeMap<>(doubleComparator); - rval.putAll(gradeMap); - - return rval; - } - - public Long getId() { - return this.id; - } - - public void setId(final Long id) { - this.id = id; - } - - - public int getVersion() { - return this.version; - } - - public void setVersion(final int version) { - this.version = version; - } - - public Map getGradeMap() { - return this.gradeMap; - } - - public void setGradeMap(final Map gradeMap) { - this.gradeMap = gradeMap; - } - - public Gradebook getGradebook() { - return this.gradebook; - } - - public void setGradebook(final Gradebook gradebook) { - this.gradebook = gradebook; - } - - @Override - public int compareTo(final Object o) { - final GradeMapping other = (GradeMapping) o; - return new CompareToBuilder().append(getName(), other.getName()).toComparison(); - } - - @Override - public boolean equals(final Object o) { - if (o == null || this.getClass() != o.getClass()) { - return false; - } - final GradeMapping other = (GradeMapping) o; - return new EqualsBuilder().append(getName(), other.getName()).isEquals(); - } - - @Override - public int hashCode() { - if (getGradingScale() == null || getName() == null) { - return 0; - } - return new HashCodeBuilder().append(getName()).toHashCode(); - - } - - @Override - public String toString() { - return new ToStringBuilder(this). - append(getName()). - append(this.id).toString(); - } - - /** - * Enable any-case input of grades (typically lowercase input - * for uppercase grades). Look for a case-insensitive match - * to the input text and if it's found, return the official - * version. - * - * @return The normalized version of the grade, or null if not found. - */ - public String standardizeInputGrade(final String inputGrade) { - String standardizedGrade = null; - for (final String grade: getGrades()) { - if (grade.equalsIgnoreCase(inputGrade)) { - standardizedGrade = grade; - break; - } - } - return standardizedGrade; - } - - /** - * @return the GradingScale used to define this mapping, or null if - * this is an old Gradebook which uses hard-coded scales - */ - public GradingScale getGradingScale() { - return this.gradingScale; - } - - public void setGradingScale(final GradingScale gradingScale) { - this.gradingScale = gradingScale; - } -} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Gradebook.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Gradebook.java deleted file mode 100644 index 27456c718aff..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Gradebook.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (c) 2003-2019 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook; - -import java.io.Serializable; -import java.util.Set; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - -/** - * A Gradebook is the top-level object in the Sakai Gradebook tool. Only one - * Gradebook should be associated with any particular course (or site, as they - * exist in Sakai 1.5) for any given academic term. How courses and terms are - * determined will likely depend on the particular Sakai installation. - */ -@Data -@EqualsAndHashCode(onlyExplicitlyIncluded = true) -@ToString(onlyExplicitlyIncluded = true) -public class Gradebook implements Serializable { - - private static final long serialVersionUID = 1L; - - @ToString.Include - private Long id; - - @ToString.Include - @EqualsAndHashCode.Include - private String uid; - - private int version; - - @ToString.Include - private String name; - - private GradeMapping selectedGradeMapping; - - private Set gradeMappings; - - private boolean assignmentsDisplayed; - - // Is the course grade to be shown at all? - private boolean courseGradeDisplayed; - - // If the course grade is displayed, should the letter grade be displayed? - private boolean courseLetterGradeDisplayed; - - // If the course grade is displayed, should the total points be displayed? - private boolean coursePointsDisplayed; - - private boolean totalPointsDisplayed; - - // If the course grade is displayed, should the percentage be displayed? - private boolean courseAverageDisplayed; - - private boolean allAssignmentsEntered; - - private boolean locked; - - private int grade_type; - - private int category_type; - - private Boolean equalWeightCategories; - - private Boolean scaledExtraCredit; - - @Getter - @Setter - private Boolean showMean; - - @Getter - @Setter - private Boolean showMedian; - - @Getter - @Setter - private Boolean showMode; - - @Getter - @Setter - private Boolean showRank; - - @Getter - @Setter - private Boolean showItemStatistics; - - @Getter - @Setter - private Boolean showStatisticsChart; - - @Getter - @Setter - private boolean assignmentStatsDisplayed; - - @Getter - @Setter - private boolean courseGradeStatsDisplayed; - - @Getter - @Setter - private boolean allowStudentsToCompareGrades; - - - @Getter - @Setter - private boolean comparingDisplayStudentNames; - - @Getter - @Setter - private boolean comparingDisplayStudentSurnames; - - @Getter - @Setter - private boolean comparingDisplayTeacherComments; - - @Getter - @Setter - private boolean comparingIncludeAllGrades; - - @Getter - @Setter - private boolean comparingRandomizeDisplayedData; - - -} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradebookArchive.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradebookArchive.java deleted file mode 100644 index 14708f71bb33..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradebookArchive.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (c) 2003-2016 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook; - -import java.beans.XMLDecoder; -import java.beans.XMLEncoder; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.util.Collection; - -import lombok.extern.slf4j.Slf4j; - -/** - * Models a gradebook and all of its dependent objects, which can all be - * serialized as xml for archiving. - * - * @author Josh Holtzman - */ -@Slf4j -public class GradebookArchive { - private Gradebook gradebook; - private GradeMapping selectedGradeMapping; - private Collection gradeMappings; - private CourseGrade courseGrade; - private Collection assignments; - - public GradebookArchive() { - // Allows for creating the archive, then populating it via readArchive() - } - - /** - * @param gradebook - * @param selectedGradeMapping - * @param gradeMappings - * @param courseGrade - * @param assignments - */ - public GradebookArchive(Gradebook gradebook, - GradeMapping selectedGradeMapping, Collection gradeMappings, - CourseGrade courseGrade, Collection assignments) { - super(); - this.gradebook = gradebook; - this.selectedGradeMapping = selectedGradeMapping; - this.gradeMappings = gradeMappings; - this.courseGrade = courseGrade; - this.assignments = assignments; - } - - /** - * Serializes this gradebook archive into an xml document - */ - public String archive() { - if(log.isDebugEnabled()) log.debug("GradebookArchive.archive() called"); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(baos)); - encoder.writeObject(this); - encoder.flush(); - String xml = baos.toString(); - if(log.isDebugEnabled()) log.debug("GradebookArchive.archive() finished"); - return xml; - } - - /** - * Read a gradebook archive from an xml input stream. - * - * @param xml The input stream containing the serialized gradebook archive - * @return A gradebook archive object modeling the data in the xml stream - */ - public void readArchive(String xml) { - ByteArrayInputStream in = new ByteArrayInputStream(xml.getBytes()); - XMLDecoder decoder = new XMLDecoder(new BufferedInputStream(in)); - GradebookArchive archive = (GradebookArchive)decoder.readObject(); - decoder.close(); - this.gradebook = archive.getGradebook(); - this.courseGrade = archive.getCourseGrade(); - this.assignments = archive.getAssignments(); - } - - public Collection getAssignments() { - return assignments; - } - public void setAssignments(Collection assignments) { - this.assignments = assignments; - } - public CourseGrade getCourseGrade() { - return courseGrade; - } - public void setCourseGrade(CourseGrade courseGrade) { - this.courseGrade = courseGrade; - } - public Gradebook getGradebook() { - return gradebook; - } - public void setGradebook(Gradebook gradebook) { - this.gradebook = gradebook; - } - public Collection getGradeMappings() { - return gradeMappings; - } - public void setGradeMappings(Collection gradeMappings) { - this.gradeMappings = gradeMappings; - } - public GradeMapping getSelectedGradeMapping() { - return selectedGradeMapping; - } - public void setSelectedGradeMapping(GradeMapping selectedGradeMapping) { - this.selectedGradeMapping = selectedGradeMapping; - } -} - - - diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradebookAssignment.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradebookAssignment.java deleted file mode 100644 index a41dea14eeea..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradebookAssignment.java +++ /dev/null @@ -1,548 +0,0 @@ -/** - * Copyright (c) 2003-2016 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook; - -import java.math.BigDecimal; -import java.util.Collection; -import java.util.Comparator; -import java.util.Date; - -import lombok.Getter; -import lombok.Setter; -import org.sakaiproject.service.gradebook.shared.GradebookException; -import org.sakaiproject.service.gradebook.shared.GradebookService; - -import lombok.extern.slf4j.Slf4j; - -/** - * An GradebookAssignment is the basic unit that composes a gradebook. It represents a single unit that, when aggregated in a gradebook, can - * be used as the denomenator in calculating a CourseGradeRecord. - * - * @author Josh Holtzman - */ -@Slf4j -public class GradebookAssignment extends GradableObject { - - /** - * - */ - private static final long serialVersionUID = -526126234461183210L; - /** - * use the SortType enum instead. This remains only for the gradebook tool - */ - @Deprecated - public static String SORT_BY_DATE = "dueDate"; - @Deprecated - public static String SORT_BY_NAME = "name"; - @Deprecated - public static String SORT_BY_MEAN = "mean"; - @Deprecated - public static String SORT_BY_POINTS = "pointsPossible"; - @Deprecated - public static String SORT_BY_RELEASED = "released"; - @Deprecated - public static String SORT_BY_COUNTED = "counted"; - @Deprecated - public static String SORT_BY_EDITOR = "gradeEditor"; - @Deprecated - public static String SORT_BY_SORTING = "sorting"; - @Deprecated - public static String DEFAULT_SORT = SORT_BY_SORTING; - - public static String item_type_points = "Points"; - public static String item_type_percentage = "Percentage"; - public static String item_type_letter = "Letter Grade"; - public static String item_type_nonCalc = "Non-calculating"; - public static String item_type_adjustment = "Adjustment"; - - public static Comparator dateComparator; - public static Comparator nameComparator; - public static Comparator pointsComparator; - public static Comparator meanComparator; - public static Comparator releasedComparator; - public static Comparator countedComparator; - public static Comparator gradeEditorComparator; - public static Comparator categoryComparator; - - // In a table per class hierarchy a subclass cannot have NOT NULL constraints so don't use primitives! - @Getter @Setter private Double pointsPossible; - @Getter @Setter private Date dueDate; - @Setter private Boolean notCounted; - @Setter private Boolean externallyMaintained; - @Getter @Setter private String externalStudentLink; - @Getter @Setter private String externalInstructorLink; - @Getter @Setter private String externalId; - @Getter @Setter private String externalAppName; - @Getter @Setter private String externalData; - @Setter @Getter private Boolean released; - @Getter @Setter private Category category; - @Getter @Setter private Double averageTotal; - @Setter private Boolean ungraded; - @Setter private Boolean extraCredit = Boolean.FALSE; - @Getter @Setter private Double assignmentWeighting; - @Getter @Setter private Boolean countNullsAsZeros; - @Setter private String itemType; - @Getter @Setter public String selectedGradeEntryValue; - @Setter private Boolean hideInAllGradesTable = Boolean.FALSE; - - static { - dateComparator = new Comparator() { - @Override - public int compare(final Object o1, final Object o2) { - if (log.isDebugEnabled()) { - log.debug("Comparing assignment + " + o1 + " to " + o2 + " by date"); - } - final GradebookAssignment one = (GradebookAssignment) o1; - final GradebookAssignment two = (GradebookAssignment) o2; - - // Sort by name if no date on either - if (one.getDueDate() == null && two.getDueDate() == null) { - return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); - } - // Null dates are last - if (one.getDueDate() == null) { - return 1; - } - if (two.getDueDate() == null) { - return -1; - } - // Sort by name if both assignments have the same date - final int comp = (one.getDueDate().compareTo(two.getDueDate())); - if (comp == 0) { - return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); - } else { - return comp; - } - } - - @Override - public String toString() { - return "GradebookAssignment.dateComparator"; - } - }; - nameComparator = new Comparator() { - @Override - public int compare(final Object o1, final Object o2) { - final GradebookAssignment one = (GradebookAssignment) o1; - final GradebookAssignment two = (GradebookAssignment) o2; - return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); - } - - @Override - public String toString() { - return "GradebookAssignment.nameComparator"; - } - }; - pointsComparator = new Comparator() { - @Override - public int compare(final Object o1, final Object o2) { - if (log.isDebugEnabled()) { - log.debug("Comparing assignment + " + o1 + " to " + o2 + " by points"); - } - final GradebookAssignment one = (GradebookAssignment) o1; - final GradebookAssignment two = (GradebookAssignment) o2; - - final int comp = one.getPointsPossible().compareTo(two.getPointsPossible()); - if (comp == 0) { - return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); - } else { - return comp; - } - } - - @Override - public String toString() { - return "GradebookAssignment.pointsComparator"; - } - }; - meanComparator = new Comparator() { - @Override - public int compare(final Object o1, final Object o2) { - if (log.isDebugEnabled()) { - log.debug("Comparing assignment + " + o1 + " to " + o2 + " by mean"); - } - final GradebookAssignment one = (GradebookAssignment) o1; - final GradebookAssignment two = (GradebookAssignment) o2; - - final Double mean1 = one.getMean(); - final Double mean2 = two.getMean(); - if (mean1 == null && mean2 == null) { - return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); - } - if (mean1 != null && mean2 == null) { - return 1; - } - if (mean1 == null && mean2 != null) { - return -1; - } - final int comp = mean1.compareTo(mean2); - if (comp == 0) { - return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); - } else { - return comp; - } - } - - @Override - public String toString() { - return "GradebookAssignment.meanComparator"; - } - }; - - releasedComparator = new Comparator() { - @Override - public int compare(final Object o1, final Object o2) { - if (log.isDebugEnabled()) { - log.debug("Comparing assignment + " + o1 + " to " + o2 + " by release"); - } - final GradebookAssignment one = (GradebookAssignment) o1; - final GradebookAssignment two = (GradebookAssignment) o2; - - final int comp = String.valueOf(one.isReleased()).compareTo(String.valueOf(two.isReleased())); - if (comp == 0) { - return one.getName().compareTo(two.getName()); - } else { - return comp; - } - } - - @Override - public String toString() { - return "GradebookAssignment.releasedComparator"; - } - }; - - countedComparator = new Comparator() { - @Override - public int compare(final Object o1, final Object o2) { - if (log.isDebugEnabled()) { - log.debug("Comparing assignment + " + o1 + " to " + o2 + " by counted"); - } - final GradebookAssignment one = (GradebookAssignment) o1; - final GradebookAssignment two = (GradebookAssignment) o2; - - final int comp = String.valueOf(one.isCounted()).compareTo(String.valueOf(two.isCounted())); - if (comp == 0) { - return one.getName().compareTo(two.getName()); - } else { - return comp; - } - } - - @Override - public String toString() { - return "GradebookAssignment.countedComparator"; - } - }; - - gradeEditorComparator = new Comparator() { - @Override - public int compare(final Object o1, final Object o2) { - if (log.isDebugEnabled()) { - log.debug("Comparing assignment + " + o1 + " to " + o2 + " by grade editor"); - } - final GradebookAssignment one = (GradebookAssignment) o1; - final GradebookAssignment two = (GradebookAssignment) o2; - - final int comp = String.valueOf(one.getExternalAppName()).compareTo(String.valueOf(two.getExternalAppName())); - if (comp == 0) { - return one.getName().compareTo(two.getName()); - } else { - return comp; - } - } - - @Override - public String toString() { - return "GradebookAssignment.gradeEditorComparator"; - } - }; - - categoryComparator = new Comparator() { - @Override - public int compare(final Object o1, final Object o2) { - if (log.isDebugEnabled()) { - log.debug("Comparing assignment + " + o1 + " to " + o2 + " by category ordering"); - } - final GradebookAssignment one = (GradebookAssignment) o1; - final GradebookAssignment two = (GradebookAssignment) o2; - - // if categories are null - if (one.getCategory() == null && two.getCategory() == null) { - // sort by assignment sort order - if (one.getSortOrder() == null && two.getSortOrder() == null) { - // if no sortOrder, then sort based on id - return one.getId().compareTo(two.getId()); - } else if (one.getSortOrder() == null) { - return 1; - } else if (two.getSortOrder() == null) { - return -1; - } else { - return one.getSortOrder().compareTo(two.getSortOrder()); - } - } else if (one.getCategory() == null) { - return 1; - } else if (two.getCategory() == null) { - return -1; - } - - // if in the same category, sort by their categorized sort order - if (one.getCategory().equals(two.getCategory())) { - // handles null orders by putting them at the end of the list - if (one.getCategorizedSortOrder() == null) { - return 1; - } else if (two.getCategorizedSortOrder() == null) { - return -1; - } - return Integer.compare(one.getCategorizedSortOrder(), two.getCategorizedSortOrder()); - - // otherwise, sort by their category order - } else { - // check if category has a order (not required) - if (one.getCategory().getCategoryOrder() == null && two.getCategory().getCategoryOrder() == null) { - // both orders are null.. so order by A-Z - if (one.getCategory().getName() == null && two.getCategory().getName() == null) { - // both names are null so order by id - return one.getCategory().getId().compareTo(two.getCategory().getId()); - } else if (one.getCategory().getName() == null) { - return 1; - } else if (two.getCategory().getName() == null) { - return -1; - } else { - return one.getCategory().getName().compareTo(two.getCategory().getName()); - } - } else if (one.getCategory().getCategoryOrder() == null) { - return 1; - } else if (two.getCategory().getCategoryOrder() == null) { - return -1; - } else { - return one.getCategory().getCategoryOrder().compareTo(two.getCategory().getCategoryOrder()); - } - } - } - - @Override - public String toString() { - return "GradebookAssignment.categoryComparator"; - } - }; - } - - public GradebookAssignment() { - this(null, null, null, null, false); - } - - public GradebookAssignment(final Gradebook gradebook, final String name, final Double pointsPossible, final Date dueDate) { - this(gradebook, name, pointsPossible, dueDate, true); - } - - /** - * constructor to support selective release - * - * @param gradebook - * @param name - * @param pointsPossible - * @param dueDate - * @param released - */ - public GradebookAssignment(final Gradebook gradebook, final String name, final Double pointsPossible, final Date dueDate, final boolean released) { - this.gradebook = gradebook; - this.name = name; - this.pointsPossible = pointsPossible; - this.dueDate = dueDate; - this.released = released; - this.extraCredit = Boolean.FALSE; - this.hideInAllGradesTable = Boolean.FALSE; - } - - @Override - public boolean isCourseGrade() { - return false; - } - - /** - * @see org.sakaiproject.tool.gradebook.GradableObject#isAssignment() - */ - @Override - public boolean isAssignment() { - return true; - } - - /** - * @see GradableObject#getIsCategory() - */ - @Override - public boolean getIsCategory() { - return false; - } - - public boolean isNotCounted() { - return this.notCounted != null ? this.notCounted : false; - } - - public boolean isCounted() { - return !isNotCounted(); - } - - /** - * This cover is for the benefit of JSF checkboxes. - */ - public void setCounted(final boolean counted) { - setNotCounted(!counted); - } - - /** - * @return Returns the externallyMaintained. - */ - public boolean isExternallyMaintained() { - return this.externallyMaintained != null ? this.externallyMaintained : false; - } - - /** - * - * @return selective release true or false - */ - public Boolean isReleased() { - return this.released != null ? this.released : false; - } - - /** - * Calculate the mean score for students with entered grades. - */ - public void calculateStatistics(final Collection gradeRecords) { - int numScored = 0; - BigDecimal total = new BigDecimal("0"); - BigDecimal pointsTotal = new BigDecimal("0"); - for (final AssignmentGradeRecord record : gradeRecords) { - // Skip grade records that don't apply to this gradable object - if (!record.getGradableObject().equals(this)) { - continue; - } - - if (record.getDroppedFromGrade() == null) { - throw new GradebookException("record.droppedFromGrade cannot be null"); - } - - Double score = null; - if (!getUngraded() && this.pointsPossible > 0) { - score = record.getGradeAsPercentage(); - } - final Double points = record.getPointsEarned(); - if (score == null && points == null || record.getDroppedFromGrade()) { - continue; - } else if (score == null) { - pointsTotal = pointsTotal.add(new BigDecimal(points.toString())); - numScored++; - } else { - total = total.add(new BigDecimal(score.toString())); - pointsTotal = pointsTotal.add(new BigDecimal(points.toString())); - numScored++; - } - } - if (numScored == 0) { - this.mean = null; - this.averageTotal = null; - } else { - final BigDecimal bdNumScored = new BigDecimal(numScored); - if (!getUngraded() && this.pointsPossible > 0) { - this.mean = Double.valueOf(total.divide(bdNumScored, GradebookService.MATH_CONTEXT).doubleValue()); - } else { - this.mean = null; - } - this.averageTotal = Double.valueOf(pointsTotal.divide(bdNumScored, GradebookService.MATH_CONTEXT).doubleValue()); - } - } - - public boolean getUngraded() { - return this.ungraded != null ? this.ungraded : false; - } - - // these two functions are needed to keep the old API and help JSF and RSF play nicely together. Since isExtraCredit already exists and - // we can't remove it - // and JSF expects Boolean values to be "getExtraCredit", this had to be added for JSF. Also, since the external GB create item page is - // in - // RSF, you can't name it getExtraCredit and keep isExtraCredit b/c of SAK-14589 - public Boolean getIsExtraCredit() { - return isExtraCredit(); - } - - public void setIsExtraCredit(final Boolean isExtraCredit) { - setExtraCredit(isExtraCredit); - } - - public Boolean isExtraCredit() { - return this.extraCredit != null ? this.extraCredit : false; - } - - public String getItemType() { - final Gradebook gb = getGradebook(); - if (gb != null) { - if (isExtraCredit() != null) { - if (isExtraCredit()) { - // if we made it in here, go ahead and return since adjustment item takes priority over the rest - this.itemType = item_type_adjustment; - return this.itemType; - } - } - - if (getUngraded()) { - // if we made it in here, go ahead and return since non-calc item takes priority over the rest - this.itemType = item_type_nonCalc; - return this.itemType; - } - - if (gb.getGrade_type() == GradebookService.GRADE_TYPE_POINTS) { - this.itemType = item_type_points; - } else if (gb.getGrade_type() == GradebookService.GRADE_TYPE_PERCENTAGE) { - this.itemType = item_type_percentage; - } else if (gb.getGrade_type() == GradebookService.GRADE_TYPE_LETTER) { - this.itemType = item_type_letter; - } - } - return this.itemType; - } - - /** - * Convenience method for checking if the grade for the assignment should be included in calculations. This is different from just the - * {@link #isCounted()} method for an assignment. This method does a more thorough check using other values, such as if removed, - * isExtraCredit, ungraded, etc in addition to the assignment's notCounted property. Now also considers category type. If categories are - * configured (setting 2 or 3), uncategorised items are not counted. - * - * @return true if grades for this assignment should be included in various calculations. - */ - public boolean isIncludedInCalculations() { - boolean isIncludedInCalculations = false; - final int categoryType = this.gradebook.getCategory_type(); - - if (!this.removed && - !getUngraded() && - isCounted() && - (isExtraCredit() || (this.pointsPossible != null && this.pointsPossible > 0))) { - isIncludedInCalculations = true; - } - - if (categoryType != 1 && this.category == null) { - isIncludedInCalculations = false; - } - - return isIncludedInCalculations; - } - - public boolean isHideInAllGradesTable() { - return this.hideInAllGradesTable != null ? this.hideInAllGradesTable : false; - } -} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradebookProperty.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradebookProperty.java deleted file mode 100644 index 7b95e5b5e7db..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradebookProperty.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook; - -import java.io.Serializable; - -import org.apache.commons.lang3.builder.ToStringBuilder; - -public class GradebookProperty implements Serializable, Comparable { - - private static final long serialVersionUID = 1L; - - private Long id; - private int version; - - private String name; - private String value; - - public GradebookProperty() { - } - public GradebookProperty(String name) { - setName(name); - } - - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public String getValue() { - return value; - } - public void setValue(String value) { - this.value = value; - } - - @Override - public int compareTo(Object o) { - return getName().compareTo(((GradebookProperty)o).getName()); - } - @Override - public String toString() { - return new ToStringBuilder(this). - append(getName()).toString(); - } - - public Long getId() { - return id; - } - public void setId(Long id) { - this.id = id; - } - public int getVersion() { - return version; - } - public void setVersion(int version) { - this.version = version; - } -} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradingScale.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradingScale.java deleted file mode 100644 index 8b57e9320d24..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradingScale.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.sakaiproject.service.gradebook.shared.GradingScaleDefinition; - -public class GradingScale implements Serializable, Comparable { - - private static final long serialVersionUID = 1L; - - private Long id; - private int version; - - private String uid; - private String name; - private List grades; - private Map defaultBottomPercents; // From grade to percentage - private boolean unavailable; - - public Map getDefaultBottomPercents() { - return defaultBottomPercents; - } - public void setDefaultBottomPercents(Map defaultBottomPercents) { - this.defaultBottomPercents = defaultBottomPercents; - } - public String getUid() { - return uid; - } - public void setUid(String uid) { - this.uid = uid; - } - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - - /** - * Because the Gradebook now supports non-calculated manual-only grades with - * no percentage equivalent, it is possible for the list of grades to include - * codes that are not included in the defaultBottomPercents map. In other - * words, callers shouldn't expect getDefaultBottomPercents.keySet() to be - * equivalent to this list. - * @return list of supported grade codes, ordered from highest to lowest - */ - public List getGrades() { - return grades; - } - public void setGrades(List grades) { - this.grades = grades; - } - public boolean isUnavailable() { - return unavailable; - } - public void setUnavailable(boolean unavailable) { - this.unavailable = unavailable; - } - public Long getId() { - return id; - } - public void setId(Long id) { - this.id = id; - } - public int getVersion() { - return version; - } - public void setVersion(int version) { - this.version = version; - } - - @Override - public int compareTo(Object o) { - return getName().compareTo(((GradingScale)o).getName()); - } - @Override - public String toString() { - return new ToStringBuilder(this). - append(getUid()).toString(); - } - - /** - * Convert this GradeingScale instance to a GradingScaleDefinition - * @return - */ - public GradingScaleDefinition toGradingScaleDefinition(){ - GradingScaleDefinition scaleDef = new GradingScaleDefinition(); - scaleDef.setUid(this.getUid()); - scaleDef.setName(this.getName()); - - Map mapBottomPercents = this.getDefaultBottomPercents(); - scaleDef.setDefaultBottomPercents(mapBottomPercents); - - //build the bottom percents as a list as well - List listBottomPercents = new ArrayList<>(); - List grades = new ArrayList<>(); - for(Map.Entry pair : mapBottomPercents.entrySet()) { - listBottomPercents.add(pair.getValue()); - grades.add(pair.getKey()); - } - scaleDef.setGrades(grades); - scaleDef.setDefaultBottomPercentsAsList(listBottomPercents); - - return scaleDef; - } - - -} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/LetterGradePercentMapping.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/LetterGradePercentMapping.java deleted file mode 100644 index 57d591562347..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/LetterGradePercentMapping.java +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.tool.gradebook; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -public class LetterGradePercentMapping implements Serializable { - - private static final long serialVersionUID = 1L; - private Long id; - private int version; - - private int mappingType; //value of 1 or 2 - 1 is the default mapping in the system. - private Long gradebookId; - private Map gradeMap; - - public LetterGradePercentMapping() - { - } - - public Double getValue(String grade) - { - if(gradeMap != null && gradeMap.containsKey(grade)) - { - return gradeMap.get(grade); - } - return null; - } - - public String getGrade(Double value) - { - if(gradeMap != null) - { - List percentList = new ArrayList(); - for(Iterator iter = gradeMap.keySet().iterator(); iter.hasNext();) - { - percentList.add(gradeMap.get((iter.next()))); - } - Collections.sort(percentList); - for(int i=0; i getGradeMap() - { - return gradeMap; - } - - public void setGradeMap(Map gradeMap) - { - this.gradeMap = gradeMap; - } - - public int getMappingType() - { - return mappingType; - } - - public void setMappingType(int mappingType) - { - this.mappingType = mappingType; - } - - public Long getGradebookId() - { - return gradebookId; - } - - public void setGradebookId(Long gradebookId) - { - this.gradebookId = gradebookId; - } - - /** - * Enable any-case input of grades (typically lowercase input - * for uppercase grades). Look for a case-insensitive match - * to the input text and if it's found, return the official - * version. - * - * @return The normalized version of the grade, or null if not found. - */ - public String standardizeInputGrade(String inputGrade) { - String standardizedGrade = null; - for (Iterator iter = gradeMap.keySet().iterator(); iter.hasNext(); ) { - String grade = (String)iter.next(); - if (grade.equalsIgnoreCase(inputGrade)) { - standardizedGrade = grade; - break; - } - } - return standardizedGrade; - } -} \ No newline at end of file diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Permission.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Permission.java deleted file mode 100644 index 02a93300216f..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Permission.java +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (c) 2003-2009 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.tool.gradebook; - -import java.io.Serializable; - -public class Permission implements Serializable -{ - private Long id; - private int version; - private Long gradebookId; - private String userId; - private String function; - private Long categoryId; - private String groupId; - - public Long getCategoryId() - { - return categoryId; - } - - public void setCategoryId(Long categoryId) - { - this.categoryId = categoryId; - } - - public Long getGradebookId() - { - return gradebookId; - } - - public void setGradebookId(Long gradebookId) - { - this.gradebookId = gradebookId; - } - - public String getGroupId() - { - return groupId; - } - - public void setGroupId(String groupId) - { - this.groupId = groupId; - } - - public Long getId() - { - return id; - } - - public void setId(Long id) - { - this.id = id; - } - - public String getFunction() - { - return function; - } - - public void setFunction(String function) - { - this.function = function; - } - - public String getUserId() - { - return userId; - } - - public void setUserId(String userId) - { - this.userId = userId; - } - - public int getVersion() - { - return version; - } - - public void setVersion(int version) - { - this.version = version; - } - -} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Spreadsheet.java b/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Spreadsheet.java deleted file mode 100644 index 9fb00d20325f..000000000000 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/Spreadsheet.java +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - - - -package org.sakaiproject.tool.gradebook; - -import java.io.Serializable; -import java.util.Date; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; -import org.apache.commons.lang3.builder.ToStringBuilder; - -/** - * User: louis - * Date: Jun 12, 2006 - * Time: 3:10:12 PM - */ -public class Spreadsheet implements Serializable { - - private static final long serialVersionUID = 1L; - - protected Gradebook gradebook; - protected Long id; - protected int version; - protected String content; - protected String creator; - protected String name; - protected Date dateCreated; - - public Spreadsheet(Gradebook gradebook, String content, String creator, String name, Date dateCreated) { - - this.gradebook = gradebook; - this.content = content; - this.creator = creator; - this.name = name; - this.dateCreated = dateCreated; - } - - - public Spreadsheet() { - } - - - public Gradebook getGradebook() { - return gradebook; - } - - public void setGradebook(Gradebook gradebook) { - this.gradebook = gradebook; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public int getVersion() { - return version; - } - - public void setVersion(int version) { - this.version = version; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getCreator() { - return creator; - } - - public void setCreator(String creator) { - this.creator = creator; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Date getDateCreated() { - return dateCreated; - } - - public void setDateCreated(Date dateCreated) { - this.dateCreated = dateCreated; - } - - - @Override - public boolean equals(Object other) { - if (!(other instanceof Spreadsheet)) { - return false; - } - Spreadsheet sp = (Spreadsheet)other; - return new EqualsBuilder() - .append(gradebook, sp.getGradebook()) - .append(id, sp.getId()) - .append(name, sp.getName()).isEquals(); - } - - @Override - public int hashCode() { - return new HashCodeBuilder(). - append(gradebook). - append(id). - append(name). - toHashCode(); - } - - @Override - public String toString() { - return new ToStringBuilder(this). - append("id", id). - append("name", name).toString(); - } -} diff --git a/edu-services/gradebook-service/impl/pom.xml b/edu-services/gradebook-service/impl/pom.xml deleted file mode 100644 index a51c4173ae36..000000000000 --- a/edu-services/gradebook-service/impl/pom.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - 4.0.0 - - edu-services - org.sakaiproject.edu-services - 23-SNAPSHOT - ../../pom.xml - - org.sakaiproject.edu-services.gradebook - gradebook-service-impl - gradebook-service-impl - jar - - - - - - org.sakaiproject.kernel - sakai-kernel-util - - - org.sakaiproject.kernel - sakai-component-manager - - - org.sakaiproject.kernel - sakai-kernel-private - - - javax.servlet - javax.servlet-api - provided - - - org.hibernate - hibernate-core - - - org.springframework - spring-core - - - org.springframework - spring-orm - - - org.springframework - spring-tx - - - org.sakaiproject.kernel - sakai-kernel-api - provided - - - org.apache.commons - commons-lang3 - - - org.sakaiproject.common - archive-api - provided - - - ${project.groupId} - gradebook-service-hibernate - - - ${project.groupId} - gradebook-service-api - - - org.sakaiproject.rubrics - rubrics-service-api - - - - org.sakaiproject.edu-services.sections - sections-api - provided - - - org.hsqldb - hsqldb - - - aopalliance - aopalliance - 1.0 - - - javax.transaction - jta - - - cglib - cglib-nodep - - - odmg - odmg - 3.0 - - - org.apache.commons - commons-collections4 - - - commons-digester - commons-digester - - - commons-beanutils - commons-beanutils - - - org.dom4j - dom4j - - - com.thoughtworks.xstream - xstream - 1.4.18 - - - diff --git a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/BaseHibernateManager.java b/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/BaseHibernateManager.java deleted file mode 100644 index ffa06aa63dc5..000000000000 --- a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/BaseHibernateManager.java +++ /dev/null @@ -1,1433 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - /********************************************************************************** -* -* $Id$ -* -*********************************************************************************** -* - * Copyright (c) 2005, 2006, 2007, 2008 The Sakai Foundation, The MIT Corporation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.opensource.org/licenses/ECL-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. -* -**********************************************************************************/ -package org.sakaiproject.component.gradebook; - - import java.math.BigDecimal; - import java.util.ArrayList; - import java.util.Collection; - import java.util.Date; - import java.util.HashMap; - import java.util.HashSet; - import java.util.Iterator; - import java.util.List; - import java.util.Map; - import java.util.Set; - import java.util.stream.Collectors; - - import org.apache.commons.lang3.BooleanUtils; - import org.apache.commons.lang3.StringUtils; - import org.hibernate.HibernateException; - import org.hibernate.Session; - import org.hibernate.StaleObjectStateException; - import org.hibernate.criterion.Projections; - import org.hibernate.criterion.Restrictions; - import org.sakaiproject.component.api.ServerConfigurationService; - import org.sakaiproject.hibernate.HibernateCriterionUtils; - import org.sakaiproject.section.api.SectionAwareness; - import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; - import org.sakaiproject.section.api.facade.Role; - import org.sakaiproject.service.gradebook.shared.AssessmentNotFoundException; - import org.sakaiproject.service.gradebook.shared.CommentDefinition; - import org.sakaiproject.service.gradebook.shared.ConflictingAssignmentNameException; - import org.sakaiproject.service.gradebook.shared.ConflictingCategoryNameException; - import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService; - import org.sakaiproject.service.gradebook.shared.GradebookHelper; - import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException; - import org.sakaiproject.service.gradebook.shared.GradebookService; - import org.sakaiproject.service.gradebook.shared.GraderPermission; - import org.sakaiproject.service.gradebook.shared.StaleObjectModificationException; - import org.sakaiproject.tool.gradebook.AbstractGradeRecord; - import org.sakaiproject.tool.gradebook.GradebookAssignment; - import org.sakaiproject.tool.gradebook.AssignmentGradeRecord; - import org.sakaiproject.tool.gradebook.Category; - import org.sakaiproject.tool.gradebook.Comment; - import org.sakaiproject.tool.gradebook.CourseGrade; - import org.sakaiproject.tool.gradebook.CourseGradeRecord; - import org.sakaiproject.tool.gradebook.GradableObject; - import org.sakaiproject.tool.gradebook.GradeMapping; - import org.sakaiproject.tool.gradebook.Gradebook; - import org.sakaiproject.tool.gradebook.GradebookProperty; - import org.sakaiproject.tool.gradebook.GradingEvent; - import org.sakaiproject.tool.gradebook.LetterGradePercentMapping; - import org.sakaiproject.tool.gradebook.Permission; - import org.sakaiproject.tool.gradebook.facades.Authn; - import org.sakaiproject.event.api.EventTrackingService; - import org.springframework.orm.hibernate5.HibernateCallback; - import org.springframework.orm.hibernate5.support.HibernateDaoSupport; - - import lombok.extern.slf4j.Slf4j; - - - /** - * Provides methods which are shared between service business logic and application business - * logic, but not exposed to external callers. - */ -@Slf4j -public abstract class BaseHibernateManager extends HibernateDaoSupport { - - protected SectionAwareness sectionAwareness; - protected Authn authn; - protected EventTrackingService eventTrackingService; - protected ServerConfigurationService serverConfigurationService; - protected GradebookExternalAssessmentService externalAssessmentService; - - // Local cache of static-between-deployment properties. - protected Map propertiesMap = new HashMap<>(); - - public Gradebook getGradebook(final String uid) throws GradebookNotFoundException { - final List list = getHibernateTemplate().findByNamedParam("from Gradebook as gb where gb.uid = :uid", "uid", uid); - if (list.size() == 1) { - return (Gradebook)list.get(0); - } else { - throw new GradebookNotFoundException("Could not find gradebook uid=" + uid); - } - } - - public boolean isGradebookDefined(final String gradebookUid) { - return ((Long) getSessionFactory().getCurrentSession().createCriteria(Gradebook.class) - .add(Restrictions.eq("uid", gradebookUid)) - .setProjection(Projections.rowCount()) - .uniqueResult()) == 1L; - - } - - protected List getAssignments(final Long gradebookId) throws HibernateException { - return getSessionFactory().getCurrentSession() - .createQuery("from GradebookAssignment as asn where asn.gradebook.id = :gradebookid and asn.removed is false") - .setParameter("gradebookid", gradebookId) - .list(); - } - - protected List getCountedStudentGradeRecords(final Long gradebookId, final String studentId) throws HibernateException { - return getSessionFactory().getCurrentSession().createQuery( - "select agr from AssignmentGradeRecord as agr, GradebookAssignment as asn where agr.studentId = :studentid and agr.gradableObject = asn and asn.removed is false and asn.notCounted is false and asn.gradebook.id = :gradebookid and asn.ungraded is false") - .setParameter("studentid", studentId) - .setParameter("gradebookid", gradebookId) - .list(); - } - - /** - */ - public CourseGrade getCourseGrade(final Long gradebookId) { - return (CourseGrade) getSessionFactory().getCurrentSession().createQuery( - "from CourseGrade as cg where cg.gradebook.id = :gradebookid") - .setParameter("gradebookid", gradebookId) - .uniqueResult(); - } - - /** - * Gets the course grade record for a student, or null if it does not yet exist. - * - * @param studentId The student ID - * @return A List of grade records - * - * @throws HibernateException - */ - protected CourseGradeRecord getCourseGradeRecord(final Gradebook gradebook, final String studentId) throws HibernateException { - return (CourseGradeRecord) getSessionFactory().getCurrentSession() - .createQuery("from CourseGradeRecord as cgr where cgr.studentId = :studentid and cgr.gradableObject.gradebook = :gradebook") - .setParameter("studentid", studentId) - .setParameter("gradebook", gradebook) - .uniqueResult(); - } - - public String getGradebookUid(final Long id) { - return getHibernateTemplate().load(Gradebook.class, id).getUid(); - } - - protected Set getAllStudentUids(final String gradebookUid) { - final List enrollments = getSectionAwareness().getSiteMembersInRole(gradebookUid, Role.STUDENT); - return enrollments.stream().map(e -> e.getUser().getUserUid()).collect(Collectors.toSet()); - } - - public String getPropertyValue(final String name) { - String value = this.propertiesMap.get(name); - if (value == null) { - final List list = getHibernateTemplate().findByNamedParam("from GradebookProperty as prop where prop.name = :name", "name", name); - if (!list.isEmpty()) { - final GradebookProperty property = (GradebookProperty)list.get(0); - value = property.getValue(); - this.propertiesMap.put(name, value); - } - } - return value; - } - - public void setPropertyValue(final String name, final String value) { - final List list = getHibernateTemplate().findByNamedParam("from GradebookProperty as prop where prop.name = :name", "name", name); - GradebookProperty property; - if (!list.isEmpty()) { - property = (GradebookProperty)list.get(0); - } else { - property = new GradebookProperty(name); - } - property.setValue(value); - getHibernateTemplate().saveOrUpdate(property); - this.propertiesMap.put(name, value); - } - - /** - * Oracle has a low limit on the maximum length of a parameter list - * in SQL queries of the form "WHERE tbl.col IN (:paramList)". - * Since enrollment lists can sometimes be very long, we've replaced - * such queries with full selects followed by filtering. This helper - * method filters out unwanted grade records. (Typically they're not - * wanted because they're either no longer officially enrolled in the - * course or they're not members of the selected section.) - */ - protected List filterGradeRecordsByStudents(final Collection gradeRecords, final Collection studentUids) { - final List filteredRecords = new ArrayList(); - for (final Iterator iter = gradeRecords.iterator(); iter.hasNext(); ) { - final AbstractGradeRecord agr = (AbstractGradeRecord)iter.next(); - if (studentUids.contains(agr.getStudentId())) { - filteredRecords.add(agr); - } - } - return filteredRecords; - } - - @Deprecated - protected GradebookAssignment getAssignmentWithoutStats(final String gradebookUid, final String assignmentName) throws HibernateException { - return (GradebookAssignment) getSessionFactory().getCurrentSession() - .createQuery("from GradebookAssignment as asn where asn.name = :assignmentname and asn.gradebook.uid = :gradebookuid and asn.removed is false") - .setParameter("assignmentname", assignmentName) - .setParameter("gradebookuid", gradebookUid) - .uniqueResult(); - } - - protected GradebookAssignment getAssignmentWithoutStats(final String gradebookUid, final Long assignmentId) throws HibernateException { - return (GradebookAssignment) getSessionFactory().getCurrentSession() - .createQuery("from GradebookAssignment as asn where asn.id = :assignmentid and asn.gradebook.uid = :gradebookuid and asn.removed is false") - .setParameter("assignmentid", assignmentId) - .setParameter("gradebookuid", gradebookUid) - .uniqueResult(); - } - - protected GradebookAssignment getAssignmentById(final Long assignmentId) throws HibernateException { - return (GradebookAssignment) getSessionFactory().getCurrentSession() - .createCriteria(GradebookAssignment.class) - .add(Restrictions.eq("id", assignmentId)) - .add(Restrictions.eq("removed", false)) - .uniqueResult(); - } - - protected void updateAssignment(final GradebookAssignment assignment) throws ConflictingAssignmentNameException, HibernateException { - // Ensure that we don't have the assignment in the session, since - // we need to compare the existing one in the db to our edited assignment - final Session session = getSessionFactory().getCurrentSession(); - session.evict(assignment); - - final GradebookAssignment asnFromDb = (GradebookAssignment) session.load(GradebookAssignment.class, assignment.getId()); - - final Long count = (Long) session.createCriteria(GradableObject.class) - .add(Restrictions.eq("name", assignment.getName())) - .add(Restrictions.eq("gradebook", assignment.getGradebook())) - .add(Restrictions.ne("id", assignment.getId())) - .add(Restrictions.eq("removed", false)) - .setProjection(Projections.rowCount()) - .uniqueResult(); - if(count > 0) { - throw new ConflictingAssignmentNameException("You can not save multiple assignments in a gradebook with the same name"); - } - - session.evict(asnFromDb); - session.update(assignment); - } - - protected AssignmentGradeRecord getAssignmentGradeRecord(final GradebookAssignment assignment, final String studentUid) throws HibernateException { - return (AssignmentGradeRecord) getSessionFactory().getCurrentSession() - .createQuery("from AssignmentGradeRecord as agr where agr.studentId = :studentid and agr.gradableObject.id = :assignmentid") - .setParameter("studentid", studentUid) - .setParameter("assignmentid", assignment.getId()) - .uniqueResult(); - } - - public Long createAssignment(final Long gradebookId, final String name, final Double points, final Date dueDate, final Boolean isNotCounted, - final Boolean isReleased, final Boolean isExtraCredit, final Integer sortOrder) throws ConflictingAssignmentNameException, StaleObjectModificationException - { - return createNewAssignment(gradebookId, null, name, points, dueDate, isNotCounted, isReleased, isExtraCredit, sortOrder, null); - } - - public Long createAssignmentForCategory(final Long gradebookId, final Long categoryId, final String name, final Double points, final Date dueDate, final Boolean isNotCounted, - final Boolean isReleased, final Boolean isExtraCredit, final Integer categorizedSortOrder) - throws ConflictingAssignmentNameException, StaleObjectModificationException, IllegalArgumentException - { - if(gradebookId == null || categoryId == null) - { - throw new IllegalArgumentException("gradebookId or categoryId is null in BaseHibernateManager.createAssignmentForCategory"); - } - - return createNewAssignment(gradebookId, categoryId, name, points, dueDate, isNotCounted, isReleased, isExtraCredit, null, categorizedSortOrder); - } - - private Long createNewAssignment(final Long gradebookId, final Long categoryId, final String name, final Double points, final Date dueDate, final Boolean isNotCounted, - final Boolean isReleased, final Boolean isExtraCredit, final Integer sortOrder, final Integer categorizedSortOrder) - throws ConflictingAssignmentNameException, StaleObjectModificationException - { - final GradebookAssignment asn = prepareNewAssignment(name, points, dueDate, isNotCounted, isReleased, isExtraCredit, sortOrder, categorizedSortOrder); - - return saveNewAssignment(gradebookId, categoryId, asn); - } - - private GradebookAssignment prepareNewAssignment(final String name, final Double points, final Date dueDate, final Boolean isNotCounted, final Boolean isReleased, - final Boolean isExtraCredit, final Integer sortOrder, final Integer categorizedSortOrder) - { - // name cannot contain these special chars as they are reserved for special columns in import/export - final String validatedName = GradebookHelper.validateGradeItemName(name); - - final GradebookAssignment asn = new GradebookAssignment(); - asn.setName(validatedName); - asn.setPointsPossible(points); - asn.setDueDate(dueDate); - asn.setUngraded(false); - if (isNotCounted != null) - { - asn.setNotCounted(isNotCounted.booleanValue()); - } - if (isExtraCredit != null) - { - asn.setExtraCredit(isExtraCredit.booleanValue()); - } - if (isReleased != null) - { - asn.setReleased(isReleased.booleanValue()); - } - if (sortOrder != null) - { - asn.setSortOrder(sortOrder); - } - if (categorizedSortOrder != null) - { - asn.setCategorizedSortOrder(categorizedSortOrder); - } - - return asn; - } - - private void loadAssignmentGradebookAndCategory(final GradebookAssignment asn, final Long gradebookId, final Long categoryId) - { - final Session session = getSessionFactory().getCurrentSession(); - final Gradebook gb = (Gradebook) session.load(Gradebook.class, gradebookId); - asn.setGradebook(gb); - if (categoryId != null) - { - final Category cat = (Category) session.load(Category.class, categoryId); - asn.setCategory(cat); - } - } - - protected Long saveNewAssignment(final Long gradebookId, final Long categoryId, final GradebookAssignment asn) throws ConflictingAssignmentNameException - { - final HibernateCallback hc = session -> { - loadAssignmentGradebookAndCategory(asn, gradebookId, categoryId); - - if (assignmentNameExists(asn.getName(), asn.getGradebook())) - { - throw new ConflictingAssignmentNameException("You cannot save multiple assignments in a gradebook with the same name"); - } - - return (Long) session.save(asn); - }; - - return getHibernateTemplate().execute(hc); - } - - public void updateGradebook(final Gradebook gradebook) throws StaleObjectModificationException { - final HibernateCallback hc = session -> { - // Get the gradebook and selected mapping from persistence - final Gradebook gradebookFromPersistence = (Gradebook)session.load(gradebook.getClass(), gradebook.getId()); - final GradeMapping mappingFromPersistence = gradebookFromPersistence.getSelectedGradeMapping(); - - // If the mapping has changed, and there are explicitly entered - // course grade records, disallow this update. - if (!mappingFromPersistence.getId().equals(gradebook.getSelectedGradeMapping().getId())) { - if(isExplicitlyEnteredCourseGradeRecords(gradebook.getId())) { - throw new IllegalStateException("Selected grade mapping can not be changed, since explicit course grades exist."); - } - } - - // Evict the persisted objects from the session and update the gradebook - // so the new grade mapping is used in the sort column update - //session.evict(mappingFromPersistence); - for (final Object element : gradebookFromPersistence.getGradeMappings()) { - session.evict(element); - } - session.evict(gradebookFromPersistence); - try { - session.update(gradebook); - session.flush(); - } catch (final StaleObjectStateException e) { - throw new StaleObjectModificationException(e); - } - - return null; - }; - getHibernateTemplate().execute(hc); - } - - public boolean isExplicitlyEnteredCourseGradeRecords(final Long gradebookId) { - - final List studentUids = new ArrayList<>(getAllStudentUids(getGradebookUid(gradebookId))); - - if (studentUids.isEmpty()) { - return false; - } - - HibernateCallback hc = session -> (Number) session.createCriteria(CourseGradeRecord.class) - .createAlias("gradableObject", "go") - .createAlias("go.gradebook", "gb") - .add(Restrictions.eq("gb.id", gradebookId)) - .add(Restrictions.isNotNull("enteredGrade")) - .add(HibernateCriterionUtils.CriterionInRestrictionSplitter("studentId", studentUids)) - .setProjection(Projections.rowCount()) - .uniqueResult(); - Number number = getHibernateTemplate().execute(hc); - log.debug("total number of explicitly entered course grade records = {}", number); - return number.intValue() > 0; - } - - public Authn getAuthn() { - return this.authn; - } - public void setAuthn(final Authn authn) { - this.authn = authn; - } - - protected String getUserUid() { - return this.authn.getUserUid(); - } - - protected SectionAwareness getSectionAwareness() { - return this.sectionAwareness; - } - public void setSectionAwareness(final SectionAwareness sectionAwareness) { - this.sectionAwareness = sectionAwareness; - } - - protected ServerConfigurationService getServerConfigurationService() { - return this.serverConfigurationService; - } - - public void setServerConfigurationService(final ServerConfigurationService serverConfigurationService) { - this.serverConfigurationService = serverConfigurationService; - } - - protected EventTrackingService getEventTrackingService() { - return this.eventTrackingService; - } - - public void setEventTrackingService(final EventTrackingService eventTrackingService) { - this.eventTrackingService = eventTrackingService; - } - - protected GradebookExternalAssessmentService getGradebookExternalAssessmentService() { - return this.externalAssessmentService; - } - - public void setGradebookExternalAssessmentService(final GradebookExternalAssessmentService externalAssessmentService) { - this.externalAssessmentService = externalAssessmentService; - } - - public void postEvent(final String event, final String objectReference) { - this.eventTrackingService.post(this.eventTrackingService.newEvent(event, objectReference, true)); - } - - - public Long createCategory(final Long gradebookId, final String name, final Double weight, final Integer drop_lowest, - final Integer dropHighest, final Integer keepHighest, final Boolean is_extra_credit, final Boolean is_equal_weight) { - return createCategory(gradebookId, name, weight, drop_lowest, dropHighest, keepHighest, is_extra_credit, is_equal_weight, null); - } - - public Long createCategory(final Long gradebookId, final String name, final Double weight, final Integer drop_lowest, - final Integer dropHighest, final Integer keepHighest, final Boolean is_extra_credit, final Boolean is_equal_weight, - final Integer categoryOrder) throws ConflictingCategoryNameException, StaleObjectModificationException { - - final HibernateCallback hc = session -> { - final Gradebook gb = (Gradebook)session.load(Gradebook.class, gradebookId); - - final Number numNameConflicts = (Number) session.createCriteria(Category.class) - .add(Restrictions.eq("name", name)) - .add(Restrictions.eq("gradebook", gb)) - .add(Restrictions.eq("removed", false)) - .setProjection(Projections.rowCount()) - .uniqueResult(); - - if(numNameConflicts.intValue() > 0) { - throw new ConflictingCategoryNameException("You can not save multiple categories in a gradebook with the same name"); - } - if(weight > 1 || weight < 0) { - throw new IllegalArgumentException("weight for category is greater than 1 or less than 0 in createCategory of BaseHibernateManager"); - } - if(((drop_lowest!=null && drop_lowest > 0) || (dropHighest!=null && dropHighest > 0)) && (keepHighest!=null && keepHighest > 0)) { - throw new IllegalArgumentException("a combination of positive values for keepHighest and either drop_lowest or dropHighest occurred in createCategory of BaseHibernateManager"); - } - - final Category ca = new Category(); - ca.setGradebook(gb); - ca.setName(name); - ca.setWeight(weight); - ca.setDropLowest(drop_lowest); - ca.setDropHighest(dropHighest); - ca.setKeepHighest(keepHighest); - //ca.setItemValue(itemValue); - ca.setRemoved(false); - ca.setExtraCredit(is_extra_credit); - ca.setEqualWeightAssignments(is_equal_weight); - ca.setCategoryOrder(categoryOrder); - - final Long id = (Long) session.save(ca); - - return id; - }; - - return getHibernateTemplate().execute(hc); - } - - public List getCategories(final Long gradebookId) throws HibernateException { - final HibernateCallback> hc = session -> session - .createQuery("from Category as ca where ca.gradebook.id = :gradebookid and ca.removed is false") - .setParameter("gradebookid", gradebookId) - .list(); - - return getHibernateTemplate().execute(hc); - } - - public List getCategoriesWithAssignments(final Long gradebookId) { - final List categories = getCategories(gradebookId); - final List categoriesWithAssignments = new ArrayList(); - if (categories != null) { - for (final Iterator catIter = categories.iterator(); catIter.hasNext();) { - final Category category = (Category) catIter.next(); - if (category != null) { - final List assignments = getAssignmentsForCategory(category.getId()); - category.setAssignmentList(assignments); - categoriesWithAssignments.add(category); - } - } - } - - return categoriesWithAssignments; - } - - public List getAssignmentsForCategory(final Long categoryId) throws HibernateException{ - final HibernateCallback> hc = session -> session - .createQuery("from GradebookAssignment as assign where assign.category.id = :categoryid and assign.removed is false") - .setParameter("categoryid", categoryId) - .list(); - return getHibernateTemplate().execute(hc); - } - - public Category getCategory(final Long categoryId) throws HibernateException{ - final HibernateCallback hc = session -> (Category) session - .createQuery("from Category as cat where cat.id = :categoryid") - .setParameter("categoryid", categoryId) - .uniqueResult(); - return getHibernateTemplate().execute(hc); - } - - public void updateCategory(final Category category) throws ConflictingCategoryNameException, StaleObjectModificationException { - final HibernateCallback hc = session -> { - session.evict(category); - final Category persistentCat = (Category) session.load(Category.class, category.getId()); - final Number numNameConflicts = (Number) session.createCriteria(Category.class) - .add(Restrictions.eq("name", category.getName())) - .add(Restrictions.eq("gradebook", category.getGradebook())) - .add(Restrictions.ne("id", category.getId())) - .add(Restrictions.eq("removed", false)) - .setProjection(Projections.rowCount()) - .uniqueResult(); - - if (numNameConflicts.intValue() > 0) { - throw new ConflictingCategoryNameException("You can not save multiple category in a gradebook with the same name"); - } - if (category.getWeight().doubleValue() > 1 || category.getWeight().doubleValue() < 0) { - throw new IllegalArgumentException("weight for category is greater than 1 or less than 0 in updateCategory of BaseHibernateManager"); - } - session.evict(persistentCat); - session.update(category); - return null; - }; - try { - getHibernateTemplate().execute(hc); - } catch (final Exception e) { - throw new StaleObjectModificationException(e); - } - } - - public void removeCategory(final Long categoryId) throws StaleObjectModificationException{ - final HibernateCallback hc = session -> { - final Category persistentCat = (Category)session.load(Category.class, categoryId); - - final List assigns = getAssignmentsForCategory(categoryId); - for(final Iterator iter = assigns.iterator(); iter.hasNext();) - { - final GradebookAssignment assignment = (GradebookAssignment) iter.next(); - assignment.setCategory(null); - updateAssignment(assignment); - } - - persistentCat.setRemoved(true); - session.update(persistentCat); - return null; - }; - try { - getHibernateTemplate().execute(hc); - } catch (final Exception e) { - throw new StaleObjectModificationException(e); - } - } - - public LetterGradePercentMapping getDefaultLetterGradePercentMapping() { - final HibernateCallback hc = session -> { - final List mapping = session.createCriteria(LetterGradePercentMapping.class) - .add(Restrictions.eq("mappingType", 1)) - .list(); - - if(mapping.size() == 0) - { - log.info("Default letter grade mapping hasn't been created in DB in BaseHibernateManager.getDefaultLetterGradePercentMapping"); - return null; - } - if(mapping.size() > 1) - { - log.error("Duplicate default letter grade mapping was created in DB in BaseHibernateManager.getDefaultLetterGradePercentMapping"); - return null; - } - - return mapping.get(0); - }; - - return getHibernateTemplate().execute(hc); - } - - public void createOrUpdateDefaultLetterGradePercentMapping(final Map gradeMap) { - if(gradeMap == null) { - throw new IllegalArgumentException("gradeMap is null in BaseHibernateManager.createOrUpdateDefaultLetterGradePercentMapping"); - } - - final LetterGradePercentMapping lgpm = getDefaultLetterGradePercentMapping(); - - if(lgpm != null) - { - updateDefaultLetterGradePercentMapping(gradeMap, lgpm); - } - else - { - createDefaultLetterGradePercentMapping(gradeMap); - } - } - - private void updateDefaultLetterGradePercentMapping(final Map gradeMap, final LetterGradePercentMapping lgpm) { - final Set keySet = gradeMap.keySet(); - - if(keySet.size() != GradebookService.validLetterGrade.length) { - throw new IllegalArgumentException("gradeMap doesn't have right size in BaseHibernateManager.updateDefaultLetterGradePercentMapping"); - } - - if(!validateLetterGradeMapping(gradeMap)) { - throw new IllegalArgumentException("gradeMap contains invalid letter in BaseHibernateManager.updateDefaultLetterGradePercentMapping"); - } - - final HibernateCallback hcb = session -> { - final Map saveMap = new HashMap<>(gradeMap); - lgpm.setGradeMap(saveMap); - session.update(lgpm); - return null; - }; - getHibernateTemplate().execute(hcb); - } - - public void createDefaultLetterGradePercentMapping(final Map gradeMap) { - if(getDefaultLetterGradePercentMapping() != null) { - throw new IllegalArgumentException("gradeMap has already been created in BaseHibernateManager.createDefaultLetterGradePercentMapping"); - } - - if(gradeMap == null) { - throw new IllegalArgumentException("gradeMap is null in BaseHibernateManager.createDefaultLetterGradePercentMapping"); - } - - final Set keySet = gradeMap.keySet(); - - if(keySet.size() != GradebookService.validLetterGrade.length) { - throw new IllegalArgumentException("gradeMap doesn't have right size in BaseHibernateManager.createDefaultLetterGradePercentMapping"); - } - - if(!validateLetterGradeMapping(gradeMap)) { - throw new IllegalArgumentException("gradeMap contains invalid letter in BaseHibernateManager.createDefaultLetterGradePercentMapping"); - } - - final HibernateCallback hcb = session -> { - final LetterGradePercentMapping lgpm = new LetterGradePercentMapping(); - final Map saveMap = new HashMap<>(gradeMap); - lgpm.setGradeMap(saveMap); - lgpm.setMappingType(1); - session.save(lgpm); - return null; - }; - getHibernateTemplate().execute(hcb); - } - - public LetterGradePercentMapping getLetterGradePercentMapping(final Gradebook gradebook) { - final HibernateCallback hc = session -> { - final LetterGradePercentMapping mapping = (LetterGradePercentMapping) session - .createQuery("from LetterGradePercentMapping as lgpm where lgpm.gradebookId = :gradebookId and lgpm.mappingType = 2") - .setParameter("gradebookId", gradebook.getId()) - .uniqueResult(); - if(mapping == null ) { - final LetterGradePercentMapping lgpm = getDefaultLetterGradePercentMapping(); - final LetterGradePercentMapping returnLgpm = new LetterGradePercentMapping(); - returnLgpm.setGradebookId(gradebook.getId()); - returnLgpm.setGradeMap(lgpm.getGradeMap()); - returnLgpm.setMappingType(2); - return returnLgpm; - } - return mapping; - }; - - return getHibernateTemplate().execute(hc); - } - - /** - * this method is different with getLetterGradePercentMapping - - * it returns null if no mapping exists for gradebook instead of - * returning default mapping. - */ - private LetterGradePercentMapping getLetterGradePercentMappingForGradebook(final Gradebook gradebook) { - final HibernateCallback hc = session -> { - final LetterGradePercentMapping mapping = (LetterGradePercentMapping) session - .createQuery("from LetterGradePercentMapping as lgpm where lgpm.gradebookId = :gradebookId and lgpm.mappingType = 2") - .setParameter("gradebookId", gradebook.getId()) - .uniqueResult(); - return mapping; - }; - - return getHibernateTemplate().execute(hc); - } - - public void saveOrUpdateLetterGradePercentMapping(final Map gradeMap, final Gradebook gradebook) { - if(gradeMap == null) { - throw new IllegalArgumentException("gradeMap is null in BaseHibernateManager.saveOrUpdateLetterGradePercentMapping"); - } - - final LetterGradePercentMapping lgpm = getLetterGradePercentMappingForGradebook(gradebook); - - if(lgpm == null) { - final Set keySet = gradeMap.keySet(); - - if(keySet.size() != GradebookService.validLetterGrade.length) { //we only consider letter grade with -/+ now. - throw new IllegalArgumentException("gradeMap doesn't have right size in BaseHibernateManager.saveOrUpdateLetterGradePercentMapping"); - } - if(!validateLetterGradeMapping(gradeMap)) { - throw new IllegalArgumentException("gradeMap contains invalid letter in BaseHibernateManager.saveOrUpdateLetterGradePercentMapping"); - } - - final HibernateCallback hcb = session -> { - final LetterGradePercentMapping lgpm1 = new LetterGradePercentMapping(); - final Map saveMap = new HashMap<>(gradeMap); - lgpm1.setGradeMap(saveMap); - lgpm1.setGradebookId(gradebook.getId()); - lgpm1.setMappingType(2); - session.save(lgpm1); - return null; - }; - getHibernateTemplate().execute(hcb); - } - else - { - udpateLetterGradePercentMapping(gradeMap, gradebook); - } - } - - private void udpateLetterGradePercentMapping(final Map gradeMap, final Gradebook gradebook) { - final HibernateCallback hcb = session -> { - final LetterGradePercentMapping lgpm = getLetterGradePercentMapping(gradebook); - - if (lgpm == null) { - throw new IllegalArgumentException("LetterGradePercentMapping is null in BaseHibernateManager.updateLetterGradePercentMapping"); - } - if (gradeMap == null) { - throw new IllegalArgumentException("gradeMap is null in BaseHibernateManager.updateLetterGradePercentMapping"); - } - final Set keySet = gradeMap.keySet(); - - if (keySet.size() != GradebookService.validLetterGrade.length) { //we only consider letter grade with -/+ now. - throw new IllegalArgumentException("gradeMap doesn't have right size in BaseHibernateManager.udpateLetterGradePercentMapping"); - } - if (validateLetterGradeMapping(gradeMap) == false) { - throw new IllegalArgumentException("gradeMap contains invalid letter in BaseHibernateManager.udpateLetterGradePercentMapping"); - } - final Map saveMap = new HashMap<>(gradeMap); - lgpm.setGradeMap(saveMap); - session.save(lgpm); - - return null; - }; - getHibernateTemplate().execute(hcb); - } - - protected boolean validateLetterGradeMapping(final Map gradeMap) { - for (final String key : gradeMap.keySet()) { - boolean validLetter = false; - for (final String element : GradebookService.validLetterGrade) { - if (key.equalsIgnoreCase(element)) { - validLetter = true; - break; - } - } - if (!validLetter) { - return false; - } - } - return true; - } - - public Long createUngradedAssignment(final Long gradebookId, final String name, final Date dueDate, final Boolean isNotCounted, - final Boolean isReleased) throws ConflictingAssignmentNameException, StaleObjectModificationException { - final HibernateCallback hc = session -> { - final Gradebook gb = (Gradebook) session.load(Gradebook.class, gradebookId); - - // trim the name before validation - final String trimmedName = StringUtils.trimToEmpty(name); - - if(assignmentNameExists(trimmedName, gb)) { - throw new ConflictingAssignmentNameException("You can not save multiple assignments in a gradebook with the same name"); - } - - final GradebookAssignment asn = new GradebookAssignment(); - asn.setGradebook(gb); - asn.setName(trimmedName); - asn.setDueDate(dueDate); - asn.setUngraded(true); - if (isNotCounted != null) { - asn.setNotCounted(isNotCounted); - } - if (isReleased != null) { - asn.setReleased(isReleased); - } - - return (Long) session.save(asn); - }; - return getHibernateTemplate().execute(hc); - } - - public Long createUngradedAssignmentForCategory(final Long gradebookId, final Long categoryId, final String name, final Date dueDate, final Boolean isNotCounted, - final Boolean isReleased) throws ConflictingAssignmentNameException, StaleObjectModificationException, IllegalArgumentException { - - if (gradebookId == null || categoryId == null) { - throw new IllegalArgumentException("gradebookId or categoryId is null in BaseHibernateManager.createUngradedAssignmentForCategory"); - } - final HibernateCallback hc = session -> { - final Gradebook gb = (Gradebook) session.load(Gradebook.class, gradebookId); - final Category cat = (Category) session.load(Category.class, categoryId); - - // trim the name before the validation - final String trimmedName = StringUtils.trimToEmpty(name); - - if (assignmentNameExists(trimmedName, gb)) { - throw new ConflictingAssignmentNameException("You can not save multiple assignments in a gradebook with the same name"); - } - - final GradebookAssignment asn = new GradebookAssignment(); - asn.setGradebook(gb); - asn.setCategory(cat); - asn.setName(trimmedName); - asn.setDueDate(dueDate); - asn.setUngraded(true); - if (isNotCounted != null) { - asn.setNotCounted(isNotCounted); - } - if (isReleased != null) { - asn.setReleased(isReleased); - } - - return (Long) session.save(asn); - }; - - return getHibernateTemplate().execute(hc); - } - - public Long addPermission(final Long gradebookId, final String userId, final String function, final Long categoryId, - final String groupId) throws IllegalArgumentException { - - if (gradebookId == null || userId == null || function == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.addPermission"); - } - if (!function.equalsIgnoreCase(GradebookService.gradePermission) - && !function.equalsIgnoreCase(GradebookService.viewPermission) - && !function.equalsIgnoreCase(GradebookService.noPermission)) { - throw new IllegalArgumentException("Function is not grade, view or none in BaseHibernateManager.addPermission"); - } - final HibernateCallback hc = session -> { - final Permission permission = new Permission(); - permission.setCategoryId(categoryId); - permission.setGradebookId(gradebookId); - permission.setGroupId(groupId); - permission.setFunction(function); - permission.setUserId(userId); - - return (Long) session.save(permission); - }; - - return getHibernateTemplate().execute(hc); - } - - @Deprecated - public List getPermissionsForGB(final Long gradebookId) throws IllegalArgumentException { - - if (gradebookId == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForGB"); - } - final HibernateCallback> hc = session -> session - .createQuery("from Permission as perm where perm.gradebookId = :gradebookId") - .setParameter("gradebookId", gradebookId) - .list(); - return getHibernateTemplate().execute(hc); - } - - @Deprecated - public void updatePermission(final Collection perms) - { - for(final Iterator iter = perms.iterator(); iter.hasNext(); ) - { - final Permission perm = (Permission) iter.next(); - if(perm != null) { - updatePermission(perm); - } - } - } - - @Deprecated - public void updatePermission(final Permission perm) throws IllegalArgumentException { - if (perm == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.updatePermission"); - } - if (perm.getId() == null) { - throw new IllegalArgumentException("Object is not persistent in BaseHibernateManager.updatePermission"); - } - - final HibernateCallback hc = session -> { - session.update(perm); - return null; - }; - - getHibernateTemplate().execute(hc); - } - - @Deprecated - public void deletePermission(final Permission perm) throws IllegalArgumentException { - if (perm == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.deletePermission"); - } - if (perm.getId() == null) { - throw new IllegalArgumentException("Object is not persistent in BaseHibernateManager.deletePermission"); - } - - final HibernateCallback hc = session -> { - session.delete(perm); - return null; - }; - - getHibernateTemplate().execute(hc); - } - - public List getPermissionsForUser(final Long gradebookId, final String userId) throws IllegalArgumentException { - if(gradebookId == null || userId == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUser"); - } - - final HibernateCallback> hc = session -> session - .createQuery("from Permission as perm where perm.gradebookId = :gradebookId and perm.userId = :userId") - .setParameter("gradebookId", gradebookId) - .setParameter("userId", userId) - .list(); - - return getHibernateTemplate().execute(hc); - } - - public List getPermissionsForUserForCategory(final Long gradebookId, final String userId, final List cateIds) throws IllegalArgumentException { - if(gradebookId == null || userId == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserForCategory"); - } - - if (cateIds != null && cateIds.size() > 0) { - final HibernateCallback> hc = session -> session - .createQuery("from Permission as perm where perm.gradebookId = :gradebookId and perm.userId = :userId and perm.categoryId in (:cateIds)") - .setParameter("gradebookId", gradebookId) - .setParameter("userId", userId) - .setParameterList("cateIds", cateIds) - .list(); - return getHibernateTemplate().execute(hc); - } - return null; - } - - public List getPermissionsForUserAnyCategory(final Long gradebookId, final String userId) throws IllegalArgumentException { - if (gradebookId == null || userId == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserAnyCategory"); - } - - final HibernateCallback> hc = session -> session - .createQuery("from Permission as perm where perm.gradebookId = :gradebookId and perm.userId = :userId and perm.categoryId is null and perm.function in (:functions)") - .setParameter("gradebookId", gradebookId) - .setParameter("userId", userId) - .setParameterList("functions", GraderPermission.getStandardPermissions()) - .list(); - - return getHibernateTemplate().execute(hc); - } - - public List getPermissionsForUserAnyGroup(final Long gradebookId, final String userId) throws IllegalArgumentException { - if (gradebookId == null || userId == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserAnyGroup"); - } - - final HibernateCallback> hc = session -> session - .createQuery("from Permission as perm where perm.gradebookId = :gradebookId and perm.userId = :userId and perm.groupId is null and perm.function in (:functions)") - .setParameter("gradebookId", gradebookId) - .setParameter("userId", userId) - .setParameterList("functions", GraderPermission.getStandardPermissions()) - .list(); - - return getHibernateTemplate().execute(hc); - } - - public List getPermissionsForUserAnyGroupForCategory(final Long gradebookId, final String userId, final List cateIds) throws IllegalArgumentException { - if (gradebookId == null || userId == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserAnyGroupForCategory"); - } - - if (cateIds != null && cateIds.size() > 0) { - final HibernateCallback> hc = session -> session - .createQuery("from Permission as perm where perm.gradebookId = :gradebookId and perm.userId = :userId and perm.categoryId in (:cateIds) and perm.groupId is null") - .setParameter("gradebookId", gradebookId) - .setParameter("userId", userId) - .setParameterList("cateIds", cateIds) - .list(); - - return getHibernateTemplate().execute(hc); - } - return null; - } - - public List getPermissionsForGBForCategoryIds(final Long gradebookId, final List cateIds) throws IllegalArgumentException { - if (gradebookId == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserAnyGroupForCategory"); - } - if (cateIds != null && cateIds.size() > 0) { - final HibernateCallback> hc = session -> session - .createQuery("from Permission as perm where perm.gradebookId = :gradebookId and perm.categoryId in (:cateIds)") - .setParameter("gradebookId", gradebookId) - .setParameterList("cateIds", cateIds) - .list(); - - return getHibernateTemplate().execute(hc); - } - return null; - } - - public List getPermissionsForUserAnyGroupAnyCategory(final Long gradebookId, final String userId) throws IllegalArgumentException { - if (gradebookId == null || userId == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserAnyGroupForCategory"); - } - - final HibernateCallback> hc = session -> session - .createQuery("from Permission as perm where perm.gradebookId=:gradebookId and perm.userId=:userId and perm.categoryId is null and perm.groupId is null") - .setParameter("gradebookId", gradebookId) - .setParameter("userId", userId) - .list(); - - return getHibernateTemplate().execute(hc); - } - - public List getPermissionsForUserForGoupsAnyCategory(final Long gradebookId, final String userId, final List groupIds) throws IllegalArgumentException { - if (gradebookId == null || userId == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserForGoupsAnyCategory"); - } - if (groupIds != null && groupIds.size() > 0) { - final HibernateCallback> hc = session -> session - .createQuery("from Permission as perm where perm.gradebookId = :gradebookId and perm.userId = :userId and perm.categoryId is null and perm.groupId in (:groupIds)") - .setParameter("gradebookId", gradebookId) - .setParameter("userId", userId) - .setParameterList("groupIds", groupIds) - .list(); - - return getHibernateTemplate().execute(hc); - } - return null; - } - - public List getPermissionsForUserForGroup(final Long gradebookId, final String userId, final List groupIds) throws IllegalArgumentException { - if (gradebookId == null || userId == null) { - throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserForGroup"); - } - if (groupIds != null && groupIds.size() > 0) { - final HibernateCallback> hc = session -> session - .createQuery("from Permission as perm where perm.gradebookId = :gradebookId and perm.userId = :userId and perm.groupId in (:groupIds) ") - .setParameter("gradebookId", gradebookId) - .setParameter("userId", userId) - .setParameterList("groupIds", groupIds) - .list(); - - return getHibernateTemplate().execute(hc); - } - return null; - } - - public boolean isAssignmentDefined(final Long gradableObjectId) { - final HibernateCallback hc = session -> (Number) session.createCriteria(GradebookAssignment.class) - .add(Restrictions.eq("id", gradableObjectId)) - .add(Restrictions.eq("removed", false)) - .setProjection(Projections.rowCount()) - .uniqueResult(); - return getHibernateTemplate().execute(hc).intValue() == 1; - } - - /** - * - * @param gradableObjectId - * @return the GradebookAssignment object with the given id - */ - public GradebookAssignment getAssignment(final Long gradableObjectId) { - return getHibernateTemplate().load(GradebookAssignment.class, gradableObjectId); - } - - /** - * - * @param doublePointsPossible - * @param doublePointsEarned - * @return the % equivalent for the given points possible and points earned - */ - protected Double calculateEquivalentPercent(final Double doublePointsPossible, final Double doublePointsEarned) { - - if (doublePointsEarned == null || doublePointsPossible == null) { - return null; - } - - // scale to handle points stored as repeating decimals - final BigDecimal pointsEarned = new BigDecimal(doublePointsEarned.toString()); - final BigDecimal pointsPossible = new BigDecimal(doublePointsPossible.toString()); - - // Avoid dividing by zero - if (pointsEarned.compareTo(BigDecimal.ZERO) == 0 || pointsPossible.compareTo(BigDecimal.ZERO) == 0) { - return new Double(0); - } - - final BigDecimal equivPercent = pointsEarned.divide(pointsPossible, GradebookService.MATH_CONTEXT).multiply(new BigDecimal("100")); - return Double.valueOf(equivPercent.doubleValue()); - - } - - /** - * Converts points to percentage for the given AssignmentGradeRecords - * @param gradebook - * @param studentRecordsFromDB - * @return - */ - protected List convertPointsToPercentage(final Gradebook gradebook, final List studentRecordsFromDB) - { - final List percentageList = new ArrayList(); - for(int i=0; i < studentRecordsFromDB.size(); i++) - { - final AssignmentGradeRecord agr = (AssignmentGradeRecord) studentRecordsFromDB.get(i); - if (agr != null) { - final Double pointsPossible = agr.getAssignment().getPointsPossible(); - if (pointsPossible == null || agr.getPointsEarned() == null) { - agr.setPercentEarned(null); - percentageList.add(agr); - } else { - agr.setDateRecorded(agr.getDateRecorded()); - agr.setGraderId(agr.getGraderId()); - agr.setPercentEarned(calculateEquivalentPercent(pointsPossible, agr.getPointsEarned())); - percentageList.add(agr); - } - } - } - return percentageList; - } - - /** - * Converts points to letter grade for the given AssignmentGradeRecords - * @param gradebook - * @param studentRecordsFromDB - * @return - */ - protected List convertPointsToLetterGrade(final Gradebook gradebook, final List studentRecordsFromDB) - { - final List letterGradeList = new ArrayList(); - final LetterGradePercentMapping lgpm = getLetterGradePercentMapping(gradebook); - for(int i=0; i < studentRecordsFromDB.size(); i++) - { - final AssignmentGradeRecord agr = (AssignmentGradeRecord) studentRecordsFromDB.get(i); - if(agr != null) { - final Double pointsPossible = agr.getAssignment().getPointsPossible(); - agr.setDateRecorded(agr.getDateRecorded()); - agr.setGraderId(agr.getGraderId()); - if (pointsPossible == null || agr.getPointsEarned() == null) { - agr.setLetterEarned(null); - letterGradeList.add(agr); - } else { - final String letterGrade = lgpm.getGrade(calculateEquivalentPercent(pointsPossible, agr.getPointsEarned())); - agr.setLetterEarned(letterGrade); - letterGradeList.add(agr); - } - } - } - return letterGradeList; - } - - protected Double calculateEquivalentPointValueForPercent(final Double doublePointsPossible, final Double doublePercentEarned) { - if (doublePointsPossible == null || doublePercentEarned == null) { - return null; - } - - final BigDecimal pointsPossible = new BigDecimal(doublePointsPossible.toString()); - final BigDecimal percentEarned = new BigDecimal(doublePercentEarned.toString()); - final BigDecimal equivPoints = pointsPossible.multiply(percentEarned.divide(new BigDecimal("100"), GradebookService.MATH_CONTEXT)); - return equivPoints.doubleValue(); - } - - public List getComments(final GradebookAssignment assignment, final Collection studentIds) { - if (studentIds.isEmpty()) { - return new ArrayList<>(); - } - final HibernateCallback> hc = session -> session.createCriteria(Comment.class) - .add(Restrictions.eq("gradableObject", assignment)) - .add(HibernateCriterionUtils.CriterionInRestrictionSplitter("studentId", studentIds)) - .list(); - return getHibernateTemplate().execute(hc); - } - - protected Map> getVisibleExternalAssignments(final Gradebook gradebook, final Collection studentIds, final List assignments) { - final String gradebookUid = gradebook.getUid(); - final Map> allExternals = this.externalAssessmentService.getVisibleExternalAssignments(gradebookUid, studentIds); - final Map allRequested = new HashMap(); - - for (final GradebookAssignment a : assignments) { - if (a.isExternallyMaintained()) { - allRequested.put(a.getExternalId(), a); - } - } - - final Map> visible = new HashMap>(); - for (final String studentId : allExternals.keySet()) { - if (studentIds.contains(studentId)) { - final Set studentAssignments = new HashSet(); - for (final String assignmentId : allExternals.get(studentId)) { - if (allRequested.containsKey(assignmentId)) { - studentAssignments.add(allRequested.get(assignmentId)); - } - } - visible.put(studentId, studentAssignments); - } - } - return visible; - } - - // NOTE: This should not be called in a loop. Anything for sets should use getVisibleExternalAssignments - protected boolean studentCanView(final String studentId, final GradebookAssignment assignment) { - if (assignment.isExternallyMaintained()) { - try { - final String gbUid = assignment.getGradebook().getUid(); - final String extId = assignment.getExternalId(); - - if (this.externalAssessmentService.isExternalAssignmentGrouped(gbUid, extId)) { - return this.externalAssessmentService.isExternalAssignmentVisible(gbUid, extId, studentId); - } - } catch (final GradebookNotFoundException e) { - if (log.isDebugEnabled()) { log.debug("Bogus graded assignment checked for course grades: " + assignment.getId()); } - } - } - - // We assume that the only disqualifying condition is that the external assignment - // is grouped and the student is not a member of one of the groups allowed. - return true; - } - - protected void finalizeNullGradeRecords(final Gradebook gradebook) { - final Set studentUids = getAllStudentUids(gradebook.getUid()); - final Date now = new Date(); - final String graderId = getAuthn().getUserUid(); - - getHibernateTemplate().execute((HibernateCallback) session -> { - final List countedAssignments = session - .createQuery("from GradebookAssignment as asn where asn.gradebook.id = :gb and asn.removed is false and asn.notCounted is false and asn.ungraded is false") - .setParameter("gb", gradebook.getId()) - .list(); - - final Map> visible = getVisibleExternalAssignments(gradebook, studentUids, countedAssignments); - - for (final GradebookAssignment assignment : countedAssignments) { - final List scoredGradeRecords = session - .createQuery("from AssignmentGradeRecord as agr where agr.gradableObject.id = :go") - .setParameter("go", assignment.getId()) - .list(); - - final Map studentToGradeRecordMap = new HashMap<>(); - for (final AssignmentGradeRecord scoredGradeRecord : scoredGradeRecords) { - studentToGradeRecordMap.put(scoredGradeRecord.getStudentId(), scoredGradeRecord); - } - - for (final String studentUid : studentUids) { - // SAK-11485 - We don't want to add scores for those grouped activities - // that this student should not see or be scored on. - if (assignment.isExternallyMaintained() && (!visible.containsKey(studentUid) || !visible.get(studentUid).contains(assignment))) { - continue; - } - AssignmentGradeRecord gradeRecord = studentToGradeRecordMap.get(studentUid); - if (gradeRecord != null) { - if (gradeRecord.getPointsEarned() == null) { - gradeRecord.setPointsEarned(0d); - } else { - continue; - } - } else { - gradeRecord = new AssignmentGradeRecord(assignment, studentUid, 0d); - } - gradeRecord.setGraderId(graderId); - gradeRecord.setDateRecorded(now); - session.saveOrUpdate(gradeRecord); - session.save(new GradingEvent(assignment, graderId, studentUid, gradeRecord.getPointsEarned())); - } - } - return null; - }); - } - - /** - * - * @param name the assignment name (will not be trimmed) - * @param gradebook the gradebook to check - * @return true if an assignment with the given name already exists in this gradebook. - */ - protected boolean assignmentNameExists(final String name, final Gradebook gradebook) - { - final Number count = (Number) getHibernateTemplate().execute(session -> session - .createCriteria(GradableObject.class) - .createAlias("gradebook", "gb") - .add(Restrictions.eq("name", name)) - .add(Restrictions.eq("gb.uid", gradebook.getUid())) - .add(Restrictions.eq("removed", false)) - .setProjection(Projections.rowCount()) - .uniqueResult()); - return count.intValue() > 0; - } - - private Comment getInternalComment(final String gradebookUid, final Long assignmentId, final String studentUid) { - return (Comment) getHibernateTemplate().execute(session -> session - .createQuery("from org.sakaiproject.tool.gradebook.Comment as c where c.studentId = :studentId and c.gradableObject.gradebook.uid = :gradebookUid and c.gradableObject.id = :assignmentId and c.gradableObject.removed is false") - .setParameter("studentId", studentUid) - .setParameter("gradebookUid", gradebookUid) - .setParameter("assignmentId", assignmentId) - .uniqueResult()); - } - - public CommentDefinition getAssignmentScoreComment(final String gradebookUid, final Long assignmentId, final String studentUid) throws GradebookNotFoundException, AssessmentNotFoundException { - if (gradebookUid == null || assignmentId == null || studentUid == null) { - throw new IllegalArgumentException("null parameter passed to getAssignmentScoreComment. Values are gradebookUid:" + gradebookUid + " assignmentId:" + assignmentId + " studentUid:"+ studentUid); - } - final GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentId); - if (assignment == null) { - throw new AssessmentNotFoundException("There is no assignmentId " + assignmentId + " for gradebookUid " + gradebookUid); - } - - CommentDefinition commentDefinition = null; - final Comment comment = getInternalComment(gradebookUid, assignmentId, studentUid); - if (comment != null) { - commentDefinition = new CommentDefinition(); - commentDefinition.setAssignmentName(assignment.getName()); - commentDefinition.setCommentText(comment.getCommentText()); - commentDefinition.setDateRecorded(comment.getDateRecorded()); - commentDefinition.setGraderUid(comment.getGraderId()); - commentDefinition.setStudentUid(comment.getStudentId()); - } - return commentDefinition; - } - - public void setAssignmentScoreComment(final String gradebookUid, final Long assignmentId, final String studentUid, final String commentText) throws GradebookNotFoundException, AssessmentNotFoundException { - getHibernateTemplate().execute((HibernateCallback) session -> { - Comment comment = getInternalComment(gradebookUid, assignmentId, studentUid); - if (comment == null) { - comment = new Comment(studentUid, commentText, getAssignmentWithoutStats(gradebookUid, assignmentId)); - } else { - comment.setCommentText(commentText); - } - comment.setGraderId(this.authn.getUserUid()); - comment.setDateRecorded(new Date()); - session.saveOrUpdate(comment); - return null; - }); - } - - public boolean getIsAssignmentExcused(final String gradebookUid, final Long assignmentId, final String studentUid) throws GradebookNotFoundException, AssessmentNotFoundException { - if(gradebookUid == null || assignmentId == null || studentUid == null){ - throw new IllegalArgumentException("null parameter passed to getAssignmentScoreComment. Values are gradebookUid:" + gradebookUid + " assignmentId:" + assignmentId + " studentUid:"+ studentUid); - } - GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentId); - AssignmentGradeRecord agr = getAssignmentGradeRecord(assignment, studentUid); - - if(agr == null){ - return false; - }else{ - return BooleanUtils.toBoolean(agr.isExcludedFromGrade()); - } - } - - public void updateGradeMapping(final Long gradeMappingId, final Map gradeMap){ - getHibernateTemplate().execute((HibernateCallback) session -> { - final GradeMapping gradeMapping = (GradeMapping)session.load(GradeMapping.class, gradeMappingId); - gradeMapping.setGradeMap(gradeMap); - session.update(gradeMapping); - session.flush(); - return null; - }); - } - - /** - * Get's all course grade overrides for a given gradebook - * - * @param gradebook The gradebook - * @return A list of {@link CourseGradeRecord} that have overrides - * - * @throws HibernateException - */ - protected List getCourseGradeOverrides(final Gradebook gradebook) throws HibernateException { - return getHibernateTemplate().execute(session -> session - .createQuery("from CourseGradeRecord as cgr where cgr.gradableObject.gradebook = :gradebook and cgr.enteredGrade is not null") - .setParameter("gradebook", gradebook) - .list()); - } -} diff --git a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookDefinition.java b/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookDefinition.java deleted file mode 100644 index f6f5d78e07ef..000000000000 --- a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookDefinition.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) 2003-2012 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.component.gradebook; - -import java.io.Externalizable; -import java.util.Collection; -import java.util.Map; -import java.util.List; - -import org.sakaiproject.service.gradebook.shared.Assignment; - -public class GradebookDefinition extends VersionedExternalizable implements Externalizable { - private static final long serialVersionUID = 1L; - public static final String EXTERNALIZABLE_VERSION = "1"; - - private String selectedGradingScaleUid; - private Map selectedGradingScaleBottomPercents; - private Collection assignments; - - private int gradeType; - private int categoryType; - private List category; - - public GradebookDefinition() { - } - - public String getExternalizableVersion() { - return EXTERNALIZABLE_VERSION; - } - - public Collection getAssignments() { - return assignments; - } - public void setAssignments(Collection assignments) { - this.assignments = assignments; - } - public Map getSelectedGradingScaleBottomPercents() { - return selectedGradingScaleBottomPercents; - } - public void setSelectedGradingScaleBottomPercents(Map selectedGradingScaleBottomPercents) { - this.selectedGradingScaleBottomPercents = selectedGradingScaleBottomPercents; - } - public String getSelectedGradingScaleUid() { - return selectedGradingScaleUid; - } - public void setSelectedGradingScaleUid(String selectedGradingScaleUid) { - this.selectedGradingScaleUid = selectedGradingScaleUid; - } - - public int getGradeType() { - return gradeType; - } - public void setGradeType(int gradeType) { - this.gradeType = gradeType; - } - public int getCategoryType() { - return categoryType; - } - public void setCategoryType(int categoryType) { - this.categoryType = categoryType; - } - - public List getCategory() { - return category; - } - public void setCategory(List category) { - this.category = category; - } - - -} diff --git a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookExternalAssessmentServiceImpl.java b/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookExternalAssessmentServiceImpl.java deleted file mode 100644 index b98c80860b4b..000000000000 --- a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookExternalAssessmentServiceImpl.java +++ /dev/null @@ -1,1003 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.component.gradebook; - -import java.lang.reflect.Method; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.OptionalLong; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.hibernate.HibernateException; -import org.hibernate.Session; -import org.hibernate.criterion.Projections; -import org.hibernate.criterion.Restrictions; -import org.sakaiproject.component.cover.ComponentManager; -import org.sakaiproject.hibernate.HibernateCriterionUtils; -import org.sakaiproject.service.gradebook.shared.AssessmentNotFoundException; -import org.sakaiproject.service.gradebook.shared.AssignmentHasIllegalPointsException; -import org.sakaiproject.service.gradebook.shared.CommentDefinition; -import org.sakaiproject.service.gradebook.shared.ConflictingAssignmentNameException; -import org.sakaiproject.service.gradebook.shared.ConflictingExternalIdException; -import org.sakaiproject.service.gradebook.shared.ExternalAssignmentProvider; -import org.sakaiproject.service.gradebook.shared.ExternalAssignmentProviderCompat; -import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService; -import org.sakaiproject.service.gradebook.shared.GradebookHelper; -import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException; -import org.sakaiproject.service.gradebook.shared.GradebookService; -import org.sakaiproject.service.gradebook.shared.InvalidCategoryException; -import org.sakaiproject.site.api.Site; -import org.sakaiproject.site.api.SiteService; -import org.sakaiproject.tool.api.ToolManager; -import org.sakaiproject.tool.gradebook.AssignmentGradeRecord; -import org.sakaiproject.tool.gradebook.Category; -import org.sakaiproject.tool.gradebook.Gradebook; -import org.sakaiproject.tool.gradebook.GradebookAssignment; -import org.sakaiproject.util.ResourceLoader; -import org.springframework.orm.hibernate5.HibernateCallback; -import org.springframework.orm.hibernate5.HibernateTemplate; - -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class GradebookExternalAssessmentServiceImpl extends BaseHibernateManager implements GradebookExternalAssessmentService { - - @Setter - private ToolManager toolManager; - - @Setter - private SiteService siteService; - - private ConcurrentHashMap externalProviders = new ConcurrentHashMap(); - - // Mapping of providers to their getAllExternalAssignments(String gradebookUid) methods, - // used to allow the method to be called on providers not declaring the Compat interface. - // This is to allow the same code to be used on 2.9 and beyond, where the secondary interface - // may be removed, without build profiles. - private final ConcurrentHashMap providerMethods = new ConcurrentHashMap(); - - /** - * Property in sakai.properties used to allow this service to update scores in the db every time the update method is called. By - * default, scores are only updated if the score is different than what is currently in the db. - */ - public static final String UPDATE_SAME_SCORE_PROP = "gradebook.externalAssessments.updateSameScore"; - public static final boolean UPDATE_SAME_SCORE_PROP_DEFAULT = false; - - public GradebookService getGradebookService() { - return (GradebookService) ComponentManager.get("org.sakaiproject.service.gradebook.GradebookService"); - } - - public ConcurrentMap getExternalAssignmentProviders() { - if (this.externalProviders == null) { - this.externalProviders = new ConcurrentHashMap<>(0); - } - return this.externalProviders; - } - - @Override - public void registerExternalAssignmentProvider(final ExternalAssignmentProvider provider) { - if (provider == null) { - throw new IllegalArgumentException("provider cannot be null"); - } else { - getExternalAssignmentProviders().put(provider.getAppKey(), provider); - - // Try to duck-type the provider so it doesn't have to declare the Compat interface. - // TODO: Remove this handling once the Compat interface has been merged or the issue is otherwise resolved. - if (!(provider instanceof ExternalAssignmentProviderCompat)) { - try { - final Method m = provider.getClass().getDeclaredMethod("getAllExternalAssignments", String.class); - if (m.getReturnType().equals(List.class)) { - this.providerMethods.put(provider, m); - } - } catch (final Exception e) { - log.warn("ExternalAssignmentProvider [" + provider.getAppKey() + " / " + provider.getClass().toString() - + "] does not implement getAllExternalAssignments. It will not be able to exclude items from student views/grades. " - + "See the ExternalAssignmentProviderCompat interface and SAK-23733 for details."); - } - } - } - } - - @Override - public void unregisterExternalAssignmentProvider(final String providerAppKey) { - if (providerAppKey == null || "".equals(providerAppKey)) { - throw new IllegalArgumentException("providerAppKey must be set"); - } else if (getExternalAssignmentProviders().containsKey(providerAppKey)) { - final ExternalAssignmentProvider provider = getExternalAssignmentProviders().get(providerAppKey); - this.providerMethods.remove(provider); - getExternalAssignmentProviders().remove(providerAppKey); - } - } - - public void init() { - log.debug("INIT"); - } - - public void destroy() { - log.debug("DESTROY"); - if (this.externalProviders != null) { - this.externalProviders.clear(); - this.externalProviders = null; - } - } - - @Override - public synchronized void addExternalAssessment(final String gradebookUid, final String externalId, final String externalUrl, - final String title, final double points, final Date dueDate, final String externalServiceDescription, String externalData) - throws ConflictingAssignmentNameException, ConflictingExternalIdException, GradebookNotFoundException { - - // Ensure that the required strings are not empty - if (StringUtils.trimToNull(externalServiceDescription) == null || - StringUtils.trimToNull(externalId) == null || - StringUtils.trimToNull(title) == null) { - throw new RuntimeException("External service description, externalId, and title must not be empty"); - } - - // Ensure that points is > zero - if (points <= 0) { - throw new AssignmentHasIllegalPointsException("Points must be > 0"); - } - - // Ensure that the assessment name is unique within this gradebook - if (isAssignmentDefined(gradebookUid, title)) { - throw new ConflictingAssignmentNameException("An assignment with that name already exists in gradebook uid=" + gradebookUid); - } - - // name cannot contain these chars as they are reserved for special columns in import/export - GradebookHelper.validateGradeItemName(title); - - getHibernateTemplate().execute(session -> { - // Ensure that the externalId is unique within this gradebook - final Number externalIdConflicts = (Number) session.createCriteria(GradebookAssignment.class) - .createAlias("gradebook", "g") - .add(Restrictions.eq("externalId", externalId)) - .add(Restrictions.eq("g.uid", gradebookUid)) - .setProjection(Projections.rowCount()) - .uniqueResult(); - - if (externalIdConflicts.intValue() > 0) { - throw new ConflictingExternalIdException( - "An external assessment with ID=" + externalId + " already exists in gradebook uid=" + gradebookUid); - } - - // Get the gradebook - final Gradebook gradebook = getGradebook(gradebookUid); - - // Create the external assignment - final GradebookAssignment asn = new GradebookAssignment(gradebook, title, Double.valueOf(points), dueDate); - asn.setExternallyMaintained(true); - asn.setExternalId(externalId); - asn.setExternalInstructorLink(externalUrl); - asn.setExternalStudentLink(externalUrl); - asn.setExternalAppName(externalServiceDescription); - asn.setExternalData(externalData); - // set released to be true to support selective release - asn.setReleased(true); - asn.setUngraded(false); - - session.save(asn); - return null; - }); - log.info("External assessment added to gradebookUid={}, externalId={} by userUid={} from externalApp={}", gradebookUid, externalId, - getUserUid(), externalServiceDescription); - } - - @Override - public void updateExternalAssessment(final String gradebookUid, final String externalId, final String externalUrl, - String externalData, final String title, final double points, final Date dueDate) - throws GradebookNotFoundException, AssessmentNotFoundException, AssignmentHasIllegalPointsException { - final GradebookAssignment asn = getExternalAssignment(gradebookUid, externalId); - - if (asn == null) { - throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); - } - - // Ensure that points is > zero - if (points <= 0) { - throw new AssignmentHasIllegalPointsException("Points must be > 0"); - } - - // Ensure that the required strings are not empty - if (StringUtils.trimToNull(externalId) == null || - StringUtils.trimToNull(title) == null) { - throw new RuntimeException("ExternalId, and title must not be empty"); - } - - // name cannot contain these chars as they are reserved for special columns in import/export - GradebookHelper.validateGradeItemName(title); - - final HibernateCallback hc = new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - asn.setExternalInstructorLink(externalUrl); - asn.setExternalStudentLink(externalUrl); - asn.setExternalData(externalData); - asn.setName(title); - asn.setDueDate(dueDate); - // support selective release - asn.setReleased(BooleanUtils.isTrue(asn.getReleased())); - asn.setPointsPossible(Double.valueOf(points)); - session.update(asn); - log.info("External assessment updated in gradebookUid={}, externalId={} by userUid={}", gradebookUid, externalId, - getUserUid()); - return null; - } - }; - getHibernateTemplate().execute(hc); - } - - /** - * @see org.sakaiproject.service.gradebook.shared.GradebookService#removeExternalAssessment(java.lang.String, java.lang.String) - */ - @Override - public void removeExternalAssessment(final String gradebookUid, - final String externalId) throws GradebookNotFoundException, AssessmentNotFoundException { - // Get the external assignment - final GradebookAssignment asn = getExternalAssignment(gradebookUid, externalId); - if (asn == null) { - throw new AssessmentNotFoundException("There is no external assessment id=" + externalId + " in gradebook uid=" + gradebookUid); - } - - // We need to go through Spring's HibernateTemplate to do - // any deletions at present. See the comments to deleteGradebook - // for the details. - final HibernateTemplate hibTempl = getHibernateTemplate(); - - hibTempl.execute((HibernateCallback) session -> { - int numDeleted = session.createQuery("delete GradingEvent where gradableObject=:go").setParameter("go", asn).executeUpdate(); - log.debug("Deleted {} records from gb_grading_event_t", numDeleted); - - numDeleted = session.createQuery("delete AssignmentGradeRecord where gradableObject=:go").setParameter("go", asn) - .executeUpdate(); - log.info("Deleted {} externally defined scores", numDeleted); - - numDeleted = session.createQuery("delete org.sakaiproject.tool.gradebook.Comment where gradableObject=:go").setParameter("go", asn).executeUpdate(); - log.info("Deleted {} externally defined comments", numDeleted); - return null; - }); - - // Delete the assessment. - hibTempl.delete(asn); - - log.info("External assessment removed from gradebookUid={}, externalId={} by userUid={}", gradebookUid, externalId, getUserUid()); - } - - private GradebookAssignment getExternalAssignment(final String gradebookUid, final String externalId) - throws GradebookNotFoundException { - final Gradebook gradebook = getGradebook(gradebookUid); - - final HibernateCallback hc = session -> (GradebookAssignment) session - .createQuery("from GradebookAssignment as asn where asn.gradebook = :gradebook and asn.externalId = :externalid") - .setParameter("gradebook", gradebook) - .setParameter("externalid", externalId) - .uniqueResult(); - return getHibernateTemplate().execute(hc); - } - - @Override - public void updateExternalAssessmentComments(final String gradebookUid, final String externalId, - final Map studentUidsToComments) throws GradebookNotFoundException, AssessmentNotFoundException { - - final GradebookAssignment asn = getExternalAssignment(gradebookUid, externalId); - if (asn == null) { - throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); - } - final Set studentIds = studentUidsToComments.keySet(); - if (studentIds.isEmpty()) { - return; - } - - getHibernateTemplate().execute(session -> { - @SuppressWarnings("unchecked") - final List existingScores = session.createCriteria(AssignmentGradeRecord.class) - .add(Restrictions.eq("gradableObject", asn)) - .add(HibernateCriterionUtils.CriterionInRestrictionSplitter("studentId", studentIds)) - .list(); - - final Set changedStudents = new HashSet<>(); - for (final AssignmentGradeRecord agr : existingScores) { - final String studentUid = agr.getStudentId(); - - // Try to reduce data contention by only updating when a score - // has changed or property has been set forcing a db update every time. - final boolean alwaysUpdate = isUpdateSameScore(); - - final CommentDefinition gradeComment = getAssignmentScoreComment(gradebookUid, asn.getId(), studentUid); - final String oldComment = gradeComment != null ? gradeComment.getCommentText() : null; - final String newComment = studentUidsToComments.get(studentUid); - - if (alwaysUpdate || (newComment != null && !newComment.equals(oldComment)) || (newComment == null && oldComment != null)) { - changedStudents.add(studentUid); - setAssignmentScoreComment(gradebookUid, asn.getId(), studentUid, newComment); - } - } - - log.debug("updateExternalAssessmentScores sent {} records, actually changed {}", studentIds.size(), changedStudents.size()); - - // Sync database. - return null; - }); - } - - @Override - public void updateExternalAssessmentScores(final String gradebookUid, final String externalId, - final Map studentUidsToScores) throws GradebookNotFoundException, AssessmentNotFoundException { - - final GradebookAssignment assignment = getExternalAssignment(gradebookUid, externalId); - if (assignment == null) { - throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); - } - final Set studentIds = studentUidsToScores.keySet(); - if (studentIds.isEmpty()) { - return; - } - final Date now = new Date(); - final String graderId = getUserUid(); - - getHibernateTemplate().execute(session -> { - @SuppressWarnings("unchecked") - final List existingScores = session.createCriteria(AssignmentGradeRecord.class) - .add(Restrictions.eq("gradableObject", assignment)) - .add(HibernateCriterionUtils.CriterionInRestrictionSplitter("studentId", studentIds)) - .list(); - - final Set previouslyUnscoredStudents = new HashSet<>(studentIds); - final Set changedStudents = new HashSet<>(); - for (final AssignmentGradeRecord agr : existingScores) { - final String studentUid = agr.getStudentId(); - previouslyUnscoredStudents.remove(studentUid); - - // Try to reduce data contention by only updating when a score - // has changed or property has been set forcing a db update every time. - final boolean alwaysUpdate = isUpdateSameScore(); - - final Double oldPointsEarned = agr.getPointsEarned(); - final Double newPointsEarned = studentUidsToScores.get(studentUid); - if (alwaysUpdate || (newPointsEarned != null && !newPointsEarned.equals(oldPointsEarned)) - || (newPointsEarned == null && oldPointsEarned != null)) { - agr.setDateRecorded(now); - agr.setGraderId(graderId); - agr.setPointsEarned(newPointsEarned); - session.update(agr); - changedStudents.add(studentUid); - postUpdateGradeEvent(gradebookUid, assignment.getName(), studentUid, newPointsEarned); - } - } - for (final String studentUid : previouslyUnscoredStudents) { - // Don't save unnecessary null scores. - final Double newPointsEarned = studentUidsToScores.get(studentUid); - if (newPointsEarned != null) { - final AssignmentGradeRecord agr = new AssignmentGradeRecord(assignment, studentUid, newPointsEarned); - agr.setDateRecorded(now); - agr.setGraderId(graderId); - session.save(agr); - changedStudents.add(studentUid); - postUpdateGradeEvent(gradebookUid, assignment.getName(), studentUid, newPointsEarned); - } - } - - log.debug("updateExternalAssessmentScores sent {} records, actually changed {}", studentIds.size(), changedStudents.size()); - - // Sync database. - return null; - }); - } - - @Override - public void updateExternalAssessmentScoresString(final String gradebookUid, final String externalId, - final Map studentUidsToScores) throws GradebookNotFoundException, AssessmentNotFoundException { - - final GradebookAssignment assignment = getExternalAssignment(gradebookUid, externalId); - if (assignment == null) { - throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); - } - final Set studentIds = studentUidsToScores.keySet(); - if (studentIds.isEmpty()) { - return; - } - final Date now = new Date(); - final String graderId = getUserUid(); - - getHibernateTemplate().execute(session -> { - - @SuppressWarnings("unchecked") - final List existingScores = session.createCriteria(AssignmentGradeRecord.class) - .add(Restrictions.eq("gradableObject", assignment)) - .add(HibernateCriterionUtils.CriterionInRestrictionSplitter("studentId", studentIds)) - .list(); - - final Set previouslyUnscoredStudents = new HashSet<>(studentIds); - final Set changedStudents = new HashSet<>(); - for (final AssignmentGradeRecord agr : existingScores) { - final String studentUid = agr.getStudentId(); - previouslyUnscoredStudents.remove(studentUid); - - // Try to reduce data contention by only updating when a score - // has changed or property has been set forcing a db update every time. - final boolean alwaysUpdate = isUpdateSameScore(); - - // TODO: for ungraded items, needs to set ungraded-grades later... - final Double oldPointsEarned = agr.getPointsEarned(); - final String newPointsEarnedString = studentUidsToScores.get(studentUid); - final Double newPointsEarned = (newPointsEarnedString == null) ? null : convertStringToDouble(newPointsEarnedString); - if (alwaysUpdate || (newPointsEarned != null && !newPointsEarned.equals(oldPointsEarned)) - || (newPointsEarned == null && oldPointsEarned != null)) { - agr.setDateRecorded(now); - agr.setGraderId(graderId); - if (newPointsEarned != null) { - agr.setPointsEarned(newPointsEarned); - } else { - agr.setPointsEarned(null); - } - session.update(agr); - changedStudents.add(studentUid); - postUpdateGradeEvent(gradebookUid, assignment.getName(), studentUid, newPointsEarned); - } - } - for (final String studentUid : previouslyUnscoredStudents) { - // Don't save unnecessary null scores. - final String newPointsEarned = studentUidsToScores.get(studentUid); - if (newPointsEarned != null) { - final AssignmentGradeRecord agr = new AssignmentGradeRecord(assignment, studentUid, - convertStringToDouble(newPointsEarned)); - agr.setDateRecorded(now); - agr.setGraderId(graderId); - session.save(agr); - changedStudents.add(studentUid); - postUpdateGradeEvent(gradebookUid, assignment.getName(), studentUid, convertStringToDouble(newPointsEarned)); - } - } - - log.debug("updateExternalAssessmentScores sent {} records, actually changed {}", studentIds.size(), changedStudents.size()); - - // Sync database. - return null; - }); - } - - @Override - public boolean isAssignmentDefined(final String gradebookUid, final String assignmentName) - throws GradebookNotFoundException { - final GradebookAssignment assignment = (GradebookAssignment) getHibernateTemplate() - .execute((HibernateCallback) session -> getAssignmentWithoutStats(gradebookUid, assignmentName)); - return (assignment != null); - } - - @Override - public boolean isExternalAssignmentDefined(final String gradebookUid, final String externalId) throws GradebookNotFoundException { - // SAK-19668 - final GradebookAssignment assignment = getExternalAssignment(gradebookUid, externalId); - return (assignment != null); - } - - @Override - public boolean isExternalAssignmentGrouped(final String gradebookUid, final String externalId) - throws GradebookNotFoundException { - // SAK-19668 - final GradebookAssignment assignment = getExternalAssignment(gradebookUid, externalId); - // If we check all available providers for an existing, externally maintained assignment - // and none manage it, return false since grouping is the outlier case and all items - // showed for all users until the 2.9 release. - boolean result = false; - boolean providerResponded = false; - if (assignment == null) { - log.info("No assignment found for external assignment check: gradebookUid=" + gradebookUid + ", externalId=" + externalId); - } else { - for (final ExternalAssignmentProvider provider : getExternalAssignmentProviders().values()) { - if (provider.isAssignmentDefined(assignment.getExternalAppName(), externalId)) { - providerResponded = true; - result = result || provider.isAssignmentGrouped(externalId); - } - } - } - return result || !providerResponded; - } - - @Override - public boolean isExternalAssignmentVisible(final String gradebookUid, final String externalId, final String userId) - throws GradebookNotFoundException { - // SAK-19668 - final GradebookAssignment assignment = getExternalAssignment(gradebookUid, externalId); - // If we check all available providers for an existing, externally maintained assignment - // and none manage it, assume that it should be visible. This matches the pre-2.9 behavior - // when a provider is not implemented to handle the assignment. Also, any provider that - // returns true will allow access (logical OR of responses). - boolean result = false; - boolean providerResponded = false; - if (assignment == null) { - log.info("No assignment found for external assignment check: gradebookUid=" + gradebookUid + ", externalId=" + externalId); - } else { - for (final ExternalAssignmentProvider provider : getExternalAssignmentProviders().values()) { - if (provider.isAssignmentDefined(assignment.getExternalAppName(), externalId)) { - providerResponded = true; - result = result || provider.isAssignmentVisible(externalId, userId); - } - } - } - return result || !providerResponded; - } - - @Override - public Map getExternalAssignmentsForCurrentUser(final String gradebookUid) - throws GradebookNotFoundException { - - final Map visibleAssignments = new HashMap<>(); - final Set providedAssignments = getProvidedExternalAssignments(gradebookUid); - - for (final ExternalAssignmentProvider provider : getExternalAssignmentProviders().values()) { - final String appKey = provider.getAppKey(); - final List assignments = provider.getExternalAssignmentsForCurrentUser(gradebookUid); - for (final String externalId : assignments) { - visibleAssignments.put(externalId, appKey); - } - } - - // We include those items that the gradebook has marked as externally maintained, but no provider has - // identified as items under its authority. This maintains the behavior prior to the grouping support - // introduced for the 2.9 release (SAK-11485 and SAK-19688), where a tool that does not have a provider - // implemented does not have its items filtered for student views and grading. - final List gbAssignments = getGradebookService() - .getViewableAssignmentsForCurrentUser(gradebookUid); - for (final org.sakaiproject.service.gradebook.shared.Assignment assignment : gbAssignments) { - final String id = assignment.getExternalId(); - if (assignment.isExternallyMaintained() && !providedAssignments.contains(id) && !visibleAssignments.containsKey(id)) { - log.debug("External assignment in gradebook [{}] is not handled by a provider; ID: {}", gradebookUid, id); - visibleAssignments.put(id, null); - } - } - - return visibleAssignments; - } - - protected Set getProvidedExternalAssignments(final String gradebookUid) { - final Set allAssignments = new HashSet<>(); - for (final ExternalAssignmentProvider provider : getExternalAssignmentProviders().values()) { - // TODO: This is a temporary cast; if this method proves to be the right fit - // and perform well enough, it will be moved to the regular interface. - if (provider instanceof ExternalAssignmentProviderCompat) { - allAssignments.addAll( - ((ExternalAssignmentProviderCompat) provider).getAllExternalAssignments(gradebookUid)); - } else if (this.providerMethods.containsKey(provider)) { - final Method m = this.providerMethods.get(provider); - try { - @SuppressWarnings("unchecked") - final List reflectedAssignments = (List) m.invoke(provider, gradebookUid); - allAssignments.addAll(reflectedAssignments); - } catch (final Exception e) { - log.debug("Exception calling getAllExternalAssignments", e); - } - } - } - return allAssignments; - } - - @Override - public Map> getVisibleExternalAssignments(final String gradebookUid, final Collection studentIds) - throws GradebookNotFoundException { - - final Set providedAssignments = getProvidedExternalAssignments(gradebookUid); - - final Map> visible = new HashMap<>(); - for (final String studentId : studentIds) { - visible.put(studentId, new HashSet()); - } - - for (final ExternalAssignmentProvider provider : getExternalAssignmentProviders().values()) { - // SAK-24407 - Some tools modify this set so we can't pass it. I considered making it an unmodifableCollection but that would - // require changing a number of tools - final Set studentIdsCopy = new HashSet<>(studentIds); - final Map> externals = provider.getAllExternalAssignments(gradebookUid, (studentIdsCopy)); - for (final String studentId : externals.keySet()) { - if (visible.containsKey(studentId)) { - visible.get(studentId).addAll(externals.get(studentId)); - } - } - } - - // SAK-23733 - This covers a tricky case where items that the gradebook thinks are external - // but are not reported by any provider should be included for everyone. This is - // to accommodate tools that use the external assessment mechanisms but have not - // implemented an ExternalAssignmentProvider. - final List allAssignments = getGradebookService() - .getViewableAssignmentsForCurrentUser(gradebookUid); - for (final org.sakaiproject.service.gradebook.shared.Assignment assignment : allAssignments) { - final String id = assignment.getExternalId(); - if (assignment.isExternallyMaintained() && !providedAssignments.contains(id)) { - for (final String studentId : visible.keySet()) { - visible.get(studentId).add(id); - } - } - } - - final Map> visibleList = new HashMap<>(); - for (final String studentId : visible.keySet()) { - visibleList.put(studentId, new ArrayList(visible.get(studentId))); - } - return visibleList; - } - - @Override - public void setExternalAssessmentToGradebookAssignment(final String gradebookUid, final String externalId) { - final GradebookAssignment assignment = getExternalAssignment(gradebookUid, externalId); - if (assignment == null) { - throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); - } - assignment.setExternalAppName(null); - assignment.setExternalId(null); - assignment.setExternalInstructorLink(null); - assignment.setExternalStudentLink(null); - assignment.setExternalData(null); - assignment.setExternallyMaintained(false); - getHibernateTemplate().execute((HibernateCallback) session -> { - session.update(assignment); - log.info("Externally-managed assignment {} moved to Gradebook management in gradebookUid={} by userUid={}", externalId, - gradebookUid, getUserUid()); - return null; - }); - } - - /** - * Wrapper created when category was added for assignments tool - */ - @Override - public void addExternalAssessment(final String gradebookUid, final String externalId, final String externalUrl, final String title, final Double points, - final Date dueDate, final String externalServiceDescription, String externalData, final Boolean ungraded) - throws GradebookNotFoundException, ConflictingAssignmentNameException, ConflictingExternalIdException, AssignmentHasIllegalPointsException { - addExternalAssessment(gradebookUid, externalId, externalUrl, title, points, dueDate, externalServiceDescription, externalData, ungraded, null); - } - - @Override - public synchronized void addExternalAssessment(final String gradebookUid, final String externalId, final String externalUrl, final String title, final Double points, - final Date dueDate, final String externalServiceDescription, String externalData, final Boolean ungraded, final Long categoryId) - throws GradebookNotFoundException, ConflictingAssignmentNameException, ConflictingExternalIdException, AssignmentHasIllegalPointsException { - // Ensure that the required strings are not empty - if (StringUtils.trimToNull(externalServiceDescription) == null || - StringUtils.trimToNull(externalId) == null || - StringUtils.trimToNull(title) == null) { - throw new RuntimeException("External service description, externalId, and title must not be empty"); - } - - // Ensure that points is > zero - if ((ungraded != null && !ungraded.booleanValue() && (points == null || points.doubleValue() <= 0)) - || (ungraded == null && (points == null || points.doubleValue() <= 0))) { - throw new AssignmentHasIllegalPointsException("Points can't be null or Points must be > 0"); - } - - // Ensure that the assessment name is unique within this gradebook - if (isAssignmentDefined(gradebookUid, title)) { - throw new ConflictingAssignmentNameException("An assignment with that name already exists in gradebook uid=" + gradebookUid); - } - - // name cannot contain these chars as they are reserved for special columns in import/export - GradebookHelper.validateGradeItemName(title); - - getHibernateTemplate().execute(session -> { - // Ensure that the externalId is unique within this gradebook - final Number externalIdConflicts = (Number) session.createCriteria(GradebookAssignment.class) - .createAlias("gradebook", "g") - .add(Restrictions.eq("externalId", externalId)) - .add(Restrictions.eq("g.uid", gradebookUid)) - .setProjection(Projections.rowCount()) - .uniqueResult(); - if (externalIdConflicts.intValue() > 0) { - throw new ConflictingExternalIdException( - "An external assessment with that ID already exists in gradebook uid=" + gradebookUid); - } - - // Get the gradebook - final Gradebook gradebook = getGradebook(gradebookUid); - - // if a category was indicated, double check that it is valid - Category persistedCategory = null; - if (categoryId != null) { - persistedCategory = getCategory(categoryId); - if (persistedCategory.isDropScores() && !persistedCategory.isEqualWeightAssignments()) { - List thisCategoryAssignments = getAssignmentsForCategory(categoryId); - for (GradebookAssignment thisAssignment : thisCategoryAssignments) { - if (!Objects.equals(thisAssignment.getPointsPossible(), points)) { - String errorMessage = "Assignment points mismatch the selected Gradebook Category (" - + thisAssignment.getPointsPossible().toString() + ") and cannot be added to Gradebook )"; - throw new InvalidCategoryException(errorMessage); - } - } - } - if (persistedCategory == null || persistedCategory.isRemoved() || - !persistedCategory.getGradebook().getId().equals(gradebook.getId())) { - throw new InvalidCategoryException("The category with id " + categoryId + - " is not valid for gradebook " + gradebook.getUid()); - } - } - - // Create the external assignment - final GradebookAssignment asn = new GradebookAssignment(gradebook, title, points, dueDate); - asn.setExternallyMaintained(true); - asn.setExternalId(externalId); - asn.setExternalInstructorLink(externalUrl); - asn.setExternalStudentLink(externalUrl); - asn.setExternalAppName(externalServiceDescription); - asn.setExternalData(externalData); - if (persistedCategory != null) { - asn.setCategory(persistedCategory); - } - // set released to be true to support selective release - asn.setReleased(true); - if (ungraded != null) { - asn.setUngraded(ungraded); - } else { - asn.setUngraded(false); - } - - session.save(asn); - return null; - }); - log.info("External assessment added to gradebookUid={}, externalId={} by userUid={} from externalApp={}", gradebookUid, externalId, - getUserUid(), externalServiceDescription); - } - - @Override - public void updateExternalAssessment(final String gradebookUid, final String externalId, final String externalUrl, String externalData, final String title, - final Double points, final Date dueDate, final Boolean ungraded) - throws GradebookNotFoundException, AssessmentNotFoundException, ConflictingAssignmentNameException, AssignmentHasIllegalPointsException { - final GradebookAssignment asn = getExternalAssignment(gradebookUid, externalId); - - if (asn == null) { - throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); - } - - // Ensure that points is > zero - if ((ungraded != null && !ungraded.booleanValue() && (points == null || points.doubleValue() <= 0)) - || (ungraded == null && (points == null || points.doubleValue() <= 0))) { - throw new AssignmentHasIllegalPointsException("Points can't be null or Points must be > 0"); - } - - // Ensure that the required strings are not empty - if (StringUtils.trimToNull(externalId) == null || - StringUtils.trimToNull(title) == null) { - throw new RuntimeException("ExternalId, and title must not be empty"); - } - - // name cannot contain these chars as they are reserved for special columns in import/export - GradebookHelper.validateGradeItemName(title); - - final HibernateCallback hc = session -> { - asn.setExternalInstructorLink(externalUrl); - asn.setExternalStudentLink(externalUrl); - asn.setExternalData(externalData); - asn.setName(title); - asn.setDueDate(dueDate); - // support selective release - asn.setReleased(BooleanUtils.isTrue(asn.getReleased())); - asn.setPointsPossible(points); - if (ungraded != null) { - asn.setUngraded(ungraded.booleanValue()); - } else { - asn.setUngraded(false); - } - session.update(asn); - log.info("External assessment updated in gradebookUid={}, externalId={} by userUid={}", gradebookUid, externalId, getUserUid()); - return null; - - }; - getHibernateTemplate().execute(hc); - } - - @Override - public void updateExternalAssessmentComment(final String gradebookUid, final String externalId, final String studentUid, - final String comment) - throws GradebookNotFoundException, AssessmentNotFoundException { - final GradebookAssignment asn = getExternalAssignment(gradebookUid, externalId); - - if (asn == null) { - throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); - } - - log.debug("BEGIN: Update 1 score for gradebookUid={}, external assessment={} from {}", gradebookUid, externalId, - asn.getExternalAppName()); - - final HibernateCallback hc = session -> { - // Try to reduce data contention by only updating when the - // score has actually changed or property has been set forcing a db update every time. - final boolean alwaysUpdate = isUpdateSameScore(); - - final CommentDefinition gradeComment = getAssignmentScoreComment(gradebookUid, asn.getId(), studentUid); - final String oldComment = gradeComment != null ? gradeComment.getCommentText() : null; - - if (alwaysUpdate || (comment != null && !comment.equals(oldComment)) || - (comment == null && oldComment != null)) { - if (comment != null) { - setAssignmentScoreComment(gradebookUid, asn.getId(), studentUid, comment); - } else { - setAssignmentScoreComment(gradebookUid, asn.getId(), studentUid, null); - } - log.debug("updateExternalAssessmentComment: grade record saved"); - } else { - log.debug("Ignoring updateExternalAssessmentComment, since the new comment is the same as the old"); - } - return null; - }; - getHibernateTemplate().execute(hc); - log.debug("END: Update 1 score for gradebookUid={}, external assessment={} from {}", gradebookUid, externalId, - asn.getExternalAppName()); - log.debug("External assessment comment updated in gradebookUid={}, externalId={} by userUid={}, new score={}", gradebookUid, - externalId, getUserUid(), comment); - } - - @Override - public void updateExternalAssessmentScore(final String gradebookUid, final String externalId, final String studentUid, - final String points) - throws GradebookNotFoundException, AssessmentNotFoundException { - final GradebookAssignment asn = getExternalAssignment(gradebookUid, externalId); - - if (asn == null) { - throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); - } - - log.debug("BEGIN: Update 1 score for gradebookUid={}, external assessment={} from {}", gradebookUid, externalId, - asn.getExternalAppName()); - - final HibernateCallback hc = session -> { - final Date now = new Date(); - - AssignmentGradeRecord agr = getAssignmentGradeRecord(asn, studentUid); - - // Try to reduce data contention by only updating when the - // score has actually changed or property has been set forcing a db update every time. - final boolean alwaysUpdate = isUpdateSameScore(); - - // TODO: for ungraded items, needs to set ungraded-grades later... - final Double oldPointsEarned = (agr == null) ? null : agr.getPointsEarned(); - final Double newPointsEarned = (points == null) ? null : convertStringToDouble(points); - if (alwaysUpdate || (newPointsEarned != null && !newPointsEarned.equals(oldPointsEarned)) || - (newPointsEarned == null && oldPointsEarned != null)) { - if (agr == null) { - if (newPointsEarned != null) { - agr = new AssignmentGradeRecord(asn, studentUid, Double.valueOf(newPointsEarned)); - } else { - agr = new AssignmentGradeRecord(asn, studentUid, null); - } - } else { - if (newPointsEarned != null) { - agr.setPointsEarned(Double.valueOf(newPointsEarned)); - } else { - agr.setPointsEarned(null); - } - } - - agr.setDateRecorded(now); - agr.setGraderId(getUserUid()); - log.debug("About to save AssignmentGradeRecord id={}, version={}, studenttId={}, pointsEarned={}", agr.getId(), - agr.getVersion(), agr.getStudentId(), agr.getPointsEarned()); - session.saveOrUpdate(agr); - - // Sync database. - postUpdateGradeEvent(gradebookUid, asn.getName(), studentUid, newPointsEarned); - } else { - log.debug("Ignoring updateExternalAssessmentScore, since the new points value is the same as the old"); - } - return null; - }; - getHibernateTemplate().execute(hc); - log.debug("END: Update 1 score for gradebookUid={}, external assessment={} from {}", gradebookUid, externalId, - asn.getExternalAppName()); - log.debug("External assessment score updated in gradebookUid={}, externalId={} by userUid={}, new score={}", gradebookUid, - externalId, getUserUid(), points); - } - - private void postUpdateGradeEvent(final String gradebookUid, final String assignmentName, final String studentUid, - final Double pointsEarned) { - postEvent("gradebook.updateItemScore", - "/gradebook/" + gradebookUid + "/" + assignmentName + "/" + studentUid + "/" + pointsEarned + "/student"); - } - - /** - * - * @param s the string we want to convert to a double - * @return a locale-aware Double value representation of the given String - * @throws ParseException - */ - private Double convertStringToDouble(final String s) { - Double scoreAsDouble = null; - String doubleAsString = s; - if (doubleAsString != null && !"".equals(doubleAsString)) { - try { - // check if grade uses a comma as separator because of number format and change to a comma y the external app sends a point - // as separator - final DecimalFormat dcformat = (DecimalFormat) getNumberFormat(); - final String decSeparator = dcformat.getDecimalFormatSymbols().getDecimalSeparator() + ""; - if (",".equals(decSeparator)) { - doubleAsString = doubleAsString.replace(".", ","); - } - final Number numericScore = getNumberFormat().parse(doubleAsString.trim()); - scoreAsDouble = numericScore.doubleValue(); - } catch (final ParseException e) { - log.error(e.getMessage()); - } - } - - return scoreAsDouble; - } - - private NumberFormat getNumberFormat() { - return NumberFormat.getInstance(new ResourceLoader().getLocale()); - } - - @Override - public Long getExternalAssessmentCategoryId(final String gradebookUId, final String externalId) { - Long categoryId = null; - final GradebookAssignment assignment = getExternalAssignment(gradebookUId, externalId); - if (assignment == null) { - throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUId); - } - if (assignment.getCategory() != null) { - categoryId = assignment.getCategory().getId(); - } - return categoryId; - } - - /** - * Determines whether to update a grade record when there have been no changes. This is useful when we need to update only - * gb_grade_record_t's 'DATE_RECORDED' field for instance. Generally uses the sakai.property - * 'gradebook.externalAssessments.updateSameScore', but a site property by the same name can override it. That is to say, the site - * property is checked first, and if it is not present, the sakai.property is used. - */ - private boolean isUpdateSameScore() { - String siteProperty = null; - try { - final String siteId = this.toolManager.getCurrentPlacement().getContext(); - final Site site = this.siteService.getSite(siteId); - siteProperty = site.getProperties().getProperty(UPDATE_SAME_SCORE_PROP); - } catch (final Exception e) { - // Can't access site property. Leave it set to null - } - - // Site property override not set. Use setting in sakai.properties - if (siteProperty == null) { - return this.serverConfigurationService.getBoolean(UPDATE_SAME_SCORE_PROP, UPDATE_SAME_SCORE_PROP_DEFAULT); - } - - return Boolean.TRUE.toString().equals(siteProperty); - } - - @Override - public boolean isCategoriesEnabled(String gradebookUid) { - final Gradebook gradebook = getGradebook(gradebookUid); - return gradebook.getCategory_type() != GradebookService.CATEGORY_TYPE_NO_CATEGORY; - } - - @Override - public OptionalLong getInternalAssessmentID(final String gradebookUUID, final String externalID) throws GradebookNotFoundException, AssessmentNotFoundException { - - final GradebookAssignment asn = getExternalAssignment(gradebookUUID, externalID); - if (asn == null) { - throw new AssessmentNotFoundException("There is no assessment id=" + gradebookUUID + " in gradebook uid=" + externalID); - } - return asn == null || asn.getId() == null ? OptionalLong.empty() : OptionalLong.of(asn.getId()); - } -} diff --git a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookFrameworkServiceImpl.java b/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookFrameworkServiceImpl.java deleted file mode 100644 index f20f000a60d8..000000000000 --- a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookFrameworkServiceImpl.java +++ /dev/null @@ -1,445 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.component.gradebook; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hibernate.HibernateException; -import org.hibernate.query.Query; -import org.hibernate.Session; -import org.sakaiproject.service.gradebook.shared.GradebookExistsException; -import org.sakaiproject.service.gradebook.shared.GradebookFrameworkService; -import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException; -import org.sakaiproject.service.gradebook.shared.GradebookService; -import org.sakaiproject.service.gradebook.shared.GradingScaleDefinition; -import org.sakaiproject.tool.gradebook.CourseGrade; -import org.sakaiproject.tool.gradebook.GradeMapping; -import org.sakaiproject.tool.gradebook.GradePointsMapping; -import org.sakaiproject.tool.gradebook.Gradebook; -import org.sakaiproject.tool.gradebook.GradingScale; -import org.sakaiproject.tool.gradebook.LetterGradeMapping; -import org.sakaiproject.tool.gradebook.LetterGradePercentMapping; -import org.sakaiproject.tool.gradebook.LetterGradePlusMinusMapping; -import org.sakaiproject.tool.gradebook.PassNotPassMapping; -import org.springframework.orm.hibernate5.HibernateCallback; -import org.springframework.orm.hibernate5.HibernateTemplate; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class GradebookFrameworkServiceImpl extends BaseHibernateManager implements GradebookFrameworkService { - - public static final String UID_OF_DEFAULT_GRADING_SCALE_PROPERTY = "uidOfDefaultGradingScale"; - - public static final String PROP_COURSE_POINTS_DISPLAYED = "gradebook.coursepoints.displayed"; - public static final String PROP_COURSE_GRADE_DISPLAYED = "gradebook.coursegrade.displayed"; - public static final String PROP_ASSIGNMENTS_DISPLAYED = "gradebook.assignments.displayed"; - public static final String PROP_ASSIGNMENT_STATS_DISPLAYED = "gradebook.stats.assignments.displayed"; - public static final String PROP_COURSE_GRADE_STATS_DISPLAYED = "gradebook.stats.coursegrade.displayed"; - - @Override - public void addGradebook(final String uid, final String name) { - if(isGradebookDefined(uid)) { - log.warn("You can not add a gradebook with uid={}. That gradebook already exists.", uid); - throw new GradebookExistsException("You can not add a gradebook with uid=" + uid + ". That gradebook already exists."); - } - if (log.isDebugEnabled()) { - log.debug("Adding gradebook uid={} by userUid={}", uid, getUserUid()); - } - - createDefaultLetterGradeMapping(getHardDefaultLetterMapping()); - - getHibernateTemplate().execute((HibernateCallback) session -> { - // Get available grade mapping templates. - List gradingScales = session - .createQuery("from GradingScale as gradingScale where gradingScale.unavailable=false").list(); - - // The application won't be able to run without grade mapping - // templates, so if for some reason none have been defined yet, - // do that now. - if (gradingScales.isEmpty()) { - if (log.isInfoEnabled()) { - log.info("No Grading Scale defined yet. This is probably because you have upgraded or you are working with a new database. Default grading scales will be created. Any customized system-wide grade mappings you may have defined in previous versions will have to be reconfigured."); - } - gradingScales = GradebookFrameworkServiceImpl.this.addDefaultGradingScales(session); - } - - // Create and save the gradebook - final Gradebook gradebook = new Gradebook(); - gradebook.setName(name); - gradebook.setUid(uid); - session.save(gradebook); - - // Create the course grade for the gradebook - final CourseGrade cg = new CourseGrade(); - cg.setGradebook(gradebook); - session.save(cg); - - // According to the specification, Display GradebookAssignment Grades is - // on by default, and Display course grade is off. But can be overridden via properties - - - final Boolean propAssignmentsDisplayed = this.serverConfigurationService.getBoolean(PROP_ASSIGNMENTS_DISPLAYED,true); - gradebook.setAssignmentsDisplayed(propAssignmentsDisplayed); - - final Boolean propCourseGradeDisplayed = this.serverConfigurationService.getBoolean(PROP_COURSE_GRADE_DISPLAYED,false); - gradebook.setCourseGradeDisplayed(propCourseGradeDisplayed); - - final Boolean propCoursePointsDisplayed = this.serverConfigurationService.getBoolean(PROP_COURSE_POINTS_DISPLAYED,false); - gradebook.setCoursePointsDisplayed(propCoursePointsDisplayed); - - final String defaultScaleUid = GradebookFrameworkServiceImpl.this.getPropertyValue(UID_OF_DEFAULT_GRADING_SCALE_PROPERTY); - - // Add and save grade mappings based on the templates. - GradeMapping defaultGradeMapping = null; - final Set gradeMappings = new HashSet<>(); - for (final GradingScale gradingScale : gradingScales) { - final GradeMapping gradeMapping = new GradeMapping(gradingScale); - gradeMapping.setGradebook(gradebook); - session.save(gradeMapping); - gradeMappings.add(gradeMapping); - if (gradingScale.getUid().equals(defaultScaleUid)) { - defaultGradeMapping = gradeMapping; - } - } - - // Check for null default. - if (defaultGradeMapping == null) { - defaultGradeMapping = gradeMappings.iterator().next(); - if (log.isWarnEnabled()) { - log.warn("No default GradeMapping found for new Gradebook={}; will set default to {}", - gradebook.getUid(), defaultGradeMapping.getName()); - } - } - gradebook.setSelectedGradeMapping(defaultGradeMapping); - - // The Hibernate mapping as of Sakai 2.2 makes this next - // call meaningless when it comes to persisting changes at - // the end of the transaction. It is, however, needed for - // the mappings to be seen while the transaction remains - // uncommitted. - gradebook.setGradeMappings(gradeMappings); - - gradebook.setGrade_type(GradebookService.GRADE_TYPE_POINTS); - gradebook.setCategory_type(GradebookService.CATEGORY_TYPE_NO_CATEGORY); - - //SAK-29740 make backwards compatible - gradebook.setCourseLetterGradeDisplayed(true); - gradebook.setCourseAverageDisplayed(true); - - // SAK-33855 turn on stats for new gradebooks - final Boolean propAssignmentStatsDisplayed = this.serverConfigurationService.getBoolean(PROP_ASSIGNMENT_STATS_DISPLAYED, true); - gradebook.setAssignmentStatsDisplayed(propAssignmentStatsDisplayed); - - final Boolean propCourseGradeStatsDisplayed = this.serverConfigurationService.getBoolean(PROP_COURSE_GRADE_STATS_DISPLAYED, true); - gradebook.setCourseGradeStatsDisplayed(propCourseGradeStatsDisplayed); - - // Update the gradebook with the new selected grade mapping - session.update(gradebook); - - return null; - - }); - } - - private List addDefaultGradingScales(final Session session) throws HibernateException { - final List gradingScales = new ArrayList<>(); - - // Base the default set of templates on the old - // statically defined GradeMapping classes. - final GradeMapping[] oldGradeMappings = { - new LetterGradeMapping(), - new LetterGradePlusMinusMapping(), - new PassNotPassMapping(), - new GradePointsMapping() - }; - - for (final GradeMapping sampleMapping : oldGradeMappings) { - sampleMapping.setDefaultValues(); - final GradingScale gradingScale = new GradingScale(); - String uid = sampleMapping.getClass().getName(); - uid = uid.substring(uid.lastIndexOf('.') + 1); - gradingScale.setUid(uid); - gradingScale.setUnavailable(false); - gradingScale.setName(sampleMapping.getName()); - gradingScale.setGrades(new ArrayList<>(sampleMapping.getGrades())); - gradingScale.setDefaultBottomPercents(new HashMap<>(sampleMapping.getGradeMap())); - session.save(gradingScale); - if (log.isInfoEnabled()) { - log.info("Added Grade Mapping " + gradingScale.getUid()); - } - gradingScales.add(gradingScale); - } - setDefaultGradingScale("LetterGradePlusMinusMapping"); - session.flush(); - return gradingScales; - } - - @Override - public void setAvailableGradingScales(final Collection gradingScaleDefinitions) { - getHibernateTemplate().execute((HibernateCallback) session -> { - mergeGradeMappings(gradingScaleDefinitions, session); - return null; - }); - } - - - - @Override - public void saveGradeMappingToGradebook(final String scaleUuid, final String gradebookUid) { - getHibernateTemplate().execute(session -> { - - final List gradingScales = session - .createQuery("from GradingScale as gradingScale where gradingScale.unavailable=false").list(); - - for (final GradingScale gradingScale : gradingScales) { - if (gradingScale.getUid().equals(scaleUuid)){ - final GradeMapping gradeMapping = new GradeMapping(gradingScale); - final Gradebook gradebookToSet = getGradebook(gradebookUid); - gradeMapping.setGradebook(gradebookToSet); - session.save(gradeMapping); - } - } - session.flush(); - return null; - }); - } - - @Override - public List getAvailableGradingScales() { - - return getHibernateTemplate().execute(session -> { - // Get available grade mapping templates. - List gradingScales = session.createQuery("from GradingScale as gradingScale where gradingScale.unavailable=false").list(); - - // The application won't be able to run without grade mapping - // templates, so if for some reason none have been defined yet, - // do that now. - if (gradingScales.isEmpty()) { - if (log.isInfoEnabled()) { - log.info("No Grading Scale defined yet. This is probably because you have upgraded or you are working with a new database. Default grading scales will be created. Any customized system-wide grade mappings you may have defined in previous versions will have to be reconfigured."); - } - gradingScales = GradebookFrameworkServiceImpl.this.addDefaultGradingScales(session); - } - return gradingScales; - }); - } - - @Override - public List getAvailableGradingScaleDefinitions() { - final List gradingScales = getAvailableGradingScales(); - - final List rval = new ArrayList<>(); - for(final GradingScale gradingScale: gradingScales) { - rval.add(gradingScale.toGradingScaleDefinition()); - } - return rval; - } - - @Override - public void setDefaultGradingScale(final String uid) { - setPropertyValue(UID_OF_DEFAULT_GRADING_SCALE_PROPERTY, uid); - } - - private void copyDefinitionToScale(final GradingScaleDefinition bean, final GradingScale gradingScale) { - gradingScale.setUnavailable(false); - gradingScale.setName(bean.getName()); - gradingScale.setGrades(bean.getGrades()); - final Map defaultBottomPercents = new HashMap<>(); - final Iterator gradesIter = bean.getGrades().iterator(); - final Iterator defaultBottomPercentsIter = bean.getDefaultBottomPercentsAsList().iterator(); - while (gradesIter.hasNext() && defaultBottomPercentsIter.hasNext()) { - final String grade = (String)gradesIter.next(); - final Double value = (Double)defaultBottomPercentsIter.next(); - defaultBottomPercents.put(grade, value); - } - gradingScale.setDefaultBottomPercents(defaultBottomPercents); - } - - private void mergeGradeMappings(final Collection gradingScaleDefinitions, - final Session session) throws HibernateException { - final Map newMappingDefinitionsMap = new HashMap<>(); - final HashSet uidsToSet = new HashSet<>(); - for (final GradingScaleDefinition bean : gradingScaleDefinitions) { - newMappingDefinitionsMap.put(bean.getUid(), bean); - uidsToSet.add(bean.getUid()); - } - - // Until we move to Hibernate 3 syntax, we need to update one record at a time. - // Toggle any scales that are no longer specified. - Query q = session.createQuery( - "from GradingScale as gradingScale where gradingScale.uid not in (:uidList) and gradingScale.unavailable=false"); - q.setParameterList("uidList", uidsToSet); - List gmtList = q.list(); - for (final GradingScale gradingScale : gmtList) { - gradingScale.setUnavailable(true); - session.update(gradingScale); - if (log.isInfoEnabled()) { - log.info("Set Grading Scale " + gradingScale.getUid() + " unavailable"); - } - } - - // Modify any specified scales that already exist. - q = session.createQuery("from GradingScale as gradingScale where gradingScale.uid in (:uidList)"); - q.setParameterList("uidList", uidsToSet); - gmtList = q.list(); - for (final GradingScale gradingScale : gmtList) { - copyDefinitionToScale(newMappingDefinitionsMap.get(gradingScale.getUid()), gradingScale); - uidsToSet.remove(gradingScale.getUid()); - session.update(gradingScale); - if (log.isInfoEnabled()) { - log.info("Updated Grading Scale " + gradingScale.getUid()); - } - } - - // Add any new scales. - for (final String uid : uidsToSet) { - final GradingScale gradingScale = new GradingScale(); - gradingScale.setUid(uid); - final GradingScaleDefinition bean = newMappingDefinitionsMap.get(uid); - copyDefinitionToScale(bean, gradingScale); - session.save(gradingScale); - if (log.isInfoEnabled()) { - log.info("Added Grading Scale " + gradingScale.getUid()); - } - } - session.flush(); - } - - - @Override - public void deleteGradebook(final String uid) throws GradebookNotFoundException { - log.debug("Deleting gradebook uid={} by userUid={}", uid, getUserUid()); - - final Long gradebookId = getGradebook(uid).getId(); - - // Worse of both worlds code ahead. We've been quick-marched - // into Hibernate 3 sessions, but we're also having to use classic query - // parsing -- which keeps us from being able to use either Hibernate's new-style - // bulk delete queries or Hibernate's old-style session.delete method. - // Instead, we're stuck with going through the Spring template for each - // deletion one at a time. - final HibernateTemplate hibTempl = getHibernateTemplate(); - // int numberDeleted = hibTempl.bulkUpdate("delete GradingEvent as ge where ge.gradableObject.gradebook.id=?", gradebookId); - // log.warn("GradingEvent numberDeleted=" + numberDeleted); - - List toBeDeleted; - int numberDeleted; - - toBeDeleted = hibTempl.findByNamedParam("from GradingEvent as ge where ge.gradableObject.gradebook.id = :gradebookid", "gradebookid", gradebookId); - numberDeleted = toBeDeleted.size(); - hibTempl.deleteAll(toBeDeleted); - log.debug("Deleted {} grading events", numberDeleted); - - toBeDeleted = hibTempl.findByNamedParam("from org.sakaiproject.tool.gradebook.Comment as gc where gc.gradableObject.gradebook.id = :gradebookid", "gradebookid", gradebookId); - numberDeleted = toBeDeleted.size(); - hibTempl.deleteAll(toBeDeleted); - log.debug("Deleted {} grade comments", numberDeleted); - - toBeDeleted = hibTempl.findByNamedParam("from AbstractGradeRecord as gr where gr.gradableObject.gradebook.id = :gradebookid", "gradebookid", gradebookId); - numberDeleted = toBeDeleted.size(); - hibTempl.deleteAll(toBeDeleted); - if (log.isDebugEnabled()) { - log.debug("Deleted {} grade records", numberDeleted); - } - - toBeDeleted = hibTempl.findByNamedParam("from GradableObject as go where go.gradebook.id = :gradebookid", "gradebookid", gradebookId); - numberDeleted = toBeDeleted.size(); - hibTempl.deleteAll(toBeDeleted); - log.debug("Deleted {} gradable objects", numberDeleted); - - toBeDeleted = hibTempl.findByNamedParam("from Category as cg where cg.gradebook.id = :gradebookid", "gradebookid", gradebookId); - numberDeleted = toBeDeleted.size(); - hibTempl.deleteAll(toBeDeleted); - log.debug("Deleted {} gradable categories", numberDeleted); - - final Gradebook gradebook = hibTempl.load(Gradebook.class, gradebookId); - gradebook.setSelectedGradeMapping(null); - - toBeDeleted = hibTempl.findByNamedParam("from GradeMapping as gm where gm.gradebook.id = :gradebookid", "gradebookid", gradebookId); - numberDeleted = toBeDeleted.size(); - hibTempl.deleteAll(toBeDeleted); - if (log.isDebugEnabled()) { - log.debug("Deleted {} grade mappings", numberDeleted); - } - - hibTempl.delete(gradebook); - hibTempl.flush(); - hibTempl.clear(); - } - - private void createDefaultLetterGradeMapping(final Map gradeMap) - { - if(getDefaultLetterGradePercentMapping() == null) - { - final Set keySet = gradeMap.keySet(); - - if(keySet.size() != GradebookService.validLetterGrade.length) { - throw new IllegalArgumentException("gradeMap doesn't have right size in BaseHibernateManager.createDefaultLetterGradePercentMapping"); - } - - if (!validateLetterGradeMapping(gradeMap)) { - throw new IllegalArgumentException("gradeMap contains invalid letter in BaseHibernateManager.createDefaultLetterGradePercentMapping"); - } - - final HibernateCallback hc = session -> { - final LetterGradePercentMapping lgpm = new LetterGradePercentMapping(); - session.save(lgpm); - final Map saveMap = new HashMap(); - for(final Iterator iter = gradeMap.keySet().iterator(); iter.hasNext();) - { - final String key = (String) iter.next(); - saveMap.put(key, gradeMap.get(key)); - } - if (lgpm != null) - { - lgpm.setGradeMap(saveMap); - lgpm.setMappingType(1); - session.update(lgpm); - } - return null; - }; - getHibernateTemplate().execute(hc); - } - } - - private Map getHardDefaultLetterMapping() - { - final Map gradeMap = new HashMap<>(); - gradeMap.put("A+", Double.valueOf(100)); - gradeMap.put("A", Double.valueOf(95)); - gradeMap.put("A-", Double.valueOf(90)); - gradeMap.put("B+", Double.valueOf(87)); - gradeMap.put("B", Double.valueOf(83)); - gradeMap.put("B-", Double.valueOf(80)); - gradeMap.put("C+", Double.valueOf(77)); - gradeMap.put("C", Double.valueOf(73)); - gradeMap.put("C-", Double.valueOf(70)); - gradeMap.put("D+", Double.valueOf(67)); - gradeMap.put("D", Double.valueOf(63)); - gradeMap.put("D-", Double.valueOf(60)); - gradeMap.put("F", Double.valueOf(0.0)); - - return gradeMap; - } -} diff --git a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookPermissionServiceImpl.java b/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookPermissionServiceImpl.java deleted file mode 100644 index 3f4143452156..000000000000 --- a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookPermissionServiceImpl.java +++ /dev/null @@ -1,1395 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.component.gradebook; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.apache.commons.lang3.StringUtils; -import org.hibernate.HibernateException; -import org.hibernate.Session; -import org.sakaiproject.component.cover.ComponentManager; -import org.sakaiproject.section.api.SectionAwareness; -import org.sakaiproject.section.api.coursemanagement.CourseSection; -import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; -import org.sakaiproject.section.api.facade.Role; -import org.sakaiproject.service.gradebook.shared.GradebookPermissionService; -import org.sakaiproject.service.gradebook.shared.GradebookService; -import org.sakaiproject.service.gradebook.shared.GraderPermission; -import org.sakaiproject.service.gradebook.shared.PermissionDefinition; -import org.sakaiproject.tool.gradebook.GradebookAssignment; -import org.sakaiproject.tool.gradebook.Category; -import org.sakaiproject.tool.gradebook.Gradebook; -import org.sakaiproject.tool.gradebook.Permission; -import org.springframework.orm.hibernate5.HibernateCallback; - -public class GradebookPermissionServiceImpl extends BaseHibernateManager implements GradebookPermissionService -{ - private SectionAwareness sectionAwareness; - private GradebookService gradebookService; - - public List getCategoriesForUser(Long gradebookId, String userId, List categoryIdList) throws IllegalArgumentException - { - if(gradebookId == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getCategoriesForUser"); - - List anyCategoryPermission = getPermissionsForUserAnyCategory(gradebookId, userId); - if(anyCategoryPermission != null && anyCategoryPermission.size() > 0 ) - { - return categoryIdList; - } - else - { - - List returnCatIds = new ArrayList(); - List permList = getPermissionsForUserForCategory(gradebookId, userId, categoryIdList); - for(Iterator iter = permList.iterator(); iter.hasNext();) - { - Permission perm = (Permission) iter.next(); - if(perm != null && !returnCatIds.contains(perm.getCategoryId())) - { - returnCatIds.add(perm.getCategoryId()); - } - } - - return returnCatIds; - } - } - - public List getCategoriesForUserForStudentView(Long gradebookId, String userId, String studentId, List categoriesIds, List sectionIds) throws IllegalArgumentException - { - if(gradebookId == null || userId == null || studentId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getCategoriesForUser"); - - List returnCategoryList = new ArrayList(); - //Map categoryMap = new HashMap(); // to keep the elements unique - if (categoriesIds == null || categoriesIds.isEmpty()) - return returnCategoryList; - - List graderPermissions = getPermissionsForUser(gradebookId, userId); - if(graderPermissions == null || graderPermissions.isEmpty()) - { - return returnCategoryList; - } - - List studentSections = new ArrayList(); - - if (sectionIds != null) { - for (Iterator sectionIter = sectionIds.iterator(); sectionIter.hasNext();) { - String sectionId = (String) sectionIter.next(); - if (sectionId != null && sectionAwareness.isSectionMemberInRole(sectionId, studentId, Role.STUDENT)) { - studentSections.add(sectionId); - } - } - } - - for (Iterator permIter = graderPermissions.iterator(); permIter.hasNext();) { - Permission perm = (Permission) permIter.next(); - String sectionId = perm.getGroupId(); - if (studentSections.contains(sectionId) || sectionId == null) { - Long catId = perm.getCategoryId(); - if (catId == null) { - return returnCategoryList; - }else{ - returnCategoryList.add(catId); - } - } - } - - return returnCategoryList; - } - - public boolean getPermissionForUserForAllAssignment(Long gradebookId, String userId) throws IllegalArgumentException - { - if(gradebookId == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getPermissionForUserForAllAssignment"); - - List anyCategoryPermission = getPermissionsForUserAnyCategory(gradebookId, userId); - - if(anyCategoryPermission != null && anyCategoryPermission.size() > 0 ) - { - return true; - } - - return false; - } - - public boolean getPermissionForUserForAllAssignmentForStudent(Long gradebookId, String userId, String studentId, List sectionIds) throws IllegalArgumentException - { - if(gradebookId == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getPermissionForUserForAllAssignment"); - - List graderPermissions = this.getPermissionsForUser(gradebookId, userId); - if(graderPermissions == null || graderPermissions.isEmpty()) - { - return false; - } - - for (Iterator permIter = graderPermissions.iterator(); permIter.hasNext();) { - Permission perm = (Permission) permIter.next(); - String sectionId = perm.getGroupId(); - if (sectionId == null || (sectionIds.contains(sectionId) && sectionAwareness.isSectionMemberInRole(sectionId, studentId, Role.STUDENT))) { - if (perm.getCategoryId() == null) { - return true; - } - } - } - - return false; - } - - public Map getStudentsForItem(Long gradebookId, String userId, List studentIds, int cateType, Long categoryId, List courseSections) - throws IllegalArgumentException - { - if(gradebookId == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getStudentsForItem"); - if(cateType != GradebookService.CATEGORY_TYPE_ONLY_CATEGORY && cateType != GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY && cateType != GradebookService.CATEGORY_TYPE_NO_CATEGORY) - throw new IllegalArgumentException("Invalid category type in GradebookPermissionServiceImpl.getStudentsForItem"); - - if(studentIds != null) - { - Map> sectionIdStudentIdsMap = getSectionIdStudentIdsMap(courseSections, studentIds); - if(cateType == GradebookService.CATEGORY_TYPE_NO_CATEGORY) - { - List perms = getPermissionsForUserAnyGroup(gradebookId, userId); - - Map studentMap = new HashMap(); - if(perms != null && perms.size() > 0) - { - boolean view = false; - boolean grade = false; - for(Iterator iter = perms.iterator(); iter.hasNext();) - { - Permission perm = (Permission) iter.next(); - if(perm != null && perm.getFunction().equalsIgnoreCase(GradebookService.gradePermission)) - { - grade = true; - break; - } - if(perm != null && perm.getFunction().equalsIgnoreCase(GradebookService.viewPermission)) - { - view = true; - } - } - for(Iterator studentIter = studentIds.iterator(); studentIter.hasNext();) - { - if(grade == true) - studentMap.put((String)studentIter.next(), GradebookService.gradePermission); - else if(view == true) - studentMap.put((String)studentIter.next(), GradebookService.viewPermission); - } - } - - perms = getPermissionsForUser(gradebookId, userId); - - if(perms != null) - { - Map studentMapForGroups = filterPermissionForGrader(perms, studentIds, sectionIdStudentIdsMap); - for(Iterator> iter = studentMapForGroups.entrySet().iterator(); iter.hasNext();) - { - Map.Entry entry = iter.next(); - String key = entry.getKey(); - if((studentMap.containsKey(key) && ((String)studentMap.get(key)).equalsIgnoreCase(GradebookService.viewPermission)) - || !studentMap.containsKey(key)) - studentMap.put(key, studentMapForGroups.get(key)); - } - } - - return studentMap; - } - else - { - List cateList = new ArrayList(); - cateList.add(categoryId); - List perms = getPermissionsForUserAnyGroupForCategory(gradebookId, userId, cateList); - - Map studentMap = new HashMap(); - if(perms != null && perms.size() > 0) - { - boolean view = false; - boolean grade = false; - for(Iterator iter = perms.iterator(); iter.hasNext();) - { - Permission perm = (Permission) iter.next(); - if(perm != null && perm.getFunction().equalsIgnoreCase(GradebookService.gradePermission)) - { - grade = true; - break; - } - if(perm != null && perm.getFunction().equalsIgnoreCase(GradebookService.viewPermission)) - { - view = true; - } - } - for(Iterator studentIter = studentIds.iterator(); studentIter.hasNext();) - { - if(grade == true) - studentMap.put((String)studentIter.next(), GradebookService.gradePermission); - else if(view == true) - studentMap.put((String)studentIter.next(), GradebookService.viewPermission); - } - } - perms = getPermissionsForUserAnyGroupAnyCategory(gradebookId, userId); - - if(perms != null) - { - Map studentMapForGroups = filterPermissionForGraderForAllStudent(perms, studentIds); - for(Iterator> iter = studentMapForGroups.entrySet().iterator(); iter.hasNext();) - { - Entry entry = iter.next(); - String key = entry.getKey(); - if((studentMap.containsKey(key) && ((String)studentMap.get(key)).equalsIgnoreCase(GradebookService.viewPermission)) - || !studentMap.containsKey(key)) - studentMap.put(key, entry.getValue()); - } - } - - if (courseSections != null && !courseSections.isEmpty()) { - List groupIds = new ArrayList(); - for(Iterator iter = courseSections.iterator(); iter.hasNext();) - { - CourseSection grp = (CourseSection) iter.next(); - if(grp != null) - groupIds.add(grp.getUuid()); - } - - perms = getPermissionsForUserForGoupsAnyCategory(gradebookId, userId, groupIds); - if(perms != null) - { - Map studentMapForGroups = filterPermissionForGrader(perms, studentIds, sectionIdStudentIdsMap); - for(Iterator> iter = studentMapForGroups.entrySet().iterator(); iter.hasNext();) - { - Entry entry = iter.next(); - String key = entry.getKey(); - if((studentMap.containsKey(key) && ((String)studentMap.get(key)).equalsIgnoreCase(GradebookService.viewPermission)) - || !studentMap.containsKey(key)) - studentMap.put(key, entry.getValue()); - } - } - } - - perms = getPermissionsForUserForCategory(gradebookId, userId, cateList); - if(perms != null) - { - Map studentMapForGroups = filterPermissionForGrader(perms, studentIds, sectionIdStudentIdsMap); - for(Iterator> iter = studentMapForGroups.entrySet().iterator(); iter.hasNext();) - { - Entry entry = iter.next(); - String key = entry.getKey(); - if((studentMap.containsKey(key) && ((String)studentMap.get(key)).equalsIgnoreCase(GradebookService.viewPermission)) - || !studentMap.containsKey(key)) - studentMap.put(key, entry.getValue()); - } - } - - return studentMap; - } - } - return null; - } - - public Map getStudentsForItem(String gradebookUid, String userId, List studentIds, int cateType, Long categoryId, List courseSections) - throws IllegalArgumentException - { - if(gradebookUid == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getStudentsForItem"); - - Long gradebookId = getGradebook(gradebookUid).getId(); - return getStudentsForItem(gradebookId, userId, studentIds, cateType, categoryId, courseSections); - } - - public List getViewableGroupsForUser(Long gradebookId, String userId, List groupIds) { - if(gradebookId == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getViewableSectionsForUser"); - - if (groupIds == null || groupIds.size() == 0) - return null; - - List anyGroupPermission = getPermissionsForUserAnyGroup(gradebookId, userId); - if(anyGroupPermission != null && anyGroupPermission.size() > 0 ) - { - return groupIds; - } - else - { - List permList = getPermissionsForUserForGroup(gradebookId, userId, groupIds); - - List filteredGroups = new ArrayList(); - for(Iterator groupIter = groupIds.iterator(); groupIter.hasNext();) - { - String groupId = (String)groupIter.next(); - if(groupId != null) - { - for(Iterator iter = permList.iterator(); iter.hasNext();) - { - Permission perm = (Permission) iter.next(); - if(perm != null && perm.getGroupId().equals(groupId)) - { - filteredGroups.add(groupId); - break; - } - } - } - } - return filteredGroups; - } - - } - - public List getViewableGroupsForUser(String gradebookUid, String userId, List groupIds) { - if(gradebookUid == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getViewableSectionsForUser"); - - Long gradebookId = getGradebook(gradebookUid).getId(); - - return getViewableGroupsForUser(gradebookId, userId, groupIds); - } - - public List getGraderPermissionsForUser(Long gradebookId, String userId) { - if(gradebookId == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getPermissionsForUser"); - - return getPermissionsForUser(gradebookId, userId); - } - - public List getGraderPermissionsForUser(String gradebookUid, String userId) { - if (gradebookUid == null || userId == null) { - throw new IllegalArgumentException("Null gradebookUid or userId passed to getGraderPermissionsForUser"); - } - - Long gradebookId = getGradebook(gradebookUid).getId(); - - return getPermissionsForUser(gradebookId, userId); - } - - private Map filterPermissionForGrader(List perms, List studentIds, Map> sectionIdStudentIdsMap) - { - if(perms != null) - { - Map permMap = new HashMap(); - for(Iterator iter = perms.iterator(); iter.hasNext();) - { - Permission perm = (Permission)iter.next(); - if(perm != null) - { - if(permMap.containsKey(perm.getGroupId()) && ((String)permMap.get(perm.getGroupId())).equalsIgnoreCase(GradebookService.viewPermission)) - { - if(perm.getFunction().equalsIgnoreCase(GradebookService.gradePermission)) - permMap.put(perm.getGroupId(), GradebookService.gradePermission); - } - else if(!permMap.containsKey(perm.getGroupId())) - { - permMap.put(perm.getGroupId(), perm.getFunction()); - } - } - } - Map studentMap = new HashMap(); - - if(perms != null) - { - for(Iterator iter = studentIds.iterator(); iter.hasNext();) - { - String studentId = (String) iter.next(); - if (sectionIdStudentIdsMap != null) { - for(Iterator>> groupIter = sectionIdStudentIdsMap.entrySet().iterator(); groupIter.hasNext();) - { - Map.Entry> entry = groupIter.next(); - String grpId = entry.getKey(); - List sectionMembers = entry.getValue(); - - if(sectionMembers != null && sectionMembers.contains(studentId) && permMap.containsKey(grpId)) - { - if(studentMap.containsKey(studentId) && ((String)studentMap.get(studentId)).equalsIgnoreCase(GradebookService.viewPermission)) - { - if(((String)permMap.get(grpId)).equalsIgnoreCase(GradebookService.gradePermission)) - studentMap.put(studentId, GradebookService.gradePermission); - } - else if(!studentMap.containsKey(studentId)) - studentMap.put(studentId, permMap.get(grpId)); - } - } - } - } - } - return studentMap; - } - else - return new HashMap(); - } - - private Map filterPermissionForGraderForAllStudent(List perms, List studentIds) - { - if(perms != null) - { - Boolean grade = false; - Boolean view = false; - for(Iterator iter = perms.iterator(); iter.hasNext();) - { - Permission perm = (Permission)iter.next(); - if(perm.getFunction().equalsIgnoreCase(GradebookService.gradePermission)) - { - grade = true; - break; - } - else if(perm.getFunction().equalsIgnoreCase(GradebookService.viewPermission)) - view = true; - } - - Map studentMap = new HashMap(); - - if(grade || view) - { - for(Iterator iter = studentIds.iterator(); iter.hasNext();) - { - String studentId = (String) iter.next(); - if(grade) - studentMap.put(studentId, GradebookService.gradePermission); - else if(view) - studentMap.put(studentId, GradebookService.viewPermission); - } - } - return studentMap; - } - else - return new HashMap(); - } - - private Map filterPermissionForGraderForAllAssignments(List perms, List assignmentList) - { - if(perms != null) - { - Boolean grade = false; - Boolean view = false; - for(Iterator iter = perms.iterator(); iter.hasNext();) - { - Permission perm = (Permission)iter.next(); - if(perm.getFunction().equalsIgnoreCase(GradebookService.gradePermission)) - { - grade = true; - break; - } - else if(perm.getFunction().equalsIgnoreCase(GradebookService.viewPermission)) - view = true; - } - - Map assignMap = new HashMap(); - - if(grade || view) - { - for(Iterator iter = assignmentList.iterator(); iter.hasNext();) - { - GradebookAssignment assign = (GradebookAssignment) iter.next(); - if(grade && assign != null) - assignMap.put(assign.getId(), GradebookService.gradePermission); - else if(view && assign != null) - assignMap.put(assign.getId(), GradebookService.viewPermission); - } - } - return assignMap; - } - else - return new HashMap(); - } - - private Map getAvailableItemsForStudent(Gradebook gradebook, String userId, String studentId, Map sectionIdCourseSectionMap, Map catIdCategoryMap, List assignments, List permsForUserAnyGroup, List allPermsForUser, List permsForAnyGroupForCategories, List permsForUserAnyGroupAnyCategory, List permsForGroupsAnyCategory, List permsForUserForCategories, Map sectionIdStudentIdsMap) throws IllegalArgumentException - { - if(gradebook == null || userId == null || studentId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getAvailableItemsForStudent"); - - List cateList = new ArrayList(catIdCategoryMap.values()); - - if(gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_NO_CATEGORY) - { - Map assignMap = new HashMap(); - if(permsForUserAnyGroup != null && permsForUserAnyGroup.size() > 0) - { - boolean view = false; - boolean grade = false; - for(Iterator iter = permsForUserAnyGroup.iterator(); iter.hasNext();) - { - Permission perm = (Permission) iter.next(); - if(perm != null && perm.getFunction().equalsIgnoreCase(GradebookService.gradePermission)) - { - grade = true; - break; - } - if(perm != null && perm.getFunction().equalsIgnoreCase(GradebookService.viewPermission)) - { - view = true; - } - } - for(Iterator iter = assignments.iterator(); iter.hasNext();) - { - GradebookAssignment as = (GradebookAssignment) iter.next(); - if(grade == true && as != null) - assignMap.put(as.getId(), GradebookService.gradePermission); - else if(view == true && as != null) - assignMap.put(as.getId(), GradebookService.viewPermission); - } - } - - if(allPermsForUser != null) - { - Map assignsMapForGroups = filterPermissionForGrader(allPermsForUser, studentId, assignments, sectionIdStudentIdsMap); - for(Iterator> iter = assignsMapForGroups.entrySet().iterator(); iter.hasNext();) - { - Map.Entry entry = iter.next(); - Long key = entry.getKey(); - if((assignMap.containsKey(key) && ((String)assignMap.get(key)).equalsIgnoreCase(GradebookService.viewPermission)) - || !assignMap.containsKey(key)) - assignMap.put(key, entry.getValue()); - } - } - return assignMap; - } - else - { - - Map assignMap = new HashMap(); - if(permsForAnyGroupForCategories != null && permsForAnyGroupForCategories.size() > 0) - { - for(Iterator iter = permsForAnyGroupForCategories.iterator(); iter.hasNext();) - { - Permission perm = (Permission)iter.next(); - if(perm != null) - { - if(perm.getCategoryId() != null) - { - for(Iterator cateIter = cateList.iterator(); cateIter.hasNext();) - { - Category cate = (Category) cateIter.next(); - if(cate != null && cate.getId().equals(perm.getCategoryId())) - { - List assignmentList = cate.getAssignmentList(); - if (assignmentList != null) { - for(Iterator assignIter = assignmentList.iterator(); assignIter.hasNext();) - { - GradebookAssignment as = (GradebookAssignment)assignIter.next(); - if(as != null) - { - Long assignId = as.getId(); - if(as.getCategory() != null) - { - if(assignMap.containsKey(assignId) && ((String)assignMap.get(assignId)).equalsIgnoreCase(GradebookService.viewPermission)) - { - if(perm.getFunction().equalsIgnoreCase(GradebookService.gradePermission)) - { - assignMap.put(assignId, GradebookService.gradePermission); - } - } - else if(!assignMap.containsKey(assignId)) - { - assignMap.put(assignId, perm.getFunction()); - } - } - } - } - } - break; - } - } - } - } - } - } - - if(permsForUserAnyGroupAnyCategory != null) - { - Map assignMapForGroups = filterPermissionForGraderForAllAssignments(permsForUserAnyGroupAnyCategory, assignments); - for(Iterator> iter = assignMapForGroups.entrySet().iterator(); iter.hasNext();) - { - Entry entry = iter.next(); - Long key = entry.getKey(); - if((assignMap.containsKey(key) && ((String)assignMap.get(key)).equalsIgnoreCase(GradebookService.viewPermission)) - || !assignMap.containsKey(key)) - assignMap.put(key, entry.getValue()); - } - } - - if(permsForGroupsAnyCategory != null) - { - Map assignMapForGroups = filterPermissionForGrader(permsForGroupsAnyCategory, studentId, assignments, sectionIdStudentIdsMap); - for(Iterator> iter = assignMapForGroups.entrySet().iterator(); iter.hasNext();) - { - Entry entry = iter.next(); - Long key = entry.getKey(); - if((assignMap.containsKey(key) && ((String)assignMap.get(key)).equalsIgnoreCase(GradebookService.viewPermission)) - || !assignMap.containsKey(key)) - assignMap.put(key, entry.getValue()); - } - } - - if(permsForUserForCategories != null) - { - Map assignMapForGroups = filterPermissionForGraderForCategory(permsForUserForCategories, studentId, cateList, sectionIdStudentIdsMap); - if(assignMapForGroups != null) - { - for(Iterator> iter = assignMapForGroups.entrySet().iterator(); iter.hasNext();) - { - Entry entry = iter.next(); - Long key = entry.getKey(); - if((assignMap.containsKey(key) && ((String)assignMap.get(key)).equalsIgnoreCase(GradebookService.viewPermission)) - || !assignMap.containsKey(key)) - { - assignMap.put(key, entry.getValue()); - } - } - } - } - - return assignMap; - } - } - - public Map getAvailableItemsForStudent(Long gradebookId, String userId, String studentId, Collection courseSections) throws IllegalArgumentException - { - if(gradebookId == null || userId == null || studentId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getAvailableItemsForStudent"); - - List categories = getCategoriesWithAssignments(gradebookId); - Map catIdCategoryMap = new HashMap(); - if (!categories.isEmpty()) { - for (Iterator catIter = categories.iterator(); catIter.hasNext();) { - Category cat = (Category)catIter.next(); - if (cat != null) - catIdCategoryMap.put(cat.getId(), cat); - } - } - Map sectionIdCourseSectionMap = new HashMap(); - if (!courseSections.isEmpty()) { - for (Iterator sectionIter = courseSections.iterator(); sectionIter.hasNext();) { - CourseSection section = (CourseSection) sectionIter.next(); - if (section != null) { - sectionIdCourseSectionMap.put(section.getUuid(), section); - } - } - } - List studentIds = new ArrayList(); - studentIds.add(studentId); - Map sectionIdStudentIdsMap = getSectionIdStudentIdsMap(courseSections, studentIds); - - Gradebook gradebook = getGradebook(getGradebookUid(gradebookId)); - List assignments = getAssignments(gradebookId); - List categoryIds = new ArrayList(catIdCategoryMap.keySet()); - List groupIds = new ArrayList(sectionIdCourseSectionMap.keySet()); - - // Retrieve all the different permission info needed here so not called repeatedly for each student - List permsForUserAnyGroup = getPermissionsForUserAnyGroup(gradebookId, userId); - List allPermsForUser = getPermissionsForUser(gradebookId, userId); - List permsForAnyGroupForCategories = getPermissionsForUserAnyGroupForCategory(gradebookId, userId, categoryIds); - List permsForUserAnyGroupAnyCategory = getPermissionsForUserAnyGroupAnyCategory(gradebookId, userId); - List permsForGroupsAnyCategory = getPermissionsForUserForGoupsAnyCategory(gradebookId, userId, groupIds); - List permsForUserForCategories = getPermissionsForUserForCategory(gradebookId, userId, categoryIds); - - return getAvailableItemsForStudent(gradebook, userId, studentId, sectionIdCourseSectionMap, catIdCategoryMap, assignments, permsForUserAnyGroup, allPermsForUser, permsForAnyGroupForCategories, permsForUserAnyGroupAnyCategory, permsForGroupsAnyCategory, permsForUserForCategories, sectionIdStudentIdsMap); - } - - public Map getAvailableItemsForStudent(String gradebookUid, String userId, String studentId, Collection courseSections) throws IllegalArgumentException { - if(gradebookUid == null || userId == null || studentId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getAvailableItemsForStudent"); - - Long gradebookId = getGradebook(gradebookUid).getId(); - - return getAvailableItemsForStudent(gradebookId, userId, studentId, courseSections); - - } - - private Map filterPermissionForGrader(List perms, String studentId, List assignmentList, Map sectionIdStudentIdsMap) - { - if(perms != null) - { - Map permMap = new HashMap(); - for(Iterator iter = perms.iterator(); iter.hasNext();) - { - Permission perm = (Permission)iter.next(); - if(perm != null) - { - if(permMap.containsKey(perm.getGroupId()) && ((String)permMap.get(perm.getGroupId())).equalsIgnoreCase(GradebookService.viewPermission)) - { - if(perm.getFunction().equalsIgnoreCase(GradebookService.gradePermission)) - permMap.put(perm.getGroupId(), GradebookService.gradePermission); - } - else if(!permMap.containsKey(perm.getGroupId())) - { - permMap.put(perm.getGroupId(), perm.getFunction()); - } - } - } - Map assignmentMap = new HashMap(); - - if(perms != null && sectionIdStudentIdsMap != null) - { - for(Iterator iter = assignmentList.iterator(); iter.hasNext();) - { - Long assignId = ((GradebookAssignment)iter.next()).getId(); - for(Iterator> groupIter = sectionIdStudentIdsMap.entrySet().iterator(); groupIter.hasNext();) - { - Map.Entry entry = groupIter.next(); - String grpId = entry.getKey(); - List sectionMembers = (List) sectionIdStudentIdsMap.get(grpId); - - if(sectionMembers != null && sectionMembers.contains(studentId) && permMap.containsKey(grpId)) - { - if(assignmentMap.containsKey(assignId) && ((String)assignmentMap.get(assignId)).equalsIgnoreCase(GradebookService.viewPermission)) - { - if(((String)permMap.get(grpId)).equalsIgnoreCase(GradebookService.gradePermission)) - assignmentMap.put(assignId, GradebookService.gradePermission); - } - else if(!assignmentMap.containsKey(assignId)) - assignmentMap.put(assignId, permMap.get(grpId)); - } - } - } - } - return assignmentMap; - } - else - return new HashMap(); - } - - private Map filterPermissionForGraderForCategory(List perms, String studentId, List categoryList, Map sectionIdStudentIdsMap) - { - if(perms != null) - { - Map assignmentMap = new HashMap(); - - for(Iterator iter = perms.iterator(); iter.hasNext();) - { - Permission perm = (Permission)iter.next(); - if(perm != null && perm.getCategoryId() != null) - { - for(Iterator cateIter = categoryList.iterator(); cateIter.hasNext();) - { - Category cate = (Category) cateIter.next(); - if(cate != null && cate.getId().equals(perm.getCategoryId())) - { - List assignmentList = cate.getAssignmentList(); - if (assignmentList != null) { - for(Iterator assignIter = assignmentList.iterator(); assignIter.hasNext();) - { - GradebookAssignment as = (GradebookAssignment)assignIter.next(); - if(as != null && sectionIdStudentIdsMap != null) - { - Long assignId = as.getId(); - for(Iterator> groupIter = sectionIdStudentIdsMap.entrySet().iterator(); groupIter.hasNext();) - { - Map.Entry entry = groupIter.next(); - String grpId = entry.getKey(); - List sectionMembers = (List) sectionIdStudentIdsMap.get(grpId); - - if(sectionMembers != null && sectionMembers.contains(studentId) && as.getCategory() != null) - { - if(assignmentMap.containsKey(assignId) && grpId.equals(perm.getGroupId()) && ((String)assignmentMap.get(assignId)).equalsIgnoreCase(GradebookService.viewPermission)) - { - if(perm.getFunction().equalsIgnoreCase(GradebookService.gradePermission)) - { - assignmentMap.put(assignId, GradebookService.gradePermission); - } - } - else if(!assignmentMap.containsKey(assignId) && grpId.equals(perm.getGroupId())) - { - assignmentMap.put(assignId, perm.getFunction()); - } - } - } - } - } - } - break; - } - } - } - } - return assignmentMap; - } - else - return new HashMap(); - } - - public Map getAvailableItemsForStudents(Long gradebookId, String userId, List studentIds, Collection courseSections) throws IllegalArgumentException - { - if(gradebookId == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getAvailableItemsForStudents"); - - Map catIdCategoryMap = new HashMap(); - List categories = getCategoriesWithAssignments(gradebookId); - if (categories != null && !categories.isEmpty()) { - for (Iterator catIter = categories.iterator(); catIter.hasNext();) { - Category cat = (Category)catIter.next(); - if (cat != null) { - catIdCategoryMap.put(cat.getId(), cat); - } - } - } - Map sectionIdCourseSectionMap = new HashMap(); - if (!courseSections.isEmpty()) { - for (Iterator sectionIter = courseSections.iterator(); sectionIter.hasNext();) { - CourseSection section = (CourseSection) sectionIter.next(); - if (section != null) { - sectionIdCourseSectionMap.put(section.getUuid(), section); - } - } - } - - Map sectionIdStudentIdsMap = getSectionIdStudentIdsMap(courseSections, studentIds); - - Gradebook gradebook = getGradebook(getGradebookUid(gradebookId)); - List assignments = getAssignments(gradebookId); - List categoryIds = new ArrayList(catIdCategoryMap.keySet()); - List groupIds = new ArrayList(sectionIdCourseSectionMap.keySet()); - - // Retrieve all the different permission info needed here so not called repeatedly for each student - List permsForUserAnyGroup = getPermissionsForUserAnyGroup(gradebookId, userId); - List allPermsForUser = getPermissionsForUser(gradebookId, userId); - List permsForAnyGroupForCategories = getPermissionsForUserAnyGroupForCategory(gradebookId, userId, categoryIds); - List permsForUserAnyGroupAnyCategory = getPermissionsForUserAnyGroupAnyCategory(gradebookId, userId); - List permsForGroupsAnyCategory = getPermissionsForUserForGoupsAnyCategory(gradebookId, userId, groupIds); - List permsForUserForCategories = getPermissionsForUserForCategory(gradebookId, userId, categoryIds); - - if(studentIds != null) - { - Map studentsMap = new HashMap(); - for(Iterator iter = studentIds.iterator(); iter.hasNext();) - { - String studentId = (String) iter.next(); - if(studentId != null) - { - Map assignMap = getAvailableItemsForStudent(gradebook, userId, studentId, sectionIdCourseSectionMap, catIdCategoryMap, assignments, permsForUserAnyGroup, allPermsForUser, permsForAnyGroupForCategories, permsForUserAnyGroupAnyCategory, permsForGroupsAnyCategory, permsForUserForCategories, sectionIdStudentIdsMap); - studentsMap.put(studentId, assignMap); - } - } - return studentsMap; - } - - return new HashMap(); - } - - public Map getAvailableItemsForStudents(String gradebookUid, String userId, List studentIds, Collection courseSections) throws IllegalArgumentException - { - if(gradebookUid == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getAvailableItemsForStudents"); - - Long gradebookId = getGradebook(gradebookUid).getId(); - return getAvailableItemsForStudents(gradebookId, userId, studentIds, courseSections); - } - - public Map getCourseGradePermission(Long gradebookId, String userId, List studentIds, List courseSections) throws IllegalArgumentException - { - if(gradebookId == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getCourseGradePermission"); - - if(studentIds != null) - { - Map studentsMap = new HashMap(); - Map sectionIdStudentIdsMap = getSectionIdStudentIdsMap(courseSections, studentIds); - - List perms = getPermissionsForUserAnyGroupAnyCategory(gradebookId, userId); - if(perms != null) - { - Map studentMapForGroups = filterPermissionForGraderForAllStudent(perms, studentIds); - for(Iterator> iter = studentMapForGroups.entrySet().iterator(); iter.hasNext();) - { - Map.Entry entry = iter.next(); - String key = entry.getKey(); - if((studentsMap.containsKey(key) && ((String)studentsMap.get(key)).equalsIgnoreCase(GradebookService.viewPermission)) - || !studentsMap.containsKey(key)) - studentsMap.put(key, studentMapForGroups.get(key)); - } - } - - List groupIds = new ArrayList(); - if(courseSections != null) - { - for(Iterator iter = courseSections.iterator(); iter.hasNext();) - { - CourseSection grp = (CourseSection) iter.next(); - if(grp != null) - groupIds.add(grp.getUuid()); - } - - perms = getPermissionsForUserForGoupsAnyCategory(gradebookId, userId, groupIds); - if(perms != null) - { - Map studentMapForGroups = filterPermissionForGrader(perms, studentIds, sectionIdStudentIdsMap); - for(Iterator> iter = studentMapForGroups.entrySet().iterator(); iter.hasNext();) - { - Entry entry = iter.next(); - String key = entry.getKey(); - if((studentsMap.containsKey(key) && ((String)studentsMap.get(key)).equalsIgnoreCase(GradebookService.viewPermission)) - || !studentsMap.containsKey(key)) - studentsMap.put(key, entry.getValue()); - } - } - - Gradebook gradebook = getGradebook(getGradebookUid(gradebookId)); - if(gradebook != null && (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_ONLY_CATEGORY || - gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY)) - { - List cateList = getCategories(gradebookId); - - perms = getPermissionsForUserForGroup(gradebookId, userId, groupIds); - if(perms != null) - { - Map studentMapForGroups = filterForAllCategoryStudents(perms, studentIds, cateList, sectionIdStudentIdsMap); - for(Iterator> iter = studentMapForGroups.entrySet().iterator(); iter.hasNext();) - { - Entry entry = iter.next(); - String key = entry.getKey(); - if((studentsMap.containsKey(key) && ((String)studentsMap.get(key)).equalsIgnoreCase(GradebookService.viewPermission)) - || !studentsMap.containsKey(key)) - studentsMap.put(key, entry.getValue()); - } - } - - List cateIdList = new ArrayList(); - for(Iterator iter = cateList.iterator(); iter.hasNext();) - { - Category cate = (Category) iter.next(); - if(cate != null) - cateIdList.add(cate.getId()); - } - perms = getPermissionsForUserAnyGroupForCategory(gradebookId, userId, cateIdList); - if(perms != null && perms.size() > 0) - { - Map studentMapForGroups = filterForAllCategoryStudentsAnyGroup(perms, courseSections, studentIds, cateList); - for(Iterator> iter = studentMapForGroups.entrySet().iterator(); iter.hasNext();) - { - Entry entry = iter.next(); - String key = entry.getKey(); - if((studentsMap.containsKey(key) && ((String)studentsMap.get(key)).equalsIgnoreCase(GradebookService.viewPermission)) - || !studentsMap.containsKey(key)) - studentsMap.put(key, entry.getValue()); - } - } - } - } - - return studentsMap; - } - return new HashMap(); - } - - public Map getCourseGradePermission(String gradebookUid, String userId, List studentIds, List courseSections) throws IllegalArgumentException - { - if(gradebookUid == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getCourseGradePermission"); - - Long gradebookId = getGradebook(gradebookUid).getId(); - return getCourseGradePermission(gradebookId, userId, studentIds, courseSections); - } - - private Map filterForAllCategoryStudents(List perms, List studentIds, List cateList, Map sectionIdStudentIdsMap) - { - if(perms != null && sectionIdStudentIdsMap != null && studentIds != null && cateList != null) - { - List cateIdList = new ArrayList(); - for(Iterator iter = cateList.iterator(); iter.hasNext();) - { - Category cate = (Category) iter.next(); - if(cate != null) - cateIdList.add(cate.getId()); - } - - Map studentCateMap = new HashMap(); - for(Iterator studentIter = studentIds.iterator(); studentIter.hasNext();) - { - String studentId = (String) studentIter.next(); - studentCateMap.put(studentId, new HashMap()); - if(studentId != null) - { - for(Iterator> grpIter = sectionIdStudentIdsMap.entrySet().iterator(); grpIter.hasNext();) - { - Map.Entry entry = grpIter.next(); - String grpId = entry.getKey(); - - if(grpId != null) - { - List grpMembers = (List)sectionIdStudentIdsMap.get(grpId); - if (grpMembers != null && !grpMembers.isEmpty() && grpMembers.contains(studentId)) { - for(Iterator permIter = perms.iterator(); permIter.hasNext();) - { - Permission perm = (Permission) permIter.next(); - if(perm != null && perm.getGroupId().equals(grpId) && perm.getCategoryId() != null && cateIdList.contains(perm.getCategoryId())) - { - Map cateMap = (Map) studentCateMap.get(studentId); - if(cateMap.get(perm.getCategoryId()) == null || ((String)cateMap.get(perm.getCategoryId())).equals(GradebookService.viewPermission)) - cateMap.put(perm.getCategoryId(), perm.getFunction()); - studentCateMap.put(studentId, cateMap); - } - } - } - } - } - } - } - - Map studentPermissionMap = new HashMap(); - for(Iterator> iter = studentCateMap.entrySet().iterator(); iter.hasNext();) - { - Entry perEntry = iter.next(); - String studentId = perEntry.getKey(); - Map cateMap = perEntry.getValue(); - if(cateMap != null) - { - for(Iterator allCatesIter = cateIdList.iterator(); allCatesIter.hasNext();) - { - Long existCateId = (Long) allCatesIter.next(); - if(existCateId != null) - { - boolean hasPermissionForCate = false; - String permission = null; - for(Iterator> cateIter = cateMap.entrySet().iterator(); cateIter.hasNext();) - { - Entry entry = cateIter.next(); - Long cateId = entry.getKey(); - if(cateId.equals(existCateId)) - { - hasPermissionForCate = true; - permission = entry.getValue(); - break; - } - } - if(hasPermissionForCate && permission != null) - { - if(studentPermissionMap.get(studentId) == null || ((String)studentPermissionMap.get(studentId)).equals(GradebookService.gradePermission)) - studentPermissionMap.put(studentId, permission); - } - else if(!hasPermissionForCate) - { - if(studentPermissionMap.get(studentId) != null) - studentPermissionMap.remove(studentId); - } - } - } - } - } - return studentPermissionMap; - } - return new HashMap(); - } - - private Map filterForAllCategoryStudentsAnyGroup(List perms, List courseSections, List studentIds, List cateList) - { - if(perms != null && courseSections != null && studentIds != null && cateList != null) - { - Map cateMap = new HashMap(); - for(Iterator cateIter = cateList.iterator(); cateIter.hasNext();) - { - Category cate = (Category) cateIter.next(); - if(cate != null) - { - boolean permissionExistForCate = false; - for(Iterator permIter = perms.iterator(); permIter.hasNext();) - { - Permission perm = (Permission) permIter.next(); - if(perm != null && perm.getCategoryId().equals(cate.getId())) - { - if((cateMap.get(cate.getId()) == null || ((String)cateMap.get(cate.getId())).equals(GradebookService.viewPermission))) - cateMap.put(cate.getId(), perm.getFunction()); - permissionExistForCate = true; - } - } - if(!permissionExistForCate) - return new HashMap(); - } - } - - boolean view = false; - for(Iterator iter = cateMap.keySet().iterator(); iter.hasNext();) - { - String permission = (String) cateMap.get((Long)iter.next()); - if(permission != null && permission.equals(GradebookService.viewPermission)) - { - view = true; - } - } - Map studentMap = new HashMap(); - for(Iterator studentIter = studentIds.iterator(); studentIter.hasNext();) - { - String studentId = (String) studentIter.next(); - if(view) - studentMap.put(studentId, GradebookService.viewPermission); - else - studentMap.put(studentId, GradebookService.gradePermission); - } - - return studentMap; - } - return new HashMap(); - } - - public List getViewableStudentsForUser(Long gradebookId, String userId, List studentIds, List sections) { - if(gradebookId == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getAvailableItemsForStudent"); - - List viewableStudents = new ArrayList(); - - if (studentIds == null || studentIds.isEmpty()) - return viewableStudents; - - - List permsForAnyGroup = getPermissionsForUserAnyGroup(gradebookId, userId); - if (!permsForAnyGroup.isEmpty()) { - return studentIds; - } - - Map sectionIdStudentIdsMap = getSectionIdStudentIdsMap(sections, studentIds); - - if (sectionIdStudentIdsMap.isEmpty()) { - return null; - } - - // use a map to make sure the student ids are unique - Map studentMap = new HashMap(); - - // Next, check for permissions for specific sections - List groupIds = new ArrayList(sectionIdStudentIdsMap.keySet()); - List permsForGroupsAnyCategory = getPermissionsForUserForGroup(gradebookId, userId, groupIds); - - if (permsForGroupsAnyCategory.isEmpty()) { - return viewableStudents; - } - - for (Iterator permsIter = permsForGroupsAnyCategory.iterator(); permsIter.hasNext();) { - Permission perm = (Permission) permsIter.next(); - String groupId = perm.getGroupId(); - if (groupId != null) { - List sectionStudentIds = (ArrayList)sectionIdStudentIdsMap.get(groupId); - if (sectionStudentIds != null && !sectionStudentIds.isEmpty()) { - for (Iterator studentIter = sectionStudentIds.iterator(); studentIter.hasNext();) { - String studentId = (String) studentIter.next(); - studentMap.put(studentId, null); - } - } - } - } - - return new ArrayList(studentMap.keySet()); - } - - public List getViewableStudentsForUser(String gradebookUid, String userId, List studentIds, List sections) { - if(gradebookUid == null || userId == null) - throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getViewableStudentsForUser"); - - Long gradebookId = getGradebook(gradebookUid).getId(); - - return getViewableStudentsForUser(gradebookId, userId, studentIds, sections); - - } - - /** - * Get a list of permissions defined for the given user based on section and role or all sections if allowed. - * This method checks realms permissions for role/section and is independent of the - * gb_permissions_t permissions. - * - * note: If user has the grade privilege, they are given the GraderPermission.VIEW_COURSE_GRADE permission to match - * GB classic functionality. This needs to be reviewed. - * - * @param userUuid - * @param siteId - * @param role user Role - * @return list of {@link org.sakaiproject.service.gradebook.shared.PermissionDefinition PermissionDefinitions} or empty list if none - */ - public List getRealmsPermissionsForUser(String userUuid,String siteId, Role role){ - - List permissions = new ArrayList(); - - if( this.getGradebookService().isUserAllowedToGrade(siteId,userUuid)){ - //FIXME:giving them view course grade (this needs to be reviewed!!), - //it appears in GB classic, User can view course grades if they have the ability to grade in realms - PermissionDefinition permDef = new PermissionDefinition(); - permDef.setFunction(GraderPermission.VIEW_COURSE_GRADE.toString()); - permDef.setUserId(userUuid); - permissions.add(permDef); - - if(this.getGradebookService().isUserAllowedToGradeAll(siteId,userUuid)){ - permDef = new PermissionDefinition(); - permDef.setFunction(GraderPermission.GRADE.toString()); - permDef.setUserId(userUuid); - permissions.add(permDef); - }else{ - //get list of sections belonging to user and set a PermissionDefinition for each one - //Didn't find a method that returned gradeable sections for a TA, only for the logged in user. - //grabbing list of sections for the site, if User is a member of the section and has privilege to - //grade their sections, they are given the grade permission. Seems straight forward?? - List sections = this.getSectionAwareness().getSections(siteId); - - for(CourseSection section: sections){ - if(this.getSectionAwareness().isSectionMemberInRole(section.getUuid(), userUuid,role)){ - //realms have no categories defined for grading, just perms and group id - permDef = new PermissionDefinition(); - permDef.setFunction(GraderPermission.GRADE.toString()); - permDef.setUserId(userUuid); - permDef.setGroupReference(section.getUuid()); - permissions.add(permDef); - } - } - } - } - - return permissions; - } - - public SectionAwareness getSectionAwareness() - { - return sectionAwareness; - } - - public void setSectionAwareness(SectionAwareness sectionAwareness) - { - this.sectionAwareness = sectionAwareness; - } - - public GradebookService getGradebookService() - { - return (GradebookService) ComponentManager.get("org.sakaiproject.service.gradebook.GradebookService"); - } - - public void setGradebookService(GradebookService gradebookService) - { - this.gradebookService = gradebookService; - } - - private Map> getSectionIdStudentIdsMap(Collection courseSections, Collection studentIds) { - Map> sectionIdStudentIdsMap = new HashMap>(); - if (courseSections != null) { - for (Iterator sectionIter = courseSections.iterator(); sectionIter.hasNext();) { - CourseSection section = (CourseSection)sectionIter.next(); - if (section != null) { - String sectionId = section.getUuid(); - List members = getSectionAwareness().getSectionMembersInRole(sectionId, Role.STUDENT); - List sectionMembersFiltered = new ArrayList(); - if (!members.isEmpty()) { - for (Iterator memberIter = members.iterator(); memberIter.hasNext();) { - EnrollmentRecord enr = (EnrollmentRecord) memberIter.next(); - String studentId = enr.getUser().getUserUid(); - if (studentIds.contains(studentId)) - sectionMembersFiltered.add(studentId); - } - } - sectionIdStudentIdsMap.put(sectionId, sectionMembersFiltered); - } - } - } - return sectionIdStudentIdsMap; - } - - @Override - public List getPermissionsForUser(final String gradebookUid, final String userId) { - Long gradebookId = getGradebook(gradebookUid).getId(); - - List permissions = getPermissionsForUser(gradebookId, userId); - List rval = new ArrayList<>(); - - for(Permission permission: permissions) { - rval.add(toPermissionDefinition(permission)); - } - - return rval; - } - - @Override - public void updatePermissionsForUser(final String gradebookUid, final String userId, List permissionDefinitions) { - Long gradebookId = getGradebook(gradebookUid).getId(); - - if (permissionDefinitions.isEmpty()) { - PermissionDefinition noPermDef = new PermissionDefinition(); - noPermDef.setFunction(GraderPermission.NONE.toString()); - noPermDef.setUserId(userId); - permissionDefinitions.add(noPermDef); - } - - //get the current list of permissions - final List currentPermissions = getPermissionsForUser(gradebookId, userId); - - //convert PermissionDefinition to Permission - final List newPermissions = new ArrayList<>(); - for(PermissionDefinition def: permissionDefinitions) { - if(!StringUtils.equalsIgnoreCase(def.getFunction(), GraderPermission.GRADE.toString()) - && !StringUtils.equalsIgnoreCase(def.getFunction(), GraderPermission.VIEW.toString()) - && !StringUtils.equalsIgnoreCase(def.getFunction(), GraderPermission.VIEW_COURSE_GRADE.toString()) - && !StringUtils.equalsIgnoreCase(def.getFunction(), GraderPermission.NONE.toString())) { - throw new IllegalArgumentException("Invalid function for permission definition: " + def.getFunction()); - } - - Permission permission = new Permission(); - permission.setCategoryId(def.getCategoryId()); - permission.setGradebookId(gradebookId); - permission.setGroupId(def.getGroupReference()); - permission.setFunction(def.getFunction()); - permission.setUserId(userId); - - newPermissions.add(permission); - } - - //Note: rather than iterate both lists and figure out the differences and add/update/delete as applicable, - //it is far simpler to just remove the existing permissions and add new ones in one transaction - HibernateCallback hc = new HibernateCallback() { - public Object doInHibernate(Session session) throws HibernateException { - - //remove existing - for(Permission currentPermission: currentPermissions) { - session.delete(currentPermission); - } - - //add new - for(Permission newPermission: newPermissions) { - session.save(newPermission); - } - - return null; - } - }; - - getHibernateTemplate().execute(hc); - } - - public void clearPermissionsForUser(final String gradebookUid, final String userId) { - Long gradebookId = getGradebook(gradebookUid).getId(); - - // remove all current permissions for user - final List currentPermissions = getPermissionsForUser(gradebookId, userId); - for (Permission currentPermission : currentPermissions) { - getHibernateTemplate().getSessionFactory().getCurrentSession().delete(currentPermission); - } - } - - - - /** - * Maps a Permission to a PermissionDefinition - * Note that the persistent groupId is actually the group reference - * @param permission - * @return a {@link PermissionDefinition} - */ - private PermissionDefinition toPermissionDefinition(Permission permission) { - PermissionDefinition rval = new PermissionDefinition(); - if (permission != null) { - rval.setId(permission.getId()); - rval.setUserId(permission.getUserId()); - rval.setCategoryId(permission.getCategoryId()); - rval.setFunction(permission.getFunction()); - rval.setGroupReference(permission.getGroupId()); - } - return rval; - } - -} diff --git a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookServiceHibernateImpl.java b/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookServiceHibernateImpl.java deleted file mode 100644 index 234810edd778..000000000000 --- a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/GradebookServiceHibernateImpl.java +++ /dev/null @@ -1,3860 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.component.gradebook; - -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.hibernate.Hibernate; -import org.hibernate.HibernateException; -import org.hibernate.Session; -import org.hibernate.StaleObjectStateException; -import org.hibernate.criterion.Restrictions; -import org.hibernate.query.Query; -import org.sakaiproject.authz.cover.SecurityService; -import org.sakaiproject.component.api.ServerConfigurationService; -import org.sakaiproject.entity.api.Entity; -import org.sakaiproject.entity.api.EntityManager; -import org.sakaiproject.entity.api.Reference; -import org.sakaiproject.exception.IdUnusedException; -import org.sakaiproject.hibernate.HibernateCriterionUtils; -import org.sakaiproject.rubrics.logic.RubricsService; -import org.sakaiproject.section.api.coursemanagement.CourseSection; -import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; -import org.sakaiproject.section.api.coursemanagement.User; -import org.sakaiproject.section.api.facade.Role; -import org.sakaiproject.service.gradebook.shared.AssessmentNotFoundException; -import org.sakaiproject.service.gradebook.shared.Assignment; -import org.sakaiproject.service.gradebook.shared.CategoryDefinition; -import org.sakaiproject.service.gradebook.shared.CategoryScoreData; -import org.sakaiproject.service.gradebook.shared.CommentDefinition; -import org.sakaiproject.service.gradebook.shared.ConflictingAssignmentNameException; -import org.sakaiproject.service.gradebook.shared.ConflictingCategoryNameException; -import org.sakaiproject.service.gradebook.shared.GradeDefinition; -import org.sakaiproject.service.gradebook.shared.GradeMappingDefinition; -import org.sakaiproject.service.gradebook.shared.GradebookHelper; -import org.sakaiproject.service.gradebook.shared.GradebookInformation; -import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException; -import org.sakaiproject.service.gradebook.shared.GradebookPermissionService; -import org.sakaiproject.service.gradebook.shared.GradebookSecurityException; -import org.sakaiproject.service.gradebook.shared.GradebookService; -import org.sakaiproject.service.gradebook.shared.GradingEventStatus; -import org.sakaiproject.service.gradebook.shared.InvalidGradeException; -import org.sakaiproject.service.gradebook.shared.SortType; -import org.sakaiproject.service.gradebook.shared.StaleObjectModificationException; -import org.sakaiproject.service.gradebook.shared.exception.UnmappableCourseGradeOverrideException; -import org.sakaiproject.site.api.Group; -import org.sakaiproject.site.api.Site; -import org.sakaiproject.site.api.SitePage; -import org.sakaiproject.site.api.SiteService; -import org.sakaiproject.site.api.ToolConfiguration; -import org.sakaiproject.tool.api.ToolManager; -import org.sakaiproject.tool.gradebook.AssignmentGradeRecord; -import org.sakaiproject.tool.gradebook.Category; -import org.sakaiproject.tool.gradebook.Comment; -import org.sakaiproject.tool.gradebook.CourseGrade; -import org.sakaiproject.tool.gradebook.CourseGradeRecord; -import org.sakaiproject.tool.gradebook.GradableObject; -import org.sakaiproject.tool.gradebook.GradeMapping; -import org.sakaiproject.tool.gradebook.Gradebook; -import org.sakaiproject.tool.gradebook.GradebookAssignment; -import org.sakaiproject.tool.gradebook.GradingEvent; -import org.sakaiproject.tool.gradebook.LetterGradePercentMapping; -import org.sakaiproject.tool.gradebook.facades.Authz; -import org.sakaiproject.util.ResourceLoader; -import org.springframework.beans.factory.ObjectFactory; -import org.springframework.orm.hibernate5.HibernateCallback; -import org.springframework.orm.hibernate5.HibernateOptimisticLockingFailureException; - -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -/** - * A Hibernate implementation of GradebookService. - */ -@Slf4j -public class GradebookServiceHibernateImpl extends BaseHibernateManager implements GradebookService { - - private Authz authz; - private GradebookPermissionService gradebookPermissionService; - protected SiteService siteService; - - @Setter - protected ServerConfigurationService serverConfigService; - - @Setter private EntityManager entityManager; - @Setter private ToolManager toolManager; - - @Getter @Setter - private RubricsService rubricsService; - - public void init() { - // register as an entity producer - entityManager.registerEntityProducer(this, REFERENCE_ROOT); - } - - @Override - public boolean isAssignmentDefined(final String gradebookUid, final String assignmentName) { - if (!isUserAbleToViewAssignments(gradebookUid)) { - log.warn("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to check for assignment {}", getUserUid(), gradebookUid, - assignmentName); - throw new GradebookSecurityException(); - } - @SuppressWarnings("unchecked") - final GradebookAssignment assignment = (GradebookAssignment) getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) { - return getAssignmentWithoutStats(gradebookUid, assignmentName); - } - }); - return (assignment != null); - } - - @Override - public Optional getEntityUrl(Reference ref, Entity.UrlType urlType) { - try { - Site site = siteService.getSite(ref.getContext()); - ToolConfiguration fromTool = site.getToolForCommonId("sakai.gradebookng"); - String entityUrl = null; - if (fromTool != null) { - entityUrl = String.format("%s/directtool/%s", serverConfigService.getPortalUrl(), fromTool.getId()); - } - return Optional.of(entityUrl); - } catch (Exception e) { - log.error("ERROR getting gradebook assignment entity URL", e); - return Optional.empty(); - } - } - - @Override - public boolean parseEntityReference(String stringReference, Reference reference) { - if (StringUtils.startsWith(stringReference, REFERENCE_ROOT)) { - String[] parts = StringUtils.splitPreserveAllTokens(stringReference, Entity.SEPARATOR); - reference.set(SAKAI_GBASSIGNMENT, parts[2], parts[4], parts[3], parts[3]); - return true; - } - return false; - } - - private boolean isUserAbleToViewAssignments(final String gradebookUid) { - final Authz authz = getAuthz(); - return (authz.isUserAbleToEditAssessments(gradebookUid) || authz.isUserAbleToGrade(gradebookUid)); - } - - @Override - public boolean isUserAbleToGradeItemForStudent(final String gradebookUid, final Long itemId, final String studentUid) { - return getAuthz().isUserAbleToGradeItemForStudent(gradebookUid, itemId, studentUid); - } - - @Override - public boolean isUserAbleToViewItemForStudent(final String gradebookUid, final Long itemId, final String studentUid) { - return getAuthz().isUserAbleToViewItemForStudent(gradebookUid, itemId, studentUid); - } - - @Override - public String getGradeViewFunctionForUserForStudentForItem(final String gradebookUid, final Long itemId, final String studentUid) { - return getAuthz().getGradeViewFunctionForUserForStudentForItem(gradebookUid, itemId, studentUid); - } - - @Override - public List getAssignments(final String gradebookUid) - throws GradebookNotFoundException { - return getAssignments(gradebookUid, SortType.SORT_BY_NONE); - } - - @Override - public List getAssignments(final String gradebookUid, final SortType sortBy) - throws GradebookNotFoundException { - if (!isUserAbleToViewAssignments(gradebookUid)) { - log.warn("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to get assignments list", getUserUid(), gradebookUid); - throw new GradebookSecurityException(); - } - - final Long gradebookId = getGradebook(gradebookUid).getId(); - - final List internalAssignments = getAssignments(gradebookId); - - sortAssignments(internalAssignments, sortBy, true); - - final List assignments = new ArrayList<>(); - for (final GradebookAssignment gradebookAssignment : internalAssignments) { - final GradebookAssignment assignment = gradebookAssignment; - assignments.add(getAssignmentDefinition(assignment)); - } - return assignments; - } - - @Override - public org.sakaiproject.service.gradebook.shared.Assignment getAssignment(final String gradebookUid, final Long assignmentId) - throws AssessmentNotFoundException { - if (assignmentId == null || gradebookUid == null) { - throw new IllegalArgumentException("null parameter passed to getAssignment. Values are assignmentId:" - + assignmentId + " gradebookUid:" + gradebookUid); - } - if (!isUserAbleToViewAssignments(gradebookUid) && !currentUserHasViewOwnGradesPerm(gradebookUid)) { - log.warn("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to get assignment with id {}", getUserUid(), gradebookUid, - assignmentId); - throw new GradebookSecurityException(); - } - - final GradebookAssignment assignment = getAssignmentWithoutStatsByID(gradebookUid, assignmentId); - - if (assignment == null) { - throw new AssessmentNotFoundException("No gradebook item exists with gradable object id = " + assignmentId); - } - - return getAssignmentDefinition(assignment); - } - - /** - * Method to retrieve Assignment by ID. - * - * @param gradeableObjectID - * @return - */ - public Assignment getAssignmentByID(final Long gradeableObjectID) throws AssessmentNotFoundException { - GradebookAssignment assignment = (GradebookAssignment) getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - return getAssignmentById(gradeableObjectID); - } - }); - - if (assignment == null) { - throw new AssessmentNotFoundException("No gradebook item exists with gradable object id = " + gradeableObjectID); - } - - return getAssignmentDefinition(assignment); - } - - @Override - @Deprecated - public org.sakaiproject.service.gradebook.shared.Assignment getAssignment(final String gradebookUid, final String assignmentName) - throws AssessmentNotFoundException { - if (assignmentName == null || gradebookUid == null) { - throw new IllegalArgumentException("null parameter passed to getAssignment. Values are assignmentName:" - + assignmentName + " gradebookUid:" + gradebookUid); - } - if (!isUserAbleToViewAssignments(gradebookUid) && !currentUserHasViewOwnGradesPerm(gradebookUid)) { - log.warn("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to get assignment {}", getUserUid(), gradebookUid, - assignmentName); - throw new GradebookSecurityException(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - final GradebookAssignment assignment = (GradebookAssignment) getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - return getAssignmentWithoutStats(gradebookUid, assignmentName); - } - }); - - if (assignment != null) { - return getAssignmentDefinition(assignment); - } else { - return null; - } - } - - public Assignment getExternalAssignment(final String gradebookUid, final String externalId) - throws GradebookNotFoundException { - - final Gradebook gradebook = getGradebook(gradebookUid); - - final HibernateCallback hc = session -> (GradebookAssignment) session - .createQuery("from GradebookAssignment as asn where asn.gradebook = :gradebook and asn.externalId = :externalid") - .setParameter("gradebook", gradebook) - .setParameter("externalid", externalId) - .uniqueResult(); - - return getAssignmentDefinition(getHibernateTemplate().execute(hc)); - } - - @Override - public org.sakaiproject.service.gradebook.shared.Assignment getAssignmentByNameOrId(final String gradebookUid, - final String assignmentName) throws AssessmentNotFoundException { - - org.sakaiproject.service.gradebook.shared.Assignment assignment = null; - try { - assignment = getAssignment(gradebookUid, assignmentName); - } catch (final AssessmentNotFoundException e) { - // Don't fail on this exception - log.debug("Assessment not found by name", e); - } - - if (assignment == null) { - // Try to get the assignment by id - if (NumberUtils.isCreatable(assignmentName)) { - final Long assignmentId = NumberUtils.toLong(assignmentName, -1L); - return getAssignment(gradebookUid, assignmentId); - } - } - return assignment; - } - - private org.sakaiproject.service.gradebook.shared.Assignment getAssignmentDefinition(final GradebookAssignment internalAssignment) { - final org.sakaiproject.service.gradebook.shared.Assignment assignmentDefinition = new org.sakaiproject.service.gradebook.shared.Assignment(); - assignmentDefinition.setName(internalAssignment.getName()); - assignmentDefinition.setPoints(internalAssignment.getPointsPossible()); - assignmentDefinition.setDueDate(internalAssignment.getDueDate()); - assignmentDefinition.setCounted(internalAssignment.isCounted()); - assignmentDefinition.setExternallyMaintained(internalAssignment.isExternallyMaintained()); - assignmentDefinition.setExternalAppName(internalAssignment.getExternalAppName()); - assignmentDefinition.setExternalId(internalAssignment.getExternalId()); - assignmentDefinition.setExternalData(internalAssignment.getExternalData()); - assignmentDefinition.setReleased(internalAssignment.isReleased()); - assignmentDefinition.setId(internalAssignment.getId()); - assignmentDefinition.setExtraCredit(internalAssignment.isExtraCredit()); - if(internalAssignment.getCategory() != null) { - assignmentDefinition.setCategoryName(internalAssignment.getCategory().getName()); - assignmentDefinition.setWeight(internalAssignment.getCategory().getWeight()); - assignmentDefinition.setCategoryExtraCredit(internalAssignment.getCategory().isExtraCredit()); - assignmentDefinition.setCategoryEqualWeight(internalAssignment.getCategory().isEqualWeightAssignments()); - assignmentDefinition.setCategoryId(internalAssignment.getCategory().getId()); - assignmentDefinition.setCategoryOrder(internalAssignment.getCategory().getCategoryOrder()); - } - assignmentDefinition.setUngraded(internalAssignment.getUngraded()); - assignmentDefinition.setSortOrder(internalAssignment.getSortOrder()); - assignmentDefinition.setCategorizedSortOrder(internalAssignment.getCategorizedSortOrder()); - - return assignmentDefinition; - } - - - - @Override - public GradeDefinition getGradeDefinitionForStudentForItem(final String gradebookUid, final Long assignmentId, final String studentUid) { - - if (gradebookUid == null || assignmentId == null || studentUid == null) { - throw new IllegalArgumentException("Null paraemter passed to getGradeDefinitionForStudentForItem"); - } - - // studentId can be a groupId (from Assignments) - final boolean studentRequestingOwnScore = this.authn.getUserUid().equals(studentUid) - || isCurrentUserFromGroup(gradebookUid, studentUid); - - @SuppressWarnings({ "unchecked", "rawtypes" }) - final GradeDefinition gradeDef = (GradeDefinition) getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - - final GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentId); - - if (assignment == null) { - throw new AssessmentNotFoundException( - "There is no assignment with the assignmentId " + assignmentId + " in gradebook " + gradebookUid); - } - - if (!studentRequestingOwnScore && !isUserAbleToViewItemForStudent(gradebookUid, assignment.getId(), studentUid)) { - log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to retrieve grade for student {} for assignment {}", - getUserUid(), gradebookUid, studentUid, assignmentId); - throw new GradebookSecurityException(); - } - - final Gradebook gradebook = assignment.getGradebook(); - - final GradeDefinition gradeDef = new GradeDefinition(); - gradeDef.setStudentUid(studentUid); - gradeDef.setGradeEntryType(gradebook.getGrade_type()); - gradeDef.setGradeReleased(assignment.isReleased()); - - // If this is the student, then the global setting needs to be enabled and the assignment needs to have - // been released. Return null score information if not released - if (studentRequestingOwnScore && (!gradebook.isAssignmentsDisplayed() || !assignment.isReleased())) { - gradeDef.setDateRecorded(null); - gradeDef.setGrade(null); - gradeDef.setGraderUid(null); - gradeDef.setGradeComment(null); - log.debug("Student {} in gradebook {} retrieving score for unreleased assignment {}", getUserUid(), gradebookUid, - assignment.getName()); - - } else { - - final AssignmentGradeRecord gradeRecord = getAssignmentGradeRecord(assignment, studentUid); - final CommentDefinition gradeComment = getAssignmentScoreComment(gradebookUid, assignmentId, studentUid); - final String commentText = gradeComment != null ? gradeComment.getCommentText() : null; - if (log.isDebugEnabled()) { - log.debug("gradeRecord=" + gradeRecord); - } - - if (gradeRecord == null) { - gradeDef.setDateRecorded(null); - gradeDef.setGrade(null); - gradeDef.setGraderUid(null); - gradeDef.setGradeComment(commentText); - gradeDef.setExcused(false); - } else { - gradeDef.setDateRecorded(gradeRecord.getDateRecorded()); - gradeDef.setGraderUid(gradeRecord.getGraderId()); - gradeDef.setGradeComment(commentText); - - gradeDef.setExcused(gradeRecord.isExcludedFromGrade()); - - if (gradebook.getGrade_type() == GradebookService.GRADE_TYPE_LETTER) { - final List gradeList = new ArrayList<>(); - gradeList.add(gradeRecord); - convertPointsToLetterGrade(gradebook, gradeList); - final AssignmentGradeRecord gradeRec = gradeList.get(0); - if (gradeRec != null) { - gradeDef.setGrade(gradeRec.getLetterEarned()); - } - } else if (gradebook.getGrade_type() == GradebookService.GRADE_TYPE_PERCENTAGE) { - final Double percent = calculateEquivalentPercent(assignment.getPointsPossible(), - gradeRecord.getPointsEarned()); - if (percent != null) { - gradeDef.setGrade(percent.toString()); - } - } else { - if (gradeRecord.getPointsEarned() != null) { - gradeDef.setGrade(gradeRecord.getPointsEarned().toString()); - } - } - } - } - - return gradeDef; - } - }); - if (log.isDebugEnabled()) { - log.debug("returning grade def for " + studentUid); - } - return gradeDef; - } - - @Override - public GradebookInformation getGradebookInformation(final String gradebookUid) { - - if (gradebookUid == null) { - throw new IllegalArgumentException("null gradebookUid " + gradebookUid); - } - - if (!currentUserHasEditPerm(gradebookUid) && !currentUserHasGradingPerm(gradebookUid)) { - log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to access gb information", getUserUid(), gradebookUid); - throw new GradebookSecurityException(); - } - - final Gradebook gradebook = getGradebook(gradebookUid); - if (gradebook == null) { - throw new IllegalArgumentException("Their is no gradbook associated with this Id: " + gradebookUid); - } - - final GradebookInformation rval = new GradebookInformation(); - - // add in all available grademappings for this gradebook - rval.setGradeMappings(getGradebookGradeMappings(gradebook.getGradeMappings())); - - // add in details about the selected one - final GradeMapping selectedGradeMapping = gradebook.getSelectedGradeMapping(); - if (selectedGradeMapping != null) { - - rval.setSelectedGradingScaleUid(selectedGradeMapping.getGradingScale().getUid()); - rval.setSelectedGradeMappingId(Long.toString(selectedGradeMapping.getId())); - - // note that these are not the DEFAULT bottom percents but the configured ones per gradebook - Map gradeMap = selectedGradeMapping.getGradeMap(); - gradeMap = GradeMappingDefinition.sortGradeMapping(gradeMap); - rval.setSelectedGradingScaleBottomPercents(gradeMap); - rval.setGradeScale(selectedGradeMapping.getGradingScale().getName()); - } - - rval.setGradeType(gradebook.getGrade_type()); - rval.setCategoryType(gradebook.getCategory_type()); - rval.setDisplayReleasedGradeItemsToStudents(gradebook.isAssignmentsDisplayed()); - - // add in the category definitions - rval.setCategories(getCategoryDefinitions(gradebookUid)); - - // add in the course grade display settings - rval.setCourseGradeDisplayed(gradebook.isCourseGradeDisplayed()); - rval.setCourseLetterGradeDisplayed(gradebook.isCourseLetterGradeDisplayed()); - rval.setCoursePointsDisplayed(gradebook.isCoursePointsDisplayed()); - rval.setCourseAverageDisplayed(gradebook.isCourseAverageDisplayed()); - - // add in stats display settings - rval.setAssignmentStatsDisplayed(gradebook.isAssignmentStatsDisplayed()); - rval.setCourseGradeStatsDisplayed(gradebook.isCourseGradeStatsDisplayed()); - - // add in compare grades with classmates settings - rval.setAllowStudentsToCompareGrades(gradebook.isAllowStudentsToCompareGrades()); - rval.setComparingDisplayStudentNames(gradebook.isComparingDisplayStudentNames()); - rval.setComparingDisplayStudentSurnames(gradebook.isComparingDisplayStudentSurnames()); - rval.setComparingDisplayTeacherComments(gradebook.isComparingDisplayTeacherComments()); - rval.setComparingIncludeAllGrades(gradebook.isComparingIncludeAllGrades()); - rval.setComparingRandomizeDisplayedData(gradebook.isComparingRandomizeDisplayedData()); - - return rval; - } - - @Override - public Map transferGradebook(final GradebookInformation gradebookInformation, - final List assignments, final String toGradebookUid, final String fromContext) { - - final Map transversalMap = new HashMap<>(); - - final Gradebook gradebook = getGradebook(toGradebookUid); - - gradebook.setCategory_type(gradebookInformation.getCategoryType()); - gradebook.setGrade_type(gradebookInformation.getGradeType()); - gradebook.setAssignmentStatsDisplayed(gradebookInformation.isAssignmentStatsDisplayed()); - gradebook.setCourseGradeStatsDisplayed(gradebookInformation.isCourseGradeStatsDisplayed()); - gradebook.setAssignmentsDisplayed(gradebookInformation.isDisplayReleasedGradeItemsToStudents()); - gradebook.setCourseGradeDisplayed(gradebookInformation.isCourseGradeDisplayed()); - gradebook.setAllowStudentsToCompareGrades(gradebookInformation.isAllowStudentsToCompareGrades()); - gradebook.setComparingDisplayStudentNames(gradebookInformation.isComparingDisplayStudentNames()); - gradebook.setComparingDisplayStudentSurnames(gradebookInformation.isComparingDisplayStudentSurnames()); - gradebook.setComparingDisplayTeacherComments(gradebookInformation.isComparingDisplayTeacherComments()); - gradebook.setComparingIncludeAllGrades(gradebookInformation.isComparingIncludeAllGrades()); - gradebook.setComparingRandomizeDisplayedData(gradebookInformation.isComparingRandomizeDisplayedData()); - gradebook.setCourseLetterGradeDisplayed(gradebookInformation.isCourseLetterGradeDisplayed()); - gradebook.setCoursePointsDisplayed(gradebookInformation.isCoursePointsDisplayed()); - gradebook.setCourseAverageDisplayed(gradebookInformation.isCourseAverageDisplayed()); - - updateGradebook(gradebook); - - // all categories that we need to end up with - final List categories = gradebookInformation.getCategories(); - - // filter out externally managed assignments. These are never imported. - assignments.removeIf(a -> a.isExternallyMaintained()); - - // this map holds the names of categories that have been created in the site to the category ids - // and is updated as we go along - // likewise for list of assignments - final Map categoriesCreated = new HashMap<>(); - final List assignmentsCreated = new ArrayList<>(); - - if (!categories.isEmpty() && gradebookInformation.getCategoryType() != CATEGORY_TYPE_NO_CATEGORY) { - - // migrate the categories with assignments - categories.forEach(c -> { - - assignments.forEach(a -> { - - if (StringUtils.equals(c.getName(), a.getCategoryName())) { - - if (!categoriesCreated.containsKey(c.getName())) { - - // create category - Long categoryId = null; - try { - categoryId = createCategory(gradebook.getId(), c.getName(), c.getWeight(), c.getDropLowest(), - c.getDropHighest(), c.getKeepHighest(), c.getExtraCredit(), c.getEqualWeight(), c.getCategoryOrder()); - } catch (final ConflictingCategoryNameException e) { - // category already exists. Could be from a merge. - log.info("Category: {} already exists in target site. Skipping creation.", c.getName()); - } - - if (categoryId == null) { - // couldn't create so look up the id in the target site - final List existingCategories = getCategoryDefinitions(gradebook.getUid()); - categoryId = existingCategories.stream().filter(e -> StringUtils.equals(e.getName(), c.getName())) - .findFirst().get().getId(); - } - // record that we have created this category - categoriesCreated.put(c.getName(), categoryId); - - } - - // create the assignment for the current category - try { - Long newId = createAssignmentForCategory(gradebook.getId(), categoriesCreated.get(c.getName()), a.getName(), a.getPoints(), - a.getDueDate(), !a.isCounted(), a.isReleased(), a.isExtraCredit(), a.getCategorizedSortOrder()); - transversalMap.put("gb/"+a.getId(),"gb/"+newId); - } catch (final ConflictingAssignmentNameException e) { - // assignment already exists. Could be from a merge. - log.info("GradebookAssignment: {} already exists in target site. Skipping creation.", a.getName()); - } catch (final Exception ex) { - log.warn("GradebookAssignment: exception {} trying to create {} in target site. Skipping creation.", ex.getMessage(), a.getName()); - } - - // record that we have created this assignment - assignmentsCreated.add(a.getName()); - } - }); - }); - - // create any remaining categories that have no assignments - categories.removeIf(c -> categoriesCreated.containsKey(c.getName())); - categories.forEach(c -> { - try { - createCategory(gradebook.getId(), c.getName(), c.getWeight(), c.getDropLowest(), c.getDropHighest(), c.getKeepHighest(), - c.getExtraCredit(), c.getEqualWeight(), c.getCategoryOrder()); - } catch (final ConflictingCategoryNameException e) { - // category already exists. Could be from a merge. - log.info("Category: {} already exists in target site. Skipping creation.", c.getName()); - } - }); - } - - // create any remaining assignments that have no categories - assignments.removeIf(a -> assignmentsCreated.contains(a.getName())); - assignments.forEach(a -> { - try { - Long newId = createAssignment(gradebook.getId(), a.getName(), a.getPoints(), a.getDueDate(), !a.isCounted(), a.isReleased(), a.isExtraCredit(), a.getSortOrder()); - transversalMap.put("gb/"+a.getId(),"gb/"+newId); - } catch (final ConflictingAssignmentNameException e) { - // assignment already exists. Could be from a merge. - log.info("GradebookAssignment: {} already exists in target site. Skipping creation.", a.getName()); - } catch (final Exception ex) { - log.warn("GradebookAssignment: exception {} trying to create {} in target site. Skipping creation.", ex.getMessage(), a.getName()); - } - }); - - // Carry over the old gradebook's selected grading scheme if possible. - final String fromGradingScaleUid = gradebookInformation.getSelectedGradingScaleUid(); - - MERGE_GRADE_MAPPING: if (!StringUtils.isEmpty(fromGradingScaleUid)) { - for (final GradeMapping gradeMapping : gradebook.getGradeMappings()) { - if (gradeMapping.getGradingScale().getUid().equals(fromGradingScaleUid)) { - // We have a match. Now make sure that the grades are as expected. - final Map inputGradePercents = gradebookInformation.getSelectedGradingScaleBottomPercents(); - final Set gradeCodes = inputGradePercents.keySet(); - - // If the grades dont map one-to-one, clear out the destination site's existing map - if (!gradeCodes.containsAll(gradeMapping.getGradeMap().keySet())) { - gradeMapping.getGradeMap().clear(); - } - - // Modify the existing grade-to-percentage map. - for (final String gradeCode : gradeCodes) { - gradeMapping.getGradeMap().put(gradeCode, inputGradePercents.get(gradeCode)); - } - gradebook.setSelectedGradeMapping(gradeMapping); - updateGradebook(gradebook); - log.info("Merge to gradebook {} updated grade mapping", toGradebookUid); - - break MERGE_GRADE_MAPPING; - } - } - // Did not find a matching grading scale. - log.info("Merge to gradebook {} skipped grade mapping change because grading scale {} is not defined", toGradebookUid, - fromGradingScaleUid); - } - return transversalMap; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public void removeAssignment(final Long assignmentId) throws StaleObjectModificationException { - - final HibernateCallback hc = new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - final GradebookAssignment asn = (GradebookAssignment) session.load(GradebookAssignment.class, assignmentId); - final Gradebook gradebook = asn.getGradebook(); - asn.setRemoved(true); - session.update(asn); - - if (log.isInfoEnabled()) { - log.info("GradebookAssignment " + asn.getName() + " has been removed from " + gradebook); - } - return null; - } - }; - getHibernateTemplate().execute(hc); - - } - - - @Override - public Long addAssignment(final String gradebookUid, final org.sakaiproject.service.gradebook.shared.Assignment assignmentDefinition) { - if (!getAuthz().isUserAbleToEditAssessments(gradebookUid)) { - log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to add an assignment", getUserUid(), gradebookUid); - throw new GradebookSecurityException(); - } - - final String validatedName = GradebookHelper.validateAssignmentNameAndPoints(assignmentDefinition); - - final Gradebook gradebook = getGradebook(gradebookUid); - - // if attaching to category - if (assignmentDefinition.getCategoryId() != null) { - return createAssignmentForCategory(gradebook.getId(), assignmentDefinition.getCategoryId(), validatedName, - assignmentDefinition.getPoints(), assignmentDefinition.getDueDate(), !assignmentDefinition.isCounted(), assignmentDefinition.isReleased(), - assignmentDefinition.isExtraCredit(), assignmentDefinition.getCategorizedSortOrder()); - } - - return createAssignment(gradebook.getId(), validatedName, assignmentDefinition.getPoints(), assignmentDefinition.getDueDate(), - !assignmentDefinition.isCounted(), assignmentDefinition.isReleased(), assignmentDefinition.isExtraCredit(), assignmentDefinition.getSortOrder()); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public void updateAssignment(final String gradebookUid, final Long assignmentId, - final org.sakaiproject.service.gradebook.shared.Assignment assignmentDefinition) { - if (!getAuthz().isUserAbleToEditAssessments(gradebookUid)) { - log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to change the definition of assignment {}", getUserUid(), - gradebookUid, assignmentId); - throw new GradebookSecurityException(); - } - - final String validatedName = GradebookHelper.validateAssignmentNameAndPoints(assignmentDefinition); - - final Gradebook gradebook = this.getGradebook(gradebookUid); - - getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - final GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentId); - if (assignment == null) { - throw new AssessmentNotFoundException( - "There is no assignment with id " + assignmentId + " in gradebook " + gradebookUid); - } - - // check if we need to scale the grades - boolean scaleGrades = false; - final Double originalPointsPossible = assignment.getPointsPossible(); - if (gradebook.getGrade_type() == GradebookService.GRADE_TYPE_PERCENTAGE - && !assignment.getPointsPossible().equals(assignmentDefinition.getPoints())) { - scaleGrades = true; - } - - if (gradebook.getGrade_type() == GradebookService.GRADE_TYPE_POINTS && assignmentDefinition.isScaleGrades()) { - scaleGrades = true; - } - - // external assessments are supported, but not these fields - if (!assignmentDefinition.isExternallyMaintained()) { - assignment.setName(validatedName); - assignment.setPointsPossible(assignmentDefinition.getPoints()); - assignment.setDueDate(assignmentDefinition.getDueDate()); - } - assignment.setExtraCredit(assignmentDefinition.isExtraCredit()); - assignment.setCounted(assignmentDefinition.isCounted()); - assignment.setReleased(assignmentDefinition.isReleased()); - - assignment.setExternalAppName(assignmentDefinition.getExternalAppName()); - assignment.setExternallyMaintained(assignmentDefinition.isExternallyMaintained()); - assignment.setExternalId(assignmentDefinition.getExternalId()); - assignment.setExternalData(assignmentDefinition.getExternalData()); - - // if we have a category, get it and set it - // otherwise clear it fully - if (assignmentDefinition.getCategoryId() != null) { - final Category cat = (Category) session.load(Category.class, assignmentDefinition.getCategoryId()); - assignment.setCategory(cat); - } else { - assignment.setCategory(null); - } - - updateAssignment(assignment); - - if (scaleGrades) { - scaleGrades(gradebook, assignment, originalPointsPossible); - } - - return null; - } - }); - } - - @Override - public CourseGrade getCourseGrade(final Long gradebookId) { - return (CourseGrade) getHibernateTemplate() - .findByNamedParam("from CourseGrade as cg where cg.gradebook.id = :gradebookid", "gradebookid", gradebookId).get(0); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public List getPointsEarnedCourseGradeRecords(final CourseGrade courseGrade, final Collection studentUids) { - final HibernateCallback hc = new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - if (studentUids == null || studentUids.isEmpty()) { - if (log.isInfoEnabled()) { - log.info("Returning no grade records for an empty collection of student UIDs"); - } - return new ArrayList(); - } - - final Query q = session.createQuery("from CourseGradeRecord as cgr where cgr.gradableObject.id=:gradableObjectId"); - q.setParameter("gradableObjectId", courseGrade.getId()); - final List records = filterAndPopulateCourseGradeRecordsByStudents(courseGrade, q.list(), studentUids); - - final Long gradebookId = courseGrade.getGradebook().getId(); - final Gradebook gradebook = getGradebook(gradebookId); - final List cates = getCategories(gradebookId); - - // get all of the AssignmentGradeRecords here to avoid repeated db calls - final Map> gradeRecMap = getGradeRecordMapForStudents(gradebookId, studentUids); - - // get all of the counted assignments - final List assignments = getCountedAssignments(session, gradebookId); - final List countedAssigns = new ArrayList<>(); - if (assignments != null) { - for (final GradebookAssignment assign : assignments) { - // extra check to account for new features like extra credit - if (assign.isIncludedInCalculations()) { - countedAssigns.add(assign); - } - } - } - // double totalPointsPossible = getTotalPointsInternal(gradebookId, session); - // if(log.isDebugEnabled()) log.debug("Total points = " + totalPointsPossible); - - for (final Iterator iter = records.iterator(); iter.hasNext();) { - final CourseGradeRecord cgr = (CourseGradeRecord) iter.next(); - // double totalPointsEarned = getTotalPointsEarnedInternal(gradebookId, cgr.getStudentId(), session); - final List studentGradeRecs = gradeRecMap.get(cgr.getStudentId()); - - applyDropScores(studentGradeRecs, gradebook.getCategory_type()); - final List totalEarned = getTotalPointsEarnedInternal(cgr.getStudentId(), gradebook, cates, studentGradeRecs, - countedAssigns); - final double totalPointsEarned = ((Double) totalEarned.get(0)); - final double literalTotalPointsEarned = ((Double) totalEarned.get(1)); - final double extraPointsEarned = ((Double) totalEarned.get(2)); - final double totalPointsPossible = getTotalPointsInternal(gradebook, cates, cgr.getStudentId(), studentGradeRecs, - countedAssigns, false); - cgr.initNonpersistentFields(totalPointsPossible, totalPointsEarned, literalTotalPointsEarned, extraPointsEarned); - if (log.isDebugEnabled()) { - log.debug("Points earned = " + cgr.getPointsEarned()); - } - if (log.isDebugEnabled()) { - log.debug("Points possible = " + cgr.getTotalPointsPossible()); - } - } - - return records; - } - }; - return (List) getHibernateTemplate().execute(hc); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private List filterAndPopulateCourseGradeRecordsByStudents(final CourseGrade courseGrade, final Collection gradeRecords, - final Collection studentUids) { - final List filteredRecords = new ArrayList(); - final Set missingStudents = new HashSet(studentUids); - for (final Iterator iter = gradeRecords.iterator(); iter.hasNext();) { - final CourseGradeRecord cgr = (CourseGradeRecord) iter.next(); - if (studentUids.contains(cgr.getStudentId())) { - filteredRecords.add(cgr); - missingStudents.remove(cgr.getStudentId()); - } - } - for (final Iterator iter = missingStudents.iterator(); iter.hasNext();) { - final String studentUid = (String) iter.next(); - final CourseGradeRecord cgr = new CourseGradeRecord(courseGrade, studentUid); - filteredRecords.add(cgr); - } - return filteredRecords; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private double getTotalPointsInternal(final Gradebook gradebook, final List categories, final String studentId, - final List studentGradeRecs, final List countedAssigns, - final boolean literalTotal) { - final int gbGradeType = gradebook.getGrade_type(); - if (gbGradeType != GradebookService.GRADE_TYPE_POINTS && gbGradeType != GradebookService.GRADE_TYPE_PERCENTAGE) { - if (log.isErrorEnabled()) { - log.error("Wrong grade type in GradebookCalculationImpl.getTotalPointsInternal"); - } - return -1; - } - - if (studentGradeRecs == null || countedAssigns == null) { - if (log.isDebugEnabled()) { - log.debug("Returning 0 from getTotalPointsInternal " + - "since studentGradeRecs or countedAssigns was null"); - } - return 0; - } - - double totalPointsPossible = 0; - - final HashSet countedSet = new HashSet<>(countedAssigns); - - // we need to filter this list to identify only "counted" grade recs - final List countedGradeRecs = new ArrayList<>(); - for (final AssignmentGradeRecord gradeRec : studentGradeRecs) { - final GradebookAssignment assign = gradeRec.getAssignment(); - boolean extraCredit = assign.isExtraCredit(); - if (gradebook.getCategory_type() != GradebookService.CATEGORY_TYPE_NO_CATEGORY && assign.getCategory() != null - && assign.getCategory().isExtraCredit()) { - extraCredit = true; - } - - final boolean excused = BooleanUtils.toBoolean(gradeRec.isExcludedFromGrade()); - if (assign.isCounted() && !assign.getUngraded() && !assign.isRemoved() && countedSet.contains(assign) && - assign.getPointsPossible() != null && assign.getPointsPossible() > 0 && !gradeRec.getDroppedFromGrade() && !extraCredit - && !excused) { - countedGradeRecs.add(gradeRec); - } - } - - final Set assignmentsTaken = new HashSet(); - final Set categoryTaken = new HashSet(); - for (final AssignmentGradeRecord gradeRec : countedGradeRecs) { - if (gradeRec.getPointsEarned() != null && !gradeRec.getPointsEarned().equals("")) { - final Double pointsEarned = gradeRec.getPointsEarned(); - final GradebookAssignment go = gradeRec.getAssignment(); - if (pointsEarned != null) { - if (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_NO_CATEGORY) { - assignmentsTaken.add(go.getId()); - } else if ((gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_ONLY_CATEGORY || gradebook - .getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY) - && go != null && categories != null) { - // assignmentsTaken.add(go.getId()); - // } - // else if(gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY && go != null && - // categories != null) - // { - for (int i = 0; i < categories.size(); i++) { - final Category cate = (Category) categories.get(i); - if (cate != null && !cate.isRemoved() && go.getCategory() != null - && cate.getId().equals(go.getCategory().getId()) - && ((cate.isExtraCredit() != null && !cate.isExtraCredit()) || cate.isExtraCredit() == null)) { - assignmentsTaken.add(go.getId()); - categoryTaken.add(cate.getId()); - break; - } - } - } - } - } - } - - if (!assignmentsTaken.isEmpty()) { - if (!literalTotal && gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY) { - for (int i = 0; i < categories.size(); i++) { - final Category cate = (Category) categories.get(i); - if (cate != null && !cate.isRemoved() && categoryTaken.contains(cate.getId())) { - totalPointsPossible += cate.getWeight(); - } - } - return totalPointsPossible; - } - final Iterator assignmentIter = countedAssigns.iterator(); - while (assignmentIter.hasNext()) { - final GradebookAssignment asn = (GradebookAssignment) assignmentIter.next(); - if (asn != null) { - final Double pointsPossible = asn.getPointsPossible(); - - if (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_NO_CATEGORY - && assignmentsTaken.contains(asn.getId())) { - totalPointsPossible += pointsPossible; - } else if (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_ONLY_CATEGORY - && assignmentsTaken.contains(asn.getId())) { - totalPointsPossible += pointsPossible; - } else if (literalTotal && gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY - && assignmentsTaken.contains(asn.getId())) { - totalPointsPossible += pointsPossible; - } - } - } - } else { - totalPointsPossible = -1; - } - - return totalPointsPossible; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private List getTotalPointsEarnedInternal(final String studentId, final Gradebook gradebook, final List categories, - final List gradeRecs, final List countedAssigns) { - final int gbGradeType = gradebook.getGrade_type(); - if (gbGradeType != GradebookService.GRADE_TYPE_POINTS && gbGradeType != GradebookService.GRADE_TYPE_PERCENTAGE) { - if (log.isErrorEnabled()) { - log.error("Wrong grade type in GradebookCalculationImpl.getTotalPointsEarnedInternal"); - } - return new ArrayList(); - } - - if (gradeRecs == null || countedAssigns == null) { - if (log.isDebugEnabled()) { - log.debug("getTotalPointsEarnedInternal for " + - "studentId=" + studentId + " returning 0 because null gradeRecs or countedAssigns"); - } - final List returnList = new ArrayList(); - returnList.add(new Double(0)); - returnList.add(new Double(0)); - returnList.add(new Double(0)); // 3rd one is for the pre-adjusted course grade - return returnList; - } - - BigDecimal totalPointsEarned = new BigDecimal(0); - BigDecimal extraPointsEarned = new BigDecimal(0); - BigDecimal literalTotalPointsEarned = new BigDecimal(0d); - - final Map cateScoreMap = new HashMap<>(); - final Map cateTotalScoreMap = new HashMap<>(); - final Set assignmentsTaken = new HashSet<>(); - - for (final AssignmentGradeRecord gradeRec : gradeRecs) { - final boolean excused = BooleanUtils.toBoolean(gradeRec.isExcludedFromGrade()); - - if (gradeRec.getPointsEarned() != null && !gradeRec.getPointsEarned().equals("") && !gradeRec.getDroppedFromGrade()) { - final GradebookAssignment go = gradeRec.getAssignment(); - if (go.isIncludedInCalculations() && countedAssigns.contains(go)) { - BigDecimal pointsEarned = BigDecimal.valueOf(gradeRec.getPointsEarned()); - final BigDecimal pointsPossible = BigDecimal.valueOf(go.getPointsPossible()); - - // if(gbGradeType == GradebookService.GRADE_TYPE_POINTS) - // { - if (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_NO_CATEGORY) { - if (!excused) { - totalPointsEarned = totalPointsEarned.add(pointsEarned, GradebookService.MATH_CONTEXT); - literalTotalPointsEarned = pointsEarned.add(literalTotalPointsEarned, GradebookService.MATH_CONTEXT); - assignmentsTaken.add(go.getId()); - } - } else if (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_ONLY_CATEGORY && go != null) { - if (!excused) { - totalPointsEarned = totalPointsEarned.add(pointsEarned, GradebookService.MATH_CONTEXT); - literalTotalPointsEarned = pointsEarned.add(literalTotalPointsEarned, GradebookService.MATH_CONTEXT); - assignmentsTaken.add(go.getId()); - } - } else if (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY && go != null - && categories != null) { - for (int i = 0; i < categories.size(); i++) { - final Category cate = (Category) categories.get(i); - if (cate != null && !cate.isRemoved() && go.getCategory() != null - && cate.getId().equals(go.getCategory().getId())) { - if (!excused) { - assignmentsTaken.add(go.getId()); - literalTotalPointsEarned = pointsEarned.add(literalTotalPointsEarned, GradebookService.MATH_CONTEXT); - - // If category is equal weight, manipulate points to be the average - if (cate.isEqualWeightAssignments()) { - pointsEarned = pointsEarned.divide(pointsPossible, GradebookService.MATH_CONTEXT); - } - - if (cateScoreMap.get(cate.getId()) != null) { - cateScoreMap.put(cate.getId(), ((BigDecimal)cateScoreMap.get(cate.getId())).add(pointsEarned, GradebookService.MATH_CONTEXT)); - } else { - cateScoreMap.put(cate.getId(), pointsEarned); - } - } - break; - } - } - } - } - } - } - - if (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY && categories != null) { - final Iterator assgnsIter = countedAssigns.iterator(); - while (assgnsIter.hasNext()) { - final GradebookAssignment asgn = (GradebookAssignment) assgnsIter.next(); - BigDecimal pointsPossible = new BigDecimal(asgn.getPointsPossible()); - - if (assignmentsTaken.contains(asgn.getId())) { - for (int i = 0; i < categories.size(); i++) { - final Category cate = (Category) categories.get(i); - if (cate != null && !cate.isRemoved() && asgn.getCategory() != null - && cate.getId().equals(asgn.getCategory().getId()) && !asgn.isExtraCredit()) { - - // If it's equal-weight category, just want to divide averages by number of items - if (cate.isEqualWeightAssignments()) { - pointsPossible = new BigDecimal("1"); - } - - if (cateTotalScoreMap.get(cate.getId()) == null) { - cateTotalScoreMap.put(cate.getId(), pointsPossible); - } else { - cateTotalScoreMap.put(cate.getId(), - ((BigDecimal) cateTotalScoreMap.get(cate.getId())).add(pointsPossible)); - } - } - } - } - } - } - - if (assignmentsTaken.isEmpty()) { - totalPointsEarned = new BigDecimal(-1); - } - - if (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY) { - for (int i = 0; i < categories.size(); i++) { - final Category cate = (Category) categories.get(i); - if (cate != null && !cate.isRemoved() && cateScoreMap.get(cate.getId()) != null - && cateTotalScoreMap.get(cate.getId()) != null) { - if (cate.getIsExtraCredit()) { - extraPointsEarned = extraPointsEarned.add(((BigDecimal) cateScoreMap.get(cate.getId())).multiply(new BigDecimal(cate.getWeight()), GradebookService.MATH_CONTEXT) - .divide((BigDecimal) cateTotalScoreMap.get(cate.getId()), GradebookService.MATH_CONTEXT)); - } - else { - totalPointsEarned = totalPointsEarned.add(((BigDecimal) cateScoreMap.get(cate.getId())).multiply(new BigDecimal(cate.getWeight()), GradebookService.MATH_CONTEXT) - .divide((BigDecimal) cateTotalScoreMap.get(cate.getId()), GradebookService.MATH_CONTEXT)); - } - } - } - } - - if (log.isDebugEnabled()) { - log.debug("getTotalPointsEarnedInternal for studentId=" + studentId + " returning " + totalPointsEarned); - } - final List returnList = new ArrayList(); - returnList.add(totalPointsEarned.doubleValue()); - returnList.add(literalTotalPointsEarned.doubleValue()); - returnList.add(extraPointsEarned.doubleValue()); - - return returnList; - } - - /** - * Internal method to get a gradebook based on its id. - * - * @param id - * @return - * - * NOTE: When the UI changes, this is to be turned private again - */ - public Gradebook getGradebook(final Long id) { - return getHibernateTemplate().load(Gradebook.class, id); - } - - private List getAssignmentsCounted(final Long gradebookId) throws HibernateException { - final HibernateCallback> hc = session -> session - .createQuery( - "from GradebookAssignment as asn where asn.gradebook.id = :gradebookid and asn.removed is false and asn.notCounted is false") - .setParameter("gradebookid", gradebookId) - .list(); - return getHibernateTemplate().execute(hc); - } - - @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) - public boolean checkStudentsNotSubmitted(final String gradebookUid) { - final Gradebook gradebook = getGradebook(gradebookUid); - final Set studentUids = getAllStudentUids(getGradebookUid(gradebook.getId())); - if (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_NO_CATEGORY - || gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_ONLY_CATEGORY) { - final List records = getAllAssignmentGradeRecords(gradebook.getId(), studentUids); - final List assigns = getAssignments(gradebook.getId(), SortType.SORT_BY_SORTING, true); - final List filteredAssigns = new ArrayList(); - for (final Iterator iter = assigns.iterator(); iter.hasNext();) { - final GradebookAssignment assignment = (GradebookAssignment) iter.next(); - if (assignment.isCounted() && !assignment.getUngraded()) { - filteredAssigns.add(assignment); - } - } - final List filteredRecords = new ArrayList(); - for (final Iterator iter = records.iterator(); iter.hasNext();) { - final AssignmentGradeRecord agr = (AssignmentGradeRecord) iter.next(); - if (!agr.isCourseGradeRecord() && agr.getAssignment().isCounted() && !agr.getAssignment().getUngraded()) { - if (agr.getPointsEarned() == null) { - return true; - } - filteredRecords.add(agr); - } - } - - return filteredRecords.size() < (filteredAssigns.size() * studentUids.size()); - } else { - final List assigns = getAssignments(gradebook.getId(), SortType.SORT_BY_SORTING, true); - final List records = getAllAssignmentGradeRecords(gradebook.getId(), studentUids); - final Set filteredAssigns = new HashSet(); - for (final Iterator iter = assigns.iterator(); iter.hasNext();) { - final GradebookAssignment assign = (GradebookAssignment) iter.next(); - if (assign != null && assign.isCounted() && !assign.getUngraded()) { - if (assign.getCategory() != null && !assign.getCategory().isRemoved()) { - filteredAssigns.add(assign.getId()); - } - } - } - - final List filteredRecords = new ArrayList(); - for (final Iterator iter = records.iterator(); iter.hasNext();) { - final AssignmentGradeRecord agr = (AssignmentGradeRecord) iter.next(); - if (filteredAssigns.contains(agr.getAssignment().getId()) && !agr.isCourseGradeRecord()) { - if (agr.getPointsEarned() == null) { - return true; - } - filteredRecords.add(agr); - } - } - - return filteredRecords.size() < filteredAssigns.size() * studentUids.size(); - } - } - - /** - * Get all assignment grade records for the given students - * - * @param gradebookId - * @param studentUids - * @return - * - * NOTE When the UI changes, this needs to be made private again - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public List getAllAssignmentGradeRecords(final Long gradebookId, final Collection studentUids) { - final HibernateCallback hc = new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - if (studentUids.isEmpty()) { - // If there are no enrollments, no need to execute the query. - if (log.isInfoEnabled()) { - log.info("No enrollments were specified. Returning an empty List of grade records"); - } - return new ArrayList(); - } else { - final Query q = session.createQuery("from AssignmentGradeRecord as agr where agr.gradableObject.removed=false and " + - "agr.gradableObject.gradebook.id=:gradebookId order by agr.pointsEarned"); - q.setParameter("gradebookId", gradebookId); - return filterGradeRecordsByStudents(q.list(), studentUids); - } - } - }; - return (List) getHibernateTemplate().execute(hc); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private List getAllAssignmentGradeRecordsForGbItem(final Long gradableObjectId, - final Collection studentUids) { - final HibernateCallback hc = new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - if (studentUids.isEmpty()) { - // If there are no enrollments, no need to execute the query. - if (log.isInfoEnabled()) { - log.info("No enrollments were specified. Returning an empty List of grade records"); - } - return new ArrayList(); - } else { - final Query q = session.createQuery("from AssignmentGradeRecord as agr where agr.gradableObject.removed=false and " + - "agr.gradableObject.id=:gradableObjectId order by agr.pointsEarned"); - q.setParameter("gradableObjectId", gradableObjectId); - return filterGradeRecordsByStudents(q.list(), studentUids); - } - } - }; - return (List) getHibernateTemplate().execute(hc); - } - - /** - * Gets all AssignmentGradeRecords on the gradableObjectIds limited to students specified by studentUids - */ - private List getAllAssignmentGradeRecordsForGbItems(final List gradableObjectIds, final List studentUids) { - final HibernateCallback hc = new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - final List gradeRecords = new ArrayList<>(); - if (studentUids.isEmpty()) { - // If there are no enrollments, no need to execute the query. - if (log.isDebugEnabled()) { - log.debug("No enrollments were specified. Returning an empty List of grade records"); - } - return gradeRecords; - } - /* - * Watch out for Oracle's "in" limit. Ignoring oracle, the query would be: - * "from AssignmentGradeRecord as agr where agr.gradableObject.removed = false and agr.gradableObject.id in (:gradableObjectIds) and agr.studentId in (:studentUids)" - * Note: the order is not important. The calling methods will iterate over all entries and add them to a map. We could have - * made this method return a map, but we'd have to iterate over the items in order to add them to the map anyway. That would - * be a waste of a loop that the calling method could use to perform additional tasks. - */ - // For Oracle, iterate over gbItems 1000 at a time (sympathies to whoever needs to query grades for a thousand gbItems) - int minGbo = 0; - int maxGbo = Math.min(gradableObjectIds.size(), 1000); - while (minGbo < gradableObjectIds.size()) { - // For Oracle, iterate over students 1000 at a time - int minStudent = 0; - int maxStudent = Math.min(studentUids.size(), 1000); - while (minStudent < studentUids.size()) { - final Query q = session - .createQuery("from AssignmentGradeRecord as agr where agr.gradableObject.removed = false and " + - "agr.gradableObject.id in (:gradableObjectIds) and agr.studentId in (:studentUids)"); - q.setParameterList("gradableObjectIds", gradableObjectIds.subList(minGbo, maxGbo)); - q.setParameterList("studentUids", studentUids.subList(minStudent, maxStudent)); - // Add the query results to our overall results (in case there's over a thousand things) - gradeRecords.addAll(q.list()); - minStudent += 1000; - maxStudent = Math.min(studentUids.size(), minStudent + 1000); - } - minGbo += 1000; - maxGbo = Math.min(gradableObjectIds.size(), minGbo + 1000); - } - return gradeRecords; - } - }; - return (List) getHibernateTemplate().execute(hc); - } - - /** - * Get a list of assignments, sorted - * - * @param gradebookId - * @param sortBy - * @param ascending - * @return - * - * NOTE: When the UI changes, this needs to go back to private - */ - public List getAssignments(final Long gradebookId, final SortType sortBy, final boolean ascending) { - final List assignments = getAssignments(gradebookId); - sortAssignments(assignments, sortBy, ascending); - return assignments; - } - - /** - * Sort the list of (internal) assignments by the given criteria - * - * @param assignments - * @param sortBy - * @param ascending - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - private void sortAssignments(final List assignments, SortType sortBy, final boolean ascending) { - - // note, this is duplicated in the tool GradebookManagerHibernateImpl class - Comparator comp; - - if (sortBy == null) { - sortBy = SortType.SORT_BY_SORTING; // default - } - - switch (sortBy) { - - case SORT_BY_NONE: - return; // no sorting - case SORT_BY_NAME: - comp = GradableObject.nameComparator; - break; - case SORT_BY_DATE: - comp = GradableObject.dateComparator; - break; - case SORT_BY_MEAN: - comp = GradableObject.meanComparator; - break; - case SORT_BY_POINTS: - comp = GradebookAssignment.pointsComparator; - break; - case SORT_BY_RELEASED: - comp = GradebookAssignment.releasedComparator; - break; - case SORT_BY_COUNTED: - comp = GradebookAssignment.countedComparator; - break; - case SORT_BY_EDITOR: - comp = GradebookAssignment.gradeEditorComparator; - break; - case SORT_BY_SORTING: - comp = GradableObject.sortingComparator; - break; - case SORT_BY_CATEGORY: - comp = GradebookAssignment.categoryComparator; - break; - default: - comp = GradableObject.defaultComparator; - } - - Collections.sort(assignments, comp); - if (!ascending) { - Collections.reverse(assignments); - } - if (log.isDebugEnabled()) { - log.debug("sortAssignments: ordering by " + sortBy + " (" + comp + "), ascending=" + ascending); - } - } - - /* - * (non-Javadoc) - * - * @see org.sakaiproject.service.gradebook.shared.GradebookService#getViewableAssignmentsForCurrentUser(java.lang.String) - */ - @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) - public List getViewableAssignmentsForCurrentUser(final String gradebookUid) - throws GradebookNotFoundException { - return getViewableAssignmentsForCurrentUser(gradebookUid, SortType.SORT_BY_SORTING); - } - - /* - * (non-Javadoc) - * - * @see org.sakaiproject.service.gradebook.shared.GradebookService#getViewableAssignmentsForCurrentUser(java.lang.String, java.) - */ - @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) - public List getViewableAssignmentsForCurrentUser(final String gradebookUid, - final SortType sortBy) - throws GradebookNotFoundException { - - List viewableAssignments = new ArrayList<>(); - final LinkedHashSet assignmentsToReturn = new LinkedHashSet<>(); - - final Gradebook gradebook = getGradebook(gradebookUid); - - // will send back all assignments if user can grade all - if (getAuthz().isUserAbleToGradeAll(gradebookUid)) { - viewableAssignments = getAssignments(gradebook.getId(), sortBy, true); - } else if (getAuthz().isUserAbleToGrade(gradebookUid)) { - // if user can grade and doesn't have grader perm restrictions, they - // may view all assigns - if (!getAuthz().isUserHasGraderPermissions(gradebookUid)) { - viewableAssignments = getAssignments(gradebook.getId(), sortBy, true); - } else { - // this user has grader perms, so we need to filter the items returned - // if this gradebook has categories enabled, we need to check for category-specific restrictions - if (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_NO_CATEGORY) { - assignmentsToReturn.addAll(getAssignments(gradebookUid, sortBy)); - } else { - final String userUid = getUserUid(); - if (getGradebookPermissionService().getPermissionForUserForAllAssignment(gradebook.getId(), userUid)) { - assignmentsToReturn.addAll(getAssignments(gradebookUid, sortBy)); - } else { - final List assignments = getAssignments(gradebookUid, sortBy); - final List categoryIds = ((List) getCategories(gradebook.getId())).stream().map(Category::getId) - .collect(Collectors.toList()); - // categories are enabled, so we need to check the category restrictions - if (!categoryIds.isEmpty()) { - final List viewableCategoryIds = getGradebookPermissionService().getCategoriesForUser(gradebook.getId(), - userUid, categoryIds); - for (final org.sakaiproject.service.gradebook.shared.Assignment assignment : assignments) { - if (assignment.getCategoryId() != null && viewableCategoryIds.contains(assignment.getCategoryId())) { - assignmentsToReturn.add(assignment); - } - } - } - } - } - } - } else if (getAuthz().isUserAbleToViewOwnGrades(gradebookUid)) { - // if user is just a student, we need to filter out unreleased items - final List allAssigns = getAssignments(gradebook.getId(), sortBy, true); - if (allAssigns != null) { - for (final Iterator aIter = allAssigns.iterator(); aIter.hasNext();) { - final GradebookAssignment assign = (GradebookAssignment) aIter.next(); - if (assign != null && assign.isReleased()) { - viewableAssignments.add(assign); - } - } - } - } - - // Now we need to convert these to the assignment template objects - if (viewableAssignments != null && !viewableAssignments.isEmpty()) { - for (final Object element : viewableAssignments) { - final GradebookAssignment assignment = (GradebookAssignment) element; - assignmentsToReturn.add(getAssignmentDefinition(assignment)); - } - } - - return new ArrayList<>(assignmentsToReturn); - - } - - @Override - public Map getViewableStudentsForItemForCurrentUser(final String gradebookUid, final Long gradableObjectId) { - final String userUid = this.authn.getUserUid(); - - return getViewableStudentsForItemForUser(userUid, gradebookUid, gradableObjectId); - } - - @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) - public Map getViewableStudentsForItemForUser(final String userUid, final String gradebookUid, - final Long gradableObjectId) { - - if (gradebookUid == null || gradableObjectId == null || userUid == null) { - throw new IllegalArgumentException("null gradebookUid or gradableObjectId or " + - "userId passed to getViewableStudentsForUserForItem." + - " gradebookUid: " + gradebookUid + " gradableObjectId:" + - gradableObjectId + " userId: " + userUid); - } - - if (!this.authz.isUserAbleToGrade(gradebookUid, userUid)) { - return new HashMap<>(); - } - - final GradebookAssignment gradebookItem = getAssignmentWithoutStatsByID(gradebookUid, gradableObjectId); - - if (gradebookItem == null) { - log.debug("The gradebook item does not exist, so returning empty set"); - return new HashMap(); - } - - final Long categoryId = gradebookItem.getCategory() == null ? null : gradebookItem.getCategory().getId(); - - final Map enrRecFunctionMap = this.authz.findMatchingEnrollmentsForItemForUser(userUid, gradebookUid, - categoryId, getGradebook(gradebookUid).getCategory_type(), null, null); - if (enrRecFunctionMap == null) { - return new HashMap(); - } - - final Map studentIdFunctionMap = new HashMap(); - for (final Entry entry : enrRecFunctionMap.entrySet()) { - final EnrollmentRecord enr = entry.getKey(); - if (enr != null && enrRecFunctionMap.get(enr) != null) { - studentIdFunctionMap.put(enr.getUser().getUserUid(), entry.getValue()); - } - } - return studentIdFunctionMap; - } - - @Override - public boolean isGradableObjectDefined(final Long gradableObjectId) { - if (gradableObjectId == null) { - throw new IllegalArgumentException("null gradableObjectId passed to isGradableObjectDefined"); - } - - return isAssignmentDefined(gradableObjectId); - } - - @Override - public Map getViewableSectionUuidToNameMap(final String gradebookUid) { - if (gradebookUid == null) { - throw new IllegalArgumentException("Null gradebookUid passed to getViewableSectionIdToNameMap"); - } - - final Map sectionIdNameMap = new HashMap<>(); - - final List viewableCourseSections = getAuthz().getViewableSections(gradebookUid); - if (viewableCourseSections == null || viewableCourseSections.isEmpty()) { - return sectionIdNameMap; - } - - for (final Iterator sectionIter = viewableCourseSections.iterator(); sectionIter.hasNext();) { - final CourseSection section = (CourseSection) sectionIter.next(); - if (section != null) { - sectionIdNameMap.put(section.getUuid(), section.getTitle()); - } - } - - return sectionIdNameMap; - } - - @Override - public boolean currentUserHasGradeAllPerm(final String gradebookUid) { - return this.authz.isUserAbleToGradeAll(gradebookUid); - } - - @Override - public boolean isUserAllowedToGradeAll(final String gradebookUid, final String userUid) { - return this.authz.isUserAbleToGradeAll(gradebookUid, userUid); - } - - @Override - public boolean currentUserHasGradingPerm(final String gradebookUid) { - return this.authz.isUserAbleToGrade(gradebookUid); - } - - @Override - public boolean isUserAllowedToGrade(final String gradebookUid, final String userUid) { - return this.authz.isUserAbleToGrade(gradebookUid, userUid); - } - - @Override - public boolean currentUserHasEditPerm(final String gradebookUid) { - return this.authz.isUserAbleToEditAssessments(gradebookUid); - } - - @Override - public boolean currentUserHasViewOwnGradesPerm(final String gradebookUid) { - return this.authz.isUserAbleToViewOwnGrades(gradebookUid); - } - - @Override - public boolean currentUserHasViewStudentNumbersPerm(final String gradebookUid) { - return this.authz.isUserAbleToViewStudentNumbers(gradebookUid); - } - - @Override - public List getGradesForStudentsForItem(final String gradebookUid, final Long gradableObjectId, - final List studentIds) { - if (gradableObjectId == null) { - throw new IllegalArgumentException("null gradableObjectId passed to getGradesForStudentsForItem"); - } - - final List studentGrades = new ArrayList(); - - if (studentIds != null && !studentIds.isEmpty()) { - // first, we need to make sure the current user is authorized to view the - // grades for all of the requested students - final GradebookAssignment gbItem = getAssignmentWithoutStatsByID(gradebookUid, gradableObjectId); - - if (gbItem != null) { - final Gradebook gradebook = gbItem.getGradebook(); - - if (!this.authz.isUserAbleToGrade(gradebook.getUid())) { - log.error( - "User {} attempted to access grade information without permission in gb {} using gradebookService.getGradesForStudentsForItem", - this.authn.getUserUid(), gradebook.getUid()); - throw new GradebookSecurityException(); - } - - final Long categoryId = gbItem.getCategory() != null ? gbItem.getCategory().getId() : null; - final Map enrRecFunctionMap = this.authz.findMatchingEnrollmentsForItem(gradebook.getUid(), categoryId, - gradebook.getCategory_type(), null, null); - final Set enrRecs = enrRecFunctionMap.keySet(); - final Map studentIdEnrRecMap = new HashMap(); - if (enrRecs != null) { - for (final Iterator enrIter = enrRecs.iterator(); enrIter.hasNext();) { - final EnrollmentRecord enr = (EnrollmentRecord) enrIter.next(); - if (enr != null) { - studentIdEnrRecMap.put(enr.getUser().getUserUid(), enr); - } - } - } - - // filter the provided studentIds if user doesn't have permissions - studentIds.removeIf(studentId -> { - return !studentIdEnrRecMap.containsKey(studentId); - }); - - // retrieve the grading comments for all of the students - final List commentRecs = getComments(gbItem, studentIds); - final Map studentIdCommentTextMap = new HashMap(); - if (commentRecs != null) { - for (final Comment comment : commentRecs) { - if (comment != null) { - studentIdCommentTextMap.put(comment.getStudentId(), comment.getCommentText()); - } - } - } - - // now, we can populate the grade information - final List studentsWithGradeRec = new ArrayList<>(); - final List gradeRecs = getAllAssignmentGradeRecordsForGbItem(gradableObjectId, studentIds); - if (gradeRecs != null) { - if (gradebook.getGrade_type() == GradebookService.GRADE_TYPE_LETTER) { - convertPointsToLetterGrade(gradebook, gradeRecs); - } else if (gradebook.getGrade_type() == GradebookService.GRADE_TYPE_PERCENTAGE) { - convertPointsToPercentage(gradebook, gradeRecs); - } - - for (final Object element : gradeRecs) { - final AssignmentGradeRecord agr = (AssignmentGradeRecord) element; - if (agr != null) { - final String commentText = studentIdCommentTextMap.get(agr.getStudentId()); - final GradeDefinition gradeDef = convertGradeRecordToGradeDefinition(agr, gbItem, gradebook, commentText); - - studentGrades.add(gradeDef); - studentsWithGradeRec.add(agr.getStudentId()); - } - } - - // if student has a comment but no grade add an empty grade definition with the comment - if (studentsWithGradeRec.size() < studentIds.size()) { - for (final String studentId : studentIdCommentTextMap.keySet()) { - if (!studentsWithGradeRec.contains(studentId)) { - final String comment = studentIdCommentTextMap.get(studentId); - final AssignmentGradeRecord emptyGradeRecord = new AssignmentGradeRecord(gbItem, studentId, null); - final GradeDefinition gradeDef = convertGradeRecordToGradeDefinition(emptyGradeRecord, gbItem, gradebook, - comment); - studentGrades.add(gradeDef); - } - } - } - } - } - } - - return studentGrades; - } - - @Override - public Map> getGradesWithoutCommentsForStudentsForItems(final String gradebookUid, - final List gradableObjectIds, final List studentIds) { - if (!this.authz.isUserAbleToGrade(gradebookUid)) { - throw new GradebookSecurityException(); - } - - if (gradableObjectIds == null || gradableObjectIds.isEmpty()) { - throw new IllegalArgumentException("null or empty gradableObjectIds passed to getGradesWithoutCommentsForStudentsForItems"); - } - - final Map> gradesMap = new HashMap<>(); - if (studentIds == null || studentIds.isEmpty()) { - // We could populate the map with (gboId : new ArrayList()), but it's cheaper to allow get(gboId) to return null. - return gradesMap; - } - - // Get all the grades for the gradableObjectIds - final List gradeRecords = getAllAssignmentGradeRecordsForGbItems(gradableObjectIds, studentIds); - // AssignmentGradeRecord is not in the API. So we need to convert grade records into GradeDefinition objects. - // GradeDefinitions are not tied to their gbos, so we need to return a map associating them back to their gbos - final List gradeDefinitions = new ArrayList<>(); - for (final AssignmentGradeRecord gradeRecord : gradeRecords) { - final GradebookAssignment gbo = (GradebookAssignment) gradeRecord.getGradableObject(); - final Long gboId = gbo.getId(); - final Gradebook gradebook = gbo.getGradebook(); - if (!gradebookUid.equals(gradebook.getUid())) { - // The user is authorized against gradebookUid, but we have grades for another gradebook. - // This is an authorization issue caused by gradableObjectIds violating the method contract. - throw new IllegalArgumentException("gradableObjectIds must belong to grades within this gradebook"); - } - - final GradeDefinition gradeDef = convertGradeRecordToGradeDefinition(gradeRecord, gbo, gradebook, null); - - List gradeList = gradesMap.get(gboId); - if (gradeList == null) { - gradeList = new ArrayList<>(); - gradesMap.put(gboId, gradeList); - } - gradeList.add(gradeDef); - } - - return gradesMap; - } - - /** - * Converts an AssignmentGradeRecord into a GradeDefinition object. - * - * @param gradeRecord - * @param gbo - * @param gradebook - * @param commentText - goes into the GradeComment attribute. Will be omitted if null - * @return a GradeDefinition object whose attributes match the passed in gradeRecord - */ - private GradeDefinition convertGradeRecordToGradeDefinition(final AssignmentGradeRecord gradeRecord, final GradebookAssignment gbo, - final Gradebook gradebook, final String commentText) { - final GradeDefinition gradeDef = new GradeDefinition(); - gradeDef.setStudentUid(gradeRecord.getStudentId()); - gradeDef.setGraderUid(gradeRecord.getGraderId()); - gradeDef.setDateRecorded(gradeRecord.getDateRecorded()); - final int gradeEntryType = gradebook.getGrade_type(); - gradeDef.setGradeEntryType(gradeEntryType); - String grade = null; - if (gradeEntryType == GradebookService.GRADE_TYPE_LETTER) { - grade = gradeRecord.getLetterEarned(); - } else if (gradeEntryType == GradebookService.GRADE_TYPE_PERCENTAGE) { - final Double percentEarned = gradeRecord.getPercentEarned(); - grade = percentEarned != null ? percentEarned.toString() : null; - } else { - final Double pointsEarned = gradeRecord.getPointsEarned(); - grade = pointsEarned != null ? pointsEarned.toString() : null; - } - gradeDef.setGrade(grade); - gradeDef.setGradeReleased(gradebook.isAssignmentsDisplayed() && gbo.isReleased()); - - if (commentText != null) { - gradeDef.setGradeComment(commentText); - } - - gradeDef.setExcused(gradeRecord.isExcludedFromGrade()); - - return gradeDef; - } - - @Override - public boolean isGradeValid(final String gradebookUuid, final String grade) { - if (gradebookUuid == null) { - throw new IllegalArgumentException("Null gradebookUuid passed to isGradeValid"); - } - Gradebook gradebook; - try { - gradebook = getGradebook(gradebookUuid); - } catch (final GradebookNotFoundException gnfe) { - throw new GradebookNotFoundException("No gradebook exists with the given gradebookUid: " + - gradebookUuid + "Error: " + gnfe.getMessage()); - } - - final int gradeEntryType = gradebook.getGrade_type(); - LetterGradePercentMapping mapping = null; - if (gradeEntryType == GradebookService.GRADE_TYPE_LETTER) { - mapping = getLetterGradePercentMapping(gradebook); - } - - return isGradeValid(grade, gradeEntryType, mapping); - } - - @Override - public boolean isValidNumericGrade(final String grade) { - boolean gradeIsValid = false; - - try { - final NumberFormat nbFormat = NumberFormat.getInstance(new ResourceLoader().getLocale()); - final Double gradeAsDouble = nbFormat.parse(grade).doubleValue(); - final String decSeparator = ((DecimalFormat) nbFormat).getDecimalFormatSymbols().getDecimalSeparator() + ""; - - // grade must be greater than or equal to 0 - if (gradeAsDouble >= 0) { - final String[] splitOnDecimal = grade.split("\\" + decSeparator); - // check that there are no more than 2 decimal places - if (splitOnDecimal == null) { - gradeIsValid = true; - - // check for a valid score matching ##########.## - // where integer is maximum of 10 integers in length - // and maximum of 2 decimal places - } else if (grade.matches("[0-9]{0,10}(\\" + decSeparator + "[0-9]{0,2})?")) { - gradeIsValid = true; - } - } - } catch (NumberFormatException | ParseException nfe) { - log.debug("Passed grade is not a numeric value"); - } - - return gradeIsValid; - } - - private boolean isGradeValid(final String grade, final int gradeEntryType, final LetterGradePercentMapping gradeMapping) { - - boolean gradeIsValid = false; - - if (grade == null || "".equals(grade)) { - - gradeIsValid = true; - - } else { - - if (gradeEntryType == GradebookService.GRADE_TYPE_POINTS || - gradeEntryType == GradebookService.GRADE_TYPE_PERCENTAGE) { - try { - final NumberFormat nbFormat = NumberFormat.getInstance(new ResourceLoader().getLocale()); - final Double gradeAsDouble = nbFormat.parse(grade).doubleValue(); - final String decSeparator = ((DecimalFormat) nbFormat).getDecimalFormatSymbols().getDecimalSeparator() + ""; - // grade must be greater than or equal to 0 - if (gradeAsDouble >= 0) { - final String[] splitOnDecimal = grade.split("\\" + decSeparator); - // check that there are no more than 2 decimal places - if (splitOnDecimal == null) { - gradeIsValid = true; - - // check for a valid score matching ##########.## - // where integer is maximum of 10 integers in length - // and maximum of 2 decimal places - } else if (grade.matches("[0-9]{0,10}(\\" + decSeparator + "[0-9]{0,2})?")) { - gradeIsValid = true; - } - } - } catch (NumberFormatException | ParseException nfe) { - log.debug("Passed grade is not a numeric value"); - } - - } else if (gradeEntryType == GradebookService.GRADE_TYPE_LETTER) { - if (gradeMapping == null) { - throw new IllegalArgumentException("Null mapping passed to isGradeValid for a letter grade-based gradeook"); - } - - final String standardizedGrade = gradeMapping.standardizeInputGrade(grade); - if (standardizedGrade != null) { - gradeIsValid = true; - } - } else { - throw new IllegalArgumentException("Invalid gradeEntryType passed to isGradeValid"); - } - } - - return gradeIsValid; - } - - @Override - public List identifyStudentsWithInvalidGrades(final String gradebookUid, final Map studentIdToGradeMap) { - if (gradebookUid == null) { - throw new IllegalArgumentException("null gradebookUid passed to identifyStudentsWithInvalidGrades"); - } - - final List studentsWithInvalidGrade = new ArrayList<>(); - - if (studentIdToGradeMap != null) { - Gradebook gradebook; - - try { - gradebook = getGradebook(gradebookUid); - } catch (final GradebookNotFoundException gnfe) { - throw new GradebookNotFoundException("No gradebook exists with the given gradebookUid: " + - gradebookUid + "Error: " + gnfe.getMessage()); - } - - LetterGradePercentMapping gradeMapping = null; - if (gradebook.getGrade_type() == GradebookService.GRADE_TYPE_LETTER) { - gradeMapping = getLetterGradePercentMapping(gradebook); - } - - for (final String studentId : studentIdToGradeMap.keySet()) { - final String grade = studentIdToGradeMap.get(studentId); - if (!isGradeValid(grade, gradebook.getGrade_type(), gradeMapping)) { - studentsWithInvalidGrade.add(studentId); - } - } - } - return studentsWithInvalidGrade; - } - - @Override - public void saveGradeAndCommentForStudent(final String gradebookUid, final Long gradableObjectId, final String studentUid, - final String grade, final String comment) { - if (gradebookUid == null || gradableObjectId == null || studentUid == null) { - throw new IllegalArgumentException( - "Null gradebookUid or gradableObjectId or studentUid passed to saveGradeAndCommentForStudent"); - } - - final GradeDefinition gradeDef = new GradeDefinition(); - gradeDef.setStudentUid(studentUid); - gradeDef.setGrade(grade); - gradeDef.setGradeComment(comment); - - final List gradeDefList = new ArrayList<>(); - gradeDefList.add(gradeDef); - - final GradebookAssignment assignment = (GradebookAssignment) getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - return getAssignmentWithoutStats(gradebookUid, gradableObjectId); - } - }); - - final AssignmentGradeRecord record = getAssignmentGradeRecord(assignment, studentUid); - if (record != null) { - gradeDef.setExcused(BooleanUtils.toBoolean(record.isExcludedFromGrade())); - } else { - gradeDef.setExcused(false); - } - saveGradesAndComments(gradebookUid, gradableObjectId, gradeDefList); - } - - @Override - public void saveGradeAndExcuseForStudent(final String gradebookUid, final Long gradableObjectId, final String studentUid, - final String grade, final boolean excuse) { - if (gradebookUid == null || gradableObjectId == null || studentUid == null) { - throw new IllegalArgumentException( - "Null gradebookUid, gradeableObjectId, or studentUid passed to saveGradeAndExcuseForStudent"); - } - - final GradeDefinition gradeDef = new GradeDefinition(); - gradeDef.setStudentUid(studentUid); - gradeDef.setGrade(grade); - gradeDef.setExcused(excuse); - - // Lookup any existing comments and set the text so that they don't get wiped out on a save - CommentDefinition gradeComment = getAssignmentScoreComment(gradebookUid, gradableObjectId, studentUid); - if (gradeComment != null) { - gradeDef.setGradeComment(gradeComment.getCommentText()); - } - - final List gradeDefList = new ArrayList<>(); - gradeDefList.add(gradeDef); - - saveGradesAndComments(gradebookUid, gradableObjectId, gradeDefList); - } - - @Override - public void saveGradesAndComments(final String gradebookUid, final Long gradableObjectId, final List gradeDefList) { - if (gradebookUid == null || gradableObjectId == null) { - throw new IllegalArgumentException("Null gradebookUid or gradableObjectId passed to saveGradesAndComments"); - } - - if (CollectionUtils.isNotEmpty(gradeDefList)) { - Gradebook gradebook; - - try { - gradebook = getGradebook(gradebookUid); - } catch (final GradebookNotFoundException gnfe) { - throw new GradebookNotFoundException("No gradebook exists with the given gradebookUid: " + - gradebookUid + "Error: " + gnfe.getMessage()); - } - - final GradebookAssignment assignment = getAssignmentWithoutStatsByID(gradebookUid, gradableObjectId); - if (assignment == null) { - throw new AssessmentNotFoundException("No gradebook item exists with gradable object id = " + gradableObjectId); - } - - if (!currentUserHasGradingPerm(gradebookUid)) { - log.warn("User attempted to save grades and comments without authorization"); - throw new GradebookSecurityException(); - } - - // identify all of the students being updated first - final Map studentIdGradeDefMap = new HashMap<>(); - final Map studentIdToGradeMap = new HashMap<>(); - - for (final GradeDefinition gradeDef : gradeDefList) { - studentIdGradeDefMap.put(gradeDef.getStudentUid(), gradeDef); - studentIdToGradeMap.put(gradeDef.getStudentUid(), gradeDef.getGrade()); - } - - /* - * TODO: this check may be unnecessary if we're validating grades in the first step of the grade import wizard BUT, this can - * only be removed if the only place it's used is in the grade import (other places may not perform the grade validation prior - * to calling this - */ - // Check for invalid grades - final List invalidStudentUUIDs = identifyStudentsWithInvalidGrades(gradebookUid, studentIdToGradeMap); - if (CollectionUtils.isNotEmpty(invalidStudentUUIDs)) { - throw new InvalidGradeException( - "At least one grade passed to be updated is " + "invalid. No grades or comments were updated."); - } - - // Retrieve all existing grade records for the given students and assignment - final List existingGradeRecords = getAllAssignmentGradeRecordsForGbItem(gradableObjectId, - studentIdGradeDefMap.keySet()); - final Map studentIdGradeRecordMap = new HashMap<>(); - if (CollectionUtils.isNotEmpty(existingGradeRecords)) { - for (final AssignmentGradeRecord agr : existingGradeRecords) { - studentIdGradeRecordMap.put(agr.getStudentId(), agr); - } - } - - // Retrieve all existing comments for the given students and assignment - final List existingComments = getComments(assignment, studentIdGradeDefMap.keySet()); - final Map studentIdCommentMap = new HashMap<>(); - if (CollectionUtils.isNotEmpty(existingComments)) { - for (final Comment comment : existingComments) { - studentIdCommentMap.put(comment.getStudentId(), comment); - } - } - - final boolean userHasGradeAllPerm = currentUserHasGradeAllPerm(gradebookUid); - final String graderId = getAuthn().getUserUid(); - final Date now = new Date(); - LetterGradePercentMapping mapping = null; - if (gradebook.getGrade_type() == GradebookService.GRADE_TYPE_LETTER) { - mapping = getLetterGradePercentMapping(gradebook); - } - - // Don't use a HashSet because you may have multiple Comments with null ID and the same comment at this point. - // The Comment object defines objects as equal if they have the same ID, comment text, and gradebook item. The - // only difference may be the student IDs - final List commentsToUpdate = new ArrayList<>(); - final Set eventsToAdd = new HashSet<>(); - final Set gradeRecordsToUpdate = new HashSet<>(); - for (final GradeDefinition gradeDef : gradeDefList) { - final String studentId = gradeDef.getStudentUid(); - - // use the grader ID from the definition if it is not null, otherwise use the current user ID - final String graderUid = gradeDef.getGraderUid() != null ? gradeDef.getGraderUid() : graderId; - // use the grade date from the definition if it is not null, otherwise use the current date - final Date gradedDate = gradeDef.getDateRecorded() != null ? gradeDef.getDateRecorded() : now; - - final boolean excuse = gradeDef.isExcused(); - - // check specific grading privileges if user does not have - // grade all perm - if (!userHasGradeAllPerm) { - if (!isUserAbleToGradeItemForStudent(gradebookUid, gradableObjectId, studentId)) { - log.warn("User {} attempted to save a grade for {} without authorization", graderId, studentId); - throw new GradebookSecurityException(); - } - } - // Determine if the AssignmentGradeRecord needs to be updated - final String newGrade = StringUtils.trimToEmpty(gradeDef.getGrade()); - final Double convertedGrade = convertInputGradeToPoints(gradebook.getGrade_type(), mapping, assignment.getPointsPossible(), - newGrade); - AssignmentGradeRecord gradeRec = studentIdGradeRecordMap.get(studentId); - boolean currentExcuse; - if (gradeRec == null) { - currentExcuse = false; - } else { - currentExcuse = BooleanUtils.toBoolean(gradeRec.isExcludedFromGrade()); - } - - if (gradeRec != null) { - final Double pointsEarned = gradeRec.getPointsEarned(); - if ((convertedGrade == null && pointsEarned != null) - || (convertedGrade != null && pointsEarned == null) - || (convertedGrade != null && pointsEarned != null && !convertedGrade.equals(pointsEarned)) - || (excuse != currentExcuse)) { - - gradeRec.setPointsEarned(convertedGrade); - gradeRec.setGraderId(graderUid); - gradeRec.setDateRecorded(gradedDate); - gradeRec.setExcludedFromGrade(excuse); - gradeRecordsToUpdate.add(gradeRec); - - // Add a GradingEvent, which stores the actual input grade rather than the converted one - final GradingEvent event = new GradingEvent(assignment, graderId, studentId, newGrade); - if(excuse != currentExcuse) { - event.setStatus(excuse ? - GradingEventStatus.GRADE_EXCLUDED : - GradingEventStatus.GRADE_INCLUDED); - } - eventsToAdd.add(event); - } - } else { - // if the grade is something other than null, add a new AGR - if (StringUtils.isNotBlank(newGrade) && (StringUtils.isNotBlank(gradeDef.getGrade()) || excuse != currentExcuse)) { - gradeRec = new AssignmentGradeRecord(assignment, studentId, convertedGrade); - gradeRec.setGraderId(graderUid); - gradeRec.setDateRecorded(gradedDate); - gradeRecordsToUpdate.add(gradeRec); - gradeRec.setExcludedFromGrade(excuse); - - // Add a GradingEvent, which stores the actual input grade rather than the converted one - final GradingEvent event = new GradingEvent(assignment, graderId, studentId, newGrade); - if(excuse != currentExcuse) { - event.setStatus(excuse ? - GradingEventStatus.GRADE_EXCLUDED: - GradingEventStatus.GRADE_INCLUDED); - } - eventsToAdd.add(event); - } - } - // Determine if the Comment needs to be updated - Comment comment = studentIdCommentMap.get(studentId); - final String newCommentText = StringUtils.trimToEmpty(gradeDef.getGradeComment()); - if (comment != null) { - final String existingCommentText = StringUtils.trimToEmpty(comment.getCommentText()); - final boolean existingCommentTextIsEmpty = existingCommentText.isEmpty(); - final boolean newCommentTextIsEmpty = newCommentText.isEmpty(); - if ((existingCommentTextIsEmpty && !newCommentTextIsEmpty) - || (!existingCommentTextIsEmpty && newCommentTextIsEmpty) - || (!existingCommentTextIsEmpty && !newCommentTextIsEmpty && !newCommentText.equals(existingCommentText))) { - comment.setCommentText(newCommentText); - comment.setGraderId(graderId); - comment.setDateRecorded(gradedDate); - commentsToUpdate.add(comment); - } - } else { - // If the comment is something other than null, add a new Comment - if (!newCommentText.isEmpty()) { - comment = new Comment(studentId, newCommentText, assignment); - comment.setGraderId(graderId); - comment.setDateRecorded(gradedDate); - commentsToUpdate.add(comment); - } - } - } - - // Save or update the necessary items - try { - for (final AssignmentGradeRecord assignmentGradeRecord : gradeRecordsToUpdate) { - getHibernateTemplate().saveOrUpdate(assignmentGradeRecord); - } - for (final Comment comment : commentsToUpdate) { - getHibernateTemplate().saveOrUpdate(comment); - } - for (final GradingEvent gradingEvent : eventsToAdd) { - getHibernateTemplate().saveOrUpdate(gradingEvent); - } - } catch (final HibernateOptimisticLockingFailureException | StaleObjectStateException holfe) { - if (log.isInfoEnabled()) { - log.info("An optimistic locking failure occurred while attempting to save scores and comments for gb Item " - + gradableObjectId); - } - throw new StaleObjectModificationException(holfe); - } - } - } - - /** - * Helper method to retrieve Assignment by ID without stats for the given gradebook. Reduces code duplication in several areas. - * - * @param gradebookUID - * @param gradeableObjectID - * @return - */ - private GradebookAssignment getAssignmentWithoutStatsByID(final String gradebookUID, final Long gradeableObjectID) { - return (GradebookAssignment) getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - return getAssignmentWithoutStats(gradebookUID, gradeableObjectID); - } - }); - } - - /** - * - * @param gradeEntryType - * @param mapping - * @param gbItemPointsPossible - * @param grade - * @return given a generic String grade, converts it to the equivalent Double point value that will be stored in the db based upon the - * gradebook's grade entry type - */ - private Double convertInputGradeToPoints(final int gradeEntryType, final LetterGradePercentMapping mapping, - final Double gbItemPointsPossible, final String grade) throws InvalidGradeException { - Double convertedValue = null; - - if (grade != null && !"".equals(grade)) { - if (gradeEntryType == GradebookService.GRADE_TYPE_POINTS) { - try { - final NumberFormat nbFormat = NumberFormat.getInstance(new ResourceLoader().getLocale()); - final Double pointValue = nbFormat.parse(grade).doubleValue(); - convertedValue = pointValue; - } catch (NumberFormatException | ParseException nfe) { - throw new InvalidGradeException("Invalid grade passed to convertInputGradeToPoints"); - } - } else if (gradeEntryType == GradebookService.GRADE_TYPE_PERCENTAGE || - gradeEntryType == GradebookService.GRADE_TYPE_LETTER) { - - // for letter or %-based grading, we need to calculate the equivalent point value - if (gbItemPointsPossible == null) { - throw new IllegalArgumentException("Null points possible passed" + - " to convertInputGradeToPoints for letter or % based grading"); - } - - Double percentage = null; - if (gradeEntryType == GradebookService.GRADE_TYPE_LETTER) { - if (mapping == null) { - throw new IllegalArgumentException("No mapping passed to convertInputGradeToPoints for a letter-based gb"); - } - - if (mapping.getGradeMap() != null) { - // standardize the grade mapping - final String standardizedGrade = mapping.standardizeInputGrade(grade); - percentage = mapping.getValue(standardizedGrade); - if (percentage == null) { - throw new IllegalArgumentException("Invalid grade passed to convertInputGradeToPoints"); - } - } - } else { - try { - final NumberFormat nbFormat = NumberFormat.getInstance(new ResourceLoader().getLocale()); - percentage = nbFormat.parse(grade).doubleValue(); - } catch (NumberFormatException | ParseException nfe) { - throw new IllegalArgumentException("Invalid % grade passed to convertInputGradeToPoints"); - } - } - - convertedValue = calculateEquivalentPointValueForPercent(gbItemPointsPossible, percentage); - - } else { - throw new InvalidGradeException("invalid grade entry type passed to convertInputGradeToPoints"); - } - } - - return convertedValue; - } - - @Override - public int getGradeEntryType(final String gradebookUid) { - if (gradebookUid == null) { - throw new IllegalArgumentException("null gradebookUid passed to getGradeEntryType"); - } - - try { - final Gradebook gradebook = getGradebook(gradebookUid); - return gradebook.getGrade_type(); - } catch (final GradebookNotFoundException gnfe) { - throw new GradebookNotFoundException("No gradebook exists with the given gradebookUid: " + gradebookUid); - } - } - - @Override - public Map getEnteredCourseGrade(final String gradebookUid) { - final HibernateCallback hc = new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - final Gradebook thisGradebook = getGradebook(gradebookUid); - - final Long gradebookId = thisGradebook.getId(); - final CourseGrade courseGrade = getCourseGrade(gradebookId); - - Map enrollmentMap; - - final Map viewableEnrollmentsMap = GradebookServiceHibernateImpl.this.authz - .findMatchingEnrollmentsForViewableCourseGrade(gradebookUid, thisGradebook.getCategory_type(), null, null); - enrollmentMap = new HashMap(); - - final Map enrollmentMapUid = new HashMap(); - for (final Iterator iter = viewableEnrollmentsMap.keySet().iterator(); iter.hasNext();) { - final EnrollmentRecord enr = (EnrollmentRecord) iter.next(); - enrollmentMap.put(enr.getUser().getUserUid(), enr); - enrollmentMapUid.put(enr.getUser().getUserUid(), enr); - } - - final Query q = session.createQuery("from CourseGradeRecord as cgr where cgr.gradableObject.id=:gradableObjectId"); - q.setParameter("gradableObjectId", courseGrade.getId()); - final List records = filterAndPopulateCourseGradeRecordsByStudents(courseGrade, q.list(), enrollmentMap.keySet()); - - final Map returnMap = new HashMap(); - - for (int i = 0; i < records.size(); i++) { - final CourseGradeRecord cgr = (CourseGradeRecord) records.get(i); - if (cgr.getEnteredGrade() != null && !cgr.getEnteredGrade().equalsIgnoreCase("")) { - final EnrollmentRecord enr = (EnrollmentRecord) enrollmentMapUid.get(cgr.getStudentId()); - if (enr != null) { - returnMap.put(enr.getUser().getDisplayId(), cgr.getEnteredGrade()); - } - } - } - - return returnMap; - } - }; - return (Map) getHibernateTemplate().execute(hc); - } - - @Override - public String getAssignmentScoreString(final String gradebookUid, final Long assignmentId, final String studentUid) - throws GradebookNotFoundException, AssessmentNotFoundException { - final boolean studentRequestingOwnScore = this.authn.getUserUid().equals(studentUid); - - if (gradebookUid == null || assignmentId == null || studentUid == null) { - throw new IllegalArgumentException("null parameter passed to getAssignment. Values are gradebookUid:" - + gradebookUid + " assignmentId:" + assignmentId + " studentUid:" + studentUid); - } - - final Double assignmentScore = (Double) getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - final GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentId); - if (assignment == null) { - throw new AssessmentNotFoundException( - "There is no assignment with id " + assignmentId + " in gradebook " + gradebookUid); - } - - if (!studentRequestingOwnScore && !isUserAbleToViewItemForStudent(gradebookUid, assignmentId, studentUid)) { - log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to retrieve grade for student {} for assignment {}", - getUserUid(), gradebookUid, studentUid, assignment.getName()); - throw new GradebookSecurityException(); - } - - // If this is the student, then the assignment needs to have - // been released. - if (studentRequestingOwnScore && !assignment.isReleased()) { - log.error("AUTHORIZATION FAILURE: Student {} in gradebook {} attempted to retrieve score for unreleased assignment {}", - getUserUid(), gradebookUid, assignment.getName()); - throw new GradebookSecurityException(); - } - - final AssignmentGradeRecord gradeRecord = getAssignmentGradeRecord(assignment, studentUid); - if (log.isDebugEnabled()) { - log.debug("gradeRecord=" + gradeRecord); - } - if (gradeRecord == null) { - return null; - } else { - return gradeRecord.getPointsEarned(); - } - } - }); - if (log.isDebugEnabled()) { - log.debug("returning " + assignmentScore); - } - - // TODO: when ungraded items is considered, change column to ungraded-grade - // its possible that the assignment score is null - if (assignmentScore == null) { - return null; - } - - // avoid scientific notation on large scores by using a formatter - final NumberFormat numberFormat = NumberFormat.getInstance(new ResourceLoader().getLocale()); - final DecimalFormat df = (DecimalFormat) numberFormat; - df.setGroupingUsed(false); - - return df.format(assignmentScore); - } - - @Override - public String getAssignmentScoreString(final String gradebookUid, final String assignmentName, final String studentUid) - throws GradebookNotFoundException, AssessmentNotFoundException { - - if (gradebookUid == null || assignmentName == null || studentUid == null) { - throw new IllegalArgumentException("null parameter passed to getAssignment. Values are gradebookUid:" - + gradebookUid + " assignmentName:" + assignmentName + " studentUid:" + studentUid); - } - - final GradebookAssignment assignment = (GradebookAssignment) getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - return getAssignmentWithoutStats(gradebookUid, assignmentName); - } - }); - - if (assignment == null) { - throw new AssessmentNotFoundException("There is no assignment with name " + assignmentName + " in gradebook " + gradebookUid); - } - - return getAssignmentScoreString(gradebookUid, assignment.getId(), studentUid); - } - - @Override - public String getAssignmentScoreStringByNameOrId(final String gradebookUid, final String assignmentName, final String studentUid) - throws GradebookNotFoundException, AssessmentNotFoundException { - String score = null; - try { - score = getAssignmentScoreString(gradebookUid, assignmentName, studentUid); - } catch (final AssessmentNotFoundException e) { - // Don't fail on this exception - log.debug("Assessment not found by name", e); - } catch (final GradebookSecurityException gse) { - log.warn("User {} does not have permission to retrieve score for assignment {}", studentUid, assignmentName, gse); - return null; - } - - if (score == null) { - // Try to get the assignment by id - if (NumberUtils.isCreatable(assignmentName)) { - final Long assignmentId = NumberUtils.toLong(assignmentName, -1L); - try { - score = getAssignmentScoreString(gradebookUid, assignmentId, studentUid); - } catch (AssessmentNotFoundException anfe) { - log.debug("Assessment could not be found for gradebook id {} and assignment id {} and student id {}", gradebookUid, assignmentName, studentUid); - } - } - } - return score; - } - - @Override - public void setAssignmentScoreString(final String gradebookUid, final Long assignmentId, final String studentUid, final String score, - final String clientServiceDescription) - throws GradebookNotFoundException, AssessmentNotFoundException { - getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - final GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentId); - if (assignment == null) { - throw new AssessmentNotFoundException( - "There is no assignment with id " + assignmentId + " in gradebook " + gradebookUid); - } - if (assignment.isExternallyMaintained()) { - log.error( - "AUTHORIZATION FAILURE: User {} in gradebook {} attempted to grade externally maintained assignment {} from {}", - getUserUid(), gradebookUid, assignmentId, clientServiceDescription); - throw new GradebookSecurityException(); - } - - if (!isUserAbleToGradeItemForStudent(gradebookUid, assignment.getId(), studentUid)) { - log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to grade student {} from {} for item {}", - getUserUid(), gradebookUid, studentUid, clientServiceDescription, assignmentId); - throw new GradebookSecurityException(); - } - - final Date now = new Date(); - final String graderId = getAuthn().getUserUid(); - AssignmentGradeRecord gradeRecord = getAssignmentGradeRecord(assignment, studentUid); - if (gradeRecord == null) { - // Creating a new grade record. - gradeRecord = new AssignmentGradeRecord(assignment, studentUid, convertStringToDouble(score)); - // TODO: test if it's ungraded item or not. if yes, set ungraded grade for this record. if not, need validation?? - } else { - // TODO: test if it's ungraded item or not. if yes, set ungraded grade for this record. if not, need validation?? - gradeRecord.setPointsEarned(convertStringToDouble(score)); - } - gradeRecord.setGraderId(graderId); - gradeRecord.setDateRecorded(now); - session.saveOrUpdate(gradeRecord); - - session.save(new GradingEvent(assignment, graderId, studentUid, score)); - - // Sync database. - session.flush(); - session.clear(); - - // Post an event in SAKAI_EVENT table - postUpdateGradeEvent(gradebookUid, assignment.getName(), studentUid, convertStringToDouble(score)); - return null; - } - }); - - if (log.isDebugEnabled()) { - log.debug("Score updated in gradebookUid=" + gradebookUid + ", assignmentId=" + assignmentId + " by userUid=" + getUserUid() - + " from client=" + clientServiceDescription + ", new score=" + score); - } - } - - @Override - public void setAssignmentScoreString(final String gradebookUid, final String assignmentName, final String studentUid, - final String score, final String clientServiceDescription) - throws GradebookNotFoundException, AssessmentNotFoundException { - - final GradebookAssignment assignment = (GradebookAssignment) getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - return getAssignmentWithoutStats(gradebookUid, assignmentName); - } - }); - - if (assignment == null) { - throw new AssessmentNotFoundException("There is no assignment with name " + assignmentName + " in gradebook " + gradebookUid); - } - - setAssignmentScoreString(gradebookUid, assignment.getId(), studentUid, score, clientServiceDescription); - } - - @Override - public void finalizeGrades(final String gradebookUid) - throws GradebookNotFoundException { - if (!getAuthz().isUserAbleToGradeAll(gradebookUid)) { - log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to finalize grades", getUserUid(), gradebookUid); - throw new GradebookSecurityException(); - } - finalizeNullGradeRecords(getGradebook(gradebookUid)); - } - - @Override - public String getLowestPossibleGradeForGbItem(final String gradebookUid, final Long gradebookItemId) { - if (gradebookUid == null || gradebookItemId == null) { - throw new IllegalArgumentException("Null gradebookUid and/or gradebookItemId " + - "passed to getLowestPossibleGradeForGbItem. gradebookUid:" + - gradebookUid + " gradebookItemId:" + gradebookItemId); - } - - final GradebookAssignment gbItem = getAssignmentWithoutStatsByID(gradebookUid, gradebookItemId); - - if (gbItem == null) { - throw new AssessmentNotFoundException("No gradebook item found with id " + gradebookItemId); - } - - final Gradebook gradebook = gbItem.getGradebook(); - - // double check that user has some permission to access gb items in this site - if (!isUserAbleToViewAssignments(gradebookUid) && !currentUserHasViewOwnGradesPerm(gradebookUid)) { - throw new GradebookSecurityException(); - } - - String lowestPossibleGrade = null; - - if (gbItem.getUngraded()) { - lowestPossibleGrade = null; - } else if (gradebook.getGrade_type() == GradebookService.GRADE_TYPE_PERCENTAGE || - gradebook.getGrade_type() == GradebookService.GRADE_TYPE_POINTS) { - lowestPossibleGrade = "0"; - } else if (gbItem.getGradebook().getGrade_type() == GradebookService.GRADE_TYPE_LETTER) { - final LetterGradePercentMapping mapping = getLetterGradePercentMapping(gradebook); - lowestPossibleGrade = mapping.getGrade(0d); - } - - return lowestPossibleGrade; - } - - @Override - public List getCategoryDefinitions(final String gradebookUid) { - if (gradebookUid == null) { - throw new IllegalArgumentException("Null gradebookUid passed to getCategoryDefinitions"); - } - - if (!isUserAbleToViewAssignments(gradebookUid)) { - log.warn("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to retrieve all categories without permission", getUserUid(), - gradebookUid); - throw new GradebookSecurityException(); - } - - final List categoryDefList = new ArrayList<>(); - - final List gbCategories = getCategories(getGradebook(gradebookUid).getId()); - - if (gbCategories != null) { - for (final Category category : gbCategories) { - categoryDefList.add(getCategoryDefinition(category)); - } - } - - return categoryDefList; - } - - private CategoryDefinition getCategoryDefinition(final Category category) { - final CategoryDefinition categoryDef = new CategoryDefinition(); - if (category != null) { - categoryDef.setId(category.getId()); - categoryDef.setName(category.getName()); - categoryDef.setWeight(category.getWeight()); - categoryDef.setDropLowest(category.getDropLowest()); - categoryDef.setDropHighest(category.getDropHighest()); - categoryDef.setKeepHighest(category.getKeepHighest()); - categoryDef.setAssignmentList(getAssignments(category.getGradebook().getUid(), category.getName())); - categoryDef.setDropKeepEnabled(category.isDropScores()); - categoryDef.setExtraCredit(category.isExtraCredit()); - categoryDef.setEqualWeight(category.isEqualWeightAssignments()); - categoryDef.setCategoryOrder(category.getCategoryOrder()); - } - - return categoryDef; - } - - /** - * - * @param gradebookId - * @param studentUids - * @return a map of studentUid to a list of that student's AssignmentGradeRecords for the given studentUids list in the given gradebook. - * the grade records are all recs for assignments that are not removed and have a points possible > 0 - */ - protected Map> getGradeRecordMapForStudents(final Long gradebookId, - final Collection studentUids) { - final Map> filteredGradeRecs = new HashMap<>(); - if (studentUids != null) { - final List allGradeRecs = getHibernateTemplate() - .execute(session -> session.createCriteria(AssignmentGradeRecord.class) - .createAlias("gradableObject", "go") - .createAlias("gradableObject.gradebook", "gb") - .add(Restrictions.eq("gb.id", gradebookId)) - .add(Restrictions.eq("go.removed", false)) - .add(HibernateCriterionUtils.CriterionInRestrictionSplitter("studentId", studentUids)) - .list()); - - if (allGradeRecs != null) { - for (final AssignmentGradeRecord gradeRec : allGradeRecs) { - if (studentUids.contains(gradeRec.getStudentId())) { - final String studentId = gradeRec.getStudentId(); - List gradeRecList = filteredGradeRecs.get(studentId); - if (gradeRecList == null) { - gradeRecList = new ArrayList<>(); - gradeRecList.add(gradeRec); - filteredGradeRecs.put(studentId, gradeRecList); - } else { - gradeRecList.add(gradeRec); - filteredGradeRecs.put(studentId, gradeRecList); - } - } - } - } - } - - return filteredGradeRecs; - } - - /** - * - * @param session - * @param gradebookId - * @return a list of Assignments that have not been removed, are "counted", graded, and have a points possible > 0 - */ - protected List getCountedAssignments(final Session session, final Long gradebookId) { - final List assignList = new ArrayList<>(); - - final List results = session.createQuery( - "from GradebookAssignment as asn where asn.gradebook.id=:gbid and asn.removed=false and " + - "asn.notCounted=false and asn.ungraded=false") - .setParameter("gbid", gradebookId).list(); - - if (results != null) { - // making sure there's no invalid points possible for normal assignments - for (final GradebookAssignment a : results) { - - if (a.getPointsPossible() != null && a.getPointsPossible() > 0) { - assignList.add(a); - } - } - } - - return assignList; - } - - /** - * set the droppedFromGrade attribute of each of the n highest and the n lowest scores of a student based on the assignment's category - * - * @param gradeRecords - * - * NOTE: When the UI changes, this needs to be made private again - */ - public void applyDropScores(final Collection gradeRecords, int categoryType) { - if (gradeRecords == null || gradeRecords.size() < 1) { - return; - } - final long start = System.currentTimeMillis(); - - final List studentIds = new ArrayList<>(); - final List categories = new ArrayList<>(); - final Map> gradeRecordMap = new HashMap<>(); - for (final AssignmentGradeRecord gradeRecord : gradeRecords) { - - if (gradeRecord == null - || gradeRecord.getPointsEarned() == null) { // don't consider grades that have null pointsEarned (this occurs when a - // previously entered score for an assignment is removed; record stays in - // database) - continue; - } - - // reset - gradeRecord.setDroppedFromGrade(false); - - if (categoryType == GradebookService.CATEGORY_TYPE_NO_CATEGORY) { - continue; - } - - final GradebookAssignment assignment = gradeRecord.getAssignment(); - if (assignment.getUngraded() // GradebookService.GRADE_TYPE_LETTER - || assignment.isNotCounted() // don't consider grades that are not counted toward course grade - || assignment.getItemType().equals(GradebookAssignment.item_type_adjustment) - || assignment.isRemoved()) { - continue; - } - // get all the students represented - final String studentId = gradeRecord.getStudentId(); - if (!studentIds.contains(studentId)) { - studentIds.add(studentId); - } - // get all the categories represented - final Category cat = gradeRecord.getAssignment().getCategory(); - if (cat != null) { - if (!categories.contains(cat)) { - categories.add(cat); - } - List gradeRecordsByCatAndStudent = gradeRecordMap.get(studentId + cat.getId()); - if (gradeRecordsByCatAndStudent == null) { - gradeRecordsByCatAndStudent = new ArrayList<>(); - gradeRecordsByCatAndStudent.add(gradeRecord); - gradeRecordMap.put(studentId + cat.getId(), gradeRecordsByCatAndStudent); - } else { - gradeRecordsByCatAndStudent.add(gradeRecord); - } - } - } - - if (categories.size() < 1 || categoryType == GradebookService.CATEGORY_TYPE_NO_CATEGORY) { - return; - } - for (final Category cat : categories) { - final Integer dropHighest = cat.getDropHighest(); - Integer dropLowest = cat.getDropLowest(); - final Integer keepHighest = cat.getKeepHighest(); - final Long catId = cat.getId(); - - if ((dropHighest != null && dropHighest > 0) || (dropLowest != null && dropLowest > 0) - || (keepHighest != null && keepHighest > 0)) { - - for (final String studentId : studentIds) { - // get the student's gradeRecords for this category - final List gradesByCategory = new ArrayList<>(); - final List gradeRecordsByCatAndStudent = gradeRecordMap.get(studentId + cat.getId()); - if (gradeRecordsByCatAndStudent != null) { - for (final AssignmentGradeRecord agr : gradeRecordsByCatAndStudent) { - if (!BooleanUtils.toBoolean(agr.isExcludedFromGrade())) { - gradesByCategory.add(agr); - } - } - - final int numGrades = gradesByCategory.size(); - - if (dropHighest > 0 && numGrades > dropHighest + dropLowest) { - for (int i = 0; i < dropHighest; i++) { - final AssignmentGradeRecord highest = Collections.max(gradesByCategory, - AssignmentGradeRecord.numericComparator); - highest.setDroppedFromGrade(true); - gradesByCategory.remove(highest); - if (log.isDebugEnabled()) { - log.debug("dropHighest applied to " + highest); - } - } - } - - if (keepHighest > 0 && numGrades > (gradesByCategory.size() - keepHighest)) { - dropLowest = gradesByCategory.size() - keepHighest; - } - - if (dropLowest > 0 && numGrades > dropLowest + dropHighest) { - for (int i = 0; i < dropLowest; i++) { - final AssignmentGradeRecord lowest = Collections.min(gradesByCategory, - AssignmentGradeRecord.numericComparator); - lowest.setDroppedFromGrade(true); - gradesByCategory.remove(lowest); - if (log.isDebugEnabled()) { - log.debug("dropLowest applied to " + lowest); - } - } - } - } - } - if (log.isDebugEnabled()) { - log.debug("processed " + studentIds.size() + "students in category " + cat.getId()); - } - } - } - - if (log.isDebugEnabled()) { - log.debug("GradebookManager.applyDropScores took " + (System.currentTimeMillis() - start) + " millis to execute"); - } - } - - @Override - public PointsPossibleValidation isPointsPossibleValid(final String gradebookUid, - final org.sakaiproject.service.gradebook.shared.Assignment gradebookItem, - final Double pointsPossible) { - if (gradebookUid == null) { - throw new IllegalArgumentException("Null gradebookUid passed to isPointsPossibleValid"); - } - if (gradebookItem == null) { - throw new IllegalArgumentException("Null gradebookItem passed to isPointsPossibleValid"); - } - - // At this time, all gradebook items follow the same business rules for - // points possible (aka relative weight in % gradebooks) so special logic - // using the properties of the gradebook item is unnecessary. - // In the future, we will have the flexibility to change - // that behavior without changing the method signature - - // the points possible must be a non-null value greater than 0 with - // no more than 2 decimal places - - if (pointsPossible == null) { - return PointsPossibleValidation.INVALID_NULL_VALUE; - } - - if (pointsPossible <= 0) { - return PointsPossibleValidation.INVALID_NUMERIC_VALUE; - } - // ensure there are no more than 2 decimal places - BigDecimal bd = new BigDecimal(pointsPossible); - bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP); // Two decimal places - final double roundedVal = bd.doubleValue(); - final double diff = pointsPossible - roundedVal; - if (diff != 0) { - return PointsPossibleValidation.INVALID_DECIMAL; - } - - return PointsPossibleValidation.VALID; - } - - /** - * - * @param doubleAsString - * @return a locale-aware Double value representation of the given String - * @throws ParseException - */ - private Double convertStringToDouble(final String doubleAsString) { - Double scoreAsDouble = null; - if (doubleAsString != null && !"".equals(doubleAsString)) { - try { - final NumberFormat numberFormat = NumberFormat.getInstance(new ResourceLoader().getLocale()); - final Number numericScore = numberFormat.parse(doubleAsString.trim()); - scoreAsDouble = numericScore.doubleValue(); - } catch (final ParseException e) { - log.error(e.getMessage()); - } - } - - return scoreAsDouble; - } - - /** - * Get a list of assignments in the gradebook attached to the given category. Note that each assignment only knows the category by name. - * - *

- * Note also that this is different to {@link BaseHibernateManager#getAssignmentsForCategory(Long)} because this method returns the - * shared GradebookAssignment object. - * - * @param gradebookUid - * @param categoryName - * @return - */ - private List getAssignments(final String gradebookUid, - final String categoryName) { - - final List allAssignments = getAssignments(gradebookUid); - final List matchingAssignments = new ArrayList<>(); - - for (final org.sakaiproject.service.gradebook.shared.Assignment assignment : allAssignments) { - if (StringUtils.equals(assignment.getCategoryName(), categoryName)) { - matchingAssignments.add(assignment); - } - } - return matchingAssignments; - } - - /** - * Post an event to Sakai's event table - * - * @param gradebookUid - * @param assignmentName - * @param studentUid - * @param pointsEarned - * @return - */ - private void postUpdateGradeEvent(final String gradebookUid, final String assignmentName, final String studentUid, - final Double pointsEarned) { - postEvent("gradebook.updateItemScore", - "/gradebook/" + gradebookUid + "/" + assignmentName + "/" + studentUid + "/" + pointsEarned + "/student"); - } - - /** - * Retrieves the calculated average course grade. - */ - @Override - public String getAverageCourseGrade(final String gradebookUid) { - if (gradebookUid == null) { - throw new IllegalArgumentException("Null gradebookUid passed to getAverageCourseGrade"); - } - // Check user has permission to invoke method. - if (!currentUserHasGradeAllPerm(gradebookUid)) { - final StringBuilder sb = new StringBuilder() - .append("User ") - .append(this.authn.getUserUid()) - .append(" attempted to access the average course grade without permission in gb ") - .append(gradebookUid) - .append(" using gradebookService.getAverageCourseGrade"); - throw new GradebookSecurityException(sb.toString()); - } - - String courseGradeLetter = null; - final Gradebook gradebook = getGradebook(gradebookUid); - if (gradebook != null) { - final CourseGrade courseGrade = getCourseGrade(gradebook.getId()); - final Set studentUids = getAllStudentUids(gradebookUid); - // This call handles the complex rules of which assignments and grades to include in the calculation - final List courseGradeRecs = getPointsEarnedCourseGradeRecords(courseGrade, studentUids); - if (courseGrade != null) { - // Calculate the course mean grade whether the student grade was manually entered or auto-calculated. - courseGrade.calculateStatistics(courseGradeRecs, studentUids.size()); - if (courseGrade.getMean() != null) { - courseGradeLetter = gradebook.getSelectedGradeMapping().getMappedGrade(courseGrade.getMean()); - } - } - - } - return courseGradeLetter; - } - - /** - * Updates the order of an assignment - * - * @see GradebookService.updateAssignmentOrder(java.lang.String gradebookUid, java.lang.Long assignmentId, java.lang.Integer order) - */ - @Override - public void updateAssignmentOrder(final String gradebookUid, final Long assignmentId, Integer order) { - - if (!getAuthz().isUserAbleToEditAssessments(gradebookUid)) { - log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to change the order of assignment {}", getUserUid(), - gradebookUid, assignmentId); - throw new GradebookSecurityException(); - } - - if (order == null) { - throw new IllegalArgumentException("Order cannot be null"); - } - - final Long gradebookId = getGradebook(gradebookUid).getId(); - - // get all assignments for this gradebook - final List assignments = getAssignments(gradebookId, SortType.SORT_BY_SORTING, true); - - // adjust order to be within bounds - if (order < 0) { - order = 0; - } else if (order > assignments.size()) { - order = assignments.size(); - } - - // find the assignment - GradebookAssignment target = null; - for (final GradebookAssignment a : assignments) { - if (a.getId().equals(assignmentId)) { - target = a; - break; - } - } - - // add the assignment to the list via a 'pad, remove, add' approach - assignments.add(null); // ensure size remains the same for the remove - assignments.remove(target); // remove item - assignments.add(order, target); // add at ordered position, will shuffle others along - - // the assignments are now in the correct order within the list, we just need to update the sort order for each one - // create a new list for the assignments we need to update in the database - final List assignmentsToUpdate = new ArrayList<>(); - - int i = 0; - for (final GradebookAssignment a : assignments) { - - // skip if null - if (a == null) { - continue; - } - - // if the sort order is not the same as the counter, update the order and add to the other list - // this allows us to skip items that have not had their position changed and saves some db work later on - // sort order may be null if never previously sorted, so give it the current index - if (a.getSortOrder() == null || !a.getSortOrder().equals(i)) { - a.setSortOrder(i); - assignmentsToUpdate.add(a); - } - - i++; - } - - // do the updates - for (final GradebookAssignment assignmentToUpdate : assignmentsToUpdate) { - getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - updateAssignment(assignmentToUpdate); - return null; - } - }); - } - - } - - /** - * {@inheritDoc} - */ - @Override - public List getGradingEvents(final String studentId, final long assignmentId) { - - if (log.isDebugEnabled()) { - log.debug("getGradingEvents called for studentId:" + studentId); - } - - List rval = new ArrayList<>(); - - if (studentId == null) { - log.debug("No student id was specified. Returning an empty GradingEvents object"); - return rval; - } - - final HibernateCallback> hc = session -> { - final Query q = session - .createQuery("from GradingEvent as ge where ge.studentId=:studentId and ge.gradableObject.id=:assignmentId order by date_graded desc"); - q.setParameter("studentId", studentId); - q.setParameter("assignmentId", assignmentId); - return q.list(); - }; - - rval = getHibernateTemplate().execute(hc); - return rval; - } - - @Override - public Optional calculateCategoryScore(final Object gradebook, final String studentUuid, - final CategoryDefinition category, final List categoryAssignments, - final Map gradeMap, final boolean includeNonReleasedItems) { - - final Gradebook gb = (Gradebook) gradebook; - - // used for translating letter grades - final Map gradingSchema = gb.getSelectedGradeMapping().getGradeMap(); - - // collect the data and turn it into a list of AssignmentGradeRecords - // this is the info that is compatible with both applyDropScores and the calculateCategoryScore method - final List gradeRecords = new ArrayList<>(); - for (final org.sakaiproject.service.gradebook.shared.Assignment assignment : categoryAssignments) { - - final Long assignmentId = assignment.getId(); - - final String rawGrade = gradeMap.get(assignmentId); - final Double pointsPossible = assignment.getPoints(); - Double grade; - - // determine the grade we should be using depending on the grading type - if (gb.getGrade_type() == GradebookService.GRADE_TYPE_PERCENTAGE) { - grade = calculateEquivalentPointValueForPercent(pointsPossible, NumberUtils.createDouble(rawGrade)); - } else if (gb.getGrade_type() == GradebookService.GRADE_TYPE_LETTER) { - grade = gradingSchema.get(rawGrade); - } else { - grade = NumberUtils.createDouble(rawGrade); - } - - // recreate the category (required fields only) - final Category c = new Category(); - c.setId(category.getId()); - c.setDropHighest(category.getDropHighest()); - c.setDropLowest(category.getDropLowest()); - c.setKeepHighest(category.getKeepHighest()); - c.setEqualWeightAssignments(category.getEqualWeight()); - - // recreate the assignment (required fields only) - final GradebookAssignment a = new GradebookAssignment(); - a.setPointsPossible(assignment.getPoints()); - a.setUngraded(assignment.isUngraded()); - a.setCounted(assignment.isCounted()); - a.setExtraCredit(assignment.isExtraCredit()); - a.setReleased(assignment.isReleased()); - a.setRemoved(false); // shared.GradebookAssignment doesn't include removed so this will always be false - a.setGradebook(gb); - a.setCategory(c); - a.setId(assignment.getId()); // store the id so we can find out later which grades were dropped, if any - - // create the AGR - final AssignmentGradeRecord gradeRecord = new AssignmentGradeRecord(a, studentUuid, grade); - - if (!a.isNotCounted()) { - gradeRecords.add(gradeRecord); - } - } - - return calculateCategoryScore(studentUuid, category.getId(), gradeRecords, includeNonReleasedItems, gb.getCategory_type(), category.getEqualWeight()); - } - - @Override - public Optional calculateCategoryScore(final Long gradebookId, final String studentUuid, final Long categoryId, - final boolean includeNonReleasedItems, int categoryType, Boolean equalWeightAssignments) { - - // get all grade records for the student - @SuppressWarnings({ "unchecked", "rawtypes" }) - final Map> gradeRecMap = (Map>) getHibernateTemplate() - .execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - return getGradeRecordMapForStudents(gradebookId, Collections.singletonList(studentUuid)); - } - }); - - // apply the settings - final List gradeRecords = gradeRecMap.get(studentUuid); - - return calculateCategoryScore(studentUuid, categoryId, gradeRecords, includeNonReleasedItems, categoryType, equalWeightAssignments); - } - - /** - * Does the heavy lifting for the category calculations. Requires the List of AssignmentGradeRecord so that we can applyDropScores. - * - * @param studentUuid the student uuid - * @param categoryId the category id we are interested in - * @param gradeRecords all grade records for the student - * @return - */ - private Optional calculateCategoryScore(final String studentUuid, final Long categoryId, - final List gradeRecords, final boolean includeNonReleasedItems, final int categoryType, Boolean equalWeightAssignments) { - - // validate - if (gradeRecords == null) { - log.debug("No grade records for student: {}. Nothing to do.", studentUuid); - return Optional.empty(); - } - - if (categoryId == null) { - log.debug("No category supplied, nothing to do."); - return Optional.empty(); - } - - // setup - int numScored = 0; - int numOfAssignments = 0; - BigDecimal totalEarned = new BigDecimal("0"); - BigDecimal totalEarnedMean = new BigDecimal("0"); - BigDecimal totalPossible = new BigDecimal("0"); - - // apply any drop/keep settings for this category - applyDropScores(gradeRecords, categoryType); - - // find the records marked as dropped (highest/lowest) before continuing, - // as gradeRecords will be modified in place after this and these records will be removed - final List droppedItemIds = gradeRecords.stream() - .filter(AssignmentGradeRecord::getDroppedFromGrade) - .map(agr -> agr.getAssignment().getId()) - .collect(Collectors.toList()); - - // Since all gradeRecords for the student are passed in, not just for this category, - // plus they may not meet the criteria for including in the calculation, - // this list is filtered down according to the following rules: - // Rule 1. remove gradeRecords that don't match the given category - // Rule 2. the assignment must have points to be assigned - // Rule 3. there is a non blank grade for the student - // Rule 4. the assignment is included in course grade calculations - // Rule 5. the assignment is released to the student (instructor gets to see category grade regardless of release status; student does not) - // Rule 6. the grade is not dropped from the calc - // Rule 7. extra credit items have their grade value counted only. Their total points possible does not apply to the calculations - log.debug("categoryId: {}", categoryId); - - gradeRecords.removeIf(gradeRecord -> { - final GradebookAssignment assignment = gradeRecord.getAssignment(); - - // remove if not for this category (rule 1) - if (assignment.getCategory() == null) { - return true; - } - if (categoryId.longValue() != assignment.getCategory().getId().longValue()) { - return true; - } - - final boolean excluded = BooleanUtils.toBoolean(gradeRecord.isExcludedFromGrade()); - // remove if the assignment/graderecord doesn't meet the criteria for the calculation (rule 2-6) - if (excluded || assignment.getPointsPossible() == null || gradeRecord.getPointsEarned() == null || !assignment.isCounted() - || (!assignment.isReleased() && !includeNonReleasedItems) || gradeRecord.getDroppedFromGrade()) { - return true; - } - - return false; - }); - - log.debug("gradeRecords.size(): {}", gradeRecords.size()); - - // pre-calculation - // Rule 1. If category only has a single EC item, don't try to calculate category total. - if (gradeRecords.size() == 1 && gradeRecords.get(0).getAssignment().isExtraCredit()) { - return Optional.empty(); - } - - // iterate the filtered list and set the variables for the calculation - for (final AssignmentGradeRecord gradeRecord : gradeRecords) { - - final GradebookAssignment assignment = gradeRecord.getAssignment(); - final BigDecimal possiblePoints = new BigDecimal(assignment.getPointsPossible().toString()); - - // EC item, don't count points possible - if (!assignment.isExtraCredit()) { - totalPossible = totalPossible.add(possiblePoints); - numOfAssignments++; - numScored++; - } - - // sanitise grade, null values to "0"; - final String gradeString = (gradeRecord.getPointsEarned() != null) ? String.valueOf(gradeRecord.getPointsEarned()) : "0"; - final BigDecimal grade = new BigDecimal(gradeString); - - // update total points earned - totalEarned = totalEarned.add(grade); - - // keep running total of averages in case the category is equal weighted - try { - totalEarnedMean = totalEarnedMean.add( - grade.divide(possiblePoints, GradebookService.MATH_CONTEXT) - ); - } catch(ArithmeticException ae) { - totalEarnedMean = totalEarnedMean.add(new BigDecimal("0")); - } - } - - if (numScored == 0 || numOfAssignments == 0 || totalPossible.doubleValue() == 0) { - return Optional.empty(); - } - - BigDecimal mean = totalEarned.divide(new BigDecimal(numScored), GradebookService.MATH_CONTEXT) - .divide((totalPossible.divide(new BigDecimal(numOfAssignments), GradebookService.MATH_CONTEXT)), - GradebookService.MATH_CONTEXT) - .multiply(new BigDecimal("100")); - - if (equalWeightAssignments == null) { - Category category = getCategory(categoryId); - equalWeightAssignments = category.isEqualWeightAssignments(); - } - if (equalWeightAssignments) { - mean = totalEarnedMean.divide(new BigDecimal(numScored), GradebookService.MATH_CONTEXT).multiply(new BigDecimal("100")); - } - - return Optional.of(new CategoryScoreData(mean.doubleValue(), droppedItemIds)); - } - - @Override - public org.sakaiproject.service.gradebook.shared.CourseGrade getCourseGradeForStudent(final String gradebookUid, - final String userUuid) { - return this.getCourseGradeForStudents(gradebookUid, Collections.singletonList(userUuid)).get(userUuid); - } - - @Override - public Map getCourseGradeForStudents(final String gradebookUid, - final List userUuids) { - - final Map rval = new HashMap<>(); - - try { - final Gradebook gradebook = getGradebook(gradebookUid); - final GradeMapping gradeMap = gradebook.getSelectedGradeMapping(); - - rval.putAll(this.getCourseGradeForStudents(gradebookUid, userUuids, gradeMap.getGradeMap())); - } catch (final Exception e) { - log.error("Error in getCourseGradeForStudents", e); - } - return rval; - } - - @Override - public Map getCourseGradeForStudents(final String gradebookUid, - final List userUuids, final Map gradeMap) { - final Map rval = new HashMap<>(); - - try { - final Gradebook gradebook = getGradebook(gradebookUid); - - // if not released, and not instructor or TA, don't do any work - // note that this will return a course grade for Instructor and TA even if not released, see SAK-30119 - if (!gradebook.isCourseGradeDisplayed() && !(currentUserHasEditPerm(gradebookUid) || currentUserHasGradingPerm(gradebookUid))) { - return rval; - } - - final List assignments = getAssignmentsCounted(gradebook.getId()); - - // this takes care of drop/keep scores - final List gradeRecords = getPointsEarnedCourseGradeRecords(getCourseGrade(gradebook.getId()), userUuids); - - // gradeMap MUST be sorted for the grade mapping to apply correctly - final Map sortedGradeMap = GradeMappingDefinition.sortGradeMapping(gradeMap); - - gradeRecords.forEach(gr -> { - - final org.sakaiproject.service.gradebook.shared.CourseGrade cg = new org.sakaiproject.service.gradebook.shared.CourseGrade(); - - // ID of the course grade item - cg.setId(gr.getCourseGrade().getId()); - - // set entered grade - cg.setEnteredGrade(gr.getEnteredGrade()); - - // set date recorded - cg.setDateRecorded(gr.getDateRecorded()); - - // set entered points - cg.setEnteredPoints(gr.getEnteredPoints()); - - if (!assignments.isEmpty()) { - - boolean showCalculatedGrade = serverConfigService.getBoolean("gradebook.coursegrade.showCalculatedGrade", true); - - // calculated grade - // may be null if no grade entries to calculate - Double calculatedGrade = showCalculatedGrade == true ? gr.getAutoCalculatedGrade() : gr.getEnteredPoints(); - - if (calculatedGrade == null) { - calculatedGrade = gr.getAutoCalculatedGrade(); - } - - if (calculatedGrade != null) { - cg.setCalculatedGrade(calculatedGrade.toString()); - - // SAK-33997 Adjust the rounding of the calculated grade so we get the appropriate - // grade mapping - BigDecimal bd = new BigDecimal(calculatedGrade) - .setScale(10, RoundingMode.HALF_UP) - .setScale(2, RoundingMode.HALF_UP); - calculatedGrade = bd.doubleValue(); - } - - // mapped grade - final String mappedGrade = GradeMapping.getMappedGrade(sortedGradeMap, calculatedGrade); - log.debug("calculatedGrade: {} -> mappedGrade: {}", calculatedGrade, mappedGrade); - cg.setMappedGrade(mappedGrade); - - // points - cg.setPointsEarned(gr.getPointsEarned()); // synonymous with gradeRecord.getCalculatedPointsEarned() - cg.setTotalPointsPossible(gr.getTotalPointsPossible()); - - } - rval.put(gr.getStudentId(), cg); - }); - } catch (final Exception e) { - log.error("Error in getCourseGradeForStudents", e); - } - return rval; - } - - @Override - public List getViewableSections(final String gradebookUid) { - return getAuthz().getViewableSections(gradebookUid); - } - - @Override - public void updateGradebookSettings(final String gradebookUid, final GradebookInformation gbInfo) { - if (gradebookUid == null) { - throw new IllegalArgumentException("null gradebookUid " + gradebookUid); - } - - // must be instructor type person - if (!currentUserHasEditPerm(gradebookUid)) { - log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to edit gb information", getUserUid(), gradebookUid); - throw new GradebookSecurityException("You do not have permission to edit gradebook information in site " + gradebookUid); - } - - final Gradebook gradebook = getGradebook(gradebookUid); - if (gradebook == null) { - throw new IllegalArgumentException("There is no gradebook associated with this id: " + gradebookUid); - } - - final Map bottomPercents = gbInfo.getSelectedGradingScaleBottomPercents(); - - // Before we do any work, check if any existing course grade overrides might be left in an unmappable state - final List courseGradeOverrides = getHibernateTemplate().execute(session -> getCourseGradeOverrides(gradebook)); - courseGradeOverrides.forEach(cgr -> { - if (!bottomPercents.containsKey(cgr.getEnteredGrade())) { - throw new UnmappableCourseGradeOverrideException( - "The grading schema could not be updated as it would leave some course grade overrides in an unmappable state."); - } - }); - - // iterate all available grademappings for this gradebook and set the one that we have the ID and bottomPercents for - final Set gradeMappings = gradebook.getGradeMappings(); - gradeMappings.forEach(gradeMapping -> { - if (StringUtils.equals(Long.toString(gradeMapping.getId()), gbInfo.getSelectedGradeMappingId())) { - gradebook.setSelectedGradeMapping(gradeMapping); - - // update the map values - updateGradeMapping(gradeMapping.getId(), bottomPercents); - } - }); - - // set grade type, but only if sakai.property is true OR user is admin - final boolean gradeTypeAvailForNonAdmins = serverConfigService.getBoolean("gradebook.settings.gradeEntry.showToNonAdmins", true); - if (gradeTypeAvailForNonAdmins || SecurityService.isSuperUser()) { - gradebook.setGrade_type(gbInfo.getGradeType()); - } - - // set category type - gradebook.setCategory_type(gbInfo.getCategoryType()); - - // set display release items to students - gradebook.setAssignmentsDisplayed(gbInfo.isDisplayReleasedGradeItemsToStudents()); - - // set course grade display settings - gradebook.setCourseGradeDisplayed(gbInfo.isCourseGradeDisplayed()); - gradebook.setCourseLetterGradeDisplayed(gbInfo.isCourseLetterGradeDisplayed()); - gradebook.setCoursePointsDisplayed(gbInfo.isCoursePointsDisplayed()); - gradebook.setCourseAverageDisplayed(gbInfo.isCourseAverageDisplayed()); - - // set stats display settings - gradebook.setAssignmentStatsDisplayed(gbInfo.isAssignmentStatsDisplayed()); - gradebook.setCourseGradeStatsDisplayed(gbInfo.isCourseGradeStatsDisplayed()); - - // set allow students to compare grades - gradebook.setAllowStudentsToCompareGrades(gbInfo.isAllowStudentsToCompareGrades()); - gradebook.setComparingDisplayStudentNames(gbInfo.isComparingDisplayStudentNames()); - gradebook.setComparingDisplayStudentSurnames(gbInfo.isComparingDisplayStudentSurnames()); - gradebook.setComparingDisplayTeacherComments(gbInfo.isComparingDisplayTeacherComments()); - gradebook.setComparingIncludeAllGrades(gbInfo.isComparingIncludeAllGrades()); - gradebook.setComparingRandomizeDisplayedData(gbInfo.isComparingRandomizeDisplayedData()); - - final List newCategoryDefinitions = gbInfo.getCategories(); - - // if we have categories and they are weighted, check the weightings sum up to 100% (or 1 since it's a fraction) - if (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY) { - double totalWeight = 0; - for (final CategoryDefinition newDef : newCategoryDefinitions) { - - if (newDef.getWeight() == null) { - throw new IllegalArgumentException("No weight specified for a category, but weightings enabled"); - } - - totalWeight += newDef.getWeight(); - } - if (Math.rint(totalWeight) != 1) { - throw new IllegalArgumentException("Weightings for the categories do not equal 100%"); - } - } - - // get current categories and build a mapping list of Category.id to Category - final List currentCategories = getCategories(gradebook.getId()); - final Map currentCategoryMap = new HashMap<>(); - for (final Category c : currentCategories) { - currentCategoryMap.put(c.getId(), c); - } - - // compare current list with given list, add/update/remove as required - // Rules: - // If category does not have an ID it is new; add these later after all removals have been processed - // If category has an ID it is to be updated. Update and remove from currentCategoryMap. - // Any categories remaining in currentCategoryMap are to be removed. - // Sort by category order as we resequence the order values to avoid gaps - Collections.sort(newCategoryDefinitions, CategoryDefinition.orderComparator); - final Map newCategories = new HashMap<>(); - int categoryIndex = 0; - for (final CategoryDefinition newDef : newCategoryDefinitions) { - - // preprocessing and validation - // Rule 1: If category has no name, it is to be removed/skipped - // Note that we no longer set weights to 0 even if unweighted category type selected. The weights are not considered if its not - // a weighted category type - // so this allows us to switch back and forth between types without losing information - - if (StringUtils.isBlank(newDef.getName())) { - continue; - } - - // new - if (newDef.getId() == null) { - newCategories.put(newDef, categoryIndex); - categoryIndex++; - } - - // update - else { - final Category existing = currentCategoryMap.get(newDef.getId()); - existing.setName(newDef.getName()); - existing.setWeight(newDef.getWeight()); - existing.setDropLowest(newDef.getDropLowest()); - existing.setDropHighest(newDef.getDropHighest()); - existing.setKeepHighest(newDef.getKeepHighest()); - existing.setExtraCredit(newDef.getExtraCredit()); - existing.setEqualWeightAssignments(newDef.getEqualWeight()); - existing.setCategoryOrder(categoryIndex); - updateCategory(existing); - - // remove from currentCategoryMap so we know not to delete it - currentCategoryMap.remove(newDef.getId()); - - categoryIndex++; - } - - } - - // handle deletes - // anything left in currentCategoryMap was not included in the new list, delete them - for (final Entry cat : currentCategoryMap.entrySet()) { - removeCategory(cat.getKey()); - } - - // Handle the additions - for (final Entry entry : newCategories.entrySet()) { - final CategoryDefinition newCat = entry.getKey(); - this.createCategory(gradebook.getId(), newCat.getName(), newCat.getWeight(), newCat.getDropLowest(), - newCat.getDropHighest(), newCat.getKeepHighest(), newCat.getExtraCredit(), newCat.getEqualWeight(), entry.getValue()); - } - - // if weighted categories, all uncategorised assignments are to be removed from course grade calcs - if (gradebook.getCategory_type() == GradebookService.CATEGORY_TYPE_WEIGHTED_CATEGORY) { - excludeUncategorisedItemsFromCourseGradeCalculations(gradebook); - } - - // persist - updateGradebook(gradebook); - - } - - public Authz getAuthz() { - return this.authz; - } - - public void setAuthz(final Authz authz) { - this.authz = authz; - } - - public GradebookPermissionService getGradebookPermissionService() { - return this.gradebookPermissionService; - } - - public void setGradebookPermissionService(final GradebookPermissionService gradebookPermissionService) { - this.gradebookPermissionService = gradebookPermissionService; - } - - public void setSiteService(final SiteService siteService) { - this.siteService = siteService; - } - - public SiteService getSiteService() { - return this.siteService; - } - - @Override - public Set getGradebookGradeMappings(final Long gradebookId) { - return (Set) getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Set doInHibernate(final Session session) throws HibernateException { - final Gradebook gradebook = (Gradebook) session.load(Gradebook.class, gradebookId); - Hibernate.initialize(gradebook.getGradeMappings()); - return gradebook.getGradeMappings(); - } - }); - } - - @Override - public Set getGradebookGradeMappings(final String gradebookUid) { - final Long gradebookId = getGradebook(gradebookUid).getId(); - return this.getGradebookGradeMappings(gradebookId); - } - - @Override - public void updateCourseGradeForStudent(final String gradebookUid, final String studentUuid, final String grade, final String gradeScale) { - - // must be instructor type person - if (!currentUserHasEditPerm(gradebookUid)) { - log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to update course grade for student: {}", getUserUid(), - gradebookUid, studentUuid); - throw new GradebookSecurityException("You do not have permission to update course grades in " + gradebookUid); - } - - final Gradebook gradebook = getGradebook(gradebookUid); - if (gradebook == null) { - throw new IllegalArgumentException("There is no gradebook associated with this id: " + gradebookUid); - } - - // get course grade for the student - CourseGradeRecord courseGradeRecord = (CourseGradeRecord) getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - return getCourseGradeRecord(gradebook, studentUuid); - } - }); - - // if user doesn't have an entered course grade, we need to find the course grade and create a record - if (courseGradeRecord == null) { - - final CourseGrade courseGrade = getCourseGrade(gradebook.getId()); - - courseGradeRecord = new CourseGradeRecord(courseGrade, studentUuid); - courseGradeRecord.setGraderId(getUserUid()); - - } else { - // if passed in grade override is same as existing grade override, nothing to do - if ( (StringUtils.equals(courseGradeRecord.getEnteredGrade(), gradeScale)) && (Double.compare(courseGradeRecord.getEnteredPoints(), Double.parseDouble(grade)) == 0) ) { - return; - } - } - - // set the grade override - courseGradeRecord.setEnteredGrade(gradeScale); - if (grade == null) { - courseGradeRecord.setEnteredPoints(null); - } else { - courseGradeRecord.setEnteredPoints(Double.parseDouble(grade)); - } - - // record the last grade override date - courseGradeRecord.setDateRecorded(new Date()); - - // create a grading event - final GradingEvent gradingEvent = new GradingEvent(courseGradeRecord.getCourseGrade(), getUserUid(), studentUuid, courseGradeRecord.getEnteredGrade()); - - // save - getHibernateTemplate().saveOrUpdate(courseGradeRecord); - getHibernateTemplate().saveOrUpdate(gradingEvent); - } - - /** - * Map a set of GradeMapping to a list of GradeMappingDefinition - * - * @param gradeMappings set of GradeMapping - * @return list of GradeMappingDefinition - */ - private List getGradebookGradeMappings(final Set gradeMappings) { - final List rval = new ArrayList<>(); - - for (final GradeMapping mapping : gradeMappings) { - rval.add(new GradeMappingDefinition(mapping.getId(), mapping.getName(), - GradeMappingDefinition.sortGradeMapping(mapping.getGradeMap()), - GradeMappingDefinition.sortGradeMapping(mapping.getDefaultBottomPercents()))); - } - return rval; - - } - - /** - * Updates the categorized order of an assignment - * - * @see GradebookService.updateAssignmentCategorizedOrder(java.lang.String gradebookUid, java.lang.Long assignmentId, java.lang.Integer - * order) - */ - @Override - public void updateAssignmentCategorizedOrder(final String gradebookUid, final Long categoryId, final Long assignmentId, Integer order) { - - if (!getAuthz().isUserAbleToEditAssessments(gradebookUid)) { - log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to change the order of assignment {}", getUserUid(), - gradebookUid, assignmentId); - throw new GradebookSecurityException(); - } - - if (order == null) { - throw new IllegalArgumentException("Categorized Order cannot be null"); - } - - final Long gradebookId = getGradebook(gradebookUid).getId(); - - // get all assignments for this gradebook - final List assignments = getAssignments(gradebookId, SortType.SORT_BY_CATEGORY, true); - final List assignmentsInNewCategory = new ArrayList<>(); - for (final GradebookAssignment assignment : assignments) { - if (assignment.getCategory() == null) { - if (categoryId == null) { - assignmentsInNewCategory.add(assignment); - } - } else if (assignment.getCategory().getId().equals(categoryId)) { - assignmentsInNewCategory.add(assignment); - } - } - - // adjust order to be within bounds - if (order < 0) { - order = 0; - } else if (order > assignmentsInNewCategory.size()) { - order = assignmentsInNewCategory.size(); - } - - // find the assignment - GradebookAssignment target = null; - for (final GradebookAssignment a : assignmentsInNewCategory) { - if (a.getId().equals(assignmentId)) { - target = a; - break; - } - } - - // add the assignment to the list via a 'pad, remove, add' approach - assignmentsInNewCategory.add(null); // ensure size remains the same for the remove - assignmentsInNewCategory.remove(target); // remove item - assignmentsInNewCategory.add(order, target); // add at ordered position, will shuffle others along - - // the assignments are now in the correct order within the list, we just need to update the sort order for each one - // create a new list for the assignments we need to update in the database - final List assignmentsToUpdate = new ArrayList<>(); - - int i = 0; - for (final GradebookAssignment a : assignmentsInNewCategory) { - - // skip if null - if (a == null) { - continue; - } - - // if the sort order is not the same as the counter, update the order and add to the other list - // this allows us to skip items that have not had their position changed and saves some db work later on - // sort order may be null if never previously sorted, so give it the current index - if (a.getCategorizedSortOrder() == null || !a.getCategorizedSortOrder().equals(i)) { - a.setCategorizedSortOrder(i); - assignmentsToUpdate.add(a); - } - - i++; - } - - // do the updates - for (final GradebookAssignment assignmentToUpdate : assignmentsToUpdate) { - getHibernateTemplate().execute(new HibernateCallback() { - @Override - public Object doInHibernate(final Session session) throws HibernateException { - updateAssignment(assignmentToUpdate); - return null; - } - }); - } - - } - - /** - * Return the grade changes made since a given time - * - * @param assignmentIds ids of assignments to check - * @param since timestamp from which to check for changes - * @return set of changes made - */ - @Override - public List getGradingEvents(final List assignmentIds, final Date since) { - if (assignmentIds == null || assignmentIds.isEmpty() || since == null) { - return new ArrayList<>(); - } - - return getHibernateTemplate().execute(session -> session.createCriteria(GradingEvent.class) - .createAlias("gradableObject", "go") - .add(Restrictions.and( - Restrictions.ge("dateGraded", since), - HibernateCriterionUtils.CriterionInRestrictionSplitter("go.id", assignmentIds))) - .list()); - } - - /** - * Update the persistent grade points for an assignment when the total points is changed. - * - * @param gradebook the gradebook - * @param assignment assignment with original total point value - */ - private void scaleGrades(final Gradebook gradebook, final GradebookAssignment assignment, - final Double originalPointsPossible) { - if (gradebook == null || assignment == null || assignment.getPointsPossible() == null) { - throw new IllegalArgumentException("null values found in convertGradePointsForUpdatedTotalPoints."); - } - - final List studentUids = getStudentsForGradebook(gradebook); - final List gradeRecords = getAllAssignmentGradeRecordsForGbItem(assignment.getId(), studentUids); - final Set eventsToAdd = new HashSet<>(); - final String currentUserUid = getAuthn().getUserUid(); - - // scale for total points changed when on percentage grading - if (gradebook.getGrade_type() == GradebookService.GRADE_TYPE_PERCENTAGE && assignment.getPointsPossible() != null) { - - log.debug("Scaling percentage grades"); - - for (final AssignmentGradeRecord gr : gradeRecords) { - if (gr.getPointsEarned() != null) { - final BigDecimal scoreAsPercentage = (new BigDecimal(gr.getPointsEarned()) - .divide(new BigDecimal(originalPointsPossible), GradebookService.MATH_CONTEXT)) - .multiply(new BigDecimal(100)); - - final BigDecimal scaledScore = new BigDecimal(calculateEquivalentPointValueForPercent(assignment.getPointsPossible(), - scoreAsPercentage.doubleValue()), GradebookService.MATH_CONTEXT).setScale(2, RoundingMode.HALF_UP); - - log.debug("scoreAsPercentage: {}, scaledScore: {}", scoreAsPercentage, scaledScore); - - gr.setPointsEarned(scaledScore.doubleValue()); - eventsToAdd.add(new GradingEvent(assignment, currentUserUid, gr.getStudentId(), scaledScore)); - } - } - } - else if (gradebook.getGrade_type() == GradebookService.GRADE_TYPE_POINTS && assignment.getPointsPossible() != null) { - - log.debug("Scaling point grades"); - - final BigDecimal previous = new BigDecimal(originalPointsPossible); - final BigDecimal current = new BigDecimal(assignment.getPointsPossible()); - final BigDecimal factor = current.divide(previous, GradebookService.MATH_CONTEXT); - - log.debug("previous points possible: {}, current points possible: {}, factor: {}", previous, current, factor); - - for (final AssignmentGradeRecord gr : gradeRecords) { - if (gr.getPointsEarned() != null) { - - final BigDecimal currentGrade = new BigDecimal(gr.getPointsEarned(), GradebookService.MATH_CONTEXT); - final BigDecimal scaledGrade = currentGrade.multiply(factor, GradebookService.MATH_CONTEXT).setScale(2, RoundingMode.HALF_UP); - - log.debug("currentGrade: {}, scaledGrade: {}", currentGrade, scaledGrade); - - gr.setPointsEarned(scaledGrade.doubleValue()); - DecimalFormat df = (DecimalFormat)NumberFormat.getNumberInstance((new ResourceLoader()).getLocale()); - df.setGroupingUsed(false); - String pointsLocale = df.format(scaledGrade); - eventsToAdd.add(new GradingEvent(assignment, currentUserUid, gr.getStudentId(), pointsLocale)); - } - } - } - - // save all - batchPersistEntities(gradeRecords); - - // Insert the new grading events (GradeRecord) - for (final GradingEvent ge : eventsToAdd) { - getHibernateTemplate().persist(ge); - } - } - - /** - * Get the list of students for the given gradebook - * - * @param gradebook the gradebook for the site - * @return a list of uuids for the students - */ - private List getStudentsForGradebook(final Gradebook gradebook) { - final List enrolments = getSectionAwareness().getSiteMembersInRole(gradebook.getUid(), Role.STUDENT); - - final List rval = enrolments.stream() - .map(EnrollmentRecord::getUser) - .map(User::getUserUid) - .collect(Collectors.toList()); - - return rval; - } - - /** - * Helper to batch persist entities - * - * @param entities a list of entities. - */ - private void batchPersistEntities(final List entities) { - final Session session = getSessionFactory().getCurrentSession(); - entities.forEach(session::update); - } - - private boolean isCurrentUserFromGroup(final String gradebookUid, final String studentId) { - boolean isFromGroup = false; - try { - final Site s = this.siteService.getSite(gradebookUid); - final Group g = s.getGroup(studentId); - isFromGroup = (g != null) && (g.getMember(this.authn.getUserUid()) != null); - } catch (final Exception e) { - // Id not found - log.error("Error in isCurrentUserFromGroup: ", e); - } - return isFromGroup; - } - - /** - * Updates all uncategorised items to exclude them from the course grade calcs - * - * @param gradebook - */ - private void excludeUncategorisedItemsFromCourseGradeCalculations(final Gradebook gradebook) { - final List allAssignments = getAssignments(gradebook.getId()); - - final List assignments = allAssignments.stream().filter(a -> a.getCategory() == null) - .collect(Collectors.toList()); - assignments.forEach(a -> a.setCounted(false)); - batchPersistEntities(assignments); - } -} diff --git a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/tool/gradebook/facades/sakai2impl/AuthnSakai2Impl.java b/edu-services/gradebook-service/impl/src/java/org/sakaiproject/tool/gradebook/facades/sakai2impl/AuthnSakai2Impl.java deleted file mode 100644 index bae141cac664..000000000000 --- a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/tool/gradebook/facades/sakai2impl/AuthnSakai2Impl.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) 2003-2016 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.tool.gradebook.facades.sakai2impl; - -import lombok.extern.slf4j.Slf4j; - -import org.sakaiproject.tool.api.Session; -import org.sakaiproject.tool.cover.SessionManager; -import org.sakaiproject.tool.gradebook.facades.Authn; - -/** - * Sakai2 implementation of the gradebook's Authn facade. - * - * @author Josh Holtzman - */ -@Slf4j -public class AuthnSakai2Impl implements Authn { - /** - * @see org.sakaiproject.tool.gradebook.facades.Authn#getUserUid() - */ - public String getUserUid() { - Session session = SessionManager.getCurrentSession(); - String userId = session.getUserId(); - if(log.isDebugEnabled()) log.debug("current user id is " + userId); - return userId; - } - - /** - * In Sakai, the framework maintains its own ThreadLocal - * user context. - */ - public void setAuthnContext(Object whatToAuthn) { - } -} - - diff --git a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/tool/gradebook/facades/sakai2impl/AuthzSakai2Impl.java b/edu-services/gradebook-service/impl/src/java/org/sakaiproject/tool/gradebook/facades/sakai2impl/AuthzSakai2Impl.java deleted file mode 100644 index eddb4236621f..000000000000 --- a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/tool/gradebook/facades/sakai2impl/AuthzSakai2Impl.java +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook.facades.sakai2impl; - -import java.util.Collection; - -import lombok.extern.slf4j.Slf4j; - -import org.sakaiproject.authz.cover.FunctionManager; -import org.sakaiproject.authz.cover.SecurityService; -import org.sakaiproject.site.cover.SiteService; -import org.sakaiproject.section.api.facade.Role; -import org.sakaiproject.tool.gradebook.facades.Authz; -import org.sakaiproject.tool.gradebook.facades.sections.AuthzSectionsImpl; -import org.sakaiproject.user.api.User; -import org.sakaiproject.user.api.UserNotDefinedException; -import org.sakaiproject.user.cover.UserDirectoryService; - -/** - * An implementation of Gradebook-specific authorization needs based - * on a combination of fine-grained site-scoped Sakai permissions and the - * shared Section Awareness API. This is a transtional stage between - * coarse-grained site-and-role-based authz and our hoped-for fine-grained - * role-determined group-scoped authz. - */ -@Slf4j -public class AuthzSakai2Impl extends AuthzSectionsImpl implements Authz { - - /** - * Perform authorization-specific framework initializations for the Gradebook. - */ - public void init() { - Collection registered = FunctionManager.getInstance().getRegisteredFunctions("gradebook"); - if(!registered.contains(PERMISSION_GRADE_ALL)) { - FunctionManager.registerFunction(PERMISSION_GRADE_ALL); - } - - if(!registered.contains(PERMISSION_GRADE_SECTION)) { - FunctionManager.registerFunction(PERMISSION_GRADE_SECTION); - } - - if(!registered.contains(PERMISSION_EDIT_ASSIGNMENTS)) { - FunctionManager.registerFunction(PERMISSION_EDIT_ASSIGNMENTS); - } - - if(!registered.contains(PERMISSION_VIEW_OWN_GRADES)) { - FunctionManager.registerFunction(PERMISSION_VIEW_OWN_GRADES); - } - - if(!registered.contains(PERMISSION_VIEW_STUDENT_NUMBERS)) { - FunctionManager.registerFunction(PERMISSION_VIEW_STUDENT_NUMBERS); - } - } - - public boolean isUserAbleToGrade(String gradebookUid) { - return (hasPermission(gradebookUid, PERMISSION_GRADE_ALL) || hasPermission(gradebookUid, PERMISSION_GRADE_SECTION)); - } - - public boolean isUserAbleToGrade(String gradebookUid, String userUid) { - try { - User user = UserDirectoryService.getUser(userUid); - return (hasPermission(user, gradebookUid, PERMISSION_GRADE_ALL) || hasPermission(user, gradebookUid, PERMISSION_GRADE_SECTION)); - } catch (UserNotDefinedException unde) { - log.warn("User not found for userUid: " + userUid); - return false; - } - - } - - public boolean isUserAbleToGradeAll(String gradebookUid) { - return hasPermission(gradebookUid, PERMISSION_GRADE_ALL); - } - - public boolean isUserAbleToGradeAll(String gradebookUid, String userUid) { - try { - User user = UserDirectoryService.getUser(userUid); - return hasPermission(user, gradebookUid, PERMISSION_GRADE_ALL); - } catch (UserNotDefinedException unde) { - log.warn("User not found for userUid: " + userUid); - return false; - } - } - - /** - * When group-scoped permissions are available, this is where - * they will go. My current assumption is that the call will look like: - * - * return hasPermission(sectionUid, PERMISSION_GRADE_ALL); - */ - public boolean isUserAbleToGradeSection(String sectionUid) { - return getSectionAwareness().isSectionMemberInRole(sectionUid, getAuthn().getUserUid(), Role.TA); - } - - public boolean isUserAbleToEditAssessments(String gradebookUid) { - return hasPermission(gradebookUid, PERMISSION_EDIT_ASSIGNMENTS); - } - - public boolean isUserAbleToViewOwnGrades(String gradebookUid) { - return hasPermission(gradebookUid, PERMISSION_VIEW_OWN_GRADES); - } - - public boolean isUserAbleToViewStudentNumbers(String gradebookUid) - { - return hasPermission(gradebookUid, PERMISSION_VIEW_STUDENT_NUMBERS); - } - - private boolean hasPermission(String gradebookUid, String permission) { - return SecurityService.unlock(permission, SiteService.siteReference(gradebookUid)); - } - - private boolean hasPermission(User user, String gradebookUid, String permission) { - return SecurityService.unlock(user, permission, SiteService.siteReference(gradebookUid)); - } - -} diff --git a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/tool/gradebook/facades/sections/AuthzSectionsImpl.java b/edu-services/gradebook-service/impl/src/java/org/sakaiproject/tool/gradebook/facades/sections/AuthzSectionsImpl.java deleted file mode 100644 index 1335bf06610e..000000000000 --- a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/tool/gradebook/facades/sections/AuthzSectionsImpl.java +++ /dev/null @@ -1,685 +0,0 @@ -/** - * Copyright (c) 2003-2017 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.sakaiproject.tool.gradebook.facades.sections; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import lombok.extern.slf4j.Slf4j; - -import org.sakaiproject.exception.IdUnusedException; -import org.sakaiproject.section.api.SectionAwareness; -import org.sakaiproject.section.api.coursemanagement.CourseSection; -import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; -import org.sakaiproject.section.api.facade.Role; -import org.sakaiproject.service.gradebook.shared.GradebookPermissionService; -import org.sakaiproject.service.gradebook.shared.GradebookService; -import org.sakaiproject.site.api.Group; -import org.sakaiproject.tool.gradebook.GradebookAssignment; -import org.sakaiproject.tool.gradebook.facades.Authn; -import org.sakaiproject.tool.gradebook.facades.Authz; - -/** - * An implementation of Gradebook-specific authorization needs based - * on the shared Section Awareness API. - */ -@Slf4j -public class AuthzSectionsImpl implements Authz { - private Authn authn; - private SectionAwareness sectionAwareness; - private GradebookPermissionService gradebookPermissionService; - - public boolean isUserAbleToGrade(String gradebookUid) { - String userUid = authn.getUserUid(); - return isUserAbleToGrade(gradebookUid, userUid); - } - - public boolean isUserAbleToGrade(String gradebookUid, String userUid) { - return (getSectionAwareness().isSiteMemberInRole(gradebookUid, userUid, Role.INSTRUCTOR) || getSectionAwareness().isSiteMemberInRole(gradebookUid, userUid, Role.TA)); - } - - public boolean isUserAbleToGradeAll(String gradebookUid) { - return isUserAbleToGradeAll(gradebookUid, authn.getUserUid()); - } - - public boolean isUserAbleToGradeAll(String gradebookUid, String userUid) { - return getSectionAwareness().isSiteMemberInRole(gradebookUid, userUid, Role.INSTRUCTOR); - } - - public boolean isUserHasGraderPermissions(String gradebookUid) { - String userUid = authn.getUserUid(); - List permissions = gradebookPermissionService.getGraderPermissionsForUser(gradebookUid, userUid); - return permissions != null && permissions.size() > 0; - } - - public boolean isUserHasGraderPermissions(Long gradebookId) { - String userUid = authn.getUserUid(); - List permissions = gradebookPermissionService.getGraderPermissionsForUser(gradebookId, userUid); - return permissions != null && permissions.size() > 0; - } - - public boolean isUserHasGraderPermissions(String gradebookUid, String userUid) { - List permissions = gradebookPermissionService.getGraderPermissionsForUser(gradebookUid, userUid); - return permissions != null && permissions.size() > 0; - } - - public boolean isUserHasGraderPermissions(Long gradebookId, String userUid) { - List permissions = gradebookPermissionService.getGraderPermissionsForUser(gradebookId, userUid); - return permissions != null && permissions.size() > 0; - } - - /** - * - * @param sectionUid - * @return whether user is Role.TA in given section - */ - private boolean isUserTAinSection(String sectionUid) { - String userUid = authn.getUserUid(); - return getSectionAwareness().isSectionMemberInRole(sectionUid, userUid, Role.TA); - } - - private boolean isUserTAinSection(String sectionUid, String userUid) { - return getSectionAwareness().isSectionMemberInRole(sectionUid, userUid, Role.TA); - } - - public boolean isUserAbleToEditAssessments(String gradebookUid) { - String userUid = authn.getUserUid(); - return getSectionAwareness().isSiteMemberInRole(gradebookUid, userUid, Role.INSTRUCTOR); - } - - public boolean isUserAbleToViewOwnGrades(String gradebookUid) { - String userUid = authn.getUserUid(); - return getSectionAwareness().isSiteMemberInRole(gradebookUid, userUid, Role.STUDENT); - } - - public boolean isUserAbleToViewStudentNumbers(String gradebookUid) - { - String userUid = authn.getUserUid(); - return getSectionAwareness().isSiteMemberInRole(gradebookUid, userUid, Role.INSTRUCTOR); - } - - public String getGradeViewFunctionForUserForStudentForItem(String gradebookUid, Long itemId, String studentUid) { - if (itemId == null || studentUid == null || gradebookUid == null) { - throw new IllegalArgumentException("Null parameter(s) in AuthzSectionsServiceImpl.isUserAbleToGradeItemForStudent"); - } - - if (isUserAbleToGradeAll(gradebookUid)) { - return GradebookService.gradePermission; - } - - String userUid = authn.getUserUid(); - - List viewableSections = getViewableSections(gradebookUid); - List sectionIds = new ArrayList(); - if (viewableSections != null && !viewableSections.isEmpty()) { - for (Iterator sectionIter = viewableSections.iterator(); sectionIter.hasNext();) { - CourseSection section = (CourseSection) sectionIter.next(); - sectionIds.add(section.getUuid()); - } - } - - if (isUserHasGraderPermissions(gradebookUid, userUid)) { - - // get the map of authorized item (assignment) ids to grade/view function - Map itemIdFunctionMap = gradebookPermissionService.getAvailableItemsForStudent(gradebookUid, userUid, studentUid, viewableSections); - - if (itemIdFunctionMap == null || itemIdFunctionMap.isEmpty()) { - return null; // not authorized to grade/view any items for this student - } - - String functionValueForItem = (String)itemIdFunctionMap.get(itemId); - String view = GradebookService.viewPermission; - String grade = GradebookService.gradePermission; - - if (functionValueForItem != null) { - if (functionValueForItem.equalsIgnoreCase(grade)) - return GradebookService.gradePermission; - - if (functionValueForItem.equalsIgnoreCase(view)) - return GradebookService.viewPermission; - } - - return null; - - } else { - // use OOTB permissions based upon TA section membership - for (Iterator iter = sectionIds.iterator(); iter.hasNext(); ) { - String sectionUuid = (String) iter.next(); - if (isUserTAinSection(sectionUuid) && getSectionAwareness().isSectionMemberInRole(sectionUuid, studentUid, Role.STUDENT)) { - return GradebookService.gradePermission; - } - } - - return null; - } - } - - private boolean isUserAbleToGradeOrViewItemForStudent(String gradebookUid, Long itemId, String studentUid, String function) throws IllegalArgumentException { - if (itemId == null || studentUid == null || function == null) { - throw new IllegalArgumentException("Null parameter(s) in AuthzSectionsServiceImpl.isUserAbleToGradeItemForStudent"); - } - - if (isUserAbleToGradeAll(gradebookUid)) { - return true; - } - - String userUid = authn.getUserUid(); - - List viewableSections = getViewableSections(gradebookUid); - List sectionIds = new ArrayList(); - if (viewableSections != null && !viewableSections.isEmpty()) { - for (Iterator sectionIter = viewableSections.iterator(); sectionIter.hasNext();) { - CourseSection section = (CourseSection) sectionIter.next(); - sectionIds.add(section.getUuid()); - } - } - - if (isUserHasGraderPermissions(gradebookUid, userUid)) { - - // get the map of authorized item (assignment) ids to grade/view function - Map itemIdFunctionMap = gradebookPermissionService.getAvailableItemsForStudent(gradebookUid, userUid, studentUid, viewableSections); - - if (itemIdFunctionMap == null || itemIdFunctionMap.isEmpty()) { - return false; // not authorized to grade/view any items for this student - } - - String functionValueForItem = (String)itemIdFunctionMap.get(itemId); - String view = GradebookService.viewPermission; - String grade = GradebookService.gradePermission; - - if (functionValueForItem != null) { - if (function.equalsIgnoreCase(grade) && functionValueForItem.equalsIgnoreCase(grade)) - return true; - - if (function.equalsIgnoreCase(view) && (functionValueForItem.equalsIgnoreCase(grade) || functionValueForItem.equalsIgnoreCase(view))) - return true; - } - - return false; - - } else { - // use OOTB permissions based upon TA section membership - for (Iterator iter = sectionIds.iterator(); iter.hasNext(); ) { - String sectionUuid = (String) iter.next(); - if (isUserTAinSection(sectionUuid) && getSectionAwareness().isSectionMemberInRole(sectionUuid, studentUid, Role.STUDENT)) { - return true; - } - } - - return false; - } - } - - - public boolean isUserAbleToGradeItemForStudent(String gradebookUid, Long itemId, String studentUid) throws IllegalArgumentException { - return isUserAbleToGradeOrViewItemForStudent(gradebookUid, itemId, studentUid, GradebookService.gradePermission); - } - - public boolean isUserAbleToViewItemForStudent(String gradebookUid, Long itemId, String studentUid) throws IllegalArgumentException { - return isUserAbleToGradeOrViewItemForStudent(gradebookUid, itemId, studentUid, GradebookService.viewPermission); - } - - public List getViewableSections(String gradebookUid) { - List viewableSections = new ArrayList(); - - List allSections = getAllSections(gradebookUid); - if (allSections == null || allSections.isEmpty()) { - return viewableSections; - } - - if (isUserAbleToGradeAll(gradebookUid)) { - return allSections; - } - - Map sectionIdCourseSectionMap = new HashMap(); - - for (Iterator sectionIter = allSections.iterator(); sectionIter.hasNext();) { - CourseSection section = (CourseSection) sectionIter.next(); - sectionIdCourseSectionMap.put(section.getUuid(), section); - } - - String userUid = authn.getUserUid(); - - if (isUserHasGraderPermissions(gradebookUid, userUid)) { - - List viewableSectionIds = gradebookPermissionService.getViewableGroupsForUser(gradebookUid, userUid, new ArrayList(sectionIdCourseSectionMap.keySet())); - if (viewableSectionIds != null && !viewableSectionIds.isEmpty()) { - for (Iterator idIter = viewableSectionIds.iterator(); idIter.hasNext();) { - String sectionUuid = (String) idIter.next(); - CourseSection viewableSection = (CourseSection)sectionIdCourseSectionMap.get(sectionUuid); - if (viewableSection != null) - viewableSections.add(viewableSection); - } - } - } else { - // return all sections that the current user is a TA for - for (Iterator> iter = sectionIdCourseSectionMap.entrySet().iterator(); iter.hasNext(); ) { - Map.Entry entry = iter.next(); - String sectionUuid = entry.getKey(); - if (isUserTAinSection(sectionUuid)) { - CourseSection viewableSection = (CourseSection)sectionIdCourseSectionMap.get(sectionUuid); - if (viewableSection != null) - viewableSections.add(viewableSection); - } - } - } - - Collections.sort(viewableSections); - - return viewableSections; - - } - - public List getAllSections(String gradebookUid) { - SectionAwareness sectionAwareness = getSectionAwareness(); - List sections = sectionAwareness.getSections(gradebookUid); - - return sections; - } - - private List getSectionEnrollmentsTrusted(String sectionUid) { - return getSectionAwareness().getSectionMembersInRole(sectionUid, Role.STUDENT); - } - - public Map findMatchingEnrollmentsForItem(String gradebookUid, Long categoryId, int gbCategoryType, String optionalSearchString, String optionalSectionUid) { - String userUid = authn.getUserUid(); - return findMatchingEnrollmentsForItemOrCourseGrade(userUid, gradebookUid, categoryId, gbCategoryType, optionalSearchString, optionalSectionUid, false); - } - - public Map findMatchingEnrollmentsForItemForUser(String userUid, String gradebookUid, Long categoryId, int gbCategoryType, String optionalSearchString, String optionalSectionUid) { - return findMatchingEnrollmentsForItemOrCourseGrade(userUid, gradebookUid, categoryId, gbCategoryType, optionalSearchString, optionalSectionUid, false); - } - - public Map findMatchingEnrollmentsForViewableCourseGrade(String gradebookUid, int gbCategoryType, String optionalSearchString, String optionalSectionUid) { - String userUid = authn.getUserUid(); - return findMatchingEnrollmentsForItemOrCourseGrade(userUid, gradebookUid, null, gbCategoryType, optionalSearchString, optionalSectionUid, true); - } - - public Map findMatchingEnrollmentsForViewableItems(String gradebookUid, List allGbItems, String optionalSearchString, String optionalSectionUid) { - Map enrollmentMap = new HashMap(); - List filteredEnrollments = null; - if (optionalSearchString != null) - filteredEnrollments = getSectionAwareness().findSiteMembersInRole(gradebookUid, Role.STUDENT, optionalSearchString); - else - filteredEnrollments = getSectionAwareness().getSiteMembersInRole(gradebookUid, Role.STUDENT); - - if (filteredEnrollments == null || filteredEnrollments.isEmpty()) - return enrollmentMap; - - // get all the students in the filtered section, if appropriate - Map studentsInSectionMap = new HashMap(); - if (optionalSectionUid != null) { - List sectionMembers = getSectionEnrollmentsTrusted(optionalSectionUid); - if (!sectionMembers.isEmpty()) { - for(Iterator memberIter = sectionMembers.iterator(); memberIter.hasNext();) { - EnrollmentRecord member = (EnrollmentRecord) memberIter.next(); - studentsInSectionMap.put(member.getUser().getUserUid(), member); - } - } - } - - Map studentIdEnrRecMap = new HashMap(); - for (Iterator enrIter = filteredEnrollments.iterator(); enrIter.hasNext();) { - EnrollmentRecord enr = (EnrollmentRecord) enrIter.next(); - String studentId = enr.getUser().getUserUid(); - if (optionalSectionUid != null) { - if (studentsInSectionMap.containsKey(studentId)) { - studentIdEnrRecMap.put(studentId, enr); - } - } else { - studentIdEnrRecMap.put(studentId, enr); - } - } - - if (isUserAbleToGradeAll(gradebookUid)) { - List enrollments = new ArrayList(studentIdEnrRecMap.values()); - - HashMap assignFunctionMap = new HashMap(); - if (allGbItems != null && !allGbItems.isEmpty()) { - for (Iterator assignIter = allGbItems.iterator(); assignIter.hasNext();) { - Object assign = assignIter.next(); - Long assignId = null; - if (assign instanceof org.sakaiproject.service.gradebook.shared.Assignment) { - assignId = ((org.sakaiproject.service.gradebook.shared.Assignment)assign).getId(); - } else if (assign instanceof GradebookAssignment) { - assignId = ((GradebookAssignment)assign).getId(); - } - - if (assignId != null) - assignFunctionMap.put(assignId, GradebookService.gradePermission); - } - } - - for (Iterator enrIter = enrollments.iterator(); enrIter.hasNext();) { - EnrollmentRecord enr = (EnrollmentRecord) enrIter.next(); - enrollmentMap.put(enr, assignFunctionMap); - } - - } else { - String userId = authn.getUserUid(); - - Map sectionIdCourseSectionMap = new HashMap(); - List viewableSections = getViewableSections(gradebookUid); - for (Iterator sectionIter = viewableSections.iterator(); sectionIter.hasNext();) { - CourseSection section = (CourseSection) sectionIter.next(); - sectionIdCourseSectionMap.put(section.getUuid(), section); - } - - if (isUserHasGraderPermissions(gradebookUid)) { - // user has special grader permissions that override default perms - - List myStudentIds = new ArrayList(studentIdEnrRecMap.keySet()); - - List selSections = new ArrayList(); - if (optionalSectionUid == null) { - // pass all sections - selSections = new ArrayList(sectionIdCourseSectionMap.values()); - } else { - // only pass the selected section - CourseSection section = (CourseSection) sectionIdCourseSectionMap.get(optionalSectionUid); - if (section != null) - selSections.add(section); - } - - // we need to get the viewable students, so first create section id --> student ids map - myStudentIds = getGradebookPermissionService().getViewableStudentsForUser(gradebookUid, userId, myStudentIds, selSections); - Map viewableStudentIdItemsMap = new HashMap(); - if (allGbItems == null || allGbItems.isEmpty()) { - if (myStudentIds != null) { - for (Iterator stIter = myStudentIds.iterator(); stIter.hasNext();) { - String stId = (String) stIter.next(); - if (stId != null) - viewableStudentIdItemsMap.put(stId, null); - } - } - } else { - viewableStudentIdItemsMap = gradebookPermissionService.getAvailableItemsForStudents(gradebookUid, userId, myStudentIds, selSections); - } - - if (!viewableStudentIdItemsMap.isEmpty()) { - for (Iterator> enrIter = viewableStudentIdItemsMap.entrySet().iterator(); enrIter.hasNext();) { - Map.Entry entry = enrIter.next(); - String studentId = entry.getKey(); - EnrollmentRecord enrRec = (EnrollmentRecord)studentIdEnrRecMap.get(studentId); - if (enrRec != null) { - Map itemIdFunctionMap = (Map)viewableStudentIdItemsMap.get(studentId); - //if (!itemIdFunctionMap.isEmpty()) { - enrollmentMap.put(enrRec, itemIdFunctionMap); - //} - } - } - } - - } else { - // use default section-based permissions - - // Determine the current user's section memberships - List availableSections = new ArrayList(); - if (optionalSectionUid != null && isUserTAinSection(optionalSectionUid)) { - if (sectionIdCourseSectionMap.containsKey(optionalSectionUid)) - availableSections.add(optionalSectionUid); - } else { - for (Iterator iter = sectionIdCourseSectionMap.keySet().iterator(); iter.hasNext(); ) { - String sectionUuid = (String)iter.next(); - if (isUserTAinSection(sectionUuid)) { - availableSections.add(sectionUuid); - } - } - } - - // Determine which enrollees are in these sections - Map uniqueEnrollees = new HashMap(); - for (Iterator iter = availableSections.iterator(); iter.hasNext(); ) { - String sectionUuid = (String)iter.next(); - List sectionEnrollments = getSectionEnrollmentsTrusted(sectionUuid); - for (Iterator eIter = sectionEnrollments.iterator(); eIter.hasNext(); ) { - EnrollmentRecord enr = (EnrollmentRecord)eIter.next(); - uniqueEnrollees.put(enr.getUser().getUserUid(), enr); - } - } - - // Filter out based upon the original filtered students - for (Iterator iter = studentIdEnrRecMap.keySet().iterator(); iter.hasNext(); ) { - String enrId = (String)iter.next(); - if (uniqueEnrollees.containsKey(enrId)) { - // iterate through the assignments - Map itemFunctionMap = new HashMap(); - if (allGbItems != null && !allGbItems.isEmpty()) { - for (Iterator itemIter = allGbItems.iterator(); itemIter.hasNext();) { - Object assign = itemIter.next(); - Long assignId = null; - if (assign instanceof org.sakaiproject.service.gradebook.shared.Assignment) { - assignId = ((org.sakaiproject.service.gradebook.shared.Assignment)assign).getId(); - } else if (assign instanceof GradebookAssignment) { - assignId = ((GradebookAssignment)assign).getId(); - } - - if (assignId != null) { - itemFunctionMap.put(assignId, GradebookService.gradePermission); - } - } - } - enrollmentMap.put(studentIdEnrRecMap.get(enrId), itemFunctionMap); - } - } - } - } - - return enrollmentMap; - } - - /** - * @param userUid - * @param gradebookUid - * @param categoryId - * @param optionalSearchString - * @param optionalSectionUid - * @param itemIsCourseGrade - * @return Map of EnrollmentRecord --> View or Grade - */ - private Map findMatchingEnrollmentsForItemOrCourseGrade(String userUid, String gradebookUid, Long categoryId, int gbCategoryType, String optionalSearchString, String optionalSectionUid, boolean itemIsCourseGrade) { - Map enrollmentMap = new HashMap(); - List filteredEnrollments = new ArrayList(); - - if (optionalSearchString != null) - filteredEnrollments = getSectionAwareness().findSiteMembersInRole(gradebookUid, Role.STUDENT, optionalSearchString); - else - filteredEnrollments = getSectionAwareness().getSiteMembersInRole(gradebookUid, Role.STUDENT); - - if (filteredEnrollments.isEmpty()) - return enrollmentMap; - - // get all the students in the filtered section, if appropriate - Map studentsInSectionMap = new HashMap(); - if (optionalSectionUid != null) { - List sectionMembers = getSectionAwareness().getSectionMembersInRole(optionalSectionUid, Role.STUDENT); - if (!sectionMembers.isEmpty()) { - for(Iterator memberIter = sectionMembers.iterator(); memberIter.hasNext();) { - EnrollmentRecord member = (EnrollmentRecord) memberIter.next(); - studentsInSectionMap.put(member.getUser().getUserUid(), member); - } - } - } - - Map studentIdEnrRecMap = new HashMap(); - for (Iterator enrIter = filteredEnrollments.iterator(); enrIter.hasNext();) { - EnrollmentRecord enr = (EnrollmentRecord) enrIter.next(); - String studentId = enr.getUser().getUserUid(); - if (optionalSectionUid != null) { - if (studentsInSectionMap.containsKey(studentId)) { - studentIdEnrRecMap.put(studentId, enr); - } - } else { - studentIdEnrRecMap.put(enr.getUser().getUserUid(), enr); - } - } - - if (isUserAbleToGradeAll(gradebookUid, userUid)) { - List enrollments = new ArrayList(studentIdEnrRecMap.values()); - - for (Iterator enrIter = enrollments.iterator(); enrIter.hasNext();) { - EnrollmentRecord enr = (EnrollmentRecord) enrIter.next(); - enrollmentMap.put(enr, GradebookService.gradePermission); - } - - } else { - Map sectionIdCourseSectionMap = new HashMap(); - List allSections = getAllSections(gradebookUid); - for (Iterator sectionIter = allSections.iterator(); sectionIter.hasNext();) { - CourseSection section = (CourseSection) sectionIter.next(); - sectionIdCourseSectionMap.put(section.getUuid(), section); - } - - if (isUserHasGraderPermissions(gradebookUid, userUid)) { - // user has special grader permissions that override default perms - - List myStudentIds = new ArrayList(studentIdEnrRecMap.keySet()); - - List selSections = new ArrayList(); - if (optionalSectionUid == null) { - // pass all sections - selSections = new ArrayList(sectionIdCourseSectionMap.values()); - } else { - // only pass the selected section - CourseSection section = (CourseSection) sectionIdCourseSectionMap.get(optionalSectionUid); - if (section != null) - selSections.add(section); - } - - Map viewableEnrollees = new HashMap(); - if (itemIsCourseGrade) { - viewableEnrollees = gradebookPermissionService.getCourseGradePermission(gradebookUid, userUid, myStudentIds, selSections); - } else { - viewableEnrollees = gradebookPermissionService.getStudentsForItem(gradebookUid, userUid, myStudentIds, gbCategoryType, categoryId, selSections); - } - - if (!viewableEnrollees.isEmpty()) { - for (Iterator> enrIter = viewableEnrollees.entrySet().iterator(); enrIter.hasNext();) { - Map.Entry entry = enrIter.next(); - String studentId = entry.getKey(); - EnrollmentRecord enrRec = (EnrollmentRecord)studentIdEnrRecMap.get(studentId); - if (enrRec != null) { - enrollmentMap.put(enrRec, (String)viewableEnrollees.get(studentId)); - } - } - } - - } else { - // use default section-based permissions - enrollmentMap = getEnrollmentMapUsingDefaultPermissions(userUid, studentIdEnrRecMap, sectionIdCourseSectionMap, optionalSectionUid); - } - } - - return enrollmentMap; - } - - /** - * - * @param userUid - * @param studentIdEnrRecMap - * @param sectionIdCourseSectionMap - * @param optionalSectionUid - * @return Map of EnrollmentRecord to function view/grade using the default permissions (based on TA section membership) - */ - private Map getEnrollmentMapUsingDefaultPermissions(String userUid, Map studentIdEnrRecMap, Map sectionIdCourseSectionMap, String optionalSectionUid) { - // Determine the current user's section memberships - Map enrollmentMap = new HashMap(); - List availableSections = new ArrayList(); - if (optionalSectionUid != null && isUserTAinSection(optionalSectionUid, userUid)) { - if (sectionIdCourseSectionMap.containsKey(optionalSectionUid)) - availableSections.add(optionalSectionUid); - } else { - for (Iterator iter = sectionIdCourseSectionMap.keySet().iterator(); iter.hasNext(); ) { - String sectionUuid = (String)iter.next(); - if (isUserTAinSection(sectionUuid, userUid)) { - availableSections.add(sectionUuid); - } - } - } - - // Determine which enrollees are in these sections - Map uniqueEnrollees = new HashMap(); - for (Iterator iter = availableSections.iterator(); iter.hasNext(); ) { - String sectionUuid = (String)iter.next(); - List sectionEnrollments = getSectionEnrollmentsTrusted(sectionUuid); - for (Iterator eIter = sectionEnrollments.iterator(); eIter.hasNext(); ) { - EnrollmentRecord enr = (EnrollmentRecord)eIter.next(); - uniqueEnrollees.put(enr.getUser().getUserUid(), enr); - } - } - - // Filter out based upon the original filtered students - for (Iterator iter = studentIdEnrRecMap.keySet().iterator(); iter.hasNext(); ) { - String enrId = (String)iter.next(); - if (uniqueEnrollees.containsKey(enrId)) { - enrollmentMap.put(studentIdEnrRecMap.get(enrId), GradebookService.gradePermission); - } - } - - return enrollmentMap; - } - - - public List findStudentSectionMemberships(String gradebookUid, String studentUid) { - List sectionMemberships = new ArrayList(); - try { - sectionMemberships = (List)org.sakaiproject.site.cover.SiteService.getSite(gradebookUid).getGroupsWithMember(studentUid); - } catch (IdUnusedException e) { - log.error("No site with id = " + gradebookUid); - } - - return sectionMemberships; - } - - public List getStudentSectionMembershipNames(String gradebookUid, String studentUid) { - List sectionNames = new ArrayList(); - List sections = findStudentSectionMemberships(gradebookUid, studentUid); - if (sections != null && !sections.isEmpty()) { - Iterator sectionIter = sections.iterator(); - while (sectionIter.hasNext()) { - Group myGroup = (Group) sectionIter.next(); - sectionNames.add(myGroup.getTitle()); - } - } - - return sectionNames; - } - - public Authn getAuthn() { - return authn; - } - public void setAuthn(Authn authn) { - this.authn = authn; - } - public SectionAwareness getSectionAwareness() { - return sectionAwareness; - } - public void setSectionAwareness(SectionAwareness sectionAwareness) { - this.sectionAwareness = sectionAwareness; - } - public GradebookPermissionService getGradebookPermissionService() { - return gradebookPermissionService; - } - public void setGradebookPermissionService(GradebookPermissionService gradebookPermissionService) { - this.gradebookPermissionService = gradebookPermissionService; - } - -} diff --git a/edu-services/gradebook-service/pom.xml b/edu-services/gradebook-service/pom.xml deleted file mode 100644 index 44f126ffe811..000000000000 --- a/edu-services/gradebook-service/pom.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - 4.0.0 - - - edu-services - org.sakaiproject.edu-services - 23-SNAPSHOT - - - org.sakaiproject.edu-services.gradebook - gradebook-service - sakai-gradebook-service - pom - - - api - hibernate - impl - sakai-pack - - - diff --git a/edu-services/gradebook-service/sakai-pack/pom.xml b/edu-services/gradebook-service/sakai-pack/pom.xml deleted file mode 100644 index 5e63fe9041ed..000000000000 --- a/edu-services/gradebook-service/sakai-pack/pom.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - 4.0.0 - - edu-services - org.sakaiproject.edu-services - 23-SNAPSHOT - ../../pom.xml - - org.sakaiproject.edu-services.gradebook - gradebook-service-pack - gradebook-service-pack - sakai-component - - components - - - - ${project.groupId} - gradebook-service-impl - ${project.version} - - - org.sakaiproject.gradebook - sakai-gradebook-service-hibernate - - - aopalliance - aopalliance - - - odmg - odmg - - - commons-digester - commons-digester - - - commons-beanutils - commons-beanutils - - - - - - - - src/resources - - - - diff --git a/edu-services/gradebook-service/sakai-pack/src/resources/hsqldb/sakai_gradebook_post_schemaupdate.sql b/edu-services/gradebook-service/sakai-pack/src/resources/hsqldb/sakai_gradebook_post_schemaupdate.sql deleted file mode 100644 index 0e6779a2bd4f..000000000000 --- a/edu-services/gradebook-service/sakai-pack/src/resources/hsqldb/sakai_gradebook_post_schemaupdate.sql +++ /dev/null @@ -1,20 +0,0 @@ --- Most automatic database initialization will be taken care of automatically --- by Hibernate's SchemaUpdate tool, triggered by the hibernate.hbm2ddl.auto --- property in vanilla Hibernate applications and by the auto.ddl property --- in the Sakai framework. --- --- Not all necessary elements might be created by SchemaUpdate, however. --- Notably, in versions of Hibernate through at least 3.1.3, no explicit --- index definitions in the mapping file will be honored except during a --- full SchemaExport. --- --- This file creates schema in reverse order of when they were added to --- Gradebook out-of-the-box SQL, to increase the chances that the script --- will have useful results as an upgrader as well as an initializer. - --- Add indexes for improved performance and reduced locking. -create index GB_GRADABLE_OBJ_ASN_IDX on GB_GRADABLE_OBJECT_T (OBJECT_TYPE_ID, GRADEBOOK_ID, NAME, REMOVED); -create index GB_GRADE_RECORD_O_T_IDX on GB_GRADE_RECORD_T (OBJECT_TYPE_ID); - --- Already handled by Hibernate as of 3.6.x --- create index GB_GRADE_RECORD_STUDENT_ID_IDX on GB_GRADE_RECORD_T (STUDENT_ID); diff --git a/edu-services/gradebook-service/sakai-pack/src/resources/mysql/sakai_gradebook_post_schemaupdate.sql b/edu-services/gradebook-service/sakai-pack/src/resources/mysql/sakai_gradebook_post_schemaupdate.sql deleted file mode 100644 index 0e6779a2bd4f..000000000000 --- a/edu-services/gradebook-service/sakai-pack/src/resources/mysql/sakai_gradebook_post_schemaupdate.sql +++ /dev/null @@ -1,20 +0,0 @@ --- Most automatic database initialization will be taken care of automatically --- by Hibernate's SchemaUpdate tool, triggered by the hibernate.hbm2ddl.auto --- property in vanilla Hibernate applications and by the auto.ddl property --- in the Sakai framework. --- --- Not all necessary elements might be created by SchemaUpdate, however. --- Notably, in versions of Hibernate through at least 3.1.3, no explicit --- index definitions in the mapping file will be honored except during a --- full SchemaExport. --- --- This file creates schema in reverse order of when they were added to --- Gradebook out-of-the-box SQL, to increase the chances that the script --- will have useful results as an upgrader as well as an initializer. - --- Add indexes for improved performance and reduced locking. -create index GB_GRADABLE_OBJ_ASN_IDX on GB_GRADABLE_OBJECT_T (OBJECT_TYPE_ID, GRADEBOOK_ID, NAME, REMOVED); -create index GB_GRADE_RECORD_O_T_IDX on GB_GRADE_RECORD_T (OBJECT_TYPE_ID); - --- Already handled by Hibernate as of 3.6.x --- create index GB_GRADE_RECORD_STUDENT_ID_IDX on GB_GRADE_RECORD_T (STUDENT_ID); diff --git a/edu-services/gradebook-service/sakai-pack/src/resources/oracle/sakai_gradebook_post_schemaupdate.sql b/edu-services/gradebook-service/sakai-pack/src/resources/oracle/sakai_gradebook_post_schemaupdate.sql deleted file mode 100644 index f3e334b9ec30..000000000000 --- a/edu-services/gradebook-service/sakai-pack/src/resources/oracle/sakai_gradebook_post_schemaupdate.sql +++ /dev/null @@ -1,23 +0,0 @@ --- Most automatic database initialization will be taken care of automatically --- by Hibernate's SchemaUpdate tool, triggered by the hibernate.hbm2ddl.auto --- property in vanilla Hibernate applications and by the auto.ddl property --- in the Sakai framework. --- --- Not all necessary elements might be created by SchemaUpdate, however. --- Notably, in versions of Hibernate through at least 3.1.3, no explicit --- index definitions in the mapping file will be honored except during a --- full SchemaExport. --- --- This file creates schema in reverse order of when they were added to --- Gradebook out-of-the-box SQL, to increase the chances that the script --- will have useful results as an upgrader as well as an initializer. - --- Add indexes for improved performance and reduced locking. -create index GRADEBOOK_ID on GB_GRADABLE_OBJECT_T (GRADEBOOK_ID); -create index GB_GRADE_MAP_GB_IDX on GB_GRADE_MAP_T (GRADEBOOK_ID); -create index GB_GRADABLE_OBJ_ASN_IDX on GB_GRADABLE_OBJECT_T (OBJECT_TYPE_ID, GRADEBOOK_ID, NAME, REMOVED); -create index GB_GRADE_RECORD_O_T_IDX on GB_GRADE_RECORD_T (OBJECT_TYPE_ID); - --- Already handled by Hibernate as of 3.6.x --- create index GB_GRADE_RECORD_G_O_IDX on GB_GRADE_RECORD_T (GRADABLE_OBJECT_ID); --- create index GB_GRADE_RECORD_STUDENT_ID_IDX on GB_GRADE_RECORD_T (STUDENT_ID); diff --git a/edu-services/gradebook-service/sakai-pack/src/webapp/WEB-INF/components.xml b/edu-services/gradebook-service/sakai-pack/src/webapp/WEB-INF/components.xml deleted file mode 100644 index 7f882cedba82..000000000000 --- a/edu-services/gradebook-service/sakai-pack/src/webapp/WEB-INF/components.xml +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - - - org/sakaiproject/tool/gradebook/Gradebook.hbm.xml - org/sakaiproject/tool/gradebook/GradableObject.hbm.xml - org/sakaiproject/tool/gradebook/GradeRecord.hbm.xml - org/sakaiproject/tool/gradebook/GradingEvent.hbm.xml - org/sakaiproject/tool/gradebook/GradingScale.hbm.xml - org/sakaiproject/tool/gradebook/GradebookProperty.hbm.xml - org/sakaiproject/tool/gradebook/GradeMapping.hbm.xml - org/sakaiproject/tool/gradebook/Spreadsheet.hbm.xml - org/sakaiproject/tool/gradebook/Comment.hbm.xml - org/sakaiproject/tool/gradebook/Category.hbm.xml - org/sakaiproject/tool/gradebook/LetterGradePercenteMapping.hbm.xml - org/sakaiproject/tool/gradebook/Permission.hbm.xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PROPAGATION_REQUIRED,readOnly,+org.sakaiproject.service.gradebook.shared.GradebookException - PROPAGATION_REQUIRED,readOnly,+org.sakaiproject.service.gradebook.shared.GradebookException - PROPAGATION_REQUIRED,+org.sakaiproject.service.gradebook.shared.GradebookException - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/edu-services/pom.xml b/edu-services/pom.xml index 8dfa7a03ea9a..1290553e5460 100644 --- a/edu-services/pom.xml +++ b/edu-services/pom.xml @@ -14,7 +14,6 @@ pom - gradebook-service cm-service sections-service scoring-service diff --git a/edu-services/sections-service/sections-api/src/java/org/sakaiproject/section/api/coursemanagement/CourseSection.java b/edu-services/sections-service/sections-api/src/java/org/sakaiproject/section/api/coursemanagement/CourseSection.java index 4f0d981979db..8ecd1037aba1 100644 --- a/edu-services/sections-service/sections-api/src/java/org/sakaiproject/section/api/coursemanagement/CourseSection.java +++ b/edu-services/sections-service/sections-api/src/java/org/sakaiproject/section/api/coursemanagement/CourseSection.java @@ -28,7 +28,7 @@ * @author Josh Holtzman * */ -public interface CourseSection extends LearningContext { +public interface CourseSection extends LearningContext, Comparable { /** * Gets the Course that this CourseSection belongs to * diff --git a/edu-services/sections-service/sections-impl/sakai/impl/src/java/org/sakaiproject/component/section/sakai/SectionAwarenessImpl.java b/edu-services/sections-service/sections-impl/sakai/impl/src/java/org/sakaiproject/component/section/sakai/SectionAwarenessImpl.java index 8502badae429..1b11d287378f 100644 --- a/edu-services/sections-service/sections-impl/sakai/impl/src/java/org/sakaiproject/component/section/sakai/SectionAwarenessImpl.java +++ b/edu-services/sections-service/sections-impl/sakai/impl/src/java/org/sakaiproject/component/section/sakai/SectionAwarenessImpl.java @@ -91,7 +91,7 @@ public void destroy() { */ public List getSections(final String siteContext) { if(log.isDebugEnabled()) log.debug("Getting sections for context " + siteContext); - List sectionList = new ArrayList(); + List sectionList = new ArrayList<>(); Collection sections; try { sections = siteService.getSite(siteContext).getGroups(); diff --git a/edu-services/sections-service/sections-impl/sakai/model/src/java/org/sakaiproject/component/section/sakai/CourseSectionImpl.java b/edu-services/sections-service/sections-impl/sakai/model/src/java/org/sakaiproject/component/section/sakai/CourseSectionImpl.java index bce28152d951..5197c5a592d4 100644 --- a/edu-services/sections-service/sections-impl/sakai/model/src/java/org/sakaiproject/component/section/sakai/CourseSectionImpl.java +++ b/edu-services/sections-service/sections-impl/sakai/model/src/java/org/sakaiproject/component/section/sakai/CourseSectionImpl.java @@ -46,7 +46,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j -public class CourseSectionImpl implements CourseSection, Comparable, Serializable { +public class CourseSectionImpl implements CourseSection, Serializable { private static final long serialVersionUID = 1L; private static final String TIME_FORMAT_LONG = "h:mm a"; private static final String TIME_FORMAT_DATE_TZ = "dd/MM/yyyy HH:mm zzzz"; diff --git a/gradebookng/api/pom.xml b/gradebookng/api/pom.xml new file mode 100644 index 000000000000..16c53315516f --- /dev/null +++ b/gradebookng/api/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + Grading Service Api + org.sakaiproject.grading + sakai-grading-api + + + org.sakaiproject.gradebookng + gradebookng + 23-SNAPSHOT + + + jar + + + shared + + + + + org.sakaiproject.kernel + sakai-kernel-api + + + org.sakaiproject.kernel + sakai-component-manager + + + org.sakaiproject.kernel + sakai-kernel-util + + + org.sakaiproject.edu-services.sections + sections-api + + + org.apache.commons + commons-lang3 + + + org.hibernate + hibernate-core + + + org.springframework.data + spring-data-jpa + + + com.fasterxml.jackson.core + jackson-annotations + + + + + + diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/AssessmentNotFoundException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/AssessmentNotFoundException.java similarity index 90% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/AssessmentNotFoundException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/AssessmentNotFoundException.java index e3b82118076c..b4f4de9dc92c 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/AssessmentNotFoundException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/AssessmentNotFoundException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; /** * Indicates that an assessment is unavailable for external management, either @@ -23,7 +23,7 @@ * @author Josh Holtzman */ public class AssessmentNotFoundException extends GradebookException { - public AssessmentNotFoundException(String message) { + public AssessmentNotFoundException(String message) { super(message); } } diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/Assignment.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/Assignment.java new file mode 100644 index 000000000000..b8e181c85f63 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/Assignment.java @@ -0,0 +1,138 @@ +/********************************************************************************** + * $URL$ + * $Id$ + *********************************************************************************** + * + * Copyright (c) 2006, 2007, 2008 The Sakai Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.opensource.org/licenses/ECL-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************************/ + +package org.sakaiproject.grading.api; + +import java.io.Serializable; +import java.util.Date; + +import org.apache.commons.lang3.builder.CompareToBuilder; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * JavaBean to hold data associated with a Gradebook assignment. The Course Grade is not considered an assignment. + */ +@NoArgsConstructor +@ToString +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@Getter @Setter +public class Assignment implements Serializable, Comparable { + + /** + * @return Returns the name of the assignment. The assignment name is unique among currently defined assignments. However, it is not a + * safe UID for persistance, since an assignment can be renamed. Also, an assignment can be deleted and a new assignment can be + * created re-using the old name. + */ + private String name; + + /** + * + * @return Returns the ID of the assignment in the gradebook + */ + @EqualsAndHashCode.Include + private Long id; + + /** + * the total points the assignment is worth. + */ + private Double points; + + /** + * the due date for the assignment, or null if none is defined. + */ + private Date dueDate; + + /** + * true if the assignment is maintained by some software other than the Gradebook itself. + */ + @JsonProperty(value = "externallyMaintained") + private Boolean externallyMaintained = Boolean.FALSE; + + /** + * the external id, or null if the assignment is maintained by the Gradebook + */ + private String externalId; + + /** + * the external app name, or null if the assignment is maintained by the Gradebook + */ + private String externalAppName; + + /** + * the external data, or null if the assignment is maintained by the Gradebook + */ + private String externalData; + + /** + * true if the assignment has been released for view to students + */ + @JsonProperty(value = "released") + private Boolean released = Boolean.FALSE; + + /** + * Note that any calls setSortOrder will not be persisted, if you want to change the sort order of an assignment you must call + * GradingService.updateAssignmentOrder as that method properly handles the reordering of all other assignments for the gradebook. + */ + private Integer sortOrder; + private Boolean counted = Boolean.FALSE; + private String categoryName; + private Double weight; + private Boolean ungraded = Boolean.FALSE; + @JsonProperty(value = "extraCredit") + private Boolean extraCredit = Boolean.FALSE; + private Boolean categoryExtraCredit = Boolean.FALSE; + private Boolean categoryEqualWeight = Boolean.FALSE; + private Long categoryId; + private Integer categoryOrder; + private Integer categorizedSortOrder; + @Getter + @Setter + private boolean createTask; + + /** + * For editing. Not persisted. + */ + @Getter + @Setter + private Boolean scaleGrades = Boolean.FALSE; + @Getter + @Setter + private String context; + @Getter + @Setter + private String gradebookId; + + @Override + public int compareTo(final Assignment o) { + return new CompareToBuilder() + .append(this.id, o.id) + .toComparison(); + } +} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/AssignmentHasIllegalPointsException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/AssignmentHasIllegalPointsException.java similarity index 95% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/AssignmentHasIllegalPointsException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/AssignmentHasIllegalPointsException.java index f8e218e1e833..70b15763aac3 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/AssignmentHasIllegalPointsException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/AssignmentHasIllegalPointsException.java @@ -13,11 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - -package org.sakaiproject.service.gradebook.shared; - - +package org.sakaiproject.grading.api; /** * indicates that there was an attempt to enter an assignments grade with a zero diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryDefinition.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryDefinition.java new file mode 100644 index 000000000000..f1ad688d8f79 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryDefinition.java @@ -0,0 +1,125 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Comparator; +import java.util.List; + +import lombok.Getter; +import lombok.Setter; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + + +/** + * Provides information describing a gradebook category that may be useful + * to consumers of the shared gradebook services. Not persisted. + */ +@Getter @Setter +public class CategoryDefinition implements Serializable { + + private static final long serialVersionUID = 1L; + + private Long id; + private String name; + private Double weight; + private Integer dropLowest; + private Integer dropHighest; + private Integer keepHighest; + private Boolean extraCredit; + private Boolean equalWeight; + private Integer categoryOrder; + private Boolean dropKeepEnabled; + private List assignmentList; + public static Comparator orderComparator; + + public CategoryDefinition() { } + + public CategoryDefinition(Long id, String name) { + + this.id = id; + this.name = name; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); + } + + static { + orderComparator = new Comparator() { + @Override + public int compare(final CategoryDefinition c1, final CategoryDefinition c2) { + if (c1.getCategoryOrder() == null && c2.getCategoryOrder() == null) { + return c1.getName().compareTo(c2.getName()); + } + if(c1.getCategoryOrder() == null) { + return -1; + } + if(c2.getCategoryOrder() == null) { + return 1; + } + return c1.getCategoryOrder().compareTo(c2.getCategoryOrder()); + } + }; + } + + /** + * Helper method to get the total points associated with a category + * + * @return the sum of all assignment totals associated with this category + */ + public Double getTotalPoints() { + BigDecimal totalPoints = new BigDecimal(0); + + if (getAssignmentList() != null) { + for (final Assignment assignment : getAssignmentList()) { + totalPoints = totalPoints.add(BigDecimal.valueOf(assignment.getPoints())); + } + } + return totalPoints.doubleValue(); + } + + public boolean isAssignmentInThisCategory(String assignmentId) { + for (Assignment thisAssignment : assignmentList) { + if (thisAssignment.getExternalId() == null) { + continue; + } + + if (thisAssignment.getExternalId().equalsIgnoreCase(assignmentId)) { + return true; + } + } + + return false; + } + + public Double getPointsForCategory() { + if (!dropKeepEnabled) { + return null; + } + + for (Assignment thisAssignment : assignmentList) { + return thisAssignment.getPoints(); + } + + return null; + } +} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CategoryScoreData.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryScoreData.java similarity index 74% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CategoryScoreData.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryScoreData.java index 812c690a803e..0e07f7a3feec 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/CategoryScoreData.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/CategoryScoreData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; import java.util.Collections; import java.util.List; @@ -24,12 +24,12 @@ */ public final class CategoryScoreData { - public final double score; - public final List droppedItems; + public final double score; + public final List droppedItems; - public CategoryScoreData(double score, List droppedItems) - { - this.score = score; - this.droppedItems = Collections.unmodifiableList(droppedItems); - } + public CategoryScoreData(double score, List droppedItems) + { + this.score = score; + this.droppedItems = Collections.unmodifiableList(droppedItems); + } } diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/CommentDefinition.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/CommentDefinition.java new file mode 100644 index 000000000000..257a3a194a62 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/CommentDefinition.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2003-2012 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api; + +import java.util.Date; + +/** + * + */ +public class CommentDefinition { + private String studentUid; + private String graderUid; + private Date dateRecorded; + private String commentText; + private String assignmentName; + + public String getAssignmentName() { + return assignmentName; + } + public void setAssignmentName(String assignmentName) { + this.assignmentName = assignmentName; + } + public String getCommentText() { + return commentText; + } + public void setCommentText(String commentText) { + this.commentText = commentText; + } + public Date getDateRecorded() { + return dateRecorded; + } + public void setDateRecorded(Date dateRecorded) { + this.dateRecorded = dateRecorded; + } + public String getGraderUid() { + return graderUid; + } + public void setGraderUid(String graderUid) { + this.graderUid = graderUid; + } + public String getStudentUid() { + return studentUid; + } + public void setStudentUid(String studentUid) { + this.studentUid = studentUid; + } +} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingAssignmentNameException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/ConflictingAssignmentNameException.java similarity index 89% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingAssignmentNameException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/ConflictingAssignmentNameException.java index ed98a0003ab4..279b7e5783de 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingAssignmentNameException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/ConflictingAssignmentNameException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; /** * Indicates that an assignment name conflict within a gradebook would ensue @@ -23,7 +23,7 @@ * @author Josh Holtzman */ public class ConflictingAssignmentNameException extends GradebookException { - public ConflictingAssignmentNameException(String message) { + public ConflictingAssignmentNameException(String message) { super(message); } } diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingCategoryNameException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/ConflictingCategoryNameException.java similarity index 86% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingCategoryNameException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/ConflictingCategoryNameException.java index c3253e63838e..aa5746e96c4c 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingCategoryNameException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/ConflictingCategoryNameException.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; public class ConflictingCategoryNameException extends GradebookException { - public ConflictingCategoryNameException(String message) { + public ConflictingCategoryNameException(String message) { super(message); } -} \ No newline at end of file +} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingExternalIdException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/ConflictingExternalIdException.java similarity index 89% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingExternalIdException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/ConflictingExternalIdException.java index 7157752ce4fa..07c43185f8be 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ConflictingExternalIdException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/ConflictingExternalIdException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; /** * Indicates that an assignment name conflict within a gradebook would ensue @@ -23,7 +23,7 @@ * @author Josh Holtzman */ public class ConflictingExternalIdException extends GradebookException { - public ConflictingExternalIdException(String message) { + public ConflictingExternalIdException(String message) { super(message); } } diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/CourseGradeTransferBean.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/CourseGradeTransferBean.java new file mode 100644 index 000000000000..49254ad8325e --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/CourseGradeTransferBean.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api; + +import java.io.Serializable; +import java.util.Date; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import lombok.Getter; +import lombok.Setter; + +/** + * Wrapper for the course grade that contains the the calculated grade (ie 46.67), the mapped grade (ie F) and any entered grade override (ie D-). + */ +@Getter @Setter +public class CourseGradeTransferBean implements Serializable { + + private static final long serialVersionUID = 1L; + + private Long id; + private String enteredGrade; + private String calculatedGrade; + private Double enteredPoints; + private String mappedGrade; + private Double pointsEarned; + private Double totalPointsPossible; + private Date dateRecorded; + private String autoCalculatedGrade; + private String displayString; + + /** + * Helper to get a grade override preferentially, or fallback to the standard mapped grade. + * @return + */ + public String getDisplayGrade() { + return StringUtils.isNotBlank(enteredGrade) ? enteredGrade : mappedGrade; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); + } +} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/DoubleComparator.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/DoubleComparator.java similarity index 58% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/DoubleComparator.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/DoubleComparator.java index 7cb9221978e9..47a668a7f908 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/DoubleComparator.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/DoubleComparator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; import java.io.Serializable; import java.util.Comparator; @@ -24,28 +24,28 @@ * Comparator to ensure correct ordering of letter grade / percent mappings */ public class DoubleComparator implements Comparator, Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private final Map base = new HashMap<>(); + private final Map base = new HashMap<>(); - public DoubleComparator(final Map base) { - this.base.putAll(base); - } + public DoubleComparator(final Map base) { + this.base.putAll(base); + } - @Override - public int compare(final String a, final String b) { + @Override + public int compare(final String a, final String b) { - final Double first = this.base.get(a); - final Double second = this.base.get(b); + final Double first = this.base.get(a); + final Double second = this.base.get(b); - if (first == null || second == null) { - return 0; // ignore this comparison - } + if (first == null || second == null) { + return 0; // ignore this comparison + } - if (first.compareTo(second) >= 0) { - return -1; - } else { - return 1; - } - } + if (first.compareTo(second) >= 0) { + return -1; + } else { + return 1; + } + } } diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/ExternalAssignmentProvider.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/ExternalAssignmentProvider.java new file mode 100644 index 000000000000..15e9d66e70b4 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/ExternalAssignmentProvider.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2003-2013 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Provider model to integrate external assessment sources with the gradebook. + * Its present use is to provide advice about activity grouping and visibility + * so that learners are not shown activities in which they cannot participate. + * + * https://jira.sakaiproject.org/browse/SAK-19668 + * @since 2.9.0 + */ +public interface ExternalAssignmentProvider { + + /** + * Get the application identifier for this provider. This must + * be unique to the external tool/service for proper operation. + */ + String getAppKey(); + + /** + * Check if an assignment/assessment exists with the given identifier. + * If the externalAppName is not the tool's responsibility or if the + * id is not recognized as matching + * for this service, false is expected to be returned. + * @param externalId + */ + boolean isAssignmentDefined(String externalAppName, String externalId); + + /** + * Check if the given assignment is grouped. + * Note that this will be a prefixed ID and must be parsed. + * + * @param id The prefixed external ID as registered with Gradebook + */ + boolean isAssignmentGrouped(String id); + + /** + * Check if the given assignment is visible to the given user. The primary + * use of this check is to see if groups are in effect for the assignment + * and whether or not the user can see the item. + * + * @param id The prefixed external ID as registered with Gradebook + * @param userId The internal user ID for the user to check + */ + boolean isAssignmentVisible(String id, String userId); + + /** + * Retrieve all assignments for a gradebook that are marked as externally + * maintained and are visible to the current user. + * + * @param gradebookUid The gradebook's unique identifier + * @return A list of IDs (as for externalId) of assignments visible to the current user + */ + List getExternalAssignmentsForCurrentUser(String gradebookUid); + + /** + * Retrieve all assignments for a gradebook that are marked as externally + * maintained and are (potentially variably) visible to a set of users. + * + * @param gradebookUid The gradebook's unique identifier + * @param studentIds The collection of user IDs to search/filter for + * @return A map of Student ID to list of external IDs of assignments visible to each user + */ + Map> getAllExternalAssignments(String gradebookUid, Collection studentIds); +} + diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ExternalAssignmentProviderCompat.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/ExternalAssignmentProviderCompat.java similarity index 82% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ExternalAssignmentProviderCompat.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/ExternalAssignmentProviderCompat.java index b66e8abecf11..a255ae19088f 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/ExternalAssignmentProviderCompat.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/ExternalAssignmentProviderCompat.java @@ -19,7 +19,7 @@ * limitations under the License. * **********************************************************************************/ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; import java.util.List; @@ -40,13 +40,13 @@ */ public interface ExternalAssignmentProviderCompat { - /** - * Retrieve all assignments for a gradebook that are marked as externally - * maintained. - * - * @param gradebookUid The gradebook's unique identifier - * @return A list of external IDs of assignments managed by this provider - */ - List getAllExternalAssignments(String gradebookUid); + /** + * Retrieve all assignments for a gradebook that are marked as externally + * maintained. + * + * @param gradebookUid The gradebook's unique identifier + * @return A list of external IDs of assignments managed by this provider + */ + List getAllExternalAssignments(String gradebookUid); } diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookNotFoundException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeDefinition.java similarity index 61% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookNotFoundException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeDefinition.java index 57bedff1c71e..0b40cf88f5dd 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookNotFoundException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeDefinition.java @@ -14,18 +14,22 @@ * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; -/** - * Indicates that a gradebook uid is unknown to the gradebook application. - * - * @author Josh Holtzman - */ -public class GradebookNotFoundException extends GradebookException { - public GradebookNotFoundException(String message) { - super(message); - } -} +import java.util.Date; +import lombok.Getter; +import lombok.Setter; +@Getter @Setter +public class GradeDefinition { + private String studentUid; + private String graderUid; + private Date dateRecorded; + private String grade; + private String gradeComment; + private GradeType gradeEntryType; + private boolean gradeReleased; + private boolean excused; +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeMappingDefinition.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeMappingDefinition.java new file mode 100644 index 000000000000..0317ddc6ef1d --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeMappingDefinition.java @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * DTO to wrap the persistent GradeMapping and provides utility methods for dealing with grade mappings + */ +public class GradeMappingDefinition implements Serializable { + + private static final long serialVersionUID = 1L; + + private String id; //note that this is a Long in GradeMapping but we convert for simplicity + private String name; + private Map gradeMap; + private Map defaultBottomPercents; + + public GradeMappingDefinition(final Long id, final String name, final Map gradeMap, final Map defaultBottomPercents){ + this.id = Long.toString(id); + this.name = name; + this.gradeMap = gradeMap; + this.defaultBottomPercents = defaultBottomPercents; + } + + public String getId() { + return this.id; + } + + public void setId(final String id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(final String name) { + this.name = name; + } + + /** + * Get the current grade mappings + * + * @return + */ + public Map getGradeMap() { + return this.gradeMap; + } + + public void setGradeMap(final Map gradeMap) { + this.gradeMap = gradeMap; + } + + /** + * Get the default grade mappings + * + * @return + */ + public Map getDefaultBottomPercents() { + return this.defaultBottomPercents; + } + + public void setDefaultBottomPercents(final Map defaultBottomPercents) { + this.defaultBottomPercents = defaultBottomPercents; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); + } + + /** + * Handles the sorting of a grade mapping. + * + * @param gradeMap the grademap to be sorted + * @return {@link LinkedHashMap} of sorted entries + */ + public static Map sortGradeMapping(final Map gradeMap) { + + // we only ever order by bottom percents now + final DoubleComparator doubleComparator = new DoubleComparator(gradeMap); + final Map tMap = new TreeMap<>(doubleComparator); + tMap.putAll(gradeMap); + final Map rval = new LinkedHashMap<>(tMap); + + return rval; + } + + /** + * Determines if the grade mapping is different to the defaults + * + * @return + */ + public boolean isModified() { + // TreeMap.equals uses compareTo for comparisons so cannot be used for equals in this case. Convert to HashMap. + final Map left = new HashMap<>(this.gradeMap); + final Map right = new HashMap<>(this.defaultBottomPercents); + return !left.equals(right); + } + +} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradingType.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeType.java similarity index 54% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradingType.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeType.java index 14bcdfa1f3df..a3e366402b9c 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradingType.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradeType.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; import java.util.HashMap; import java.util.Map; @@ -21,39 +21,41 @@ /** * The grading types that a gradebook could be configured as */ -public enum GradingType { +public enum GradeType { - POINTS(1), - PERCENTAGE(2), - LETTER(3); - - private int value; + POINTS, + PERCENTAGE, + LETTER; - GradingType(int value) { + /* + private int value; + + GradeType(int value) { this.value = value; } + */ /** - * Get the value for the type - * - * @return - */ - public int getValue() { - return this.value; - } - - // maintain a map of the types so we can lookup the enum based on type - private static Map map = new HashMap(); - - static { - for (final GradingType type : GradingType.values()) { - map.put(type.value, type); - } - } - - public static GradingType valueOf(final int value) { - return map.get(value); - } - + * Get the value for the type + * + * @return + */ + /* + public int getValue() { + return this.value; + } + + // maintain a map of the types so we can lookup the enum based on type + private static Map map = new HashMap<>(); + static { + for (final GradeType type : GradeType.values()) { + map.put(type.value, type); + } + } + + public static GradeType valueOf(final int value) { + return map.get(value); + } + */ } diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookException.java similarity index 83% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookException.java index 252ef3e58417..03456acdc6ff 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; /** * An exception thrown by the gradebook application. If a gradebook @@ -30,17 +30,17 @@ * behavior, this exception class is explicitly called out in the Spring * TransactionProxyFactoryBean configuration file using the following syntax: * - * PROPAGATION_REQUIRED,+org.sakaiproject.service.gradebook.shared.GradebookException + * PROPAGATION_REQUIRED,+org.sakaiproject.grading.api.GradebookException */ public class GradebookException extends RuntimeException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public GradebookException(final String message) { + public GradebookException(final String message) { super(message); } - public GradebookException(final Throwable t) { + public GradebookException(final Throwable t) { super(t); } } diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookExistsException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookExistsException.java similarity index 89% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookExistsException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookExistsException.java index 972fa0702b6d..fa15f33a0aa8 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookExistsException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookExistsException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; /** * Indicates that a gradebook already exists and can not be created again. @@ -22,7 +22,7 @@ * @author Josh Holtzman */ public class GradebookExistsException extends RuntimeException { - public GradebookExistsException(String message) { + public GradebookExistsException(String message) { super(message); } } diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookHelper.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookHelper.java similarity index 70% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookHelper.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookHelper.java index 6e05697afe3d..5f58d915268d 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookHelper.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookHelper.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; import org.apache.commons.lang3.StringUtils; @@ -32,7 +32,7 @@ public static String validateGradeItemName(String title) throws InvalidGradeItem if (StringUtils.isBlank(title)) { throw new ConflictingAssignmentNameException("You cannot save an assignment without a name"); } - else if (StringUtils.startsWithAny(title, GradebookService.INVALID_CHARS_AT_START_OF_GB_ITEM_NAME)) { + else if (StringUtils.startsWithAny(title, GradingService.INVALID_CHARS_AT_START_OF_GB_ITEM_NAME)) { throw new InvalidGradeItemNameException("Grade Item name is invalid: " + title); } return title; @@ -46,14 +46,14 @@ else if (StringUtils.startsWithAny(title, GradebookService.INVALID_CHARS_AT_STAR * @throws ConflictingAssignmentNameException * @return validated name */ - - public static String validateAssignmentNameAndPoints(final org.sakaiproject.service.gradebook.shared.Assignment assignmentDefinition) - throws InvalidGradeItemNameException, AssignmentHasIllegalPointsException, ConflictingAssignmentNameException { - // Ensure that points is > zero. - final Double points = assignmentDefinition.getPoints(); - if ((points == null) || (points <= 0)) { - throw new AssignmentHasIllegalPointsException("Points must be > 0"); - } - return validateGradeItemName(assignmentDefinition.getName()); - } + + public static String validateAssignmentNameAndPoints(final org.sakaiproject.grading.api.Assignment assignmentDefinition) + throws InvalidGradeItemNameException, AssignmentHasIllegalPointsException, ConflictingAssignmentNameException { + // Ensure that points is > zero. + final Double points = assignmentDefinition.getPoints(); + if ((points == null) || (points <= 0)) { + throw new AssignmentHasIllegalPointsException("Points must be > 0"); + } + return validateGradeItemName(assignmentDefinition.getName()); + } } diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookInformation.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookInformation.java new file mode 100644 index 000000000000..eb77cd5cbbdf --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradebookInformation.java @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import lombok.Data; + +/** + * Represents the settings for the gradebook + * + */ +@Data +public class GradebookInformation implements Serializable { + + private static final long serialVersionUID = 1L; + + private String selectedGradingScaleUid; + + /** + * The ID of the GradeMapping that should be used for this gradebook + */ + private String selectedGradeMappingId; + + /** + * The list of GradeMappings defined for this gradebook but as a DTO representation + */ + private List gradeMappings; + + /** + * The grading schema map currently in use for the this gradebook. For example A+ = 100 etc. + */ + private Map selectedGradingScaleBottomPercents; + + private Boolean displayReleasedGradeItemsToStudents; + + private GradeType gradeType = GradeType.POINTS; + + private GradingCategoryType categoryType; + + private List categories; + + /** + * The name of the grading scale, e.g. Pass / Not Pass + */ + private String gradeScale; + + /** + * Is the course grade to be shown at all? + */ + private Boolean courseGradeDisplayed; + + /** + * If the course grade is displayed, should the letter grade be displayed? + */ + private Boolean courseLetterGradeDisplayed; + + /** + * If the course grade is displayed, should the total points be displayed? + */ + private Boolean coursePointsDisplayed; + + /** + * If the course grade is displayed, should the percentage be displayed? + */ + private Boolean courseAverageDisplayed; + + /** + * Are assignment stats to be shown to students? + */ + private Boolean assignmentStatsDisplayed; + + /** + * Are course grade stats to be shown to students? + */ + private Boolean courseGradeStatsDisplayed; + + + /** + * Are students allowed to compare grades with classmates? + */ + private Boolean allowStudentsToCompareGrades; + + + /** + * Are student names displayed when comparing grades with classmates? + */ + private Boolean comparingDisplayStudentNames; + + /** + * Are student surnames displayed when comparing grades with classmates? + */ + private Boolean comparingDisplayStudentSurnames; + + /** + * Are teacher comments displayed when comparing grades with classmates? + */ + private Boolean comparingDisplayTeacherComments; + + /** + * Include grades that doesn't count when comparing grades with classmates? + */ + private Boolean comparingIncludeAllGrades; + + /** + * Randomize displayed data order when comparing grades with classmates? + */ + private Boolean comparingRandomizeDisplayedData; + +} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GraderPermission.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GraderPermission.java similarity index 56% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GraderPermission.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/GraderPermission.java index 8b4ea3b6b7a2..3a1409d9e3e4 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GraderPermission.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GraderPermission.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; import java.util.ArrayList; import java.util.List; @@ -23,29 +23,29 @@ */ public enum GraderPermission { - VIEW, - GRADE, - VIEW_COURSE_GRADE, - NONE; - - /** - * Return a lowercase version of the enum - */ - @Override - public String toString() { - return this.name().toLowerCase(); - } - - /** - * Helper to get the view and grade permissions as a list - * Used in a few places - * @return - */ - public static List getStandardPermissions() { - List rval = new ArrayList<>(); - rval.add(GraderPermission.VIEW.toString()); - rval.add(GraderPermission.GRADE.toString()); - return rval; - } + VIEW, + GRADE, + VIEW_COURSE_GRADE, + NONE; + + /** + * Return a lowercase version of the enum + */ + @Override + public String toString() { + return this.name().toLowerCase(); + } + + /** + * Helper to get the view and grade permissions as a list + * Used in a few places + * @return + */ + public static List getStandardPermissions() { + List rval = new ArrayList<>(); + rval.add(GraderPermission.VIEW.toString()); + rval.add(GraderPermission.GRADE.toString()); + return rval; + } } diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingAuthz.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingAuthz.java new file mode 100644 index 000000000000..cfeefebf2e44 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingAuthz.java @@ -0,0 +1,150 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api; + +import java.util.List; +import java.util.Map; + +import org.sakaiproject.section.api.coursemanagement.CourseSection; +import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; + +/** + * Facade to external role and authorization service. + */ +public interface GradingAuthz { + + public static final String + PERMISSION_GRADE_ALL = "gradebook.gradeAll", + PERMISSION_GRADE_SECTION = "gradebook.gradeSection", + PERMISSION_EDIT_ASSIGNMENTS = "gradebook.editAssignments", + PERMISSION_VIEW_OWN_GRADES = "gradebook.viewOwnGrades", + PERMISSION_VIEW_STUDENT_NUMBERS = "gradebook.viewStudentNumbers"; + + public boolean isUserAbleToGrade(String gradebookUid); + public boolean isUserAbleToGrade(String gradebookUid, String userUid); + public boolean isUserAbleToGradeAll(String gradebookUid); + public boolean isUserAbleToGradeAll(String gradebookUid, String userUid); + public boolean isUserAbleToEditAssessments(String gradebookUid); + public boolean isUserAbleToViewOwnGrades(String gradebookUid); + public boolean isUserAbleToViewStudentNumbers(String gradebookUid); + + /** + * + * @param gradebookUid + * @param itemId + * @param studentUid + * @return is user authorized to grade this gradebook item for this student? + * first checks for special grader perms. if none, uses default perms + */ + public boolean isUserAbleToGradeItemForStudent(String gradebookUid, Long itemId, String studentUid) throws IllegalArgumentException; + + /** + * + * @param gradebookUid + * @param itemId + * @param studentUid + * @return is user authorized to view this gradebook item for this student? + * first checks for special grader perms. if none, uses default perms + */ + public boolean isUserAbleToViewItemForStudent(String gradebookUid, Long itemId, String studentUid) throws IllegalArgumentException; + + /** + * @param gradebookUid + * @return all of the CourseSections for this site + */ + public List getAllSections(String gradebookUid); + + /** + * + * @param gradebookUid + * @return all CourseSections that the current user may view or grade + */ + public List getViewableSections(String gradebookUid); + + /** + * @param gradebookUid + * @param userUid + * @param categoryId + * The category id that the desired item is associated with + * @param gbCategoryType + * The category type setting for this gradebook + * @param optionalSearchString + * a substring search for student name or display UID; the exact rules are + * up to the implementation - leave null to use all students + * @param optionalSectionUid + * null if the search should be made across all sections + * @return a map of EnrollmentRecords to grade/view permission that the given user is authorized to + * view or grade for the given gradebook item + */ + public Map findMatchingEnrollmentsForItemForUser(String userUid, String gradebookUid, Long categoryId, GradingCategoryType gbCategoryType, String optionalSearchString, String optionalSectionUid); + + /** + * + * @param gradebookUid + * @param categoryId + * The category id that the desired item is associated with + * @param gbCategoryType + * The category type setting for this gradebook + * @param optionalSearchString + * a substring search for student name or display UID; the exact rules are + * up to the implementation - leave null to use all students + * @param optionalSectionUid + * null if the search should be made across all sections + * @return a map of EnrollmentRecords to grade/view permission that the current user is authorized to + * view or grade for the given gradebook item + */ + public Map findMatchingEnrollmentsForItem(String gradebookUid, Long categoryId, GradingCategoryType gbCategoryType, String optionalSearchString, String optionalSectionUid); + + /** + * + * @param gradebookUid + * @param gbCategoryType + * The category type setting for this gradebook + * @param optionalSearchString + * @param optionalSectionUid + * @return Map of EnrollmentRecord --> function (grade/view) for all students that the current user has permission to + * view/grade every item in the gradebook. If he/she can grade everything, GRADE function is + * returned. Otherwise, function is VIEW. May only modify course grade if he/she can grade + * everything in the gradebook for that student. If he/she can grade only a subset of the items, the + * student is not returned. + */ + public Map findMatchingEnrollmentsForViewableCourseGrade(String gradebookUid, GradingCategoryType gbCategoryType, String optionalSearchString, String optionalSectionUid); + /** + * + * @param gradebookUid + * @param allGbItems + * List of all Assignments associated with this gradebook + * @param optionalSearchString + * a substring search for student name or display UID; the exact rules are + * up to the implementation - leave null to use all students + * @param optionalSectionUid + * null if the search should be made across all sections + * @return a map of EnrollmentRecords to a map of item id and function (grade/view) that the user is + * authorized to view/grade + */ + public Map> findMatchingEnrollmentsForViewableItems(String gradebookUid, List allGbItems, String optionalSearchString, String optionalSectionUid); + + /** + * Check to see if current user may grade or view the given student for the given item in the given gradebook. + * Returns string representation of function per GradingService vars (view/grade) or null if no permission + * @param gradebookUid + * @param itemId + * @param studentUid + * @return GradingService.gradePermission, GradingService.viewPermission, or null if no permission + */ + public String getGradeViewFunctionForUserForStudentForItem(String gradebookUid, Long itemId, String studentUid); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingCategoryType.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingCategoryType.java new file mode 100644 index 000000000000..ac68b71689b0 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingCategoryType.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2003-2016 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents the category types allowed in the gradebook. + * + * @author Steve Swinsburg (steve.swinsburg@gmail.com) + * + */ +public enum GradingCategoryType { + + NO_CATEGORY(1), + ONLY_CATEGORY(2), + WEIGHTED_CATEGORY(3); + + private int value; + + GradingCategoryType(int value) { + this.value = value; + } + + /** + * Get the value for the type + * + * @return + */ + public int getValue() { + return this.value; + } + + // also need to maintain a map of the types so we can lookup the enum based on type + private static Map map = new HashMap<>(); + + static { + for (final GradingCategoryType type : GradingCategoryType.values()) { + map.put(type.value, type); + } + } + + public static GradingCategoryType valueOf(int value) { + return map.get(value); + } +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingConstants.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingConstants.java new file mode 100644 index 000000000000..40366b05a5fa --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingConstants.java @@ -0,0 +1,63 @@ +/********************************************************************************** + * + * $Id$ + * + *********************************************************************************** + * + * Copyright (c) 2005, 2006, 2007, 2008, 2009 The Sakai Foundation, The MIT Corporation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.opensource.org/licenses/ECL-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************************/ +package org.sakaiproject.grading.api; + +import java.math.MathContext; +import java.math.RoundingMode; + +public interface GradingConstants { + + // These have been deprecated in favour of the {@link GradingType} enum + @Deprecated + public static final int GRADE_TYPE_POINTS = 1; + @Deprecated + public static final int GRADE_TYPE_PERCENTAGE = 2; + @Deprecated + public static final int GRADE_TYPE_LETTER = 3; + + public static final int CATEGORY_TYPE_NO_CATEGORY = 1; + public static final int CATEGORY_TYPE_ONLY_CATEGORY = 2; + public static final int CATEGORY_TYPE_WEIGHTED_CATEGORY = 3; + + public static final String REFERENCE_ROOT = "/gbassignment"; + + public static final String[] validLetterGrade = { "a+", "a", "a-", "b+", "b", "b-", + "c+", "c", "c-", "d+", "d", "d-", "f" }; + + // These Strings have been kept for backwards compatibility as they are used everywhere, + // however the {@link GraderPermission} enum should be used going forward. + @Deprecated + public static final String gradePermission = GradingPermission.GRADE.toString(); + @Deprecated + public static final String viewPermission = GradingPermission.VIEW.toString(); + @Deprecated + public static final String noPermission = GradingPermission.NONE.toString(); + + public static final String enableLetterGradeString = "gradebook_enable_letter_grade"; + + public static final MathContext MATH_CONTEXT = new MathContext(10, RoundingMode.HALF_DOWN); + + /** + * Array of chars that are not allowed at the beginning of a gb item title + */ + public static final String[] INVALID_CHARS_AT_START_OF_GB_ITEM_NAME = { "#", "*", "[" }; +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingEventStatus.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingEventStatus.java new file mode 100644 index 000000000000..6b5f2ded4cc6 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingEventStatus.java @@ -0,0 +1,10 @@ +package org.sakaiproject.grading.api; + +// The order of this enum is important. +// Do not modify or delete items. If changes are needed, add values only at the end +// TO-DO: Create a custom class to parse it to Integer +public enum GradingEventStatus { + GRADE_NONE, + GRADE_EXCLUDED, + GRADE_INCLUDED +} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradingEvents.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingEvents.java similarity index 94% rename from edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradingEvents.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingEvents.java index 406ce689a298..4fe366e5f869 100644 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradingEvents.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingEvents.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sakaiproject.tool.gradebook; +package org.sakaiproject.grading.api; import java.io.Serializable; import java.util.ArrayList; @@ -21,6 +21,8 @@ import java.util.List; import java.util.Map; +import org.sakaiproject.grading.api.model.GradingEvent; + /** * Represents the grading events for a group of students in a particular gradebook * diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingPermission.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingPermission.java new file mode 100644 index 000000000000..d7b8214fbd88 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingPermission.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2003-2015 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api; + +import java.util.ArrayList; +import java.util.List; + +/** + * The list of permissions that can be assigned to a grader + */ +public enum GradingPermission { + + VIEW, + GRADE, + VIEW_COURSE_GRADE, + NONE; + + /** + * Return a lowercase version of the enum + */ + @Override + public String toString() { + return this.name().toLowerCase(); + } + + /** + * Helper to get the view and grade permissions as a list + * Used in a few places + * @return + */ + public static List getStandardPermissions() { + List rval = new ArrayList<>(); + rval.add(GradingPermission.VIEW.toString()); + rval.add(GradingPermission.GRADE.toString()); + return rval; + } +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingPermissionService.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingPermissionService.java new file mode 100644 index 000000000000..44ec54a5b53b --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingPermissionService.java @@ -0,0 +1,334 @@ +/** + * Copyright (c) 2003-2015 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.sakaiproject.section.api.facade.Role; +import org.sakaiproject.grading.api.GradingCategoryType; +import org.sakaiproject.grading.api.model.Permission; +import org.sakaiproject.section.api.coursemanagement.CourseSection; + +public interface GradingPermissionService { + + /** + * Get all available categories for a user that the user can either view or grade. + * (For overview page) + * + * @param gradebookId Gradebook ID + * @param userId grader ID + * @param categoryList List of Category. (should be all categories for this gradebook) + * @throws IllegalArgumentException + * @return List of categories + */ + public List getCategoriesForUser(Long gradebookId, String userId, List categoryIdList) throws IllegalArgumentException; + + /** + * Returns viewable categorie id's for a user for a specific student + * @param gradebookId + * @param userId + * @param studentId + * @param categories + * @param sectionIds + * @return + * @throws IllegalArgumentException + */ + public List getCategoriesForUserForStudentView(Long gradebookId, String userId, String studentId, List categories, List sectionIds) throws IllegalArgumentException; + + /** + * Get true/false value for current user which indicats if he has permission for all + * assignments in a gradebook with category turned off or he has permission for + * assignments without category associated with in a gradebook with category + * turned on. + * (For overview page) + * + * @param gradebookId Gradebook ID + * @param userId grader ID + * @throws IllegalArgumentException + * @return boolean of true/false + */ + public boolean getPermissionForUserForAllAssignment(Long gradebookId, String userId) throws IllegalArgumentException; + + /** + * Get true/false value for current user which indicates if he has permission for + * all gb items for a given student + * @param gradebookId + * @param userId + * @param studentId + * @param sectionIds + * @return + * @throws IllegalArgumentException + */ + public boolean getPermissionForUserForAllAssignmentForStudent(Long gradebookId, String userId, String studentId, List sectionIds) throws IllegalArgumentException; + + /** + * Get students IDs that the current grader can either view or grade. + * When categoryId is null and cateType is with category - return students' map that the grader + * can grade/view any category for course sections. (this is mostly for items that have no category + * associated with them in a gradebook with category turned on) + * (For item detail page) + * + * @param gradebookId Gradebook ID + * @param userId grader ID + * @param studentIds List of student IDs + * @param cateType gradebook category type + * @param categoryId current category ID that the permission check is based on. it can be null. + * @param courseSections List of course sections for current site + * @throws IllegalArgumentException + * @return Map of student IDs with grade/view as function value + */ + public Map getStudentsForItem(Long gradebookId, String userId, List studentIds, GradingCategoryType cateType, Long categoryId, List courseSections) throws IllegalArgumentException; + +/** + * Get students IDs that the current grader can either view or grade. + * When categoryId is null and cateType is with category - return students' map that the grader + * can grade/view any category for course sections. (this is mostly for items that have no category + * associated with them in a gradebook with category turned on) + * (For item detail page) + * + * @param gradebookUid gradebook uid + * @param userId grader ID + * @param studentIds List of student IDs + * @param cateType gradebook category type + * @param categoryId current category ID that the permission check is based on. it can be null. + * @param courseSections List of course sections for current site + * @throws IllegalArgumentException + * @return Map of student IDs with grade/view as function value + */ + public Map getStudentsForItem(String gradebookUid, String userId, List studentIds, GradingCategoryType cateType, Long categoryId, List courseSections) throws IllegalArgumentException; + + + /** + * Get a map of itemId/permission(grade/view) of a student for a grader that he can grade + * or view for gradebook. + * (For a student's roster page) + * + * @param gradebookUid Gradebook UID + * @param userId grader ID + * @param studentId student ID + * @param courseSections List of course sections for current site + * @throws IllegalArgumentException + * @return Map of item IDs with grade/view as function value + */ + public Map getAvailableItemsForStudent(String gradebookUid, String userId, String studentId, List courseSections) throws IllegalArgumentException; + + /** + * Get a map of itemId/permission(grade/view) of a student for a grader that he can grade + * or view for gradebook. + * (For a student's roster page) + * + * @param gradebookId Gradebook ID + * @param userId grader ID + * @param studentId student ID + * @param courseSections List of course sections for current site + * @throws IllegalArgumentException + * @return Map of item IDs with grade/view as function value + */ + public Map getAvailableItemsForStudent(Long gradebookId, String userId, String studentId, List courseSections) throws IllegalArgumentException; + + /** + * Get a map of map for students whose IDs are in studentIds with id as key and another map + * as value: itemId/permission(grade/view) for a grader that he can grade + * or view for gradebook. + * (For a student's roster page) + * + * @param gradebookId Gradebook ID + * @param userId grader ID + * @param studentIds List of student IDs + * @param courseSections List of course sections for current site + * @throws IllegalArgumentException + * @return Map + */ + public Map> getAvailableItemsForStudents(Long gradebookId, String userId, List studentIds, List courseSections) throws IllegalArgumentException; + + /** + * Get a map of map for students whose IDs are in studentIds with id as key and another map + * as value: itemId/permission(grade/view) for a grader that he can grade + * or view for gradebook. + * (For a student's roster page) + * + * @param gradebookUid Gradebook UID + * @param userId grader ID + * @param studentIds List of student IDs + * @param courseSections List of course sections for current site + * @throws IllegalArgumentException + * @return Map + */ + public Map> getAvailableItemsForStudents(String gradebookUid, String userId, List studentIds, List courseSections) throws IllegalArgumentException; + + + /** + * Get a map with student IDs as key and view/grade as value for their course grade. + * (For course grade page) + * + * @param gradebookId Gradebook ID + * @param userId grader ID + * @param studentIds List of student IDs + * @param courseSections List of course sections for current site (Should be all course sections the current site has.) + * @throws IllegalArgumentException + * @return Map of student IDs with view/grade as function value + */ + public Map getCourseGradePermission(Long gradebookId, String userId, List studentIds, List courseSections) throws IllegalArgumentException; + + /** + * Get a map with student IDs as key and view/grade as value for their course grade. + * (For course grade page) + * + * @param gradebookUid Gradebook Uid + * @param userId grader ID + * @param studentIds List of student IDs + * @param courseSections List of course sections for current site (Should be all course sections the current site has.) + * @throws IllegalArgumentException + * @return Map of student IDs with view/grade as function value + */ + //public Map getCourseGradePermission(String gradebookUid, String userId, List studentIds, List courseSections) throws IllegalArgumentException; + public Map getCourseGradePermission(String gradebookUid, String userId, List studentIds, List courseSections) throws IllegalArgumentException; + + /** + * Get all Permission records associated with the current user and gradebook + * + * @param gradebookId + * @return List of Permission records for current user and gradebook + */ + public List getGraderPermissionsForCurrentUser(Long gradebookId); + + /** + * Get all Permission records associated with this user and gradebook + * + * @param gradebookId + * @param userId + * @return List of Permission records for user and gradebook + */ + public List getGraderPermissionsForUser(Long gradebookId, String userId); + + /** + * Get all Permission records associated with the current user and gradebook + * + * @param gradebookUid + * @return List of Permission records for current user and gradebook + */ + public List getGraderPermissionsForCurrentUser(String gradebookUid); + + /** + * Get all Permission records associated with this user and gradebook + * + * @param gradebookUid + * @param userId + * @return List of Permission records for user and gradebook + */ + public List getGraderPermissionsForUser(String gradebookUid, String userId); + + /** + * Get all group ids associated with groups that contain at least one member + * that the user is authorized to view/grade + * @param gradebookId + * @param userId + * @param groupIds + * @return List of group ids that user has some authorization to view + */ + public List getViewableGroupsForUser(Long gradebookId, String userId, List groupIds); + + /** + * Get all group ids associated with groups that contain at least one member + * that the user is authorized to view/grade + * @param gradebookUid + * @param userId + * @param groupIds + * @return List of group ids that user has some authorization to view + */ + public List getViewableGroupsForUser(String gradebookUid, String userId, List groupIds); + + /** + * Get a unique list of students that the current user has grader permissions to view in some capacity + * @param gradebookId + * @param userId + * @param studentIds + * @param sections + * @return List of student ids + */ + public List getViewableStudentsForUser(Long gradebookId, String userId, List studentIds, List sections); + + /** + * Get a unique list of students that the current user has grader permissions to view in some capacity + * @param gradebookUid + * @param userId + * @param studentIds + * @param sections + * @return List of student ids + */ + public List getViewableStudentsForUser(String gradebookUid, String userId, List studentIds, List sections); + + /** + * Get the grader specific permissions for the given user + * + * @param gradebookUid + * @param userId + * @return + */ + public List getPermissionsForUser(String gradebookUid, String userId); + + /** + * Update the set of grader specific permissions for the given user + * + * @param gradebookUid + * @param userId + * @param permissionDefinitions + * @return + */ + public void updatePermissionsForUser(String gradebookUid, String userId, List permissionDefinitions); + + /** + * Remove all grader specific permissions for the given user + * + * @param gradebookUid + * @param userId + * @return + */ + public void clearPermissionsForUser(String gradebookUid, String userId); + + /** + * Get a list of permissions defined for the given user based on section and role or all sections if allowed. + * This method checks realms permissions for role/section and is independent of the + * gb_permissions_t permissions. + * + * note: If user has the grade privilege, they are given the GraderPermission.VIEW_COURSE_GRADE permission to match + * GB classic functionality. This needs to be reviewed. + * + * @param userUuid + * @param siteId + * @return list of {@link org.sakaiproject.grading.api.PermissionDefinition PermissionDefinitions} or empty list if none + */ + public List getRealmsPermissionsForUser(String userUuid,String siteId, Role role); + + /** + * Returns true if the current user can grade assignments in the specified gradebook + * + * @param gradebookUid The gradebook to check against + * @return true if the current user can, false otherwise. + */ + public boolean currentUserHasGraderPermissions(String gradebookUid); + + /** + * Returns true if the specified user can grade assignments in the specified gradebook + * + * @param gradebookUid The gradebook to check against + * @param userId The user to check + * @return true if the current user can, false otherwise. + */ + public boolean userHasGraderPermissions(String gradebookUid, String userId); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingPersistenceManager.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingPersistenceManager.java new file mode 100644 index 000000000000..0a6f51a612d0 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingPersistenceManager.java @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2003-2015 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.sakaiproject.grading.api.model.AssignmentGradeRecord; +import org.sakaiproject.grading.api.model.Category; +import org.sakaiproject.grading.api.model.Comment; +import org.sakaiproject.grading.api.model.CourseGrade; +import org.sakaiproject.grading.api.model.CourseGradeRecord; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradebookAssignment; +import org.sakaiproject.grading.api.model.GradebookProperty; +import org.sakaiproject.grading.api.model.GradeMapping; +import org.sakaiproject.grading.api.model.GradingEvent; +import org.sakaiproject.grading.api.model.GradingScale; +import org.sakaiproject.grading.api.model.LetterGradePercentMapping; +import org.sakaiproject.grading.api.model.Permission; + +public interface GradingPersistenceManager { + + Gradebook saveGradebook(Gradebook gradebook); + Optional getGradebook(String gradebookUid); + Optional getGradebook(Long gradebookId); + void deleteGradebook(String gradebookUid); + + CourseGrade saveCourseGrade(CourseGrade courseGrade); + List getCourseGradesByGradebookId(Long gradebookId); + + GradingScale saveGradingScale(GradingScale gradingScale); + List getAvailableGradingScales(); + List getOtherAvailableGradingScales(Set notTheseUids); + List getGradingScalesByUids(Set theseUids); + + LetterGradePercentMapping saveLetterGradePercentMapping(LetterGradePercentMapping lgpm); + List getDefaultLetterGradePercentMappings(); + Optional getLetterGradePercentMappingForGradebook(Long gradebookId); + + Optional getGradeMapping(Long id); + GradeMapping saveGradeMapping(GradeMapping gradeMapping); + + GradebookAssignment saveAssignment(GradebookAssignment assignment); + void deleteAssignment(GradebookAssignment assignment); + Optional getAssignmentByNameAndGradebook(String name, String gradebookUid); + List getAssignmentsForGradebook(Long gradebookId); + List getAssignmentsForCategory(Long categoryId); + Optional getAssignmentByIdAndGradebook(Long id, String gradebookUid); + Optional getAssignmentById(Long id); + List getCountedAssignmentsForGradebook(Long gradebookId); + List getCountedAndGradedAssignmentsForGradebook(Long gradebookId); + Long countAssignmentsByGradbookAndExternalId(String gradebookUid, String externalId); + Long countAssignmentsByNameAndGradebookUid(String name, String gradebookUid); + Long countDuplicateAssignments(GradebookAssignment assignment); + Optional getExternalAssignment(String gradebookUid, String externalId); + GradebookAssignment saveGradebookAssignment(GradebookAssignment assignment); + + Optional getInternalComment(String studentUid, String gradebookUid, Long assignmentId); + Comment saveComment(Comment comment); + List getCommentsForStudents(GradebookAssignment assignment, Collection studentIds); + int deleteCommentsForAssignment(GradebookAssignment assignment); + + Optional getCategory(Long categoryId); + List getCategoriesForGradebook(Long gradebookId); + boolean isCategoryDefined(String name, Gradebook gradebook); + boolean existsDuplicateCategory(String name, Gradebook gradebook, Long id); + Category saveCategory(Category category); + + boolean isAssignmentDefined(Long assignmentId); + + List getPermissionsForGradebook(Long gradebookId); + List getPermissionsForGradebookAndUser(Long gradebookId, String userId); + List getPermissionsForGradebookAndUserAndCategories(Long gradebookId, String userId, List categoryIds); + List getUncategorisedPermissionsForGradebookAndUserAndFunctions(Long gradebookId, String userId, List functionNames); + List getUngroupedPermissionsForGradebookAndUserAndFunctions(Long gradebookId, String userId, List functionNames); + List getUngroupedPermissionsForGradebookAndUserAndCategories(Long gradebookId, String userId, List categoryIds); + List getPermissionsForGradebookAndCategories(Long gradebookId, List categoryIds); + List getPermissionsForGradebookAnyGroupAnyCategory(Long gradebookId, String userId); + List getUncategorisedPermissionsForGradebookAndGroups(Long gradebookId, String userId, List groupIds); + List getPermissionsForGradebookAndGroups(Long gradebookId, String userId, List groupIds); + Permission savePermission(Permission permission); + void deletePermission(Permission permission); + + List getCourseGradeRecordsForCourseGrade(Long courseGradeId); + CourseGradeRecord saveCourseGradeRecord(CourseGradeRecord courseGradeRecord); + List getCourseGradeOverrides(Gradebook gradebook); + Optional getCourseGradeRecord(Gradebook gradebook, String studentId); + boolean hasCourseGradeRecordEntries(Long gradebookId, Set studentIds); + + List getAllAssignmentGradeRecordsForGradebook(Long gradebookId); + List getAllAssignmentGradeRecordsForAssignment(Long assignmentId); + AssignmentGradeRecord getAssignmentGradeRecordForAssignmentAndStudent(Long assignmentId, String studentUid); + AssignmentGradeRecord saveAssignmentGradeRecord(AssignmentGradeRecord record); + int deleteGradeRecordsForAssignment(GradebookAssignment assignment); + List getAssignmentGradeRecordsForAssignmentIdsAndStudentIds( + List gradableObjectIds, List studentUids); + List getAssignmentGradeRecordsForGradebookAndStudents(Long gradebookId, Collection studentIds); + List getAssignmentGradeRecordsForAssignmentAndStudents(GradebookAssignment assignment, Collection studentIds); + + GradingEvent saveGradingEvent(GradingEvent ge); + List getGradingEventsForAssignment(Long assignmentId, String studentId); + List getGradingEventsForAssignmentsSince(List assignmentIds, Date since); + int deleteGradingEventsForAssignment(GradebookAssignment assignment); + + Optional getGradebookProperty(String name); + GradebookProperty saveGradebookProperty(GradebookProperty property); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingScaleDefinition.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingScaleDefinition.java new file mode 100644 index 000000000000..3ad85a264466 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingScaleDefinition.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import lombok.Getter; +import lombok.Setter; + +/** + * DTO for the persistent GradingScale + */ +@Getter @Setter +public class GradingScaleDefinition implements Serializable { + + private static final long serialVersionUID = 1L; + + private String uid; + private String name; + private List grades; + + /** + * This was added specifically to support CXF and is a different pattern to the original + */ + private List defaultBottomPercentsAsList; + + /** + * The original map + */ + private Map defaultBottomPercents; + + public void setDefaultBottomPercentsAsList(List defaultBottomPercentsList) { + + // Depending on how this was called, the list may + // be of Double, String, emtpy String, or null objects. Convert the strings. + List doubleScores = new ArrayList(); + for (Object obj : defaultBottomPercentsList) { + if (obj instanceof String) { + String str = (String)obj; + if (str.trim().length() == 0) { + obj = null; + } else { + obj = Double.valueOf((String)obj); + } + } + doubleScores.add((Double)obj); + } + this.defaultBottomPercentsAsList = doubleScores; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); + } +} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookSecurityException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingSecurityException.java similarity index 59% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookSecurityException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingSecurityException.java index c0db6a1fd289..a170c66bfcf4 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/GradebookSecurityException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingSecurityException.java @@ -14,31 +14,31 @@ * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; /** * Wrapper for the common security exception that can be thrown from the gradebook when a user doesn't have permission to perform an action */ -public class GradebookSecurityException extends SecurityException { +public class GradingSecurityException extends SecurityException { - private static final long serialVersionUID = 1L; - private static final String MSG = "You do not have permission to perform this operation"; + private static final long serialVersionUID = 1L; + private static final String MSG = "You do not have permission to perform this operation"; - /** - * Throw with a default message - */ - public GradebookSecurityException() { - super(MSG); + /** + * Throw with a default message + */ + public GradingSecurityException() { + super(MSG); } - /** - * Throw with the supplied message - * - * @param message - */ - public GradebookSecurityException(final String message) { - super(message); - } + /** + * Throw with the supplied message + * + * @param message + */ + public GradingSecurityException(final String message) { + super(message); + } } diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingService.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingService.java new file mode 100644 index 000000000000..24f97b66fbe0 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/GradingService.java @@ -0,0 +1,1191 @@ +/********************************************************************************** + * + * $Id$ + * + *********************************************************************************** + * + * Copyright (c) 2005, 2006, 2007, 2008, 2009 The Sakai Foundation, The MIT Corporation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.opensource.org/licenses/ECL-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************************/ +package org.sakaiproject.grading.api; + +import java.math.MathContext; +import java.math.RoundingMode; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalLong; +import java.util.Set; + +import org.sakaiproject.grading.api.model.Category; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradingEvent; +import org.sakaiproject.grading.api.model.GradingScale; +import org.sakaiproject.section.api.coursemanagement.CourseSection; + +import org.apache.commons.lang3.StringUtils; +import org.sakaiproject.entity.api.Entity; +import org.sakaiproject.entity.api.EntityProducer; + +/** + * This is the externally exposed API of the gradebook application. + * + * This interface is principally intended for clients of application services -- that is, clients who want to "act like the Gradebook would" + * to automate what would normally be done in the UI, including any authorization checks. + * + * As a result, these methods may throw security exceptions. Call the service's authorization-check methods if you want to avoid them. + * + *

+ * WARNING: For documentation of the deprecated methods, please see the service interfaces which own them. + */ +public interface GradingService extends EntityProducer { + // Application service hooks. + + // These have been deprecated in favour of the {@link GradingType} enum + @Deprecated + public static final int GRADE_TYPE_POINTS = 1; + @Deprecated + public static final int GRADE_TYPE_PERCENTAGE = 2; + @Deprecated + public static final int GRADE_TYPE_LETTER = 3; + + public static final int CATEGORY_TYPE_NO_CATEGORY = 1; + public static final int CATEGORY_TYPE_ONLY_CATEGORY = 2; + public static final int CATEGORY_TYPE_WEIGHTED_CATEGORY = 3; + public static final String SAKAI_GBASSIGNMENT = "sakai:gbassignment"; + + public static final String[] validLetterGrade = { "a+", "a", "a-", "b+", "b", "b-", + "c+", "c", "c-", "d+", "d", "d-", "f" }; + + // These Strings have been kept for backwards compatibility as they are used everywhere, + // however the {@link GraderPermission} enum should be used going forward. + @Deprecated + public static final String gradePermission = GraderPermission.GRADE.toString(); + @Deprecated + public static final String viewPermission = GraderPermission.VIEW.toString(); + @Deprecated + public static final String noPermission = GraderPermission.NONE.toString(); + + public static final String enableLetterGradeString = "gradebook_enable_letter_grade"; + + public static final MathContext MATH_CONTEXT = new MathContext(10, RoundingMode.HALF_DOWN); + + /** + * An enum for defining valid/invalid information for a points possible/relative weight value for a gradebook item. See + * {@link GradingService#isPointsPossibleValid(String, Assignment, Double)} for usage + */ + public enum PointsPossibleValidation { + /** + * The points possible/relative weight is valid + */ + VALID, + /** + * The points possible/relative weight is invalid because it is null and a value is required. + */ + INVALID_NULL_VALUE, + /** + * The points possible/relative weight is invalid because it is a value <= 0 + */ + INVALID_NUMERIC_VALUE, + /** + * The points possible/relative weight is invalid because it contains more than 2 decimal places + */ + INVALID_DECIMAL + } + + /** + * Array of chars that are not allowed at the beginning of a gb item title + */ + public static final String[] INVALID_CHARS_AT_START_OF_GB_ITEM_NAME = { "#", "*", "[" }; + + /** + * Comparator to ensure correct ordering of letter grades, catering for + and - in the grade This is duplicated in GradebookNG. If + * changing here, please change there as well. TODO combine them + */ + public static Comparator lettergradeComparator = new Comparator() { + @Override + public int compare(String o1, String o2) { + if (o1.toLowerCase().charAt(0) == o2.toLowerCase().charAt(0)) { + // only take the first 2 chars, to cater for GradePointsMapping as well + String s1 = StringUtils.trim(StringUtils.left(o1, 2)); + String s2 = StringUtils.trim(StringUtils.left(o2, 2)); + + if (s1.length() == 2 && s2.length() == 2) { + if (s1.charAt(1) == '+') { + return -1; // SAK-30094 + } else { + return 1; + } + } + if (s1.length() == 1 && s2.length() == 2) { + if (o2.charAt(1) == '+') { + return 1; // SAK-30094 + } else { + return -1; + } + } + if (s1.length() == 2 && s2.length() == 1) { + if (s1.charAt(1) == '+') { + return -1; // SAK-30094 + } else { + return 1; + } + } + return 0; + } else { + return o1.toLowerCase().compareTo(o2.toLowerCase()); + } + } + }; + + /** + * Check to see if the current user is allowed to grade the given item for the given student in the given gradebook. This will give + * clients a chance to avoid a security exception. + * + * @param gradebookUid + * @param assignmentId + * @param studentUid + */ + public boolean isUserAbleToGradeItemForStudent(String gradebookUid, Long assignmentId, String studentUid); + + /** + * Check to see if the current user is allowed to view the given item for the given student in the given gradebook. This will give + * clients a chance to avoid a security exception. + * + * @param gradebookUid + * @param assignmentId + * @param studentUid + * @return + */ + public boolean isUserAbleToViewItemForStudent(String gradebookUid, Long assignmentId, String studentUid); + + /** + * Check to see if current user may grade or view the given student for the given item in the given gradebook. Returns string + * representation of function per GradingService vars (view/grade) or null if no permission + * + * @param gradebookUid + * @param assignmentId + * @param studentUid + * @return GradingService.gradePermission, GradingService.viewPermission, or null if no permission + */ + public String getGradeViewFunctionForUserForStudentForItem(String gradebookUid, Long assignmentId, String studentUid); + + + /** + * @return Returns a list of Assignment objects describing the assignments that are currently defined in the given gradebook. + */ + public List getAssignments(String gradebookUid); + + /** + * @return Returns a list of Assignment objects describing the assignments that are currently defined in the given gradebook, sorted by + * the given sort type. + */ + public List getAssignments(String gradebookUid, SortType sortBy); + + /** + * Get an assignment based on its id + * + * @param gradebookUid + * @param assignmentId + * @return the associated Assignment with the given assignmentId + * @throws AssessmentNotFoundException + */ + public Assignment getAssignment(String gradebookUid, Long assignmentId) throws AssessmentNotFoundException; + + /** + * Get an assignment based on its name. This is provided for backward compatibility only. + * + * @param gradebookUid + * @param assignmentName + * @return the associated Assignment with the given name + * @throws AssessmentNotFoundException + * + * @deprecated Use {@link #getAssignment(String,Long)} instead. + */ + @Deprecated + public Assignment getAssignment(String gradebookUid, String assignmentName) + throws AssessmentNotFoundException; + + public Assignment getExternalAssignment(String gradebookUid, String externalId); + + /** + * Get an assignment based on its name or id. This is intended as a migration path from the deprecated + * {@link #getAssignment(String,String)} to the new {@link #getAssignment(String,Long)} + * + * This method will attempt to lookup the name as provided then fall back to the ID as a Long (If it is a Long) You should use + * {@link #getAssignment(String,Long)} if you always can use the Long instead. + * + * @param gradebookUid + * @param assignmentName + * @return the associated Assignment with the given name + * @throws AssessmentNotFoundException + * + */ + public Assignment getAssignmentByNameOrId(String gradebookUid, String assignmentName) + throws AssessmentNotFoundException; + + /** + * + * @param gradebookUid + * @param assignmentId + * @param studentUid + * @return Returns a GradeDefinition for the student, respecting the grade entry type for the gradebook (ie in %, letter grade, or + * points format). Returns null if no grade + * @throws AssessmentNotFoundException + */ + public GradeDefinition getGradeDefinitionForStudentForItem(String gradebookUid, + Long assignmentId, String studentUid) + throws AssessmentNotFoundException; + + /** + * Get the comment (if any) currently provided for the given combination of student and assignment. + * + * @param gradebookUid + * @param assignmentId + * @param studentUid + * @return null if no comment is avaailable + * @throws AssessmentNotFoundException + */ + public CommentDefinition getAssignmentScoreComment(String gradebookUid, Long assignmentId, String studentUid) + throws AssessmentNotFoundException; + + /** + * + * @param gradebookUid + * @param assignmentId + * @param studentUid + * @return + * @throws AssessmentNotFoundException + */ + public boolean getIsAssignmentExcused(String gradebookUid, Long assignmentId, String studentUid) + throws AssessmentNotFoundException; + + /** + * Provide a student-viewable comment on the score (or lack of score) associated with the given assignment. + * + * @param gradebookUid + * @param assignmentId + * @param studentUid + * @param comment a plain text comment, or null to remove any currrent comment + * @throws AssessmentNotFoundException + */ + public void setAssignmentScoreComment(String gradebookUid, Long assignmentId, String studentUid, String comment) + throws AssessmentNotFoundException; + + + /** + * Check to see if an assignment with the given name already exists in the given gradebook. This will give clients a chance to avoid the + * ConflictingAssignmentNameException. + * + * This is not deprecated as we currently need the ability to check for duplciate assignment names in the given gradebook + * + */ + public boolean isAssignmentDefined(String gradebookUid, String assignmentTitle); + + /** + * Transfer the gradebook information and assignments from one gradebook to another + * + * @param gradebookInformation GradebookInformation to copy + * @param assignments list of Assignments to copy + * @param toGradebookUid target gradebook uid + */ + public Map transferGradebook(GradebookInformation gradebookInformation, List assignments, + String toGradebookUid, String fromContext); + + /** + * + * @param gradebookUid + * @return a {@link GradebookInformation} object that contains information about this Gradebook that may be useful to consumers outside + * the Gradebook tool + * + */ + public GradebookInformation getGradebookInformation(String gradebookUid); + + /** + * Removes an assignment from a gradebook. The assignment should not be deleted, but the assignment and all grade records associated + * with the assignment should be ignored by the application. A removed assignment should not count toward the total number of points in + * the gradebook. + * + * @param assignmentId The assignment id + */ + public void removeAssignment(Long assignmentId) throws StaleObjectModificationException; + + /** + * + * Get the categories for the given gradebook. This method cannot be used outside of the gradebook because it returns the + * org.sakaiproject.tool.gradebook.Category object. If you require info on the categories from a consumer outside the gradebook, use + * {@link #getCategoryDefinitions(String)} + * + * @param gradebookId + * @return List of categories + * @throws HibernateException + * + * @deprecated + */ + @Deprecated + public List getCategories(Long gradebookId); + + /** + * Get the categories for the given gradebook + * + * @param gradebookUid + * @return {@link CategoryDefinition}s for the categories defined for the given gradebook. Returns an empty list if the gradebook does + * not have categories. + */ + public List getCategoryDefinitions(String gradebookUid); + + /** + * remove category from gradebook + * + * @param categoryId + * @throws StaleObjectModificationException + */ + + public void removeCategory(Long categoryId) throws StaleObjectModificationException; + + /** + * Create a new Gradebook-managed assignment. + * + * @param assignmentDefinition + * @return the id of the newly created assignment + */ + public Long addAssignment(String gradebookUid, Assignment assignmentDefinition); + + /** + * Modify the definition of an existing Gradebook item. + * + * Clients should be aware that it's allowed to change the points value of an assignment even if students have already been scored on + * it. Any existing scores will not be adjusted. + * + * This method can be used to manage both internal and external gradebook items, however the title, due date and total points will not + * be edited for external gradebook items. + * + * @param assignmentId the id of the assignment that needs to be changed + * @param assignmentDefinition the new properties of the assignment + */ + public void updateAssignment(String gradebookUid, Long assignmentId, Assignment assignmentDefinition); + + /** + * + * @param gradebookUid + * @return list of gb items that the current user is authorized to view. If user has gradeAll permission, returns all gb items. If user + * has gradeSection perm with no grader permissions, returns all gb items. If user has gradeSection with grader perms, returns + * only the items that the current user is authorized to view or grade. If user does not have grading privileges but does have + * viewOwnGrades perm, will return all released gb items. + */ + public List getViewableAssignmentsForCurrentUser(String gradebookUid); + + /** + * + * @param gradebookUid + * @return list of gb items that the current user is authorized to view sorted by the provided SortType. If user has gradeAll + * permission, returns all gb items. If user has gradeSection perm with no grader permissions, returns all gb items. If user has + * gradeSection with grader perms, returns only the items that the current user is authorized to view or grade. If user does not + * have grading privileges but does have viewOwnGrades perm, will return all released gb items. + */ + public List getViewableAssignmentsForCurrentUser(String gradebookUid, SortType sortBy); + + /** + * + * @param gradebookUid + * @param assignmentId + * @return a map of studentId to view/grade function for the given gradebook and gradebook item. students who are not viewable or + * gradable will not be returned. if the current user does not have grading privileges, an empty map is returned + */ + public Map getViewableStudentsForItemForCurrentUser(String gradebookUid, Long assignmentId); + + /** + * @param userUid + * @param gradebookUid + * @param assignmentId + * @return a map of studentId to view/grade function for the given gradebook and gradebook item that the given userUid is allowed to + * view or grade. students who are not viewable or gradable will not be returned. if the given user does not have grading + * privileges, an empty map is returned + */ + public Map getViewableStudentsForItemForUser(String userUid, String gradebookUid, Long assignmentId); + + /** + * Get the Gradebook. Note that this returns Object to avoid circular dependency with sakai-gradebook-tool Consumers will need to cast + * to {@link org.sakaiproject.tool.gradebook.Gradebook} + * + */ + public Gradebook getGradebook(String uid); + + /** + * Check if there are students that have not submitted + * + * @param gradebookUid + * @return + */ + public boolean checkStudentsNotSubmitted(String gradebookUid); + + /** + * Check if a gradeable object with the given id exists + * + * @param gradableObjectId + * @return true if a gradable object with the given id exists and was not removed + */ + public boolean isGradableObjectDefined(Long gradableObjectId); + + /** + * Using the grader permissions, return map of section uuid to section name that includes all sections that the current user may view or + * grade + * + * @param gradebookUid + * @return + */ + public Map getViewableSectionUuidToNameMap(String gradebookUid); + + /** + * Check if the current user has the gradebook.gradeAll permission + * + * @param gradebookUid + * @return true if current user has the gradebook.gradeAll permission + */ + public boolean currentUserHasGradeAllPerm(String gradebookUid); + + /** + * Check if the given user is allowed to grade all students in this gradebook + * + * @param gradebookUid + * @param userUid + * @return true if the given user is allowed to grade all students in this gradebook + */ + public boolean isUserAllowedToGradeAll(String gradebookUid, String userUid); + + /** + * @param gradebookUid + * @return true if the current user has some form of grading privileges in the gradebook (grade all, grade section, etc) + */ + public boolean currentUserHasGradingPerm(String gradebookUid); + + /** + * + * @param gradebookUid + * @param userUid + * @return true if the given user has some form of grading privileges in the gradebook (grade all, grade section, etc) + */ + public boolean isUserAllowedToGrade(String gradebookUid, String userUid); + + /** + * @param gradebookUid + * @return true if the current user has the gradebook.editAssignments permission + */ + public boolean currentUserHasEditPerm(String gradebookUid); + + /** + * @param gradebookUid + * @return true if the current user has the gradebook.viewOwnGrades permission + */ + public boolean currentUserHasViewOwnGradesPerm(String gradebookUid); + + /** + * @param gradebookUid + * @return true if the current user has the gradebook.viewStudentNumbers permission + */ + public boolean currentUserHasViewStudentNumbersPerm(String gradebookUid); + + /** + * Get the grade records for the given list of students and the given assignment. This can only be called by an instructor or TA that + * has access, not student. + * + * See {@link #getGradeDefinitionForStudentForItem} for the method call that can be made as a student. + * + * @param gradebookUid + * @param assignmentId + * @param studentIds + * @return a list of GradeDefinition with the grade information for the given students for the given gradableObjectId + * @throws SecurityException if the current user is not authorized to view or grade a student in the passed list + */ + public List getGradesForStudentsForItem(String gradebookUid, Long assignmentId, List studentIds); + + /** + * This method gets grades for multiple gradebook items with emphasis on performance. This is particularly useful for reporting tools + * + * @param gradebookUid + * @param gradableObjectIds + * @param studentIds + * @return a Map of GradableObjectIds to a List of GradeDefinitions containing the grade information for the given students for the + * given gradableObjectIds. Comments are excluded which can be useful for performance. If a student does not have a grade on a + * gradableObject, the GradeDefinition will be omitted + * @throws SecurityException if the current user is not authorized with gradeAll in this gradebook + * @throws IllegalArgumentException if gradableObjectIds is null/empty, or if gradableObjectIds contains items that are not members of + * the gradebook with uid = gradebookUid + */ + public Map> getGradesWithoutCommentsForStudentsForItems(String gradebookUid, List gradableOjbectIds, + List studentIds); + + /** + * + * @param gradebookUuid + * @param grade + * @return true if the given grade is a valid grade given the gradebook's grade entry type. ie, if gradebook is set to grade entry by + * points, will check for valid point value. if entry by letter, will check for valid letter, etc + */ + public boolean isGradeValid(String gradebookUuid, String grade); + + /** + * Determines if the given string contains a valid numeric grade. + * @param grade the grade as a string, expected to contain a numeric value + * @return true if the string contains a valid numeric grade + */ + public boolean isValidNumericGrade(String grade); + + /** + * + * @param gradebookUid + * @param studentIdToGradeMap - the student's username mapped to their grade that you want to validate + * @return a list of the studentIds that were associated with invalid grades given the gradebook's grade entry type. useful if + * validating a list of student/grade pairs for a single gradebook (more efficient than calling gradeIsValid repeatedly). + * returns empty list if all grades are valid + */ + public List identifyStudentsWithInvalidGrades(String gradebookUid, Map studentIdToGradeMap); + + /** + * Save a student score and comment for a gradebook item. The input score must be valid according to the given gradebook's grade entry + * type. + * + * @param gradebookUid + * @param assignmentId + * @param studentId + * @param grade - must be in format according to gradebook's grade entry type + * @param comment + * @throws InvalidGradeException - if grade is invalid. grade and comment will not be saved + * @throws AssessmentNotFoundException + * @throws SecurityException if current user is not authorized to grade student + */ + public void saveGradeAndCommentForStudent(String gradebookUid, Long assignmentId, String studentId, String grade, String comment) + throws InvalidGradeException, AssessmentNotFoundException; + + /** + * Given a list of GradeDefinitions for students for a given gradebook and gradable object, will save the associated scores and + * comments. Scores must be in a format according to the gradebook's grade entry type (ie points, %, letter). + * + * @param gradebookUid + * @param assignmentId + * @param gradeDefList + * @throws InvalidGradeException if any of the grades are not valid - none will be saved + * @throws SecurityException if the user does not have access to a student in the list - no grades or comments will be saved for any + * student + * @throws AssessmentNotFoundException + */ + public void saveGradesAndComments(String gradebookUid, Long assignmentId, List gradeDefList) + throws InvalidGradeException, AssessmentNotFoundException; + + public void saveGradeAndExcuseForStudent(String gradebookUid, Long assignmentId, String studentId, String grade, boolean excuse) + throws InvalidGradeException, AssessmentNotFoundException; + + /** + * + * @param gradebookUid + * @return the constant representation of the grade entry type (ie points, %, letter grade) + */ + public GradeType getGradeEntryType(String gradebookUid); + + /** + * Get a Map of overridden CourseGrade for students. + * + * @param gradebookUid + * @return Map of enrollment displayId as key, point as value string + * + */ + public Map getEnteredCourseGrade(String gradebookUid); + + /** + * Get student's assignment's score as string. + * @param gradebookUid + * @param assignmentId + * @param studentUid + * @return String of score + */ + public String getAssignmentScoreString(String gradebookUid, Long assignmentId, String studentUid) + throws AssessmentNotFoundException; + + /** + * Get student's assignment's score as string. This is provided for backward compatibility only. + * + * @param gradebookUid + * @param assignmentName + * @param studentUid + * @return String of score + * + * @deprecated See {@link #getAssignmentScoreString(String, Long, String)} + */ + @Deprecated + public String getAssignmentScoreString(String gradebookUid, String assignmentName, String studentUid) + throws AssessmentNotFoundException; + + /** + * Get student's assignment's score as string. + * + * This is intended as a migration path from the deprecated {@link #getAssignmentScoreString(String,String)} to the new + * {@link #getAssignmentScoreString(String,Long)} + * + * This method will attempt to lookup the name as provided then fallback to the ID as a Long (If it is a Long) You should use + * {@link #getAssignmentScoreString(String,Long)} if you always can use the Long instead. + * + * @param gradebookUid + * @param assignmentName + * @param studentUid + * @return String of score + */ + public String getAssignmentScoreStringByNameOrId(String gradebookUid, String assignmentName, String studentUid) + throws AssessmentNotFoundException; + + /** + * Set student's score for assignment. + * + * @param gradebookUid + * @param assignmentId + * @param studentUid + * @param score + * @param clientServiceDescription + * + */ + public void setAssignmentScoreString(String gradebookUid, Long assignmentId, String studentUid, String score, + String clientServiceDescription) + throws AssessmentNotFoundException; + + /** + * Set student's score for assignment. This is provided for backward compatibility only. + * + * @param gradebookUid + * @param assignmentName + * @param studentUid + * @param score + * @param clientServiceDescription + * + * @deprecated See {@link #setAssignmentScoreString(String, Long, String, String, String)} + */ + @Deprecated + public void setAssignmentScoreString(String gradebookUid, String assignmentName, String studentUid, String score, + String clientServiceDescription) + throws AssessmentNotFoundException; + + + /** + * Finalize the gradebook's course grades by setting all still-unscored assignments to zero scores. + * + * @param gradebookUid + */ + public void finalizeGrades(String gradebookUid); + + /** + * + * @param gradebookUid + * @param assignmentId + * @return the lowest possible grade allowed for the given assignmentId. For example, in a points or %-based gradebook, the lowest + * possible grade for a gradebook item is 0. In a letter-grade gb, it may be 'F' depending on the letter grade mapping. Ungraded + * items have a lowest value of null. + * @throws SecurityException if user does not have permission to view assignments in the given gradebook + * @throws AssessmentNotFoundException if there is no gradebook item with the given gradebookItemId + */ + public String getLowestPossibleGradeForGbItem(String gradebookUid, Long assignmentId); + + /** + * + * @param gradebookUid (non-null) + * @param assignment (non-null) the Assignment object representing the gradebook item for which you are setting the points possible (aka + * relative weight). May be a new gradebook item without an id yet. + * @param pointsPossible the points possible/relative weight you would like to validate for the gradebookItem above. + * @return {@link PointsPossibleValidation} value indicating the validity of the given points possible/relative weight or a problem code + * defining why it is invalid + */ + public PointsPossibleValidation isPointsPossibleValid(String gradebookUid, Assignment assignment, Double pointsPossible); + + /** + * Computes the Average Course Grade as a letter. + * + * @param gradebookUid + * @return + */ + public String getAverageCourseGrade(String gradebookUid); + + /** + * Update the ordering of an assignment. This can be performed on internal and external assignments. + * @param gradebookUid uid of the gradebook + * @param assignmentId id of the assignment in the gradebook + * @param order the new order for this assignment. Note it is 0 based index ordering. + * @return + */ + public void updateAssignmentOrder(String gradebookUid, Long assignmentId, Integer order); + + /** + * Gets the grading events for the given student and the given assignment + * + * @param studentId + * @param assignmentId + * @return List of GradingEvent objects. + */ + public List getGradingEvents(String studentId, long assignmentId); + + /** + * Calculate the category score for the given gradebook, student and category, looking up the grades. Safe to call in context of a + * student. + * + * @param gradebookId Id of the gradebook + * @param studentUuid uuid of the student + * @param categoryId id of category + * @param isInstructor will determine whether category score includes non-released items + * @param categoryType category type of the gradebook + * @param equalWeightAssignments whether category is equal-weighting regardless of points + * @return percentage and dropped items, or empty if no calculations were made + * + */ + Optional calculateCategoryScore(Long gradebookId, String studentUuid, Long categoryId, boolean includeNonReleasedItems, GradingCategoryType categoryType, Boolean equalWeightAssignments); + + /** + * Calculate the category score for the given gradebook, category, assignments in the category and grade map. This doesn't do any + * additional grade lookups. Safe to call in context of a student. + * + * @param gradebook the gradebook. As this method is called for every student at once, this is passed in to save additional lookups by + * id. + * @param studentUuid uuid of the student + * @param category the category + * @param categoryAssignments list of assignments the student can view, and are in the category + * @param gradeMap map of assignmentId to grade, to use for the calculations + * @param includeNonReleasedItems relevant for student view + * @return percentage and dropped items, or empty if no calculations were made + */ + Optional calculateCategoryScore(Object gradebook, String studentUuid, CategoryDefinition category, + final List categoryAssignments, Map gradeMap, boolean includeNonReleasedItems); + + /** + * Get the course grade for a student + * + * @param gradebookUid + * @param userUuid uuid of the user + * @return The {@link CourseGradeTransferBean} for the student + */ + CourseGradeTransferBean getCourseGradeForStudent(String gradebookUid, String userUuid); + + /** + * Get the course grade for a list of students + * + * @param gradebookUid + * @param userUuids uuids of the users + * @return a Map of {@link CourseGradeTransferBean} for the students. Key is the student uuid. + */ + Map getCourseGradeForStudents(String gradebookUid, List userUuids); + + /** + * Get the course grade for a list of students using the given grading schema + * + * @param gradebookUid + * @param userUuids uuids of the users + * @param schema the grading schema (bottom percents) to use in the calculation + * @return a Map of {@link CourseGrade} for the students. Key is the student uuid. + */ + Map getCourseGradeForStudents(String gradebookUid, List userUuids, Map schema); + + /** + * Get a list of CourseSections that the current user has access to in the given gradebook. This is a combination of sections and groups + * and is permission filtered. + * + * @param gradebookUid + * @return list of CourseSection objects. + */ + List getViewableSections(String gradebookUid); + + /** + * Update the settings for this gradebook + * + * @param gradebookUid + * @param gbInfo GradebookInformation object + */ + void updateGradebookSettings(String gradebookUid, GradebookInformation gbInfo); + + /** + * Return the GradeMappings for the given gradebook. The normal getGradebook(siteId) doesn't return the GradeMapping. + * + * @param gradebookId + * @return Set of GradeMappings for the gradebook + */ + Set getGradebookGradeMappings(Long gradebookId); + + /** + * Return the GradeMappings for the given gradebook. + * @param gradebookUid + * @return Set of GradeMappings for the gradebook + */ + Set getGradebookGradeMappings(String gradebookUid); + + /** + * Allows an instructor to set a course grade override for the given student + * + * @param gradebookUid uuid of the gradebook + * @param studentUuid uuid of the student + * @param grade the new course grade + */ + void updateCourseGradeForStudent(String gradebookUid, String studentUuid, String grade, String gradeScale); + + /** + * Updates the categorized order of an assignment + * + * @param gradebookUid uuid of the gradebook + * @param categoryId id of the category + * @param assignmentId id of the assignment + * @param order new position of the assignment + */ + void updateAssignmentCategorizedOrder(String gradebookUid, Long categoryId, Long assignmentId, Integer order); + + /** + * Return the grade changes made since a given time + * + * @param assignmentIds list of assignment ids to check + * @param since timestamp from which to check for changes + * @return set of changes made + */ + List getGradingEvents(List assignmentIds, Date since); + + /** + * @deprecated Replaced by + * {@link addExternalAssessment(String, String, String, String, Double, Date, String, Boolean)} + */ + public void addExternalAssessment(String gradebookUid, String externalId, String externalUrl, + String title, double points, Date dueDate, String externalServiceDescription, String externalData) + throws ConflictingAssignmentNameException, ConflictingExternalIdException, AssignmentHasIllegalPointsException; + + /** + * Add an externally-managed assessment to a gradebook to be treated as a + * read-only assignment. The gradebook application will not modify the + * assessment properties or create any scores for the assessment. + * Since each assignment in a given gradebook must have a unique name, + * conflicts are possible. + * @param gradebookUid + * @param externalId some unique identifier which Samigo uses for the assessment. + * The externalId is globally namespaced within the gradebook, so + * if other apps decide to put assessments into the gradebook, + * they should prefix their externalIds with a well known (and + * unique within sakai) string. + * @param externalUrl a link to go to if the instructor or student wants to look at the assessment + * in Samigo; if null, no direct link will be provided in the + * gradebook, and the user will have to navigate to the assessment + * within the other application + * @param title + * @param points this is the total amount of points available and must be greater than zero. + * It could be null if it's an ungraded item. + * @param dueDate + * @param externalServiceDescription + * @param externalData if there is some data that the external service wishes to store. + * @param ungraded + * + * + */ + public void addExternalAssessment(String gradebookUid, String externalId, String externalUrl, String title, Double points, + Date dueDate, String externalServiceDescription, String externalData, Boolean ungraded) + throws ConflictingAssignmentNameException, ConflictingExternalIdException, AssignmentHasIllegalPointsException; + + /** + * This method is identical to {@link #addExternalAssessment(String, String, String, String, Double, Date, String, String, Boolean)} but + * allows you to also specify the associated Category for this assignment. If the gradebook is set up for categories and + * categoryId is null, assignment category will be unassigned + * @param gradebookUid + * @param externalId + * @param externalUrl + * @param title + * @param points + * @param dueDate + * @param externalServiceDescription + * @param externalData if there is some data that the external service wishes to store. + * @param ungraded + * @param categoryId + * @throws ConflictingAssignmentNameException + * @throws ConflictingExternalIdException + * @throws AssignmentHasIllegalPointsException + * @throws InvalidCategoryException + */ + public void addExternalAssessment(String gradebookUid, String externalId, String externalUrl, String title, Double points, + Date dueDate, String externalServiceDescription, String externalData, Boolean ungraded, Long categoryId) + throws ConflictingAssignmentNameException, ConflictingExternalIdException, AssignmentHasIllegalPointsException, InvalidCategoryException; + + /** + * @deprecated Replaced by + * {@link updateExternalAssessment(String, String, String, String, Double, Date, Boolean)} + */ + public void updateExternalAssessment(String gradebookUid, String externalId, String externalUrl, String externalData, + String title, double points, Date dueDate) + throws AssessmentNotFoundException, ConflictingAssignmentNameException, AssignmentHasIllegalPointsException; + + /** + * Update an external assessment + * @param gradebookUid + * @param externalId + * @param externalUrl + * @param externalData + * @param title + * @param points + * @param dueDate + * @param ungraded + * @throws AssessmentNotFoundException + * @throws ConflictingAssignmentNameException + * @throws AssignmentHasIllegalPointsException + */ + public void updateExternalAssessment(String gradebookUid, String externalId, String externalUrl, String externalData, + String title, Double points, Date dueDate, Boolean ungraded) + throws AssessmentNotFoundException, ConflictingAssignmentNameException, AssignmentHasIllegalPointsException; + + /** + * Remove the assessment reference from the gradebook. Although Samigo + * doesn't currently delete assessments, an instructor can retract an + * assessment to keep it from students. Since such an assessment would + * presumably no longer be used to calculate final grades, Samigo should + * also remove that assessment from the gradebook. + * + * @param externalId + * the UID of the assessment + */ + public void removeExternalAssignment(String gradebookUid, String externalId) + throws AssessmentNotFoundException; + + /** + * Updates an external score for an external assignment in the gradebook. + * + * @param gradebookUid + * The Uid of the gradebook + * @param externalId + * The external ID of the assignment/assessment + * @param studentUid + * The unique id of the student + * @param points + * The number of points earned on this assessment, or null if a score + * should be removed + */ + public void updateExternalAssessmentScore(String gradebookUid, String externalId, + String studentUid, String points) + throws AssessmentNotFoundException; + + /** + * + * @param gradebookUid + * @param externalId + * @param studentUidsToScores + * @throws AssessmentNotFoundException + * + * @deprecated Replaced by + * {@link updateExternalAssessmentScoresString(String, String, Map studentUidsToScores) + throws AssessmentNotFoundException; + + /** + * Updates a set of external scores for an external assignment in the gradebook. + * + * @param gradebookUid + * The Uid of the gradebook + * @param externalId + * The external ID of the assignment/assessment + * @param studentUidsToScores + * A map whose String keys are the unique ID strings of the students and whose + * String values are points earned on this assessment or null if the score + * should be removed. + */ + public void updateExternalAssessmentScoresString(String gradebookUid, + String externalId, Map studentUidsToScores) + throws AssessmentNotFoundException; + + /** + * Updates an external comment for an external assignment in the gradebook. + * + * @param gradebookUid + * The Uid of the gradebook + * @param externalId + * The external ID of the assignment/assessment + * @param studentUid + * The unique id of the student + * @param comment + * The comment to be added to this grade, or null if a comment + * should be removed + */ + public void updateExternalAssessmentComment(String gradebookUid, + String externalId, String studentUid, String comment ) + throws AssessmentNotFoundException; + /** + * Updates a set of external comments for an external assignment in the gradebook. + * + * @param gradebookUid + * The Uid of the gradebook + * @param externalId + * The external ID of the assignment/assessment + * @param studentUidsToScores + * A map whose String keys are the unique ID strings of the students and whose + * String values are comments or null if the comments + * should be removed. + */ + public void updateExternalAssessmentComments(String gradebookUid, + String externalId, Map studentUidsToComments) + throws AssessmentNotFoundException; + + /** + * Check to see if an assignment with the given external id already exists + * in the given gradebook. This will give external assessment systems + * a chance to avoid the ConflictingExternalIdException. + * + * @param gradebookUid The gradebook's unique identifier + * @param externalId The external assessment's external identifier + */ + public boolean isExternalAssignmentDefined(String gradebookUid, String externalId); + + /** + * Check with the appropriate external service if a specific assignment is + * available only to groups. + * + * @param gradebookUid The gradebook's unique identifier + * @param externalId The external assessment's external identifier + */ + public boolean isExternalAssignmentGrouped(String gradebookUid, String externalId); + + /** + * Check with the appropriate external service if a specific assignment is + * available to a specific user (i.e., the user is in an appropriate group). + * Note that this method will return true if the assignment exists in the + * gradebook and is marked as externally maintained while no provider + * recognizes it; this is to maintain a safer default (no change from the + * 2.8 release) for tools that have not implemented a provider. + * + * @param gradebookUid The gradebook's unique identifier + * @param externalId The external assessment's external identifier + * @param userId The user ID to check + */ + public boolean isExternalAssignmentVisible(String gradebookUid, String externalId, String userId); + + /** + * Retrieve all assignments for a gradebook that are marked as externally + * maintained and are visible to the current user. Assignments may be included + * with a null providerAppKey, indicating that the gradebook references the + * assignment, but no provider claims responsibility for it. + * + * @param gradebookUid The gradebook's unique identifier + * @return A map from the externalId of each activity to the providerAppKey + */ + public Map getExternalAssignmentsForCurrentUser(String gradebookUid); + + /** + * Retrieve a list of all visible, external assignments for a set of users. + * + * @param gradebookUid The gradebook's unique identifier + * @param studentIds The collection of student IDs for which to retrieve assignments + * @return A map from the student ID to all visible, external activity IDs + */ + public Map> getVisibleExternalAssignments(String gradebookUid, Collection studentIds); + + /** + * Register a new ExternalAssignmentProvider for handling the integration of external + * assessment sources with the sakai gradebook + * Registering more than once will overwrite the current with the new one + * + * @param provider the provider implementation object + */ + public void registerExternalAssignmentProvider(ExternalAssignmentProvider provider); + + /** + * Remove/unregister any ExternalAssignmentProvider which is currently registered, + * does nothing if they provider does not exist + * + * @param providerAppKey the unique app key for a provider + */ + public void unregisterExternalAssignmentProvider(String providerAppKey); + + /** + * Break the connection between an external assessment engine and an assessment which + * it created, giving it up to the Gradebook application to control from now on. + * + * @param gradebookUid + * @param externalId + */ + public void setExternalAssessmentToGradebookAssignment(String gradebookUid, String externalId); + + /** + * Get the category of a gradebook with the externalId given + * + * @param gradebookUId + * @param externalId + * @return + */ + public Long getExternalAssessmentCategoryId(String gradebookUId, String externalId); + + /** + * Checks to see whether a gradebook has the categories option enabled. + * + * @param gradebookUid + * The gradebook UID to check + * @return Whether the gradebook has categories enabled + */ + public boolean isCategoriesEnabled(String gradebookUid); + + /** + * Creates a new gradebook with the given UID + * + * @param uid + * The UID used to specify a gradebook and its associated data. + * It is the caller's responsibility to ensure that this is + * unique within gradebook storage. + */ + public Gradebook addGradebook(String uid); + + /** + * Deletes the gradebook with the given UID, along with all its associated + * data. + */ + public void deleteGradebook(String uid); + + /** + * @param gradingScaleDefinitions + * A collection of GradingScaleDefinition beans. + */ + public void setAvailableGradingScales(Collection gradingScaleDefinitions); + + /** + * @param uid + * The UID of the grading scale to use as the default for new gradebooks. + */ + public void setDefaultGradingScale(String uid); + + /** + * Get all of the available Grading Scales in the system. + * @return List of GradingScale + */ + public List getAvailableGradingScales(); + + /** + * Get all of the available Grading Scales in the system, as shared DTOs. + * @return List of GradingScaleDefinition + */ + public List getAvailableGradingScaleDefinitions(); + + /** + * Adds a new grade scale to an existing gradebook. + * + * @param scaleUuid + * The uuid of the scale we want to be added to the gradebook + * @param gradebookUid + * The gradebook with GradeMappings where we will add the grading scale. + * + */ + public void saveGradeMappingToGradebook(String scaleUuid, String gradebookUid); + + /** + * Update a grademapping with new values. + * + * @param gradeMappingId id of GradeMapping to update + * @param gradeMap the updated map of grades + * + */ + public void updateGradeMapping(Long gradeMappingId, Map gradeMap); +} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/InvalidCategoryException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/InvalidCategoryException.java similarity index 95% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/InvalidCategoryException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/InvalidCategoryException.java index 546add3e72bf..334d08a0bcbd 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/InvalidCategoryException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/InvalidCategoryException.java @@ -15,7 +15,7 @@ */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/InvalidGradeException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/InvalidGradeException.java similarity index 84% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/InvalidGradeException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/InvalidGradeException.java index ae202212df9d..3ad14d10328d 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/InvalidGradeException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/InvalidGradeException.java @@ -13,11 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - -package org.sakaiproject.service.gradebook.shared; - - +package org.sakaiproject.grading.api; /** * indicates that there was an attempt to save a score that was not valid @@ -26,9 +22,9 @@ */ public class InvalidGradeException extends GradebookException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public InvalidGradeException(String message) { + public InvalidGradeException(String message) { super(message); } } diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/InvalidGradeItemNameException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/InvalidGradeItemNameException.java similarity index 79% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/InvalidGradeItemNameException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/InvalidGradeItemNameException.java index 2d26aa0e203a..75b5579bd195 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/InvalidGradeItemNameException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/InvalidGradeItemNameException.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; public class InvalidGradeItemNameException extends GradebookException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public InvalidGradeItemNameException(String message) { - super(message); - } + public InvalidGradeItemNameException(String message) { + super(message); + } } diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/PermissionDefinition.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/PermissionDefinition.java new file mode 100644 index 000000000000..39fe0dd0a092 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/PermissionDefinition.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api; + +import java.io.Serializable; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * DTO for the {@link org.sakaiproject.gradebook.tool.Permission} to pass to external services. Not persisted. + */ +@EqualsAndHashCode +@Getter @Setter +public class PermissionDefinition implements Serializable { + + private static final long serialVersionUID = 1L; + + @EqualsAndHashCode.Exclude + private Long id; + private String userId; + private String functionName; + private Long categoryId; + private String groupReference; +} diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/SortType.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/SortType.java similarity index 74% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/SortType.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/SortType.java index 4ba5869a1d44..6aefd6d4e8b3 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/SortType.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/SortType.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; /** * Represents the different ways an (internal) Assignment can be sorted @@ -23,14 +23,14 @@ */ public enum SortType { - SORT_BY_NONE, //no explicit sorting - SORT_BY_DATE, - SORT_BY_NAME, - SORT_BY_MEAN, - SORT_BY_POINTS, - SORT_BY_RELEASED, - SORT_BY_COUNTED, - SORT_BY_EDITOR, - SORT_BY_CATEGORY, - SORT_BY_SORTING; //default + SORT_BY_NONE, //no explicit sorting + SORT_BY_DATE, + SORT_BY_NAME, + SORT_BY_MEAN, + SORT_BY_POINTS, + SORT_BY_RELEASED, + SORT_BY_COUNTED, + SORT_BY_EDITOR, + SORT_BY_CATEGORY, + SORT_BY_SORTING; //default } diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/StaleObjectModificationException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/StaleObjectModificationException.java similarity index 95% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/StaleObjectModificationException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/StaleObjectModificationException.java index 91859f634804..49b43b1164c7 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/StaleObjectModificationException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/StaleObjectModificationException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.sakaiproject.service.gradebook.shared; +package org.sakaiproject.grading.api; /** * Indicates that the caller attempted to modify a stale object. The gradebook diff --git a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/exception/UnmappableCourseGradeOverrideException.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/UnmappableCourseGradeOverrideException.java similarity index 75% rename from edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/exception/UnmappableCourseGradeOverrideException.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/UnmappableCourseGradeOverrideException.java index 7a4a8f8aa6f9..eb3ead0fb190 100644 --- a/edu-services/gradebook-service/api/src/java/org/sakaiproject/service/gradebook/shared/exception/UnmappableCourseGradeOverrideException.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/UnmappableCourseGradeOverrideException.java @@ -8,18 +8,16 @@ * the publisher. Infringers of copyright render themselves liable for * prosecution. */ -package org.sakaiproject.service.gradebook.shared.exception; - -import org.sakaiproject.service.gradebook.shared.GradebookException; +package org.sakaiproject.grading.api; /** * Exception throws when a course grade override is found to be unmappable due to a change in the grading schema. */ public class UnmappableCourseGradeOverrideException extends GradebookException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public UnmappableCourseGradeOverrideException(String message) { + public UnmappableCourseGradeOverrideException(String message) { super(message); } } diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/AbstractGradeRecord.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/AbstractGradeRecord.java new file mode 100644 index 000000000000..c639f1cf95ba --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/AbstractGradeRecord.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api.model; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +import org.sakaiproject.springframework.data.PersistableEntity; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * AbstractGradeRecord is the abstract base class for Grade Records, which are + * records of instructors (or the application, in the case of autocalculated + * gradebooks) assigning a grade to a student for a particular GradableObject. + * + * @author Josh Holtzman + */ +@Entity +@Table(name = "GB_GRADE_RECORD_T" + , indexes = @Index(name = "GB_GRADE_RECORD_G_O_IDX", columnList = "GRADABLE_OBJECT_ID") + , uniqueConstraints = @UniqueConstraint(name = "GB_GRADE_RECORD_STUDENT_ID_IDX", columnNames = { "GRADABLE_OBJECT_ID", "STUDENT_ID" }) +) +@DiscriminatorColumn(name = "OBJECT_TYPE_ID") +@DiscriminatorValue("0") +@ToString(onlyExplicitlyIncluded = true) +@Getter @Setter +public abstract class AbstractGradeRecord implements PersistableEntity, Serializable { + + @Id + @GeneratedValue + @Column(name = "ID") + @ToString.Include + protected Long id; + + @Column(name = "VERSION") + protected Integer version = 1; + + @Column(name = "STUDENT_ID", nullable = false) + @ToString.Include + protected String studentId; + + @Column(name = "GRADER_ID", nullable = false) + @ToString.Include + protected String graderId; + + @ManyToOne + @JoinColumn(name = "GRADABLE_OBJECT_ID", nullable = false) + protected GradableObject gradableObject; + + @Column(name = "DATE_RECORDED", nullable = false) + protected Date dateRecorded; + + @Column(name = "POINTS_EARNED") + protected Double pointsEarned; + + public abstract Double getGradeAsPercentage(); + + /** + * @return Whether this is a course grade record + */ + public abstract boolean isCourseGradeRecord(); +} + + + diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/AssignmentGradeRecord.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/AssignmentGradeRecord.java similarity index 53% rename from edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/AssignmentGradeRecord.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/AssignmentGradeRecord.java index 209cd8d89d87..8cdaa322fe6a 100644 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/AssignmentGradeRecord.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/AssignmentGradeRecord.java @@ -14,12 +14,20 @@ * limitations under the License. */ -package org.sakaiproject.tool.gradebook; +package org.sakaiproject.grading.api.model; import java.math.BigDecimal; import java.util.Comparator; -import org.sakaiproject.service.gradebook.shared.GradebookService; +import javax.persistence.Column; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.Transient; + +import org.sakaiproject.grading.api.GradingConstants; + +import lombok.Getter; +import lombok.Setter; /** * An AssignmentGradeRecord is a grade record that can be associated with an @@ -27,41 +35,57 @@ * * @author Josh Holtzman */ +@Entity +@DiscriminatorValue("1") +@Getter +@Setter public class AssignmentGradeRecord extends AbstractGradeRecord implements Cloneable { - /** - * - */ - private static final long serialVersionUID = 8259092798479256962L; - - private Double pointsEarned; + + @Transient private String letterEarned; + + @Transient private Double percentEarned; + + @Transient private boolean userAbleToView; - private Boolean excludedFromGrade; + + @Column(name = "IS_EXCLUDED_FROM_GRADE") + private Boolean excludedFromGrade = Boolean.FALSE; + + @Transient private transient BigDecimal earnedWeightedPercentage; + + @Transient private transient BigDecimal overallWeight; - private transient Boolean isDropped; + + @Transient + private transient Boolean isDropped = Boolean.FALSE; + // used for drop highest/lowest score functionality - private Boolean droppedFromGrade; + @Transient + private Boolean droppedFromGrade = Boolean.FALSE; public static Comparator numericComparator; static{ - numericComparator = new Comparator() { + numericComparator = new Comparator() { + @Override - public int compare(AssignmentGradeRecord agr1, AssignmentGradeRecord agr2) { - if(agr1 == null && agr2 == null) { + public int compare(AssignmentGradeRecord agr1, AssignmentGradeRecord agr2) { + + if (agr1 == null && agr2 == null) { return 0; } - if(agr1 == null) { + if (agr1 == null) { return -1; } - if(agr2 == null) { + if (agr2 == null) { return 1; } Double agr1Points = agr1.getGradeAsPercentage(); // Use percent in case the category is equal weight Double agr2Points = agr2.getGradeAsPercentage(); - + if (agr1Points == null && agr2Points == null) { return 0; } @@ -73,37 +97,36 @@ public int compare(AssignmentGradeRecord agr1, AssignmentGradeRecord agr2) { } try { return agr1Points.compareTo(agr2Points); - } catch(NumberFormatException e) { + } catch (NumberFormatException e) { return agr1Points.compareTo(agr2Points); // if not number, default to calcComparator functionality } } }; } - public AssignmentGradeRecord() { - super(); - } + public AssignmentGradeRecord() { } /** * The graderId and dateRecorded properties will be set explicitly by the * grade manager before the database is updated. - * @param assignment The assignment this grade record is associated with + * @param assignment The assignment this grade record is associated with * @param studentId The student id for whom this grade record belongs - * @param grade The grade, or points earned - */ - public AssignmentGradeRecord(GradebookAssignment assignment, String studentId, Double grade) { + * @param grade The grade, or points earned + */ + public AssignmentGradeRecord(GradebookAssignment assignment, String studentId, Double grade) { + super(); this.gradableObject = assignment; this.studentId = studentId; this.pointsEarned = grade; - } - - public static Comparator calcComparator; + } + + public static Comparator calcComparator; static { calcComparator = new Comparator() { @Override - public int compare(AssignmentGradeRecord agr1, AssignmentGradeRecord agr2) { + public int compare(AssignmentGradeRecord agr1, AssignmentGradeRecord agr2) { if(agr1 == null && agr2 == null) { return 0; } @@ -115,141 +138,62 @@ public int compare(AssignmentGradeRecord agr1, AssignmentGradeRecord agr2) { } Double agr1Points = agr1.getPointsEarned(); Double agr2Points = agr2.getPointsEarned(); - + if (agr1Points == null && agr2Points == null) { - return 0; + return 0; } if (agr1Points == null && agr2Points != null) { - return -1; + return -1; } if (agr1Points != null && agr2Points == null) { - return 1; + return 1; } return agr1Points.compareTo(agr2Points); } }; } - /** - * @return Returns the pointsEarned - */ - @Override - public Double getPointsEarned() { - return pointsEarned; - } - - /** - * @param pointsEarned The pointsEarned to set. - */ - public void setPointsEarned(Double pointsEarned) { - this.pointsEarned = pointsEarned; - } - /** * Returns null if the points earned is null. Otherwise, returns earned / points possible * 100. * * @see org.sakaiproject.tool.gradebook.AbstractGradeRecord#getGradeAsPercentage() */ @Override - public Double getGradeAsPercentage() { + public Double getGradeAsPercentage() { + if (pointsEarned == null) { return null; } BigDecimal bdPointsEarned = new BigDecimal(pointsEarned.toString()); BigDecimal bdPossible = new BigDecimal(((GradebookAssignment)getGradableObject()).getPointsPossible().toString()); - BigDecimal bdPercent = bdPointsEarned.divide(bdPossible, GradebookService.MATH_CONTEXT).multiply(new BigDecimal("100")); + BigDecimal bdPercent = bdPointsEarned.divide(bdPossible, GradingConstants.MATH_CONTEXT).multiply(new BigDecimal("100")); return Double.valueOf(bdPercent.doubleValue()); } - /** - * @see org.sakaiproject.tool.gradebook.AbstractGradeRecord#isCourseGradeRecord() - */ - @Override - public boolean isCourseGradeRecord() { - return false; - } - - public GradebookAssignment getAssignment() { - return (GradebookAssignment)getGradableObject(); - } - - public Double getPercentEarned() { - return percentEarned; - } - - public void setPercentEarned(Double percentEarned) { - this.percentEarned = percentEarned; - } - - public String getLetterEarned() - { - return letterEarned; - } - - public void setLetterEarned(String letterEarned) - { - this.letterEarned = letterEarned; - } - - public boolean isUserAbleToView() { - return userAbleToView; - } - public void setUserAbleToView(boolean userAbleToView) { - this.userAbleToView = userAbleToView; - } - + /** + * @see org.sakaiproject.tool.gradebook.AbstractGradeRecord#isCourseGradeRecord() + */ @Override - public AssignmentGradeRecord clone() - { - AssignmentGradeRecord agr = new AssignmentGradeRecord(); - agr.setDateRecorded(dateRecorded); - agr.setGradableObject(gradableObject); - agr.setGraderId(graderId); - agr.setLetterEarned(letterEarned); - agr.setPointsEarned(pointsEarned); - agr.setPercentEarned(percentEarned); - agr.setStudentId(studentId); - return agr; + public boolean isCourseGradeRecord() { + return false; } - public Boolean isExcludedFromGrade() { - return excludedFromGrade; - } - - public void setExcludedFromGrade(Boolean isExcludedFromGrade) { - this.excludedFromGrade = isExcludedFromGrade; - } - - public BigDecimal getEarnedWeightedPercentage() { - return earnedWeightedPercentage; - } - - public void setEarnedWeightedPercentage(BigDecimal earnedWeightedPercentage) { - this.earnedWeightedPercentage = earnedWeightedPercentage; - } - - public Boolean isDropped() { - return isDropped; - } - - public void setDropped(Boolean isDropped) { - this.isDropped = isDropped; - } - - public BigDecimal getOverallWeight() { - return overallWeight; - } - - public void setOverallWeight(BigDecimal overallWeight) { - this.overallWeight = overallWeight; - } - - public Boolean getDroppedFromGrade() { - return this.droppedFromGrade == null ? false : this.droppedFromGrade; + public GradebookAssignment getAssignment() { + return (GradebookAssignment) this.getGradableObject(); } - public void setDroppedFromGrade(Boolean droppedFromGrade) { - this.droppedFromGrade = droppedFromGrade; + @Override + public AssignmentGradeRecord clone() { + + AssignmentGradeRecord agr = new AssignmentGradeRecord(); + agr.setDateRecorded(dateRecorded); + agr.setGradableObject(gradableObject); + agr.setGraderId(graderId); + agr.setLetterEarned(letterEarned); + agr.setPointsEarned(pointsEarned); + agr.setPercentEarned(percentEarned); + agr.setStudentId(studentId); + return agr; } } diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Category.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Category.java new file mode 100644 index 000000000000..078e94d5d48e --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Category.java @@ -0,0 +1,352 @@ +/** + * Copyright (c) 2003-2014 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api.model; + +import java.io.Serializable; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Transient; + +import org.sakaiproject.grading.api.GradingConstants; +import org.sakaiproject.springframework.data.PersistableEntity; + +import lombok.Getter; +import lombok.Setter; + +// TODO: Check this against SAK-46484. I cut this code before that patch. + +@Entity +@Table(name = "GB_CATEGORY_T", indexes = @Index(name = "GB_CATEGORY_GB_IDX", columnList = "GRADEBOOK_ID")) +@Getter @Setter +public class Category implements PersistableEntity, Serializable { + + @Id + @GeneratedValue + @Column(name = "ID") + private Long id; + + @Column(name = "VERSION") + private Integer version = 1; + + @ManyToOne + @JoinColumn(name = "GRADEBOOK_ID", nullable = false) + private Gradebook gradebook; + + @Column(name = "NAME", nullable = false) + private String name; + + @Column(name = "WEIGHT") + private Double weight; + + @Column(name = "DROP_LOWEST") + private Integer dropLowest = 0; + + @Column(name = "DROP_HIGHEST") + private Integer dropHighest = 0; + + @Column(name = "KEEP_HIGHEST") + private Integer keepHighest = 0; + + private Boolean removed = Boolean.FALSE; + + @Transient + private Double averageTotalPoints; // average total points possible for this category + @Transient + private Double averageScore; // average scores that students got for this category + @Transient + private Double mean; // mean value of percentage for this category + @Transient + private Double totalPointsEarned; // scores that students got for this category + @Transient + private Double totalPointsPossible; // total points possible for this category + @Transient + private List assignmentList = new ArrayList<>();; + @Transient + private int assignmentCount; + @Transient + private Boolean extraCredit = Boolean.FALSE; + @Transient + private Boolean unweighted; + @Transient + private Boolean equalWeightAssignments = Boolean.FALSE; + @Transient + private Integer categoryOrder; + @Transient + private Boolean enforcePointWeighting; + + public static final Comparator nameComparator; + public static final Comparator averageScoreComparator; + public static final Comparator weightComparator; + + public static String SORT_BY_NAME = "name"; + public static String SORT_BY_AVERAGE_SCORE = "averageScore"; + public static String SORT_BY_WEIGHT = "weight"; + + static { + nameComparator = new Comparator() { + @Override + public int compare(final Object o1, final Object o2) { + return ((Category) o1).getName().toLowerCase().compareTo(((Category) o2).getName().toLowerCase()); + } + }; + averageScoreComparator = new Comparator() { + @Override + public int compare(final Object o1, final Object o2) { + final Category one = (Category) o1; + final Category two = (Category) o2; + + if (one.getAverageScore() == null && two.getAverageScore() == null) { + return one.getName().compareTo(two.getName()); + } + + if (one.getAverageScore() == null) { + return -1; + } + if (two.getAverageScore() == null) { + return 1; + } + + final int comp = (one.getAverageScore().compareTo(two.getAverageScore())); + if (comp == 0) { + return one.getName().compareTo(two.getName()); + } else { + return comp; + } + } + }; + weightComparator = new Comparator() { + @Override + public int compare(final Object o1, final Object o2) { + final Category one = (Category) o1; + final Category two = (Category) o2; + + if (one.getWeight() == null && two.getWeight() == null) { + return one.getName().compareTo(two.getName()); + } + + if (one.getWeight() == null) { + return -1; + } + if (two.getWeight() == null) { + return 1; + } + + final int comp = (one.getWeight().compareTo(two.getWeight())); + if (comp == 0) { + return one.getName().compareTo(two.getName()); + } else { + return comp; + } + } + }; + } + + /* + * returns true if this category drops any scores + */ + public boolean isDropScores() { + return dropLowest > 0 || dropHighest > 0 || keepHighest > 0; + } + + public Double getItemValue() { + + if (isAssignmentsEqual()) { + Double returnVal = 0.0; + final List assignments = getAssignmentList(); + if (assignments != null) { + for (final Object obj : assignments) { + if (obj instanceof GradebookAssignment) { + final GradebookAssignment assignment = (GradebookAssignment) obj; + if (!GradebookAssignment.item_type_adjustment.equals(assignment.getItemType())) {// ignore adjustment items + returnVal = assignment.getPointsPossible(); + return returnVal; + } + } + } + } + // didn't find any, so return 0.0 + return returnVal; + } else { + return 0.0; + } + } + + public void calculateStatistics(final List assignmentsWithStats) { + + int numScored = 0; + int numOfAssignments = 0; + BigDecimal total = new BigDecimal("0"); + BigDecimal totalPossible = new BigDecimal("0"); + + for (final GradebookAssignment assign : assignmentsWithStats) { + final Double score = assign.getAverageTotal(); + + if (assign.getCounted() && !assign.getUngraded() && assign.getPointsPossible() != null + && assign.getPointsPossible().doubleValue() > 0.0) { + if (score != null) { + total = total.add(new BigDecimal(score.toString())); + if (assign.getPointsPossible() != null && !assign.isExtraCredit()) { + totalPossible = totalPossible.add(new BigDecimal(assign.getPointsPossible().toString())); + numOfAssignments++; + } + if (!assign.isExtraCredit()) { + numScored++; + } + } + } + } + + if (numScored == 0 || numOfAssignments == 0) { + this.averageScore = null; + this.averageTotalPoints = null; + this.mean = null; + this.totalPointsEarned = null; + this.totalPointsPossible = null; + } else { + final BigDecimal bdNumScored = new BigDecimal(numScored); + final BigDecimal bdNumAssign = new BigDecimal(numOfAssignments); + this.averageScore = Double.valueOf(total.divide(bdNumScored, GradingConstants.MATH_CONTEXT).doubleValue()); + this.averageTotalPoints = Double.valueOf(totalPossible.divide(bdNumAssign, GradingConstants.MATH_CONTEXT).doubleValue()); + final BigDecimal value = total.divide(bdNumScored, GradingConstants.MATH_CONTEXT) + .divide(new BigDecimal(this.averageTotalPoints.doubleValue()), GradingConstants.MATH_CONTEXT) + .multiply(new BigDecimal("100")); + this.mean = Double.valueOf(value.doubleValue()); + } + } + + public void calculateStatisticsPerStudent(final List gradeRecords, final String studentUid) { + + getGradebook().getGradeType(); + int numScored = 0; + int numOfAssignments = 0; + BigDecimal total = new BigDecimal("0"); + BigDecimal totalPossible = new BigDecimal("0"); + + if (gradeRecords == null) { + setAverageScore(null); + setAverageTotalPoints(null); + setMean(null); + setTotalPointsEarned(null); + setTotalPointsPossible(null); + return; + } + + for (final AssignmentGradeRecord gradeRecord : gradeRecords) { + if (gradeRecord != null && gradeRecord.getStudentId().equals(studentUid)) { + final GradebookAssignment assignment = gradeRecord.getAssignment(); + if (assignment.getCounted() && !assignment.getUngraded() && assignment.getPointsPossible().doubleValue() > 0.0 + && !gradeRecord.getDroppedFromGrade()) { + + final Category assignCategory = assignment.getCategory(); + if (assignCategory != null && assignCategory.getId().equals(this.id)) { + final Double score = gradeRecord.getPointsEarned(); + if (score != null) { + final BigDecimal bdScore = new BigDecimal(score.toString()); + total = total.add(bdScore); + if (assignment.getPointsPossible() != null && !assignment.isExtraCredit()) { + final BigDecimal bdPointsPossible = new BigDecimal(assignment.getPointsPossible().toString()); + totalPossible = totalPossible.add(bdPointsPossible); + numOfAssignments++; + } + if (!assignment.isExtraCredit()) { + numScored++; + } + } + } + } + } + } + + // if totalPossible is 0, this prevents a division by zero scenario likely from + // an adjustment item being the only thing graded. + if (numScored == 0 || numOfAssignments == 0 || totalPossible.doubleValue() == 0) { + this.averageScore = null; + this.averageTotalPoints = null; + this.mean = null; + this.totalPointsEarned = null; + this.totalPointsPossible = null; + } else { + final BigDecimal bdNumScored = new BigDecimal(numScored); + final BigDecimal bdNumAssign = new BigDecimal(numOfAssignments); + this.averageScore = Double.valueOf(total.divide(bdNumScored, GradingConstants.MATH_CONTEXT).doubleValue()); + this.averageTotalPoints = Double.valueOf(totalPossible.divide(bdNumAssign, GradingConstants.MATH_CONTEXT).doubleValue()); + final BigDecimal value = total.divide(bdNumScored, GradingConstants.MATH_CONTEXT) + .divide((totalPossible.divide(bdNumAssign, GradingConstants.MATH_CONTEXT)), GradingConstants.MATH_CONTEXT) + .multiply(new BigDecimal("100")); + + this.mean = Double.valueOf(value.doubleValue()); + } + } + + /* + * The methods below are used with the GradableObjects because all three are displayed in a dataTable together + */ + /* + public boolean getIsCategory() { + return true; + } + + public boolean isCourseGrade() { + return false; + } + + public boolean isAssignment() { + return false; + } + */ + + public boolean isAssignmentsEqual() { + + boolean isEqual = true; + Double pointsPossible = null; + final List assignments = getAssignmentList(); + if (assignments == null) { + return isEqual; + } else { + for (final Object obj : assignments) { + if (obj instanceof GradebookAssignment) { + final GradebookAssignment assignment = (GradebookAssignment) obj; + if (pointsPossible == null) { + if (!GradebookAssignment.item_type_adjustment.equals(assignment.getItemType())) {// ignore adjustment items + pointsPossible = assignment.getPointsPossible(); + } + } else { + if (assignment.getPointsPossible() != null + && !GradebookAssignment.item_type_adjustment.equals(assignment.getItemType()) // ignore adjustment items + // that are not equal + && !pointsPossible.equals(assignment.getPointsPossible()) && !equalWeightAssignments) { + isEqual = false; + return isEqual; + } + } + } + } + } + return isEqual; + } +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Comment.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Comment.java new file mode 100644 index 000000000000..cef02ddbdea3 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Comment.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 The Sakai Foundation, The MIT Corporation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.opensource.org/licenses/ECL-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package org.sakaiproject.grading.api.model; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.Lob; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +import org.sakaiproject.springframework.data.PersistableEntity; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +// TODO: Check this against SAK-46484. I cut this code before that patch. + +@Entity +@Table(name = "GB_COMMENT_T", uniqueConstraints = @UniqueConstraint(name = "G_O_STUDENT", columnNames = {"STUDENT_ID", "GRADABLE_OBJECT_ID"})) +@ToString(onlyExplicitlyIncluded = true) +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@Getter @Setter +public class Comment implements PersistableEntity, Serializable { + + @Id + @GeneratedValue + @Column(name = "ID") + @ToString.Include + @EqualsAndHashCode.Include + private Long id; + + @Column(name = "VERSION") + private Integer version = 1; + + @Column(name = "STUDENT_ID", nullable = false) + @ToString.Include(name = "studentid") + private String studentId; + + @Column(name = "GRADER_ID", nullable = false) + @ToString.Include(name = "grader") + private String graderId; + + @Column(name = "DATE_RECORDED", nullable = false) + private Date dateRecorded; + + @Column(name = "COMMENT_TEXT") + @Lob + @ToString.Include(name = "comment") + @EqualsAndHashCode.Include + private String commentText; + + @ManyToOne + @JoinColumn(name = "GRADABLE_OBJECT_ID", nullable = false) + @EqualsAndHashCode.Include + private GradableObject gradableObject; + + public Comment(String studentId, String comment, GradableObject gradableObject) { + + this.gradableObject = gradableObject; + this.studentId = studentId; + this.commentText = comment; + } + + public Comment() { } +} + diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/CourseGrade.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/CourseGrade.java new file mode 100644 index 000000000000..24e554040ea7 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/CourseGrade.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2003-2012 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api.model; + +import java.math.BigDecimal; +import java.util.Collection; + +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.Transient; + +import org.sakaiproject.grading.api.GradingConstants; +import org.sakaiproject.springframework.data.PersistableEntity; + +/** + * A CourseGrade is a GradableObject that represents the overall course grade in a gradebook. + * + * @author Josh Holtzman + */ +@Entity +@DiscriminatorValue("2") +public class CourseGrade extends GradableObject implements PersistableEntity { + + // Should only be used to fill in the DB column. + private static final String COURSE_GRADE_NAME = "Course Grade"; + + public static String SORT_BY_OVERRIDE_GRADE = "override"; + public static String SORT_BY_CALCULATED_GRADE = "autoCalc"; + public static String SORT_BY_POINTS_EARNED = "pointsEarned"; + + @Transient + private Double averageScore; + + public CourseGrade() { + setName(COURSE_GRADE_NAME); + } + + /** + * @see org.sakaiproject.tool.gradebook.GradableObject#isCourseGrade() + */ + @Override + public boolean isCourseGrade() { + return true; + } + + /** + * @see org.sakaiproject.tool.gradebook.GradableObject#isAssignment() + */ + @Override + public boolean isAssignment() { + return false; + } + + /** + * @see org.sakaiproject.tool.gradebook.GradableObject#isCategory() + */ + @Override + public boolean getIsCategory() { + return false; + } + + /** + * Calculate the mean course grade (whether entered or calulated) as a percentage for all enrollments, leaving students who've + * explicitly been given non-percentage-valued manual-only course grades (such as "I" for incomplete) or null scores out of the + * calculation. + */ + public void calculateStatistics(final Collection gradeRecords, final int numEnrollments) { + // Ungraded but enrolled students count as if they have 0% in the course. + int numScored = numEnrollments - gradeRecords.size(); + BigDecimal total = new BigDecimal("0"); + BigDecimal average = new BigDecimal("0"); + + for (final CourseGradeRecord record : gradeRecords) { + final Double score = record.getGradeAsPercentage(); + + // Skip manual-only course grades. + if ((record.getEnteredGrade() != null) && (score == null)) { + continue; + } + } + if (numScored == 0) { + this.mean = null; + this.averageScore = null; + } else { + this.mean = Double.valueOf(total.divide(new BigDecimal(numScored), GradingConstants.MATH_CONTEXT).doubleValue()); + this.averageScore = Double.valueOf(average.divide(new BigDecimal(numScored), GradingConstants.MATH_CONTEXT).doubleValue()); + } + } + + public Double getAverageScore() { + return this.averageScore; + } + + public void setAverageScore(final Double averageScore) { + this.averageScore = averageScore; + } +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/CourseGradeRecord.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/CourseGradeRecord.java new file mode 100644 index 000000000000..b8b520aa50da --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/CourseGradeRecord.java @@ -0,0 +1,244 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api.model; + +import java.math.BigDecimal; +import java.util.Comparator; +import java.util.List; + +import javax.persistence.Column; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.Transient; + +import org.sakaiproject.grading.api.GradingConstants; + +import lombok.Getter; +import lombok.Setter; + +import org.apache.commons.lang3.StringUtils; + +/** + * A CourseGradeRecord is a grade record that can be associated with a CourseGrade. + * + * @author Josh Holtzman + */ +@Entity +@DiscriminatorValue("2") +@Getter +@Setter +public class CourseGradeRecord extends AbstractGradeRecord { + + @Column(name = "ENTERED_GRADE") + private String enteredGrade; + + @Column(name = "ENTERED_POINTS") + private Double enteredPoints; + + @Transient + private Double autoCalculatedGrade; + + @Transient + private Double calculatedPointsEarned; + + @Transient + private Double totalPointsPossible; + + public static Comparator calcComparator; + + static { + calcComparator = new Comparator() { + @Override + public int compare(final CourseGradeRecord cgr1, final CourseGradeRecord cgr2) { + if ((cgr1 == null || cgr2 == null) || (cgr1.getGradeAsPercentage() == null && cgr2.getGradeAsPercentage() == null)) { + return 0; + } + if (cgr1.getGradeAsPercentage() == null) { + return -1; + } + if (cgr2.getGradeAsPercentage() == null) { + return 1; + } + // SAK-12017 - Commented out as getPointsEarned is no longer an accurate comparator + // due to nulls no longer being calculated in to the Course Grade + // return cgr1.getPointsEarned().compareTo(cgr2.getPointsEarned()); + // Better to use getGradeAsPercentage + return cgr1.getGradeAsPercentage().compareTo(cgr2.getGradeAsPercentage()); + } + }; + } + + public static Comparator getOverrideComparator(final GradeMapping mapping) { + return new Comparator() { + @Override + public int compare(final CourseGradeRecord cgr1, final CourseGradeRecord cgr2) { + + if (cgr1 == null && cgr2 == null) { + return 0; + } + if (cgr1 == null) { + return -1; + } + if (cgr2 == null) { + return 1; + } + + final String enteredGrade1 = StringUtils.trimToEmpty(cgr1.getEnteredGrade()); + final String enteredGrade2 = StringUtils.trimToEmpty(cgr2.getEnteredGrade()); + + // Grading scales are always defined in descending order. + final List grades = mapping.getGradingScale().getGrades(); + int gradePos1 = -1; + int gradePos2 = -1; + for (int i = 0; (i < grades.size()) && ((gradePos1 == -1) || (gradePos2 == -1)); i++) { + final String grade = grades.get(i); + if (grade.equals(enteredGrade1)) { + gradePos1 = i; + } + if (grade.equals(enteredGrade2)) { + gradePos2 = i; + } + } + return gradePos2 - gradePos1; + } + }; + + } + + /** + * The graderId and dateRecorded properties will be set explicitly by the grade manager before the database is updated. + * + * @param courseGrade + * @param studentId + */ + public CourseGradeRecord(CourseGrade courseGrade, String studentId) { + + this.gradableObject = courseGrade; + this.studentId = studentId; + } + + /** + * Default no-arg constructor + */ + public CourseGradeRecord() { } + + /** + * This method will fail unless this course grade was fetched "with statistics", since it relies on having the total number of points + * possible available to calculate the percentage. + * + * @see org.sakaiproject.tool.gradebook.AbstractGradeRecord#getGradeAsPercentage() + */ + @Override + public Double getGradeAsPercentage() { + + if (this.enteredGrade == null) { + return this.autoCalculatedGrade; + } else { + return getCourseGrade().getGradebook().getSelectedGradeMapping().getValue(this.enteredGrade); + } + } + + /** + * Convenience method to get the correctly cast CourseGrade that this CourseGradeRecord references. + * + * @return CourseGrade referenced by this GradableObject + */ + public CourseGrade getCourseGrade() { + return (CourseGrade) this.gradableObject; + } + + /** + * @return Returns the autoCalculatedGrade. + */ + public Double getAutoCalculatedGrade() { + return this.autoCalculatedGrade; + } + + /** + * @return Returns the displayGrade. + */ + public String getDisplayGrade() { + + if (this.enteredGrade != null) { + return this.enteredGrade; + } else { + return getCourseGrade().getGradebook().getSelectedGradeMapping().getMappedGrade(this.autoCalculatedGrade); + } + } + + /** + * @see org.sakaiproject.tool.gradebook.AbstractGradeRecord#isCourseGradeRecord() + */ + @Override + public boolean isCourseGradeRecord() { + return true; + } + + /** + * For use by the Course Grade UI. + */ + public Double getNonNullAutoCalculatedGrade() { + + Double percent = getAutoCalculatedGrade(); + if (percent == null) { + percent = Double.valueOf(0); + } + return percent; + } + + public void initNonpersistentFields(final double totalPointsPossible, final double totalPointsEarned) { + + BigDecimal percentageEarned; + this.totalPointsPossible = totalPointsPossible; + this.calculatedPointsEarned = totalPointsEarned; + final BigDecimal bdTotalPointsPossible = BigDecimal.valueOf(totalPointsPossible); + final BigDecimal bdTotalPointsEarned = BigDecimal.valueOf(totalPointsEarned); + if (totalPointsPossible == 0.0) { + this.autoCalculatedGrade = null; + } else { + percentageEarned = bdTotalPointsEarned.divide(bdTotalPointsPossible, GradingConstants.MATH_CONTEXT) + .multiply(new BigDecimal("100")); + this.autoCalculatedGrade = percentageEarned.doubleValue(); + } + } + + // Added compatibility in GB Classic (12.x) - SAK-41295 + public void initNonpersistentFields(double totalPointsPossible, double totalPointsEarned, double literalTotalPointsEarned) { + initNonpersistentFields(totalPointsPossible, totalPointsEarned, literalTotalPointsEarned, 0.0); + } + + public void initNonpersistentFields(double totalPointsPossible, double totalPointsEarned, + double literalTotalPointsEarned, double totalPointsExtra) { + + BigDecimal percentageEarned; + BigDecimal percentageExtraEarned; + this.calculatedPointsEarned = literalTotalPointsEarned; + this.totalPointsPossible = totalPointsPossible; + BigDecimal bdTotalPointsPossible = BigDecimal.valueOf(totalPointsPossible); + BigDecimal bdTotalPointsEarned = BigDecimal.valueOf(totalPointsEarned); + + if (totalPointsPossible <= 0.0) { + this.autoCalculatedGrade = null; + } else { + percentageEarned = bdTotalPointsEarned.divide(bdTotalPointsPossible, GradingConstants.MATH_CONTEXT) + .multiply(new BigDecimal("100")); + percentageExtraEarned = BigDecimal.valueOf(totalPointsExtra).multiply(new BigDecimal("100"), GradingConstants.MATH_CONTEXT); + percentageEarned = percentageEarned.add(percentageExtraEarned, GradingConstants.MATH_CONTEXT); + this.autoCalculatedGrade = percentageEarned.doubleValue(); + } + } +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradableObject.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradableObject.java new file mode 100644 index 000000000000..258319042b21 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradableObject.java @@ -0,0 +1,274 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Transient; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +// TODO: Check this against SAK-46484. I cut this code before that patch. + +@Entity +@Table(name = "GB_GRADABLE_OBJECT_T", indexes = { + @Index(name = "GB_GRADABLE_OBJ_GB_IDX", columnList = "GRADEBOOK_ID"), + @Index(name = "GB_GRADABLE_OBJ_ASN_IDX", columnList = "OBJECT_TYPE_ID, GRADEBOOK_ID, NAME, REMOVED"), + @Index(name = "GB_GRADABLE_OBJ_CT_IDX", columnList = "CATEGORY_ID") +}) +@DiscriminatorColumn(name = "OBJECT_TYPE_ID") +@DiscriminatorValue("0") +@ToString(onlyExplicitlyIncluded = true) +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@Getter @Setter +public abstract class GradableObject implements Serializable { + + @Id + @GeneratedValue + @Column(name = "ID") + @EqualsAndHashCode.Include + @ToString.Include + protected Long id; + + @Column(name = "VERSION") + protected Integer version = 1; + + @ManyToOne + @JoinColumn(name = "GRADEBOOK_ID", nullable = false) + @EqualsAndHashCode.Include + protected Gradebook gradebook; + + @Column(name = "NAME", nullable = false) + @EqualsAndHashCode.Include + @ToString.Include + protected String name; + + @Column(name = "SORT_ORDER") + @ToString.Include(name = "sort") + protected Integer sortOrder; + + @Column(name = "CATEGORIZED_SORT_ORDER") + protected Integer categorizedSortOrder; + + @Column(name = "REMOVED") + protected Boolean removed = Boolean.FALSE; + + @ManyToOne + @JoinColumn(name = "CATEGORY_ID") + protected Category category; + + @Transient + protected Double mean; + + public static Comparator defaultComparator; + public static Comparator sortingComparator; + public static Comparator dateComparator; + public static Comparator meanComparator; + public static Comparator nameComparator; + public static Comparator idComparator; + public static Comparator categoryComparator; + static { + categoryComparator = new Comparator() { + @Override + @SuppressWarnings("unchecked") + public int compare(final GradebookAssignment one, final GradebookAssignment two) { + if (one.getCategory() == null && two.getCategory() == null) { + return 0; + } else if (one.getCategory() == null) { + return 1; // no cats to the end + } else if (two.getCategory() == null) { + return -1; // no cats to the end + } else { + // compare the category names the same way as the normal comparator + return Category.nameComparator.compare(one.getCategory(), two.getCategory()); + } + } + + @Override + public String toString() { + return "GradableObject.categoryComparator"; + } + }; + idComparator = new Comparator() { + @Override + public int compare(final GradableObject one, final GradableObject two) { + if (one.getId() == null && two.getId() == null) { + return 0; + } else if (one.getName() == null) { + return 1; + } else if (two.getName() == null) { + return -1; + } else { + return one.getId().compareTo(two.getId()); + } + } + + @Override + public String toString() { + return "GradableObject.idComparator"; + } + }; + nameComparator = new Comparator() { + @Override + public int compare(final GradableObject one, final GradableObject two) { + if (one.getName() == null && two.getName() == null) { + return idComparator.compare(one, two); + } else if (one.getName() == null) { + return 1; + } else if (two.getName() == null) { + return -1; + } else { + return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); + } + } + + @Override + public String toString() { + return "GradableObject.nameComparator"; + } + }; + meanComparator = new Comparator() { + @Override + public int compare(final GradableObject one, final GradableObject two) { + if (one.getMean() == null && two.getMean() == null) { + return nameComparator.compare(one, two); + } else if (one.getMean() == null) { + return 1; + } else if (two.getMean() == null) { + return -1; + } else { + return one.getMean().compareTo(two.getMean()); + } + } + + @Override + public String toString() { + return "GradableObject.meanComparator"; + } + }; + dateComparator = new Comparator() { + @Override + public int compare(final GradebookAssignment one, final GradebookAssignment two) { + if (one.getDueDate() == null && two.getDueDate() == null) { + return nameComparator.compare(one, two); + } else if (one.getDueDate() == null) { + return 1; + } else if (two.getDueDate() == null) { + return -1; + } else { + return one.getDueDate().compareTo(two.getDueDate()); + } + } + + @Override + public String toString() { + return "GradableObject.dateComparator"; + } + }; + sortingComparator = new Comparator() { + @Override + public int compare(final GradableObject one, final GradableObject two) { + if (one.getSortOrder() == null && two.getSortOrder() == null) { + if (one.getClass().equals(two.getClass()) + && one.getClass().isAssignableFrom(GradebookAssignment.class)) { + // special handling for assignments + return dateComparator.compare((GradebookAssignment) one, (GradebookAssignment) two); + } else { + return nameComparator.compare(one, two); + } + } else if (one.getSortOrder() == null) { + return 1; + } else if (two.getSortOrder() == null) { + return -1; + } else { + return one.getSortOrder().compareTo(two.getSortOrder()); + } + } + + @Override + public String toString() { + return "GradableObject.sortingComparator"; + } + }; + defaultComparator = sortingComparator; + } + + /** + * @return Returns the mean while protecting against displaying NaN. + */ + public Double getFormattedMean() { + + if (this.mean == null || this.mean.equals(Double.valueOf(Double.NaN))) { + return null; + } else { + return Double.valueOf(this.mean.doubleValue() / 100.0); + } + } + + /** + * @return Whether this gradable object is a course grade + */ + public abstract boolean isCourseGrade(); + + /** + * @return Whether this gradable object is an assignment + */ + public abstract boolean isAssignment(); + + /** + * @return Whether this gradable object is a category + */ + public abstract boolean getIsCategory(); + + @Transient + private int sortTotalItems = 1; + @Transient + private int sortTruePosition = -1; + + public void assignSorting(int sortTotalItems, int sortTruePosition) { + + // this will help correctly figure out the first/last setting and sorting + this.sortTotalItems = sortTotalItems; + this.sortTruePosition = sortTruePosition; + } + + public boolean isFirst() { + return this.sortTruePosition == 0; + } + + public boolean isLast() { + return this.sortTruePosition >= (this.sortTotalItems - 1); + } +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradeMapping.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradeMapping.java new file mode 100644 index 000000000000..d90d67388527 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradeMapping.java @@ -0,0 +1,237 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api.model; + +import java.io.Serializable; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import javax.persistence.Column; +import javax.persistence.CollectionTable; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorValue; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; + +import org.apache.commons.lang3.builder.CompareToBuilder; + +import org.sakaiproject.grading.api.DoubleComparator; +import org.sakaiproject.springframework.data.PersistableEntity; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +// TODO: Check this against SAK-46484. I cut this code before that patch. + +/** + * A GradeMapping provides a means to convert between an arbitrary set of grades + * (letter grades, pass / not pass, 4,0 scale) and numeric percentages. + * + */ +@Entity +@Table(name = "GB_GRADE_MAP_T", indexes = @Index(name = "GB_GRADE_MAP_GB_IDX", columnList = "GRADEBOOK_ID")) +@DiscriminatorColumn(name = "OBJECT_TYPE_ID") +@DiscriminatorValue("0") +@ToString(onlyExplicitlyIncluded = true) +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@Getter @Setter +public class GradeMapping implements PersistableEntity, Serializable, Comparable { + + @Id + @GeneratedValue + @Column(name = "ID") + @ToString.Include + protected Long id; + + @Column(name = "VERSION") + protected Integer version = 1; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "GRADEBOOK_ID", nullable = false) + protected Gradebook gradebook; + + @ElementCollection + @CollectionTable(name = "GB_GRADE_TO_PERCENT_MAPPING_T", joinColumns = @JoinColumn(name = "GRADE_MAP_ID")) + @MapKeyColumn(name = "LETTER_GRADE") + @Column(name = "PERCENT") + protected Map gradeMap; + + @ManyToOne + @JoinColumn(name = "GB_GRADING_SCALE_T") + private GradingScale gradingScale; + + public GradeMapping() { } + + public GradeMapping(GradingScale gradingScale) { + + this.gradingScale = gradingScale; + this.gradeMap = new HashMap<>(gradingScale.getDefaultBottomPercents()); + } + + @ToString.Include + @EqualsAndHashCode.Include + public String getName() { + return (gradingScale != null) ? gradingScale.getName() : null; + } + + /** + * Sets the percentage values for this GradeMapping to their default values. + */ + public void setDefaultValues() { + this.gradeMap = new HashMap<>(getDefaultBottomPercents()); + } + + /** + * Backwards-compatible wrapper to get to grading scale. + */ + public Map getDefaultBottomPercents() { + + GradingScale scale = getGradingScale(); + if (scale != null) { + return scale.getDefaultBottomPercents(); + } else { + Map defaultBottomPercents = new HashMap(); + Iterator gradesIter = getGrades().iterator(); + Iterator defaultValuesIter = getDefaultValues().iterator(); + while (gradesIter.hasNext()) { + String grade = gradesIter.next(); + Double value = defaultValuesIter.next(); + defaultBottomPercents.put(grade, value); + } + return defaultBottomPercents; + } + } + + /** + * + * @return An (ordered) collection of the available grade values + */ + public Collection getGrades() { + return (getGradingScale() != null) ? getGradingScale().getGrades() : Collections.emptyList(); + } + + /** + * + * @return A List of the default grade values. Only used for backward + * compatibility to pre-grading-scale mappings. + * + * @deprecated + */ + @Deprecated + public List getDefaultValues() { + throw new UnsupportedOperationException("getDefaultValues called for GradeMapping " + getName()); + } + + /** + * Gets the percentage mapped to a particular grade. + */ + public Double getValue(String grade) { + return this.gradeMap.get(grade); + } + + /** + * Get the mapped grade based on the persistent grade mappings + * + */ + public String getMappedGrade(Double value) { + return getMappedGrade(getGradeMap(), value); + } + + /** + * Get the mapped grade based on the passed in grade mappings. + * + * NOTE: The gradeMap MUST be sorted! + */ + public static String getMappedGrade(Map gradeMap, Double value) { + + if (value == null) { + return null; + } + + for (Map.Entry entry : sortGradeMapping(gradeMap).entrySet()) { + String grade = entry.getKey(); + Double mapVal = entry.getValue(); + + // If the value in the map is less than the value passed, then the + // map value is the letter grade for this value + if (mapVal != null && mapVal.compareTo(value) <= 0) { + return grade; + } + } + // As long as 'F' is zero, this should never happen. + return null; + } + + /** + * Handles the sorting of the grade mapping. + * + * @param gradeMap + * @return + */ + public static Map sortGradeMapping(Map gradeMap) { + + // we only ever order by bottom percents now + DoubleComparator doubleComparator = new DoubleComparator(gradeMap); + Map rval = new TreeMap<>(doubleComparator); + rval.putAll(gradeMap); + + return rval; + } + + @Override + public int compareTo(Object o) { + + GradeMapping other = (GradeMapping) o; + return new CompareToBuilder().append(getName(), other.getName()).toComparison(); + } + + /** + * Enable any-case input of grades (typically lowercase input + * for uppercase grades). Look for a case-insensitive match + * to the input text and if it's found, return the official + * version. + * + * @return The normalized version of the grade, or null if not found. + */ + public String standardizeInputGrade(String inputGrade) { + + String standardizedGrade = null; + for (String grade: getGrades()) { + if (grade.equalsIgnoreCase(inputGrade)) { + standardizedGrade = grade; + break; + } + } + return standardizedGrade; + } +} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradePointsMapping.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradePointsMapping.java similarity index 83% rename from edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradePointsMapping.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradePointsMapping.java index 2e68d4ea74c7..365f1b0b6c80 100644 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradePointsMapping.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradePointsMapping.java @@ -14,38 +14,43 @@ * limitations under the License. */ -package org.sakaiproject.tool.gradebook; +package org.sakaiproject.grading.api.model; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; + /** * This is a modified LetterGradePlusMinusMapping. * Details at: from https://github.com/sakaiproject/sakai/issues/3432 */ +@Entity +@DiscriminatorValue("4") public class GradePointsMapping extends GradeMapping { - - private static final long serialVersionUID = 1L; - - private List grades; - private List defaultValues; - - @Override - public Collection getGrades() { - return grades; - } - - @Override - public List getDefaultValues() { + + private static final long serialVersionUID = 1L; + + private List grades; + private List defaultValues; + + @Override + public Collection getGrades() { + return grades; + } + + @Override + public List getDefaultValues() { return defaultValues; } public GradePointsMapping() { super.setGradeMap(new LinkedHashMap()); - // If these values change, they also need to be updated in GradebookNG SettingsGradingSchemaPanel + // If these values change, they also need to be updated in GradebookNG SettingsGradingSchemaPanel grades = new ArrayList(); grades.add("A (4.0)"); grades.add("A- (3.67)"); diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Gradebook.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Gradebook.java new file mode 100644 index 000000000000..317dcc48397b --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Gradebook.java @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2003-2019 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api.model; + +import java.io.Serializable; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Table; + +import org.sakaiproject.grading.api.GradeType; +import org.sakaiproject.grading.api.GradingCategoryType; +import org.sakaiproject.springframework.data.PersistableEntity; + +// TODO: Check this against SAK-46484. I cut this code before that patch. + +/** + * A Gradebook is the top-level object in the Sakai Gradebook tool. Only one + * Gradebook should be associated with any particular course (or site, as they + * exist in Sakai 1.5) for any given academic term. How courses and terms are + * determined will likely depend on the particular Sakai installation. + */ +@Entity +@Table(name = "GB_GRADEBOOK_T") +@Data +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@ToString(onlyExplicitlyIncluded = true) +public class Gradebook implements PersistableEntity, Serializable { + + @Id + @GeneratedValue + @Column(name = "ID") + @ToString.Include + private Long id; + + @Column(name = "GRADEBOOK_UID", unique = true, nullable = false) + @ToString.Include + @EqualsAndHashCode.Include + private String uid; + + @Column(name = "VERSION") + private Integer version = 1; + + @Column(name = "NAME", nullable = false) + @ToString.Include + private String name; + + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "SELECTED_GRADE_MAPPING_ID") + private GradeMapping selectedGradeMapping; + + @OneToMany(mappedBy = "gradebook", orphanRemoval = true, cascade = CascadeType.ALL) + private Set gradeMappings = new HashSet<>(); + + @Column(name = "ASSIGNMENTS_DISPLAYED", nullable = false) + private Boolean assignmentsDisplayed = Boolean.FALSE; + + @Column(name = "COURSE_GRADE_DISPLAYED", nullable = false) + private Boolean courseGradeDisplayed = Boolean.FALSE; + + @Column(name = "COURSE_LETTER_GRADE_DISPLAYED", nullable = false) + private Boolean courseLetterGradeDisplayed = Boolean.FALSE; + + @Column(name = "COURSE_POINTS_DISPLAYED", nullable = false) + private Boolean coursePointsDisplayed = Boolean.FALSE; + + @Column(name = "TOTAL_POINTS_DISPLAYED", nullable = false) + private Boolean totalPointsDisplayed = Boolean.FALSE; + + @Column(name = "COURSE_AVERAGE_DISPLAYED", nullable = false) + private Boolean courseAverageDisplayed = Boolean.FALSE; + + @Column(name = "ALL_ASSIGNMENTS_ENTERED", nullable = false) + private Boolean allAssignmentsEntered = Boolean.FALSE; + + @Column(name = "LOCKED", nullable = false) + private Boolean locked = Boolean.FALSE; + + @Column(name = "GRADE_TYPE", nullable = false) + @Enumerated + private GradeType gradeType = GradeType.POINTS; + + @Column(name = "CATEGORY_TYPE", nullable = false) + @Enumerated + private GradingCategoryType categoryType = GradingCategoryType.NO_CATEGORY; + + @Column(name = "IS_EQUAL_WEIGHT_CATS") + private Boolean equalWeightCategories = Boolean.FALSE; + + @Column(name = "IS_SCALED_EXTRA_CREDIT") + private Boolean scaledExtraCredit = Boolean.FALSE; + + @Column(name = "DO_SHOW_MEAN") + private Boolean showMean = Boolean.FALSE; + + @Column(name = "DO_SHOW_MEDIAN") + private Boolean showMedian = Boolean.FALSE; + + @Column(name = "DO_SHOW_MODE") + private Boolean showMode = Boolean.FALSE; + + @Column(name = "DO_SHOW_RANK") + private Boolean showRank = Boolean.FALSE; + + @Column(name = "DO_SHOW_ITEM_STATS") + private Boolean showItemStatistics = Boolean.FALSE; + + @Column(name = "DO_SHOW_STATISTICS_CHART") + private Boolean showStatisticsChart = Boolean.FALSE; + + @Column(name = "ASSIGNMENT_STATS_DISPLAYED", nullable = false) + private Boolean assignmentStatsDisplayed = Boolean.FALSE; + + @Column(name = "COURSE_GRADE_STATS_DISPLAYED", nullable = false) + private Boolean courseGradeStatsDisplayed = Boolean.FALSE; + + @Column(name = "ALLOW_COMPARE_GRADES", nullable = false) + private Boolean allowStudentsToCompareGrades = Boolean.FALSE; + + @Column(name = "COMPARING_DISPLAY_FIRSTNAMES", nullable = false) + private Boolean comparingDisplayStudentNames = Boolean.FALSE; + + @Column(name = "COMPARING_DISPLAY_SURNAMES", nullable = false) + private Boolean comparingDisplayStudentSurnames = Boolean.FALSE; + + @Column(name = "COMPARING_DISPLAY_COMMENTS", nullable = false) + private Boolean comparingDisplayTeacherComments = Boolean.FALSE; + + @Column(name = "COMPARING_DISPLAY_ALLITEMS", nullable = false) + private Boolean comparingIncludeAllGrades = Boolean.FALSE; + + @Column(name = "COMPARING_RANDOMIZEDATA", nullable = false) + private Boolean comparingRandomizeDisplayedData = Boolean.FALSE; +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradebookAssignment.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradebookAssignment.java new file mode 100644 index 000000000000..307d6838fe83 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradebookAssignment.java @@ -0,0 +1,580 @@ +/** + * Copyright (c) 2003-2016 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api.model; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.Transient; + +import org.sakaiproject.grading.api.GradeType; +import org.sakaiproject.grading.api.GradingCategoryType; +import org.sakaiproject.grading.api.GradingConstants; +import org.sakaiproject.springframework.data.PersistableEntity; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +/** + * An GradebookAssignment is the basic unit that composes a gradebook. It represents a single unit that, when aggregated in a gradebook, can + * be used as the denomenator in calculating a CourseGradeRecord. + */ +@Entity +@DiscriminatorValue("1") +@Slf4j +@Getter @Setter +public class GradebookAssignment extends GradableObject implements PersistableEntity { + + @Deprecated + public static String SORT_BY_DATE = "dueDate"; + @Deprecated + public static String SORT_BY_NAME = "name"; + @Deprecated + public static String SORT_BY_MEAN = "mean"; + @Deprecated + public static String SORT_BY_POINTS = "pointsPossible"; + @Deprecated + public static String SORT_BY_RELEASED = "released"; + @Deprecated + public static String SORT_BY_COUNTED = "counted"; + @Deprecated + public static String SORT_BY_EDITOR = "gradeEditor"; + @Deprecated + public static String SORT_BY_SORTING = "sorting"; + @Deprecated + public static String DEFAULT_SORT = SORT_BY_SORTING; + + public static String item_type_points = "Points"; + public static String item_type_percentage = "Percentage"; + public static String item_type_letter = "Letter Grade"; + public static String item_type_nonCalc = "Non-calculating"; + public static String item_type_adjustment = "Adjustment"; + + public static Comparator dateComparator; + public static Comparator nameComparator; + public static Comparator pointsComparator; + public static Comparator meanComparator; + public static Comparator releasedComparator; + public static Comparator countedComparator; + public static Comparator gradeEditorComparator; + public static Comparator categoryComparator; + + @Column(name = "POINTS_POSSIBLE") + private Double pointsPossible; + + @Column(name = "DUE_DATE") + private Date dueDate; + + @Column(name = "NOT_COUNTED") + private Boolean notCounted = Boolean.FALSE; + + @Column(name = "EXTERNALLY_MAINTAINED") + private Boolean externallyMaintained = Boolean.FALSE; + + @Column(name = "EXTERNAL_STUDENT_LINK") + private String externalStudentLink; + + @Column(name = "EXTERNAL_INSTRUCTOR_LINK") + private String externalInstructorLink; + + @Column(name = "EXTERNAL_ID") + private String externalId; + + @Column(name = "EXTERNAL_APP_NAME") + private String externalAppName; + + @Column(name = "EXTERNAL_DATA") + private String externalData; + + @Column(name = "RELEASED") + private Boolean released = Boolean.FALSE; + + @Transient + private Double averageTotal; + + @Column(name = "UNGRADED") + private Boolean ungraded = Boolean.FALSE; + + @Column(name = "IS_EXTRA_CREDIT") + private Boolean extraCredit = Boolean.FALSE; + + @Column(name = "ASSIGNMENT_WEIGHTING") + private Double assignmentWeighting; + + @Column(name = "IS_NULL_ZERO") + private Boolean countNullsAsZeros = Boolean.FALSE; + + @Transient + private String itemType; + + @Transient + public String selectedGradeEntryValue; + + @Column(name = "HIDE_IN_ALL_GRADES_TABLE") + private Boolean hideInAllGradesTable = Boolean.FALSE; + + static { + dateComparator = new Comparator() { + @Override + public int compare(final Object o1, final Object o2) { + if (log.isDebugEnabled()) { + log.debug("Comparing assignment + " + o1 + " to " + o2 + " by date"); + } + final GradebookAssignment one = (GradebookAssignment) o1; + final GradebookAssignment two = (GradebookAssignment) o2; + + // Sort by name if no date on either + if (one.getDueDate() == null && two.getDueDate() == null) { + return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); + } + // Null dates are last + if (one.getDueDate() == null) { + return 1; + } + if (two.getDueDate() == null) { + return -1; + } + // Sort by name if both assignments have the same date + final int comp = (one.getDueDate().compareTo(two.getDueDate())); + if (comp == 0) { + return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); + } else { + return comp; + } + } + + @Override + public String toString() { + return "GradebookAssignment.dateComparator"; + } + }; + nameComparator = new Comparator() { + @Override + public int compare(final Object o1, final Object o2) { + final GradebookAssignment one = (GradebookAssignment) o1; + final GradebookAssignment two = (GradebookAssignment) o2; + return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); + } + + @Override + public String toString() { + return "GradebookAssignment.nameComparator"; + } + }; + pointsComparator = new Comparator() { + @Override + public int compare(final Object o1, final Object o2) { + if (log.isDebugEnabled()) { + log.debug("Comparing assignment + " + o1 + " to " + o2 + " by points"); + } + final GradebookAssignment one = (GradebookAssignment) o1; + final GradebookAssignment two = (GradebookAssignment) o2; + + final int comp = one.getPointsPossible().compareTo(two.getPointsPossible()); + if (comp == 0) { + return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); + } else { + return comp; + } + } + + @Override + public String toString() { + return "GradebookAssignment.pointsComparator"; + } + }; + meanComparator = new Comparator() { + @Override + public int compare(final Object o1, final Object o2) { + if (log.isDebugEnabled()) { + log.debug("Comparing assignment + " + o1 + " to " + o2 + " by mean"); + } + final GradebookAssignment one = (GradebookAssignment) o1; + final GradebookAssignment two = (GradebookAssignment) o2; + + final Double mean1 = one.getMean(); + final Double mean2 = two.getMean(); + if (mean1 == null && mean2 == null) { + return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); + } + if (mean1 != null && mean2 == null) { + return 1; + } + if (mean1 == null && mean2 != null) { + return -1; + } + final int comp = mean1.compareTo(mean2); + if (comp == 0) { + return one.getName().toLowerCase().compareTo(two.getName().toLowerCase()); + } else { + return comp; + } + } + + @Override + public String toString() { + return "GradebookAssignment.meanComparator"; + } + }; + + releasedComparator = new Comparator() { + @Override + public int compare(final Object o1, final Object o2) { + if (log.isDebugEnabled()) { + log.debug("Comparing assignment + " + o1 + " to " + o2 + " by release"); + } + final GradebookAssignment one = (GradebookAssignment) o1; + final GradebookAssignment two = (GradebookAssignment) o2; + + final int comp = String.valueOf(one.isReleased()).compareTo(String.valueOf(two.isReleased())); + if (comp == 0) { + return one.getName().compareTo(two.getName()); + } else { + return comp; + } + } + + @Override + public String toString() { + return "GradebookAssignment.releasedComparator"; + } + }; + + countedComparator = new Comparator() { + @Override + public int compare(final Object o1, final Object o2) { + if (log.isDebugEnabled()) { + log.debug("Comparing assignment + " + o1 + " to " + o2 + " by counted"); + } + final GradebookAssignment one = (GradebookAssignment) o1; + final GradebookAssignment two = (GradebookAssignment) o2; + + final int comp = String.valueOf(one.getCounted()).compareTo(String.valueOf(two.getCounted())); + if (comp == 0) { + return one.getName().compareTo(two.getName()); + } else { + return comp; + } + } + + @Override + public String toString() { + return "GradebookAssignment.countedComparator"; + } + }; + + gradeEditorComparator = new Comparator() { + @Override + public int compare(final Object o1, final Object o2) { + if (log.isDebugEnabled()) { + log.debug("Comparing assignment + " + o1 + " to " + o2 + " by grade editor"); + } + final GradebookAssignment one = (GradebookAssignment) o1; + final GradebookAssignment two = (GradebookAssignment) o2; + + final int comp = String.valueOf(one.getExternalAppName()).compareTo(String.valueOf(two.getExternalAppName())); + if (comp == 0) { + return one.getName().compareTo(two.getName()); + } else { + return comp; + } + } + + @Override + public String toString() { + return "GradebookAssignment.gradeEditorComparator"; + } + }; + + categoryComparator = new Comparator() { + @Override + public int compare(final Object o1, final Object o2) { + if (log.isDebugEnabled()) { + log.debug("Comparing assignment + " + o1 + " to " + o2 + " by category ordering"); + } + final GradebookAssignment one = (GradebookAssignment) o1; + final GradebookAssignment two = (GradebookAssignment) o2; + + // if categories are null + if (one.getCategory() == null && two.getCategory() == null) { + // sort by assignment sort order + if (one.getSortOrder() == null && two.getSortOrder() == null) { + // if no sortOrder, then sort based on id + return one.getId().compareTo(two.getId()); + } else if (one.getSortOrder() == null) { + return 1; + } else if (two.getSortOrder() == null) { + return -1; + } else { + return one.getSortOrder().compareTo(two.getSortOrder()); + } + } else if (one.getCategory() == null) { + return 1; + } else if (two.getCategory() == null) { + return -1; + } + + // if in the same category, sort by their categorized sort order + if (one.getCategory().equals(two.getCategory())) { + // handles null orders by putting them at the end of the list + if (one.getCategorizedSortOrder() == null) { + return 1; + } else if (two.getCategorizedSortOrder() == null) { + return -1; + } + return Integer.compare(one.getCategorizedSortOrder(), two.getCategorizedSortOrder()); + + // otherwise, sort by their category order + } else { + // check if category has a order (not required) + if (one.getCategory().getCategoryOrder() == null && two.getCategory().getCategoryOrder() == null) { + // both orders are null.. so order by A-Z + if (one.getCategory().getName() == null && two.getCategory().getName() == null) { + // both names are null so order by id + return one.getCategory().getId().compareTo(two.getCategory().getId()); + } else if (one.getCategory().getName() == null) { + return 1; + } else if (two.getCategory().getName() == null) { + return -1; + } else { + return one.getCategory().getName().compareTo(two.getCategory().getName()); + } + } else if (one.getCategory().getCategoryOrder() == null) { + return 1; + } else if (two.getCategory().getCategoryOrder() == null) { + return -1; + } else { + return one.getCategory().getCategoryOrder().compareTo(two.getCategory().getCategoryOrder()); + } + } + } + + @Override + public String toString() { + return "GradebookAssignment.categoryComparator"; + } + }; + } + + public GradebookAssignment() { + this(null, null, null, null, false); + } + + public GradebookAssignment(Gradebook gradebook, String name, Double pointsPossible, Date dueDate) { + this(gradebook, name, pointsPossible, dueDate, true); + } + + /** + * constructor to support selective release + * + * @param gradebook + * @param name + * @param pointsPossible + * @param dueDate + * @param released + */ + public GradebookAssignment(Gradebook gradebook, String name, Double pointsPossible, Date dueDate, boolean released) { + + this.gradebook = gradebook; + this.name = name; + this.pointsPossible = pointsPossible; + this.dueDate = dueDate; + this.released = released; + this.extraCredit = Boolean.FALSE; + this.hideInAllGradesTable = Boolean.FALSE; + } + + @Override + public boolean isCourseGrade() { + return false; + } + + /** + * @see org.sakaiproject.tool.gradebook.GradableObject#isAssignment() + */ + @Override + public boolean isAssignment() { + return true; + } + + /** + * @see GradableObject#getIsCategory() + */ + @Override + public boolean getIsCategory() { + return false; + } + + public Boolean getCounted() { + return !notCounted; + } + + /** + * This cover is for the benefit of JSF checkboxes. + */ + public void setCounted(boolean counted) { + notCounted = !counted; + } + + /** + * @return Returns the externallyMaintained. + */ + public boolean isExternallyMaintained() { + return this.externallyMaintained != null ? this.externallyMaintained : false; + } + + /** + * + * @return selective release true or false + */ + public Boolean isReleased() { + return this.released != null ? this.released : false; + } + + /** + * Calculate the mean score for students with entered grades. + */ + public void calculateStatistics(final Collection gradeRecords) throws Exception { + + int numScored = 0; + BigDecimal total = new BigDecimal("0"); + BigDecimal pointsTotal = new BigDecimal("0"); + for (final AssignmentGradeRecord record : gradeRecords) { + // Skip grade records that don't apply to this gradable object + if (!record.getGradableObject().equals(this)) { + continue; + } + + if (record.getDroppedFromGrade() == null) { + throw new Exception("record.droppedFromGrade cannot be null"); + } + + Double score = null; + if (!getUngraded() && this.pointsPossible > 0) { + score = record.getGradeAsPercentage(); + } + final Double points = record.getPointsEarned(); + if (score == null && points == null || record.getDroppedFromGrade()) { + continue; + } else if (score == null) { + pointsTotal = pointsTotal.add(new BigDecimal(points.toString())); + numScored++; + } else { + total = total.add(new BigDecimal(score.toString())); + pointsTotal = pointsTotal.add(new BigDecimal(points.toString())); + numScored++; + } + } + if (numScored == 0) { + this.mean = null; + this.averageTotal = null; + } else { + final BigDecimal bdNumScored = new BigDecimal(numScored); + if (!getUngraded() && this.pointsPossible > 0) { + this.mean = Double.valueOf(total.divide(bdNumScored, GradingConstants.MATH_CONTEXT).doubleValue()); + } else { + this.mean = null; + } + this.averageTotal = Double.valueOf(pointsTotal.divide(bdNumScored, GradingConstants.MATH_CONTEXT).doubleValue()); + } + } + + public boolean getUngraded() { + return this.ungraded != null ? this.ungraded : false; + } + + // these two functions are needed to keep the old API and help JSF and RSF play nicely together. Since isExtraCredit already exists and + // we can't remove it + // and JSF expects Boolean values to be "getExtraCredit", this had to be added for JSF. Also, since the external GB create item page is + // in + // RSF, you can't name it getExtraCredit and keep isExtraCredit b/c of SAK-14589 + public Boolean getIsExtraCredit() { + return isExtraCredit(); + } + + public void setIsExtraCredit(final Boolean isExtraCredit) { + setExtraCredit(isExtraCredit); + } + + public Boolean isExtraCredit() { + return this.extraCredit != null ? this.extraCredit : false; + } + + public String getItemType() { + final Gradebook gb = getGradebook(); + if (gb != null) { + if (isExtraCredit() != null) { + if (isExtraCredit()) { + // if we made it in here, go ahead and return since adjustment item takes priority over the rest + this.itemType = item_type_adjustment; + return this.itemType; + } + } + + if (getUngraded()) { + // if we made it in here, go ahead and return since non-calc item takes priority over the rest + this.itemType = item_type_nonCalc; + return this.itemType; + } + + if (gb.getGradeType() == GradeType.POINTS) { + this.itemType = item_type_points; + } else if (gb.getGradeType() == GradeType.PERCENTAGE) { + this.itemType = item_type_percentage; + } else if (gb.getGradeType() == GradeType.LETTER) { + this.itemType = item_type_letter; + } + } + return this.itemType; + } + + /** + * Convenience method for checking if the grade for the assignment should be included in calculations. This is different from just the + * {@link #getCounted()} method for an assignment. This method does a more thorough check using other values, such as if removed, + * isExtraCredit, ungraded, etc in addition to the assignment's notCounted property. Now also considers category type. If categories are + * configured (setting 2 or 3), uncategorised items are not counted. + * + * @return true if grades for this assignment should be included in various calculations. + */ + public boolean isIncludedInCalculations() { + + boolean isIncludedInCalculations = false; + + if (!this.removed && + !getUngraded() && + getCounted() && + (isExtraCredit() || (this.pointsPossible != null && this.pointsPossible > 0))) { + isIncludedInCalculations = true; + } + + if (gradebook.getCategoryType() != GradingCategoryType.NO_CATEGORY && this.category == null) { + isIncludedInCalculations = false; + } + + return isIncludedInCalculations; + } + + public boolean isHideInAllGradesTable() { + return this.hideInAllGradesTable != null ? this.hideInAllGradesTable : false; + } +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradebookProperty.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradebookProperty.java new file mode 100644 index 000000000000..05ffe31360c3 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradebookProperty.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api.model; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.sakaiproject.springframework.data.PersistableEntity; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Entity +@Table(name = "GB_PROPERTY_T") +@Getter @Setter +@ToString(onlyExplicitlyIncluded = true) +public class GradebookProperty implements PersistableEntity, Comparable, Serializable { + + @Id + @GeneratedValue + @Column(name = "ID") + @ToString.Include + private Long id; + + @Column(name = "VERSION") + private Integer version = 1; + + @Column(name = "NAME", unique = true, nullable = false) + @ToString.Include + private String name; + + @Column(name = "VALUE") + private String value; + + public GradebookProperty() { } + + public GradebookProperty(String name) { + this.name = name; + } + + @Override + public int compareTo(Object o) { + return this.name.compareTo(((GradebookProperty)o).getName()); + } +} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradingEvent.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradingEvent.java similarity index 51% rename from edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradingEvent.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradingEvent.java index 938fcc9b3477..cd1c69678834 100644 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/GradingEvent.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradingEvent.java @@ -14,17 +14,26 @@ * limitations under the License. */ -package org.sakaiproject.tool.gradebook; +package org.sakaiproject.grading.api.model; import java.io.Serializable; -import java.util.Comparator; import java.util.Date; -import org.sakaiproject.service.gradebook.shared.GradingEventStatus; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.sakaiproject.grading.api.GradingEventStatus; +import org.sakaiproject.springframework.data.PersistableEntity; -import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; +import lombok.Getter; +import lombok.Setter; /** * A log of grading activity. A GradingEvent should be saved any time a grade @@ -34,35 +43,67 @@ * * @author Josh Holtzman */ -@Data -@NoArgsConstructor +@Entity +@Table(name = "GB_GRADING_EVENT_T", indexes = { + @Index(name = "GB_GRADING_EVENT_T_STU_OBJ_ID", columnList = "STUDENT_ID, GRADABLE_OBJECT_ID"), + @Index(name = "GB_GRADING_EVENT_T_DATE_OBJ_ID", columnList = "DATE_GRADED, GRADABLE_OBJECT_ID") } +) @EqualsAndHashCode(onlyExplicitlyIncluded = true) -public class GradingEvent implements Serializable { - - private static final long serialVersionUID = 1L; +@Getter +@Setter +public class GradingEvent implements PersistableEntity, Comparable, Serializable { + @Id + @GeneratedValue + @Column(name = "ID") @EqualsAndHashCode.Include private Long id; + + @Column(name = "GRADER_ID", nullable = false) @EqualsAndHashCode.Include private String graderId; + + @Column(name = "STUDENT_ID", nullable = false) @EqualsAndHashCode.Include private String studentId; + + @ManyToOne + @JoinColumn(name = "GRADABLE_OBJECT_ID", nullable = false) private GradableObject gradableObject; + + @Column(name = "GRADE") private String grade; + + @Column(name = "DATE_GRADED", nullable = false) @EqualsAndHashCode.Include - private Date dateGraded = new Date(); + private Date dateGraded; + + @Column(name = "IS_EXCLUDED") private GradingEventStatus status; - public static final Comparator compareByDateGraded = Comparator.comparing(GradingEvent::getDateGraded); + public GradingEvent() { + this.dateGraded = new Date(); + } public GradingEvent(GradableObject gradableObject, String graderId, String studentId, Object grade) { + this.gradableObject = gradableObject; this.graderId = graderId; this.studentId = studentId; if (grade != null) { - this.grade = grade.toString(); + this.grade = grade.toString(); } - this.status = GradingEventStatus.GRADE_NONE; + this.dateGraded = new Date(); + } + + /** + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public int compareTo(Object o) { + return this.dateGraded.compareTo(((GradingEvent)o).dateGraded); } } + + diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradingScale.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradingScale.java new file mode 100644 index 000000000000..70b9b8e23f7b --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/GradingScale.java @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.sakaiproject.grading.api.GradingScaleDefinition; +import org.sakaiproject.springframework.data.PersistableEntity; + +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorValue; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Index; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.MapKeyColumn; +import javax.persistence.OrderColumn; +import javax.persistence.Table; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Entity +@Table(name = "GB_GRADING_SCALE_T") +@DiscriminatorColumn(name = "OBJECT_TYPE_ID") +@DiscriminatorValue("0") +@Getter @Setter +@ToString(onlyExplicitlyIncluded = true) +public class GradingScale implements PersistableEntity, Comparable, Serializable { + + @Id + @GeneratedValue + @Column(name = "ID") + private Long id; + + @Column(name = "VERSION") + private Integer version = 1; + + @Column(name = "SCALE_UID", unique = true, nullable = false) + @ToString.Include + private String uid; + + @Column(name = "NAME", nullable = false) + private String name; + + /** + * Because the Gradebook now supports non-calculated manual-only grades with + * no percentage equivalent, it is possible for the list of grades to include + * codes that are not included in the defaultBottomPercents map. In other + * words, callers shouldn't expect getDefaultBottomPercents.keySet() to be + * equivalent to this list. + * @return list of supported grade codes, ordered from highest to lowest + */ + @ElementCollection + @CollectionTable(name = "GB_GRADING_SCALE_GRADES_T", joinColumns = @JoinColumn(name = "GRADING_SCALE_ID"), indexes = @Index(columnList = "GRADING_SCALE_ID")) + @OrderColumn(name = "GRADE_IDX") + @Column(name = "LETTER_GRADE", nullable = false) + private List grades; + + @ElementCollection + @CollectionTable(name = "GB_GRADING_SCALE_PERCENTS_T", joinColumns = @JoinColumn(name = "GRADING_SCALE_ID")) + @MapKeyColumn(name = "LETTER_GRADE") + @Column(name = "PERCENT") + private Map defaultBottomPercents; // From grade to percentage + + @Column(name = "UNAVAILABLE") + private Boolean unavailable = Boolean.FALSE; + + @Override + public int compareTo(Object o) { + return name.compareTo(((GradingScale) o).getName()); + } + + /** + * Convert this GradeingScale instance to a GradingScaleDefinition + * @return + */ + public GradingScaleDefinition toGradingScaleDefinition() { + + GradingScaleDefinition scaleDef = new GradingScaleDefinition(); + scaleDef.setUid(this.getUid()); + scaleDef.setName(this.getName()); + + Map mapBottomPercents = this.getDefaultBottomPercents(); + scaleDef.setDefaultBottomPercents(mapBottomPercents); + + //build the bottom percents as a list as well + List listBottomPercents = new ArrayList<>(); + List grades = new ArrayList<>(); + for (Map.Entry pair : mapBottomPercents.entrySet()) { + listBottomPercents.add(pair.getValue()); + grades.add(pair.getKey()); + } + scaleDef.setGrades(grades); + scaleDef.setDefaultBottomPercentsAsList(listBottomPercents); + + return scaleDef; + } +} diff --git a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/LetterGradeMapping.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/LetterGradeMapping.java similarity index 79% rename from edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/LetterGradeMapping.java rename to gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/LetterGradeMapping.java index 9d6194668e24..a4515dcc18ed 100644 --- a/edu-services/gradebook-service/hibernate/src/java/org/sakaiproject/tool/gradebook/LetterGradeMapping.java +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/LetterGradeMapping.java @@ -14,31 +14,37 @@ * limitations under the License. */ -package org.sakaiproject.tool.gradebook; +package org.sakaiproject.grading.api.model; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; + /** * A LetterGradeMapping defines the set of grades available to a gradebook as * "A", "B", "C", "D", and "F", each of which can be mapped to a minimum * percentage value. * */ +@Entity +@DiscriminatorValue("2") public class LetterGradeMapping extends GradeMapping { - - private static final long serialVersionUID = 1L; - - private List grades; - private List defaultValues; - @Override - public Collection getGrades() { - return grades; - } - @Override - public List getDefaultValues() { + + private static final long serialVersionUID = 1L; + + private List grades; + private List defaultValues; + + @Override + public Collection getGrades() { + return grades; + } + @Override + public List getDefaultValues() { return defaultValues; } diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/LetterGradePercentMapping.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/LetterGradePercentMapping.java new file mode 100644 index 000000000000..49bd0b54928e --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/LetterGradePercentMapping.java @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.MapKeyColumn; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +import org.sakaiproject.springframework.data.PersistableEntity; + +import lombok.Getter; +import lombok.Setter; + +@Entity +@Table(name = "GB_LETTERGRADE_PERCENT_MAPPING", uniqueConstraints = { + @UniqueConstraint(name = "uniqueTypeGradebook", columnNames = {"GRADEBOOK_ID", "MAPPING_TYPE"}) +}) +@Getter @Setter +public class LetterGradePercentMapping implements PersistableEntity, Serializable { + + @Id + @GeneratedValue + @Column(name = "LGP_MAPPING_ID") + private Long id; + + @Column(name = "VERSION") + private Integer version = 1; + + @Column(name = "GRADEBOOK_ID", nullable = true) + private Long gradebookId; + + @Column(name = "MAPPING_TYPE", nullable = false) + private Integer mappingType = 1; //value of 1 or 2 - 1 is the default mapping in the system. + + @ElementCollection + @CollectionTable(name = "GB_LETTERGRADE_MAPPING_T", joinColumns = @JoinColumn(name = "LG_MAPPING_ID")) + @MapKeyColumn(name = "grade") + @Column(name = "value") + private Map gradeMap; + + public Double getValue(String grade) { + + if (gradeMap != null && gradeMap.containsKey(grade)) { + return gradeMap.get(grade); + } + return null; + } + + public String getGrade(Double value) { + + if (gradeMap != null) { + List percentList = new ArrayList(); + for (Iterator iter = gradeMap.keySet().iterator(); iter.hasNext();) { + percentList.add(gradeMap.get((iter.next()))); + } + Collections.sort(percentList); + for (int i=0; i grades; + private List defaultValues; + + @Override + public Collection getGrades() { + return grades; + } + + @Override + public List getDefaultValues() { return defaultValues; } public PassNotPassMapping() { + setGradeMap(new LinkedHashMap()); grades = new ArrayList(); diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Permission.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Permission.java new file mode 100644 index 000000000000..494ce4bc8abd --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Permission.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2003-2009 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.api.model; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.sakaiproject.springframework.data.PersistableEntity; + +import lombok.Getter; +import lombok.Setter; + +@Entity +@Table(name = "GB_PERMISSION_T") +@Getter @Setter +public class Permission implements PersistableEntity, Serializable { + + @Id + @GeneratedValue + @Column(name = "ID") + private Long id; + + @Column(name = "VERSION") + private Integer version = 1; + + @Column(name = "GRADEBOOK_ID", nullable = false) + private Long gradebookId; + + @Column(name = "USER_ID", nullable = false) + private String userId; + + @Column(name = "FUNCTION_NAME", nullable = false) + private String functionName; + + @Column(name = "CATEGORY_ID") + private Long categoryId; + + @Column(name = "GROUP_ID") + private String groupId; +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Spreadsheet.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Spreadsheet.java new file mode 100644 index 000000000000..dead8f323f65 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/model/Spreadsheet.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.api.model; + +import java.io.Serializable; +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.Lob; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +// TODO: Check this against SAK-46484. I cut this code before that patch. + +@Entity +@Table(name = "GB_SPREADSHEET_T") +@Getter @Setter +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@ToString(onlyExplicitlyIncluded = true) +public class Spreadsheet implements Serializable { + + @Id + @GeneratedValue + @Column(name = "ID") + @EqualsAndHashCode.Include + @ToString.Include + protected Long id; + + @ManyToOne + @JoinColumn(name = "GRADEBOOK_ID", nullable = false) + @EqualsAndHashCode.Include + protected Gradebook gradebook; + + @Column(name = "VERSION") + protected Integer version = 1; + + @Lob + @Column(name = "CONTENT", length = 16777215, nullable = false) + protected String content; + + @Column(name = "CREATOR", nullable = false) + protected String creator; + + @Column(name = "NAME", nullable = false) + @EqualsAndHashCode.Include + @ToString.Include + protected String name; + + @Column(name = "DATE_CREATED", nullable = false) + protected Date dateCreated; + + public Spreadsheet() { } + + public Spreadsheet(Gradebook gradebook, String content, String creator, String name, Date dateCreated) { + + this.gradebook = gradebook; + this.content = content; + this.creator = creator; + this.name = name; + this.dateCreated = dateCreated; + } +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/AssignmentGradeRecordRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/AssignmentGradeRecordRepository.java new file mode 100644 index 000000000000..bbb2fb405d3c --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/AssignmentGradeRecordRepository.java @@ -0,0 +1,23 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import org.sakaiproject.grading.api.model.AssignmentGradeRecord; +import org.sakaiproject.grading.api.model.GradebookAssignment; + +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface AssignmentGradeRecordRepository extends SpringCrudRepository { + + List findByGradableObject_Gradebook_IdAndGradableObject_RemovedOrderByPointsEarned(Long gradebookId, Boolean removed); + List findByGradableObject_IdAndGradableObject_RemovedOrderByPointsEarned(Long gradableObjectId, Boolean removed); + Optional findByGradableObject_IdAndStudentId(Long assignmentId, String studentId); + List findByGradableObject_Gradebook_Id(Long gradebookId); + List findByGradableObject_Gradebook_Uid(String gradebookUid); + List findByGradableObject_RemovedAndGradableObject_IdInAndStudentIdIn(Boolean removed, List gradableObjectIds, List studentIds); + List findByGradableObject_Gradebook_IdAndGradableObject_RemovedAndStudentIdIn(Long gradebookId, Boolean removed, Collection studentIds); + List findByGradableObjectAndStudentIdIn(GradebookAssignment assignment, Collection studentIds); + int deleteByGradableObject(GradebookAssignment assignment); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CategoryRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CategoryRepository.java new file mode 100644 index 000000000000..ade4ceb5dc9e --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CategoryRepository.java @@ -0,0 +1,16 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.List; + +import org.sakaiproject.grading.api.model.Category; +import org.sakaiproject.grading.api.model.Gradebook; + +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface CategoryRepository extends SpringCrudRepository { + + List findByGradebook_IdAndRemoved(Long gradebookId, Boolean removed); + boolean existsByNameAndGradebookAndRemoved(String name, Gradebook gradebook, Boolean removed); + boolean existsByNameAndGradebookAndNotIdAndRemoved(String name, Gradebook gradebook, Long id, Boolean removed); + List findByGradebook_Uid(String gradebookUid); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CommentRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CommentRepository.java new file mode 100644 index 000000000000..d66fe88c2f0b --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CommentRepository.java @@ -0,0 +1,23 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.sakaiproject.grading.api.model.Comment; +import org.sakaiproject.grading.api.model.GradebookAssignment; + +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface CommentRepository extends SpringCrudRepository { + + Optional findByStudentIdAndGradableObject_Gradebook_UidAndGradableObject_IdAndGradableObject_Removed( + String studentUid, String gradebookUid, Long assignmentId, Boolean removed); + + List findByGradableObjectAndStudentIdIn(GradebookAssignment assignment, Collection studentIds); + + List findByGradableObject_Gradebook_Uid(String gradebookUid); + + int deleteByGradableObject(GradebookAssignment assignment); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CourseGradeRecordRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CourseGradeRecordRepository.java new file mode 100644 index 000000000000..10412deb9de7 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CourseGradeRecordRepository.java @@ -0,0 +1,19 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.sakaiproject.grading.api.model.CourseGradeRecord; +import org.sakaiproject.grading.api.model.Gradebook; + +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface CourseGradeRecordRepository extends SpringCrudRepository { + + List findByGradableObject_Id(Long id); + List findByGradableObject_GradebookAndEnteredGradeNotNull(Gradebook gradebook); + Optional findByGradableObject_GradebookAndStudentId(Gradebook gradebook, String studentId); + Long countByGradableObject_Gradebook_IdAndEnteredGradeNotNullAndStudentIdIn(Long gradebookId, Set studentIds); + List findByGradableObject_Gradebook_Uid(String gradebookUid); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CourseGradeRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CourseGradeRepository.java new file mode 100644 index 000000000000..f297230f1370 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/CourseGradeRepository.java @@ -0,0 +1,13 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.List; + +import org.sakaiproject.grading.api.model.CourseGrade; + +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface CourseGradeRepository extends SpringCrudRepository { + + List findByGradebook_Id(Long gradebookId); + List findByGradebook_Uid(String gradebookUid); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradeMappingRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradeMappingRepository.java new file mode 100644 index 000000000000..b15512cffcd8 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradeMappingRepository.java @@ -0,0 +1,12 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.List; + +import org.sakaiproject.grading.api.model.GradeMapping; + +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface GradeMappingRepository extends SpringCrudRepository { + + public List findByGradebook_Uid(String gradebookUid); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradebookAssignmentRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradebookAssignmentRepository.java new file mode 100644 index 000000000000..18e3157234e0 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradebookAssignmentRepository.java @@ -0,0 +1,25 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.List; +import java.util.Optional; + +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradebookAssignment; + +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface GradebookAssignmentRepository extends SpringCrudRepository { + + Optional findByNameAndGradebook_UidAndRemoved(String name, String gradebookUid, Boolean removed); + Optional findByIdAndGradebook_UidAndRemoved(Long id, String gradebookUid, Boolean removed); + List findByGradebook_IdAndRemoved(Long gradebookId, Boolean removed); + List findByCategory_IdAndRemoved(Long categoryId, Boolean removed); + List findByGradebook_IdAndRemovedAndNotCounted(Long gradebookId, Boolean removed, Boolean notCounted); + List findByGradebook_IdAndRemovedAndNotCountedAndUngraded(Long gradebookId, Boolean removed, Boolean notCounted, Boolean ungraded); + Optional findByGradebook_UidAndExternalId(String gradebookUid, String externalId); + Long countByGradebook_UidAndExternalId(String gradebookUid, String externalId); + Long countByNameAndGradebook_UidAndRemoved(String name, String gradebookUid, Boolean removed); + Long countByNameAndGradebookAndNotIdAndRemoved(String name, Gradebook gradebook, Long id, Boolean removed); + boolean existsByIdAndRemoved(Long id, Boolean removed); + List findByGradebook_Uid(String gradebookUid); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradebookPropertyRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradebookPropertyRepository.java new file mode 100644 index 000000000000..de80214082e7 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradebookPropertyRepository.java @@ -0,0 +1,11 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.Optional; + +import org.sakaiproject.grading.api.model.GradebookProperty; +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface GradebookPropertyRepository extends SpringCrudRepository { + + Optional findByName(String name); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradebookRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradebookRepository.java new file mode 100644 index 000000000000..b07046a8844e --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradebookRepository.java @@ -0,0 +1,14 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.List; +import java.util.Optional; + +import org.sakaiproject.grading.api.model.Gradebook; + +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface GradebookRepository extends SpringCrudRepository { + + Optional findByUid(String uid); + int deleteByUid(String uid); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradingEventRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradingEventRepository.java new file mode 100644 index 000000000000..3c52ce79723b --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradingEventRepository.java @@ -0,0 +1,16 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.Date; +import java.util.List; + +import org.sakaiproject.grading.api.model.GradebookAssignment; +import org.sakaiproject.grading.api.model.GradingEvent; +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface GradingEventRepository extends SpringCrudRepository { + + List findByGradableObject_Gradebook_Uid(String gradebookUid); + List findByGradableObject_IdAndStudentIdOrderByDateGraded(Long assignmentId, String studentId); + List findByDateGreaterThanEqualAndGradableObject_IdIn(Date since, List assignmentIds); + int deleteByGradableObject(GradebookAssignment assignment); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradingScaleRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradingScaleRepository.java new file mode 100644 index 000000000000..ade5a0897053 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/GradingScaleRepository.java @@ -0,0 +1,16 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.List; +import java.util.Set; +import java.util.Optional; + +import org.sakaiproject.grading.api.model.GradingScale; + +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface GradingScaleRepository extends SpringCrudRepository { + + List findByUnavailable(Boolean unavailable); + List findByUnavailableAndUidNotIn(Boolean unavailable, Set notTheseUids); + List findByUidIn(Set theseUids); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/LetterGradePercentMappingRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/LetterGradePercentMappingRepository.java new file mode 100644 index 000000000000..18377061d5bb --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/LetterGradePercentMappingRepository.java @@ -0,0 +1,14 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.List; +import java.util.Optional; + +import org.sakaiproject.grading.api.model.LetterGradePercentMapping; + +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface LetterGradePercentMappingRepository extends SpringCrudRepository { + + List findByMappingType(Integer mappingType); + Optional findByGradebookIdAndMappingType(Long gradebookId, Integer mappingType); +} diff --git a/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/PermissionRepository.java b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/PermissionRepository.java new file mode 100644 index 000000000000..0d51d93a5fc1 --- /dev/null +++ b/gradebookng/api/src/main/java/org/sakaiproject/grading/api/repository/PermissionRepository.java @@ -0,0 +1,21 @@ +package org.sakaiproject.grading.api.repository; + +import java.util.List; + +import org.sakaiproject.grading.api.model.Permission; + +import org.sakaiproject.springframework.data.SpringCrudRepository; + +public interface PermissionRepository extends SpringCrudRepository { + + List findByGradebookId(Long gradebookId); + List findByGradebookIdAndUserId(Long gradebookId, String userId); + List findByGradebookIdAndUserIdAndCategoryIdIn(Long gradebookId, String userId, List categoryIds); + List findByGradebookIdAndUserIdAndCategoryIdIsNullAndFunctionNameIn(Long gradebookId, String userId, List functionNames); + List findByGradebookIdAndUserIdAndGroupIdIsNullAndFunctionNameIn(Long gradebookId, String userId, List functionNames); + List findByGradebookIdAndUserIdAndGroupIdIsNullAndCategoryIdIn(Long gradebookId, String userId, List categoryIds); + List findByGradebookIdAndCategoryIdIn(Long gradebookId, List categoryIds); + List findByGradebookIdAndUserIdAndCategoryIdIsNullAndGroupIdIsNull(Long gradebookId, String userId); + List findByGradebookIdAndUserIdAndCategoryIdIsNullAndGroupIdIn(Long gradebookId, String userId, List groupIds); + List findByGradebookIdAndUserIdAndGroupIdIn(Long gradebookId, String userId, List groupIds); +} diff --git a/gradebookng/impl/pom.xml b/gradebookng/impl/pom.xml new file mode 100644 index 000000000000..12f9111f3ff8 --- /dev/null +++ b/gradebookng/impl/pom.xml @@ -0,0 +1,124 @@ + + + 4.0.0 + Grading Service Impl + org.sakaiproject.grading + sakai-grading-impl + + + org.sakaiproject.gradebookng + gradebookng + 23-SNAPSHOT + + + sakai-component + + + + org.sakaiproject.grading + sakai-grading-api + + + org.sakaiproject.kernel + sakai-kernel-api + + + org.sakaiproject.kernel + sakai-component-manager + + + org.sakaiproject.kernel + sakai-kernel-util + + + org.sakaiproject.kernel + sakai-kernel-private + + + org.sakaiproject.kernel + sakai-kernel-test + test + + + org.sakaiproject.edu-services.sections + sections-api + + + org.apache.commons + commons-collections4 + + + org.apache.commons + commons-lang3 + + + org.hibernate + hibernate-core + + + org.springframework.data + spring-data-jpa + + + org.springframework + spring-test + test + + + org.mockito + mockito-core + test + + + org.hsqldb + hsqldb + test + + + javax.servlet + javax.servlet-api + test + + + com.fasterxml.jackson.core + jackson-annotations + + + com.thoughtworks.xstream + xstream + 1.4.18 + + + + + + + true + org.sakaiproject.maven.plugins + sakai + true + + ${maven.tomcat.home} + ${basedir}/src/main/webapp + + + + + true + org.apache.maven.plugins + maven-war-plugin + + ${basedir}/src/main/webapp + + + + + + ${basedir}/src/main/webapp + + + ${basedir}/src/test/resources + + + + diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradebookDefinition.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradebookDefinition.java new file mode 100644 index 000000000000..8f8693dd3fb4 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradebookDefinition.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2003-2012 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.impl; + +import java.io.Externalizable; +import java.util.Collection; +import java.util.Map; +import java.util.List; + +import org.sakaiproject.grading.api.Assignment; + +import lombok.Getter; +import lombok.Setter; + +@Getter @Setter +public class GradebookDefinition extends VersionedExternalizable implements Externalizable { + + private static final long serialVersionUID = 1L; + public static final String EXTERNALIZABLE_VERSION = "1"; + + private String selectedGradingScaleUid; + private Map selectedGradingScaleBottomPercents; + private Collection assignments; + private int gradeType; + private int categoryType; + private List category; + + public String getExternalizableVersion() { + return EXTERNALIZABLE_VERSION; + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingAuthzImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingAuthzImpl.java new file mode 100644 index 000000000000..d5365c8d4cd8 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingAuthzImpl.java @@ -0,0 +1,685 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import lombok.extern.slf4j.Slf4j; + +import org.sakaiproject.authz.api.FunctionManager; +import org.sakaiproject.authz.api.SecurityService; +import org.sakaiproject.exception.IdUnusedException; +import org.sakaiproject.grading.api.Assignment; +import org.sakaiproject.grading.api.GradingAuthz; +import org.sakaiproject.grading.api.GradingCategoryType; +import org.sakaiproject.grading.api.GradingPermissionService; +import org.sakaiproject.grading.api.GradingService; +import org.sakaiproject.grading.api.model.GradebookAssignment; +import org.sakaiproject.section.api.SectionAwareness; +import org.sakaiproject.section.api.coursemanagement.CourseSection; +import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; +import org.sakaiproject.section.api.facade.Role; +import org.sakaiproject.site.api.Group; +import org.sakaiproject.site.api.SiteService; +import org.sakaiproject.tool.api.SessionManager; +import org.sakaiproject.user.api.User; +import org.sakaiproject.user.api.UserNotDefinedException; +import org.sakaiproject.user.api.UserDirectoryService; + +import org.springframework.beans.factory.annotation.Autowired; + +/** + * An implementation of Gradebook-specific authorization needs based + * on a combination of fine-grained site-scoped Sakai permissions and the + * shared Section Awareness API. This is a transtional stage between + * coarse-grained site-and-role-based authz and our hoped-for fine-grained + * role-determined group-scoped authz. + */ +@Slf4j +public class GradingAuthzImpl implements GradingAuthz { + + @Autowired private SectionAwareness sectionAwareness; + @Autowired private GradingPermissionService gradingPermissionService; + @Autowired private FunctionManager functionManager; + @Autowired private UserDirectoryService userDirectoryService; + @Autowired private SecurityService securityService; + @Autowired private SessionManager sessionManager; + @Autowired private SiteService siteService; + + /** + * Perform authorization-specific framework initializations for the Gradebook. + */ + public void init() { + + Collection registered = functionManager.getRegisteredFunctions("gradebook"); + if (!registered.contains(PERMISSION_GRADE_ALL)) { + functionManager.registerFunction(PERMISSION_GRADE_ALL); + } + + if (!registered.contains(PERMISSION_GRADE_SECTION)) { + functionManager.registerFunction(PERMISSION_GRADE_SECTION); + } + + if (!registered.contains(PERMISSION_EDIT_ASSIGNMENTS)) { + functionManager.registerFunction(PERMISSION_EDIT_ASSIGNMENTS); + } + + if (!registered.contains(PERMISSION_VIEW_OWN_GRADES)) { + functionManager.registerFunction(PERMISSION_VIEW_OWN_GRADES); + } + + if (!registered.contains(PERMISSION_VIEW_STUDENT_NUMBERS)) { + functionManager.registerFunction(PERMISSION_VIEW_STUDENT_NUMBERS); + } + } + + public boolean isUserAbleToGrade(String gradebookUid) { + + return (hasPermission(gradebookUid, PERMISSION_GRADE_ALL) || hasPermission(gradebookUid, PERMISSION_GRADE_SECTION)); + } + + public boolean isUserAbleToGrade(String gradebookUid, String userUid) { + + try { + User user = userDirectoryService.getUser(userUid); + return (hasPermission(user, gradebookUid, PERMISSION_GRADE_ALL) || hasPermission(user, gradebookUid, PERMISSION_GRADE_SECTION)); + } catch (UserNotDefinedException unde) { + log.warn("User not found for userUid: " + userUid); + return false; + } + + } + + public boolean isUserAbleToGradeAll(String gradebookUid) { + + return hasPermission(gradebookUid, PERMISSION_GRADE_ALL); + } + + public boolean isUserAbleToGradeAll(String gradebookUid, String userUid) { + + try { + User user = userDirectoryService.getUser(userUid); + return hasPermission(user, gradebookUid, PERMISSION_GRADE_ALL); + } catch (UserNotDefinedException unde) { + log.warn("User not found for userUid: " + userUid); + return false; + } + } + + /** + * When group-scoped permissions are available, this is where + * they will go. My current assumption is that the call will look like: + * + * return hasPermission(sectionUid, PERMISSION_GRADE_ALL); + */ + public boolean isUserAbleToGradeSection(String sectionUid) { + + return sectionAwareness.isSectionMemberInRole(sectionUid, sessionManager.getCurrentSessionUserId(), Role.TA); + } + + public boolean isUserAbleToEditAssessments(String gradebookUid) { + + return hasPermission(gradebookUid, PERMISSION_EDIT_ASSIGNMENTS); + } + + public boolean isUserAbleToViewOwnGrades(String gradebookUid) { + + return hasPermission(gradebookUid, PERMISSION_VIEW_OWN_GRADES); + } + + public boolean isUserAbleToViewStudentNumbers(String gradebookUid) { + + return hasPermission(gradebookUid, PERMISSION_VIEW_STUDENT_NUMBERS); + } + + private boolean hasPermission(String gradebookUid, String permission) { + + return securityService.unlock(permission, siteService.siteReference(gradebookUid)); + } + + private boolean hasPermission(User user, String gradebookUid, String permission) { + + return securityService.unlock(user, permission, siteService.siteReference(gradebookUid)); + } + + /** + * + * @param sectionUid + * @return whether user is Role.TA in given section + */ + private boolean isUserTAinSection(String sectionUid) { + + String userUid = sessionManager.getCurrentSessionUserId(); + return sectionAwareness.isSectionMemberInRole(sectionUid, userUid, Role.TA); + } + + private boolean isUserTAinSection(String sectionUid, String userUid) { + + return sectionAwareness.isSectionMemberInRole(sectionUid, userUid, Role.TA); + } + + public String getGradeViewFunctionForUserForStudentForItem(String gradebookUid, Long itemId, String studentUid) { + + if (itemId == null || studentUid == null || gradebookUid == null) { + throw new IllegalArgumentException("Null parameter(s) in AuthzSectionsServiceImpl.isUserAbleToGradeItemForStudent"); + } + + if (isUserAbleToGradeAll(gradebookUid)) { + return GradingService.gradePermission; + } + + String userUid = sessionManager.getCurrentSessionUserId(); + + List viewableSections = getViewableSections(gradebookUid); + + if (gradingPermissionService.currentUserHasGraderPermissions(gradebookUid)) { + + // get the map of authorized item (assignment) ids to grade/view function + Map itemIdFunctionMap + = gradingPermissionService.getAvailableItemsForStudent( + gradebookUid, userUid, studentUid, viewableSections); + + if (itemIdFunctionMap == null || itemIdFunctionMap.isEmpty()) { + return null; // not authorized to grade/view any items for this student + } + + String functionValueForItem = (String)itemIdFunctionMap.get(itemId); + String view = GradingService.viewPermission; + String grade = GradingService.gradePermission; + + if (functionValueForItem != null) { + if (functionValueForItem.equalsIgnoreCase(grade)) { + return GradingService.gradePermission; + } + + if (functionValueForItem.equalsIgnoreCase(view)) { + return GradingService.viewPermission; + } + } + + return null; + + } else { + // use OOTB permissions based upon TA section membership + List sectionIds = viewableSections.stream().map(CourseSection::getUuid).collect(Collectors.toList()); + for (String sectionUuid : sectionIds) { + if (isUserTAinSection(sectionUuid) && sectionAwareness.isSectionMemberInRole(sectionUuid, studentUid, Role.STUDENT)) { + return GradingService.gradePermission; + } + } + + return null; + } + } + + private boolean isUserAbleToGradeOrViewItemForStudent(String gradebookUid, Long itemId, String studentUid, String function) throws IllegalArgumentException { + + if (itemId == null || studentUid == null || function == null) { + throw new IllegalArgumentException("Null parameter(s) in AuthzSectionsServiceImpl.isUserAbleToGradeItemForStudent"); + } + + if (isUserAbleToGradeAll(gradebookUid)) { + return true; + } + + String userUid = sessionManager.getCurrentSessionUserId(); + + List viewableSections = getViewableSections(gradebookUid); + List sectionIds = new ArrayList<>(); + if (!viewableSections.isEmpty()) { + viewableSections.forEach(cs -> sectionIds.add(cs.getUuid())); + } + + if (gradingPermissionService.currentUserHasGraderPermissions(gradebookUid)) { + + // get the map of authorized item (assignment) ids to grade/view function + Map itemIdFunctionMap = gradingPermissionService.getAvailableItemsForStudent(gradebookUid, userUid, studentUid, viewableSections); + + if (itemIdFunctionMap == null || itemIdFunctionMap.isEmpty()) { + return false; // not authorized to grade/view any items for this student + } + + String functionValueForItem = (String)itemIdFunctionMap.get(itemId); + String view = GradingService.viewPermission; + String grade = GradingService.gradePermission; + + if (functionValueForItem != null) { + if (function.equalsIgnoreCase(grade) && functionValueForItem.equalsIgnoreCase(grade)) + return true; + + if (function.equalsIgnoreCase(view) && (functionValueForItem.equalsIgnoreCase(grade) || functionValueForItem.equalsIgnoreCase(view))) + return true; + } + + return false; + + } else { + // use OOTB permissions based upon TA section membership + for (String sectionUuid : sectionIds) { + if (isUserTAinSection(sectionUuid) && sectionAwareness.isSectionMemberInRole(sectionUuid, studentUid, Role.STUDENT)) { + return true; + } + } + + return false; + } + } + + public boolean isUserAbleToGradeItemForStudent(String gradebookUid, Long itemId, String studentUid) throws IllegalArgumentException { + + return isUserAbleToGradeOrViewItemForStudent(gradebookUid, itemId, studentUid, GradingService.gradePermission); + } + + public boolean isUserAbleToViewItemForStudent(String gradebookUid, Long itemId, String studentUid) throws IllegalArgumentException { + + return isUserAbleToGradeOrViewItemForStudent(gradebookUid, itemId, studentUid, GradingService.viewPermission); + } + + public List getViewableSections(String gradebookUid) { + + List viewableSections = new ArrayList<>(); + + List allSections = getAllSections(gradebookUid); + if (allSections.isEmpty()) { + return viewableSections; + } + + if (isUserAbleToGradeAll(gradebookUid)) { + return allSections; + } + + final Map sectionIdCourseSectionMap + = allSections.stream().collect(Collectors.toMap(s -> s.getUuid(), s -> s)); + + String userUid = sessionManager.getCurrentSessionUserId(); + + if (gradingPermissionService.userHasGraderPermissions(gradebookUid, userUid)) { + + List viewableSectionIds = gradingPermissionService.getViewableGroupsForUser(gradebookUid, userUid, new ArrayList<>(sectionIdCourseSectionMap.keySet())); + if (viewableSectionIds != null && !viewableSectionIds.isEmpty()) { + for (String sectionUuid : viewableSectionIds) { + CourseSection viewableSection = sectionIdCourseSectionMap.get(sectionUuid); + if (viewableSection != null) { + viewableSections.add(viewableSection); + } + } + } + } else { + // return all sections that the current user is a TA for + for (Map.Entry entry : sectionIdCourseSectionMap.entrySet()) { + String sectionUuid = entry.getKey(); + if (isUserTAinSection(sectionUuid)) { + CourseSection viewableSection = sectionIdCourseSectionMap.get(sectionUuid); + if (viewableSection != null) { + viewableSections.add(viewableSection); + } + } + } + } + + Collections.sort(viewableSections); + + return viewableSections; + } + + public List getAllSections(String gradebookUid) { + + return sectionAwareness.getSections(gradebookUid); + } + + private List getSectionEnrollmentsTrusted(String sectionUid) { + + return sectionAwareness.getSectionMembersInRole(sectionUid, Role.STUDENT); + } + + public Map findMatchingEnrollmentsForItem(String gradebookUid, Long categoryId, GradingCategoryType gbCategoryType, String optionalSearchString, String optionalSectionUid) { + + String userUid = sessionManager.getCurrentSessionUserId(); + return findMatchingEnrollmentsForItemOrCourseGrade(userUid, gradebookUid, categoryId, gbCategoryType, optionalSearchString, optionalSectionUid, false); + } + + public Map findMatchingEnrollmentsForItemForUser(String userUid, String gradebookUid, Long categoryId, GradingCategoryType gbCategoryType, String optionalSearchString, String optionalSectionUid) { + + return findMatchingEnrollmentsForItemOrCourseGrade(userUid, gradebookUid, categoryId, gbCategoryType, optionalSearchString, optionalSectionUid, false); + } + + public Map findMatchingEnrollmentsForViewableCourseGrade(String gradebookUid, GradingCategoryType gbCategoryType, String optionalSearchString, String optionalSectionUid) { + + String userUid = sessionManager.getCurrentSessionUserId(); + return findMatchingEnrollmentsForItemOrCourseGrade(userUid, gradebookUid, null, gbCategoryType, optionalSearchString, optionalSectionUid, true); + } + + public Map> findMatchingEnrollmentsForViewableItems(String gradebookUid, List allGbItems, String optionalSearchString, String optionalSectionUid) { + + Map> enrollmentMap = new HashMap<>(); + List filteredEnrollments = null; + if (optionalSearchString != null) { + filteredEnrollments = sectionAwareness.findSiteMembersInRole(gradebookUid, Role.STUDENT, optionalSearchString); + } else { + filteredEnrollments = sectionAwareness.getSiteMembersInRole(gradebookUid, Role.STUDENT); + } + + if (filteredEnrollments == null || filteredEnrollments.isEmpty()) { + return enrollmentMap; + } + + // get all the students in the filtered section, if appropriate + Map studentsInSectionMap = new HashMap<>(); + if (optionalSectionUid != null) { + List sectionMembers = getSectionEnrollmentsTrusted(optionalSectionUid); + if (!sectionMembers.isEmpty()) { + sectionMembers.forEach(m -> studentsInSectionMap.put(m.getUser().getUserUid(), m)); + } + } + + Map studentIdEnrRecMap = new HashMap<>(); + for (EnrollmentRecord enr : filteredEnrollments) { + String studentId = enr.getUser().getUserUid(); + if (optionalSectionUid != null) { + if (studentsInSectionMap.containsKey(studentId)) { + studentIdEnrRecMap.put(studentId, enr); + } + } else { + studentIdEnrRecMap.put(studentId, enr); + } + } + + if (isUserAbleToGradeAll(gradebookUid)) { + List enrollments = new ArrayList<>(studentIdEnrRecMap.values()); + + HashMap assignFunctionMap = new HashMap<>(); + if (allGbItems != null && !allGbItems.isEmpty()) { + for (Object assign : allGbItems) { + Long assignId = null; + if (assign instanceof Assignment) { + assignId = ((Assignment) assign).getId(); + } else if (assign instanceof GradebookAssignment) { + assignId = ((GradebookAssignment)assign).getId(); + } + + if (assignId != null) { + assignFunctionMap.put(assignId, GradingService.gradePermission); + } + } + } + + enrollments.forEach(e -> enrollmentMap.put(e, assignFunctionMap)); + } else { + String userId = sessionManager.getCurrentSessionUserId(); + + Map sectionIdCourseSectionMap = getViewableSections(gradebookUid) + .stream().collect(Collectors.toMap(s -> s.getUuid(), s -> s)); + + if (gradingPermissionService.currentUserHasGraderPermissions(gradebookUid)) { + // user has special grader permissions that override default perms + + List myStudentIds = new ArrayList<>(studentIdEnrRecMap.keySet()); + + List selSections = new ArrayList<>(); + if (optionalSectionUid == null) { + // pass all sections + selSections = new ArrayList<>(sectionIdCourseSectionMap.values()); + } else { + // only pass the selected section + CourseSection section = sectionIdCourseSectionMap.get(optionalSectionUid); + if (section != null) + selSections.add(section); + } + + // we need to get the viewable students, so first create section id --> student ids map + myStudentIds = gradingPermissionService.getViewableStudentsForUser(gradebookUid, userId, myStudentIds, selSections); + Map> viewableStudentIdItemsMap = new HashMap<>(); + if (allGbItems == null || allGbItems.isEmpty()) { + if (myStudentIds != null) { + for (String studentId : myStudentIds) { + if (studentId != null) { + viewableStudentIdItemsMap.put(studentId, null); + } + } + } + } else { + viewableStudentIdItemsMap = gradingPermissionService.getAvailableItemsForStudents(gradebookUid, userId, myStudentIds, selSections); + } + + if (!viewableStudentIdItemsMap.isEmpty()) { + for (Map.Entry> entry : viewableStudentIdItemsMap.entrySet()) { + String studentId = entry.getKey(); + EnrollmentRecord enrRec = studentIdEnrRecMap.get(studentId); + if (enrRec != null) { + enrollmentMap.put(enrRec, entry.getValue()); + } + } + } + + } else { + // use default section-based permissions + + // Determine the current user's section memberships + List availableSections = new ArrayList<>(); + if (optionalSectionUid != null && isUserTAinSection(optionalSectionUid)) { + if (sectionIdCourseSectionMap.containsKey(optionalSectionUid)) + availableSections.add(optionalSectionUid); + } else { + for (String sectionUuid : sectionIdCourseSectionMap.keySet()) { + if (isUserTAinSection(sectionUuid)) { + availableSections.add(sectionUuid); + } + } + } + + // Determine which enrollees are in these sections + Map uniqueEnrollees = new HashMap<>(); + for (String sectionUuid : availableSections) { + List sectionEnrollments = getSectionEnrollmentsTrusted(sectionUuid); + sectionEnrollments.forEach(e -> uniqueEnrollees.put(e.getUser().getUserUid(), e)); + } + + // Filter out based upon the original filtered students + for (String enrId : studentIdEnrRecMap.keySet()) { + if (uniqueEnrollees.containsKey(enrId)) { + // iterate through the assignments + Map itemFunctionMap = new HashMap<>(); + if (allGbItems != null && !allGbItems.isEmpty()) { + for (Object assign : allGbItems) { + Long assignId = null; + if (assign instanceof org.sakaiproject.grading.api.Assignment) { + assignId = ((org.sakaiproject.grading.api.Assignment)assign).getId(); + } else if (assign instanceof GradebookAssignment) { + assignId = ((GradebookAssignment)assign).getId(); + } + + if (assignId != null) { + itemFunctionMap.put(assignId, GradingService.gradePermission); + } + } + } + enrollmentMap.put(studentIdEnrRecMap.get(enrId), itemFunctionMap); + } + } + } + } + + return enrollmentMap; + } + + /** + * @param userUid + * @param gradebookUid + * @param categoryId + * @param optionalSearchString + * @param optionalSectionUid + * @param itemIsCourseGrade + * @return Map of EnrollmentRecord --> View or Grade + */ + private Map findMatchingEnrollmentsForItemOrCourseGrade(String userUid, String gradebookUid, Long categoryId, GradingCategoryType gbCategoryType, String optionalSearchString, String optionalSectionUid, boolean itemIsCourseGrade) { + + Map enrollmentMap = new HashMap<>(); + List filteredEnrollments = new ArrayList<>(); + + if (optionalSearchString != null) { + filteredEnrollments = sectionAwareness.findSiteMembersInRole(gradebookUid, Role.STUDENT, optionalSearchString); + } else { + filteredEnrollments = sectionAwareness.getSiteMembersInRole(gradebookUid, Role.STUDENT); + } + + if (filteredEnrollments.isEmpty()) { + return enrollmentMap; + } + + // get all the students in the filtered section, if appropriate + Map studentsInSectionMap = new HashMap<>(); + if (optionalSectionUid != null) { + List sectionMembers = sectionAwareness.getSectionMembersInRole(optionalSectionUid, Role.STUDENT); + if (!sectionMembers.isEmpty()) { + sectionMembers.forEach(m -> studentsInSectionMap.put(m.getUser().getUserUid(), m)); + } + } + + Map studentIdEnrRecMap = new HashMap<>(); + for (EnrollmentRecord enr : filteredEnrollments) { + String studentId = enr.getUser().getUserUid(); + if (optionalSectionUid != null) { + if (studentsInSectionMap.containsKey(studentId)) { + studentIdEnrRecMap.put(studentId, enr); + } + } else { + studentIdEnrRecMap.put(enr.getUser().getUserUid(), enr); + } + } + + if (isUserAbleToGradeAll(gradebookUid, userUid)) { + studentIdEnrRecMap.values().forEach(e -> enrollmentMap.put(e, GradingService.gradePermission)); + } else { + Map sectionIdCourseSectionMap = new HashMap<>(); + getAllSections(gradebookUid).forEach(cs -> sectionIdCourseSectionMap.put(cs.getUuid(), cs)); + + if (gradingPermissionService.userHasGraderPermissions(gradebookUid, userUid)) { + // user has special grader permissions that override default perms + + List myStudentIds = new ArrayList<>(studentIdEnrRecMap.keySet()); + + List selSections = new ArrayList<>(); + if (optionalSectionUid == null) { + // pass all sections + selSections = new ArrayList<>(sectionIdCourseSectionMap.values()); + } else { + // only pass the selected section + CourseSection section = sectionIdCourseSectionMap.get(optionalSectionUid); + if (section != null) { + selSections.add(section); + } + } + + Map viewableEnrollees = new HashMap<>(); + if (itemIsCourseGrade) { + viewableEnrollees = gradingPermissionService.getCourseGradePermission(gradebookUid, userUid, myStudentIds, selSections); + } else { + viewableEnrollees = gradingPermissionService.getStudentsForItem(gradebookUid, userUid, myStudentIds, gbCategoryType, categoryId, selSections); + } + + if (!viewableEnrollees.isEmpty()) { + for (Map.Entry entry : viewableEnrollees.entrySet()) { + String studentId = entry.getKey(); + EnrollmentRecord enrRec = studentIdEnrRecMap.get(studentId); + if (enrRec != null) { + enrollmentMap.put(enrRec, entry.getValue()); + } + } + } + + } else { + // use default section-based permissions + return getEnrollmentMapUsingDefaultPermissions(userUid, studentIdEnrRecMap, sectionIdCourseSectionMap, optionalSectionUid); + } + } + + return enrollmentMap; + } + + /** + * + * @param userUid + * @param studentIdEnrRecMap + * @param sectionIdCourseSectionMap + * @param optionalSectionUid + * @return Map of EnrollmentRecord to function view/grade using the default permissions (based on TA section membership) + */ + private Map getEnrollmentMapUsingDefaultPermissions(String userUid, Map studentIdEnrRecMap, Map sectionIdCourseSectionMap, String optionalSectionUid) { + + // Determine the current user's section memberships + Map enrollmentMap = new HashMap<>(); + List availableSections = new ArrayList<>(); + if (optionalSectionUid != null && isUserTAinSection(optionalSectionUid, userUid)) { + if (sectionIdCourseSectionMap.containsKey(optionalSectionUid)) { + availableSections.add(optionalSectionUid); + } + } else { + for (String sectionUuid : sectionIdCourseSectionMap.keySet()) { + if (isUserTAinSection(sectionUuid, userUid)) { + availableSections.add(sectionUuid); + } + } + } + + // Determine which enrollees are in these sections + Map uniqueEnrollees = new HashMap<>(); + for (String sectionUuid : availableSections) { + getSectionEnrollmentsTrusted(sectionUuid) + .forEach(e -> uniqueEnrollees.put(e.getUser().getUserUid(), e)); + } + + // Filter out based upon the original filtered students + for (String enrId : studentIdEnrRecMap.keySet()) { + if (uniqueEnrollees.containsKey(enrId)) { + enrollmentMap.put(studentIdEnrRecMap.get(enrId), GradingService.gradePermission); + } + } + + return enrollmentMap; + } + + /* + public List findStudentSectionMemberships(String gradebookUid, String studentUid) { + + List sectionMemberships = new ArrayList<>(); + try { + sectionMemberships = siteService.getSite(gradebookUid).getGroupsWithMember(studentUid); + } catch (IdUnusedException e) { + log.error("No site with id = " + gradebookUid); + } + + return sectionMemberships; + } + + public List getStudentSectionMembershipNames(String gradebookUid, String studentUid) { + + List sectionNames = new ArrayList<>(); + List sections = findStudentSectionMemberships(gradebookUid, studentUid); + if (sections != null && !sections.isEmpty()) { + sections.forEach(g -> sectionNames.add(myGroup.getTitle())); + } + + return sectionNames; + } + */ +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingPermissionServiceImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingPermissionServiceImpl.java new file mode 100644 index 000000000000..2eb629e9de5e --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingPermissionServiceImpl.java @@ -0,0 +1,1230 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + +import org.sakaiproject.component.cover.ComponentManager; +import org.sakaiproject.grading.api.GraderPermission; +import org.sakaiproject.grading.api.GradingPermissionService; +import org.sakaiproject.grading.api.GradingPersistenceManager; +import org.sakaiproject.grading.api.GradingService; +import org.sakaiproject.grading.api.GradingCategoryType; +import org.sakaiproject.grading.api.PermissionDefinition; +import org.sakaiproject.grading.api.model.Category; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradebookAssignment; +import org.sakaiproject.grading.api.model.Permission; +import org.sakaiproject.section.api.SectionAwareness; +import org.sakaiproject.section.api.coursemanagement.CourseSection; +import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; +import org.sakaiproject.section.api.facade.Role; +import org.sakaiproject.tool.api.SessionManager; + +import org.springframework.beans.factory.annotation.Autowired; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class GradingPermissionServiceImpl implements GradingPermissionService { + + @Autowired private GradingPersistenceManager gradingPersistenceManager; + @Autowired protected SectionAwareness sectionAwareness; + @Autowired private SessionManager sessionManager; + + public List getCategoriesForUser(Long gradebookId, String userId, List categoryIdList) throws IllegalArgumentException { + + if (gradebookId == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getCategoriesForUser"); + } + + List anyCategoryPermission = getPermissionsForUserAnyCategory(gradebookId, userId); + if (anyCategoryPermission.size() > 0 ) { + return categoryIdList; + } else { + List returnCatIds = new ArrayList<>(); + for (Permission perm : getPermissionsForUserForCategory(gradebookId, userId, categoryIdList)) { + if (perm != null && !returnCatIds.contains(perm.getCategoryId())) { + returnCatIds.add(perm.getCategoryId()); + } + } + + return returnCatIds; + } + } + + public List getCategoriesForUserForStudentView(Long gradebookId, String userId, String studentId, List categoriesIds, List sectionIds) throws IllegalArgumentException { + + if (gradebookId == null || userId == null || studentId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getCategoriesForUser"); + } + + List returnCategoryList = new ArrayList<>(); + if (categoriesIds == null || categoriesIds.isEmpty()) { + return returnCategoryList; + } + + List graderPermissions = getPermissionsForUser(gradebookId, userId); + if (graderPermissions == null || graderPermissions.isEmpty()) { + return returnCategoryList; + } + + List studentSections = new ArrayList<>(); + + if (sectionIds != null) { + for (String sectionId : sectionIds) { + if (sectionId != null && sectionAwareness.isSectionMemberInRole(sectionId, studentId, Role.STUDENT)) { + studentSections.add(sectionId); + } + } + } + + for (Permission perm : graderPermissions) { + String sectionId = perm.getGroupId(); + if (studentSections.contains(sectionId) || sectionId == null) { + Long catId = perm.getCategoryId(); + if (catId == null) { + return returnCategoryList; + } else { + returnCategoryList.add(catId); + } + } + } + + return returnCategoryList; + } + + public boolean getPermissionForUserForAllAssignment(Long gradebookId, String userId) throws IllegalArgumentException { + + if (gradebookId == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getPermissionForUserForAllAssignment"); + } + + return getPermissionsForUserAnyCategory(gradebookId, userId).size() > 0; + } + + public boolean getPermissionForUserForAllAssignmentForStudent(Long gradebookId, String userId, String studentId, List sectionIds) throws IllegalArgumentException { + + if (gradebookId == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getPermissionForUserForAllAssignment"); + } + + List graderPermissions = this.getPermissionsForUser(gradebookId, userId); + if (graderPermissions == null || graderPermissions.isEmpty()) { + return false; + } + + for (Permission perm : graderPermissions) { + String sectionId = perm.getGroupId(); + if (sectionId == null || (sectionIds.contains(sectionId) && sectionAwareness.isSectionMemberInRole(sectionId, studentId, Role.STUDENT))) { + if (perm.getCategoryId() == null) { + return true; + } + } + } + + return false; + } + + private void addToStudentMap(Map studentMap, Map studentMapForGroups) { + + for (Entry entry : studentMapForGroups.entrySet()) { + String key = entry.getKey(); + if ((studentMap.containsKey(key) && studentMap.get(key).equalsIgnoreCase(GradingService.viewPermission)) + || !studentMap.containsKey(key)) { + studentMap.put(key, entry.getValue()); + } + } + } + + public Map getStudentsForItem(Long gradebookId, String userId, List studentIds, GradingCategoryType cateType, Long categoryId, List courseSections) + throws IllegalArgumentException { + + if (gradebookId == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getStudentsForItem"); + } + + if (cateType != GradingCategoryType.ONLY_CATEGORY && cateType != GradingCategoryType.WEIGHTED_CATEGORY && cateType != GradingCategoryType.NO_CATEGORY) { + throw new IllegalArgumentException("Invalid category type in GradebookPermissionServiceImpl.getStudentsForItem"); + } + + if (studentIds != null) { + Map> sectionIdStudentIdsMap = getSectionIdStudentIdsMap(courseSections, studentIds); + if (cateType == GradingCategoryType.NO_CATEGORY) { + List perms = getPermissionsForUserAnyGroup(gradebookId, userId); + + Map studentMap = new HashMap<>(); + if (perms.size() > 0) { + boolean view = false; + boolean grade = false; + for (Permission perm : perms) { + if (perm != null && perm.getFunctionName().equalsIgnoreCase(GradingService.gradePermission)) { + grade = true; + break; + } + if (perm != null && perm.getFunctionName().equalsIgnoreCase(GradingService.viewPermission)) { + view = true; + } + } + for (String studentId : studentIds) { + if (grade == true) { + studentMap.put(studentId, GradingService.gradePermission); + } else if (view == true) { + studentMap.put(studentId, GradingService.viewPermission); + } + } + } + + perms = getPermissionsForUser(gradebookId, userId); + Map studentMapForGroups = filterPermissionForGrader(perms, studentIds, sectionIdStudentIdsMap); + addToStudentMap(studentMap, studentMapForGroups); + + return studentMap; + } else { + List cateList = new ArrayList<>(); + cateList.add(categoryId); + List perms = getPermissionsForUserAnyGroupForCategory(gradebookId, userId, cateList); + + Map studentMap = new HashMap<>(); + if (perms.size() > 0) { + boolean view = false; + boolean grade = false; + for (Permission perm : perms) { + if (perm != null && perm.getFunctionName().equalsIgnoreCase(GradingService.gradePermission)) { + grade = true; + break; + } + if (perm != null && perm.getFunctionName().equalsIgnoreCase(GradingService.viewPermission)) { + view = true; + } + } + for (String studentId : studentIds) { + if (grade == true) { + studentMap.put(studentId, GradingService.gradePermission); + } else if (view == true) { + studentMap.put(studentId, GradingService.viewPermission); + } + } + } + perms = getPermissionsForUserAnyGroupAnyCategory(gradebookId, userId); + Map studentMapForGroups = filterPermissionForGraderForAllStudent(perms, studentIds); + addToStudentMap(studentMap, studentMapForGroups); + + if (courseSections != null && !courseSections.isEmpty()) { + final List groupIds = courseSections.stream().filter(Objects::nonNull) + .collect(Collectors.mapping(g -> g.getUuid(), Collectors.toList())); + + perms = getPermissionsForUserForGoupsAnyCategory(gradebookId, userId, groupIds); + studentMapForGroups = filterPermissionForGrader(perms, studentIds, sectionIdStudentIdsMap); + addToStudentMap(studentMap, studentMapForGroups); + } + + perms = getPermissionsForUserForCategory(gradebookId, userId, cateList); + studentMapForGroups = filterPermissionForGrader(perms, studentIds, sectionIdStudentIdsMap); + addToStudentMap(studentMap, studentMapForGroups); + + return studentMap; + } + } + return null; + } + + public Map getStudentsForItem(String gradebookUid, String userId, List studentIds, GradingCategoryType cateType, Long categoryId, List courseSections) + throws IllegalArgumentException { + + if (gradebookUid == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getStudentsForItem"); + } + + Optional optGradebook = gradingPersistenceManager.getGradebook(gradebookUid); + + if (optGradebook.isEmpty()) { + log.warn("No gradebook for uid {}", gradebookUid); + return Collections.emptyMap(); + } + + Long gradebookId = optGradebook.get().getId(); + return getStudentsForItem(gradebookId, userId, studentIds, cateType, categoryId, courseSections); + } + + public List getViewableGroupsForUser(Long gradebookId, String userId, List groupIds) { + + if (gradebookId == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getViewableSectionsForUser"); + } + + if (groupIds == null || groupIds.size() == 0) { + return null; + } + + List anyGroupPermission = getPermissionsForUserAnyGroup(gradebookId, userId); + if (anyGroupPermission != null && anyGroupPermission.size() > 0 ) { + return groupIds; + } else { + List permList = getPermissionsForUserForGroup(gradebookId, userId, groupIds); + + List filteredGroups = new ArrayList<>(); + for (String groupId : groupIds) { + if (groupId != null) { + for (Permission perm : permList) { + if (perm != null && perm.getGroupId().equals(groupId)) { + filteredGroups.add(groupId); + break; + } + } + } + } + return filteredGroups; + } + } + + public List getViewableGroupsForUser(String gradebookUid, String userId, List groupIds) { + + if (gradebookUid == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getViewableSectionsForUser"); + } + + Long gradebookId = getGradebook(gradebookUid).getId(); + + return getViewableGroupsForUser(gradebookId, userId, groupIds); + } + + public List getGraderPermissionsForCurrentUser(Long gradebookId) { + + return getGraderPermissionsForUser(gradebookId, sessionManager.getCurrentSessionUserId()); + } + + public List getGraderPermissionsForUser(Long gradebookId, String userId) { + + if (gradebookId == null || StringUtils.isBlank(userId)) { + throw new IllegalArgumentException("The gradebookId and userId must be supplied"); + } + + return getPermissionsForUser(gradebookId, userId); + } + + public List getGraderPermissionsForCurrentUser(String gradebookUid) { + + return getGraderPermissionsForUser(gradebookUid, sessionManager.getCurrentSessionUserId()); + } + + public List getGraderPermissionsForUser(String gradebookUid, String userId) { + + if (StringUtils.isBlank(gradebookUid) || StringUtils.isBlank(userId)) { + throw new IllegalArgumentException("The gradebookUid and userId must be supplied"); + } + + if (StringUtils.isBlank(userId)) { + userId = sessionManager.getCurrentSessionUserId(); + } + + Long gradebookId = getGradebook(gradebookUid).getId(); + + return getPermissionsForUser(gradebookId, userId); + } + + private Map filterPermissionForGrader(final List perms, List studentIds, Map> sectionIdStudentIdsMap) { + + Map permMap = new HashMap<>(); + for (Permission perm : perms) { + if (perm != null) { + if (permMap.containsKey(perm.getGroupId()) && permMap.get(perm.getGroupId()).equalsIgnoreCase(GradingService.viewPermission)) { + if (perm.getFunctionName().equalsIgnoreCase(GradingService.gradePermission)) { + permMap.put(perm.getGroupId(), GradingService.gradePermission); + } + } else if (!permMap.containsKey(perm.getGroupId())) { + permMap.put(perm.getGroupId(), perm.getFunctionName()); + } + } + } + Map studentMap = new HashMap<>(); + + for (String studentId : studentIds) { + if (sectionIdStudentIdsMap != null) { + for (Map.Entry> entry : sectionIdStudentIdsMap.entrySet()) { + String grpId = entry.getKey(); + List sectionMembers = entry.getValue(); + + if (sectionMembers != null && sectionMembers.contains(studentId) && permMap.containsKey(grpId)) { + if (studentMap.containsKey(studentId) && studentMap.get(studentId).equalsIgnoreCase(GradingService.viewPermission)) { + if (permMap.get(grpId).equalsIgnoreCase(GradingService.gradePermission)) { + studentMap.put(studentId, GradingService.gradePermission); + } + } else if (!studentMap.containsKey(studentId)) { + studentMap.put(studentId, permMap.get(grpId)); + } + } + } + } + } + return studentMap; + } + + private Map filterPermissionForGraderForAllStudent(List perms, List studentIds) { + + Boolean grade = false; + Boolean view = false; + for (Permission perm : perms) { + if (perm.getFunctionName().equalsIgnoreCase(GradingService.gradePermission)) { + grade = true; + break; + } else if (perm.getFunctionName().equalsIgnoreCase(GradingService.viewPermission)) { + view = true; + } + } + + Map studentMap = new HashMap<>(); + + if (grade || view) { + for (String studentId : studentIds) { + if (grade) { + studentMap.put(studentId, GradingService.gradePermission); + } else if (view) { + studentMap.put(studentId, GradingService.viewPermission); + } + } + } + return studentMap; + } + + private Map filterPermissionForGraderForAllAssignments(List perms, List assignmentList) { + + Boolean grade = false; + Boolean view = false; + for (Permission perm : perms) { + if (perm.getFunctionName().equalsIgnoreCase(GradingService.gradePermission)) { + grade = true; + break; + } else if (perm.getFunctionName().equalsIgnoreCase(GradingService.viewPermission)) { + view = true; + } + } + + Map assignMap = new HashMap<>(); + + if (grade || view) { + for (GradebookAssignment assign : assignmentList) { + if (grade && assign != null) { + assignMap.put(assign.getId(), GradingService.gradePermission); + } else if (view && assign != null) { + assignMap.put(assign.getId(), GradingService.viewPermission); + } + } + } + return assignMap; + } + + private Map getAvailableItemsForStudent(Gradebook gradebook, String userId, String studentId, Map sectionIdCourseSectionMap, Map catIdCategoryMap, List assignments, List permsForUserAnyGroup, List allPermsForUser, List permsForAnyGroupForCategories, List permsForUserAnyGroupAnyCategory, List permsForGroupsAnyCategory, List permsForUserForCategories, Map> sectionIdStudentIdsMap) throws IllegalArgumentException { + + if (gradebook == null || userId == null || studentId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getAvailableItemsForStudent"); + } + + List cateList = new ArrayList<>(catIdCategoryMap.values()); + + if (gradebook.getCategoryType() == GradingCategoryType.NO_CATEGORY) { + + Map assignMap = new HashMap<>(); + if (permsForUserAnyGroup != null && permsForUserAnyGroup.size() > 0) { + boolean view = false; + boolean grade = false; + for (Permission perm : permsForUserAnyGroup) { + if (perm != null && perm.getFunctionName().equalsIgnoreCase(GradingService.gradePermission)) { + grade = true; + break; + } + if (perm != null && perm.getFunctionName().equalsIgnoreCase(GradingService.viewPermission)) { + view = true; + } + } + for (GradebookAssignment as : assignments) { + if (grade == true && as != null) { + assignMap.put(as.getId(), GradingService.gradePermission); + } else if (view == true && as != null) { + assignMap.put(as.getId(), GradingService.viewPermission); + } + } + } + + if (allPermsForUser != null) { + Map assignsMapForGroups = filterPermissionForGrader(allPermsForUser, studentId, assignments, sectionIdStudentIdsMap); + for (Map.Entry entry : assignsMapForGroups.entrySet()) { + Long key = entry.getKey(); + if ((assignMap.containsKey(key) && assignMap.get(key).equalsIgnoreCase(GradingService.viewPermission)) + || !assignMap.containsKey(key)) { + assignMap.put(key, entry.getValue()); + } + } + } + return assignMap; + } else { + Map assignMap = new HashMap<>(); + if (permsForAnyGroupForCategories != null && permsForAnyGroupForCategories.size() > 0) { + for (Permission perm : permsForAnyGroupForCategories) { + if (perm != null) { + if (perm.getCategoryId() != null) { + for (Category cate : cateList) { + if (cate != null && cate.getId().equals(perm.getCategoryId())) { + for (GradebookAssignment as : cate.getAssignmentList()) { + if (as != null) { + Long assignId = as.getId(); + if (as.getCategory() != null) { + if (assignMap.containsKey(assignId) && assignMap.get(assignId).equalsIgnoreCase(GradingService.viewPermission)) { + if (perm.getFunctionName().equalsIgnoreCase(GradingService.gradePermission)) { + assignMap.put(assignId, GradingService.gradePermission); + } + } else if (!assignMap.containsKey(assignId)) { + assignMap.put(assignId, perm.getFunctionName()); + } + } + } + } + break; + } + } + } + } + } + } + + if (permsForUserAnyGroupAnyCategory != null) { + Map assignMapForGroups = filterPermissionForGraderForAllAssignments(permsForUserAnyGroupAnyCategory, assignments); + for (Entry entry : assignMapForGroups.entrySet()) { + Long key = entry.getKey(); + if ((assignMap.containsKey(key) && assignMap.get(key).equalsIgnoreCase(GradingService.viewPermission)) + || !assignMap.containsKey(key)) { + assignMap.put(key, entry.getValue()); + } + } + } + + if (permsForGroupsAnyCategory != null) { + Map assignMapForGroups = filterPermissionForGrader(permsForGroupsAnyCategory, studentId, assignments, sectionIdStudentIdsMap); + for (Entry entry : assignMapForGroups.entrySet()) { + Long key = entry.getKey(); + if ((assignMap.containsKey(key) && assignMap.get(key).equalsIgnoreCase(GradingService.viewPermission)) + || !assignMap.containsKey(key)) { + assignMap.put(key, entry.getValue()); + } + } + } + + if (permsForUserForCategories != null) { + Map assignMapForGroups = filterPermissionForGraderForCategory(permsForUserForCategories, studentId, cateList, sectionIdStudentIdsMap); + if (assignMapForGroups != null) { + for (Entry entry : assignMapForGroups.entrySet()) { + Long key = entry.getKey(); + if ((assignMap.containsKey(key) && assignMap.get(key).equalsIgnoreCase(GradingService.viewPermission)) + || !assignMap.containsKey(key)) { + assignMap.put(key, entry.getValue()); + } + } + } + } + + return assignMap; + } + } + + public Map getAvailableItemsForStudent(Long gradebookId, String userId, String studentId, List courseSections) throws IllegalArgumentException { + + if (gradebookId == null || userId == null || studentId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getAvailableItemsForStudent"); + } + + final Map sectionIdCourseSectionMap + = courseSections.stream().filter(Objects::nonNull).collect(Collectors.toMap(s -> s.getUuid(), s -> s)); + + final Map catIdCategoryMap + = getCategoriesWithAssignments(gradebookId).stream() + .filter(Objects::nonNull).collect(Collectors.toMap(c -> c.getId(), c -> c)); + + List studentIds = new ArrayList<>(); + studentIds.add(studentId); + Map> sectionIdStudentIdsMap = getSectionIdStudentIdsMap(courseSections, studentIds); + + Optional optGradebook = gradingPersistenceManager.getGradebook(gradebookId); + + if (optGradebook.isEmpty()) { + log.warn("No gradebook for id {}", gradebookId); + return null; + } + + List assignments = gradingPersistenceManager.getAssignmentsForGradebook(gradebookId); + List categoryIds = new ArrayList<>(catIdCategoryMap.keySet()); + List groupIds = new ArrayList<>(sectionIdCourseSectionMap.keySet()); + + // Retrieve all the different permission info needed here so not called repeatedly for each student + List permsForUserAnyGroup = getPermissionsForUserAnyGroup(gradebookId, userId); + List allPermsForUser = getPermissionsForUser(gradebookId, userId); + List permsForAnyGroupForCategories = getPermissionsForUserAnyGroupForCategory(gradebookId, userId, categoryIds); + List permsForUserAnyGroupAnyCategory = getPermissionsForUserAnyGroupAnyCategory(gradebookId, userId); + List permsForGroupsAnyCategory = getPermissionsForUserForGoupsAnyCategory(gradebookId, userId, groupIds); + List permsForUserForCategories = getPermissionsForUserForCategory(gradebookId, userId, categoryIds); + + return getAvailableItemsForStudent(optGradebook.get(), userId, studentId, sectionIdCourseSectionMap, catIdCategoryMap, assignments, permsForUserAnyGroup, allPermsForUser, permsForAnyGroupForCategories, permsForUserAnyGroupAnyCategory, permsForGroupsAnyCategory, permsForUserForCategories, sectionIdStudentIdsMap); + } + + public Map getAvailableItemsForStudent(String gradebookUid, String userId, String studentId, List courseSections) throws IllegalArgumentException { + + if (gradebookUid == null || userId == null || studentId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getAvailableItemsForStudent"); + } + + Long gradebookId = getGradebook(gradebookUid).getId(); + + return getAvailableItemsForStudent(gradebookId, userId, studentId, courseSections); + + } + + private Map filterPermissionForGrader(List perms, String studentId, List assignmentList, Map> sectionIdStudentIdsMap) { + + Map permMap = new HashMap<>(); + for (Permission perm : perms) { + if (perm != null) { + if (permMap.containsKey(perm.getGroupId()) && permMap.get(perm.getGroupId()).equalsIgnoreCase(GradingService.viewPermission)) { + if (perm.getFunctionName().equalsIgnoreCase(GradingService.gradePermission)) { + permMap.put(perm.getGroupId(), GradingService.gradePermission); + } + } else if (!permMap.containsKey(perm.getGroupId())) { + permMap.put(perm.getGroupId(), perm.getFunctionName()); + } + } + } + Map assignmentMap = new HashMap<>(); + + if (sectionIdStudentIdsMap != null) { + for (GradebookAssignment assign : assignmentList) { + Long assignId = assign.getId(); + for (Map.Entry> entry : sectionIdStudentIdsMap.entrySet()) { + String grpId = entry.getKey(); + List sectionMembers = sectionIdStudentIdsMap.get(grpId); + + if (sectionMembers != null && sectionMembers.contains(studentId) && permMap.containsKey(grpId)) { + if (assignmentMap.containsKey(assignId) && assignmentMap.get(assignId).equalsIgnoreCase(GradingService.viewPermission)) { + if (permMap.get(grpId).equalsIgnoreCase(GradingService.gradePermission)) { + assignmentMap.put(assignId, GradingService.gradePermission); + } + } else if (!assignmentMap.containsKey(assignId)) { + assignmentMap.put(assignId, permMap.get(grpId)); + } + } + } + } + } + return assignmentMap; + } + + private Map filterPermissionForGraderForCategory(List perms, String studentId, List categoryList, Map> sectionIdStudentIdsMap) { + + Map assignmentMap = new HashMap<>(); + + for (Permission perm : perms) { + if (perm != null && perm.getCategoryId() != null) { + for (Category cate : categoryList) { + if (cate != null && cate.getId().equals(perm.getCategoryId())) { + for (GradebookAssignment as : cate.getAssignmentList()) { + if (as != null && sectionIdStudentIdsMap != null) { + Long assignId = as.getId(); + for (Map.Entry> entry : sectionIdStudentIdsMap.entrySet()) { + String grpId = entry.getKey(); + List sectionMembers = sectionIdStudentIdsMap.get(grpId); + + if (sectionMembers != null && sectionMembers.contains(studentId) && as.getCategory() != null) { + if (assignmentMap.containsKey(assignId) && grpId.equals(perm.getGroupId()) && assignmentMap.get(assignId).equalsIgnoreCase(GradingService.viewPermission)) { + if (perm.getFunctionName().equalsIgnoreCase(GradingService.gradePermission)) { + assignmentMap.put(assignId, GradingService.gradePermission); + } + } else if (!assignmentMap.containsKey(assignId) && grpId.equals(perm.getGroupId())) { + assignmentMap.put(assignId, perm.getFunctionName()); + } + } + } + } + } + break; + } + } + } + } + return assignmentMap; + } + + public Map> getAvailableItemsForStudents(Long gradebookId, String userId, List studentIds, List courseSections) throws IllegalArgumentException { + + if (gradebookId == null || userId == null || studentIds == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getAvailableItemsForStudents"); + } + + final Map sectionIdCourseSectionMap + = courseSections.stream().filter(Objects::nonNull).collect(Collectors.toMap(s -> s.getUuid(), s -> s)); + + final Map catIdCategoryMap + = getCategoriesWithAssignments(gradebookId).stream() + .filter(Objects::nonNull).collect(Collectors.toMap(c -> c.getId(), c -> c)); + + Map> sectionIdStudentIdsMap = getSectionIdStudentIdsMap(courseSections, studentIds); + + Optional optGradebook = gradingPersistenceManager.getGradebook(gradebookId); + + if (optGradebook.isEmpty()) { + log.warn("No gradebook for id {}", gradebookId); + return null; + } + + List assignments = gradingPersistenceManager.getAssignmentsForGradebook(gradebookId); + List categoryIds = new ArrayList<>(catIdCategoryMap.keySet()); + List groupIds = new ArrayList<>(sectionIdCourseSectionMap.keySet()); + + // Retrieve all the different permission info needed here so not called repeatedly for each student + List permsForUserAnyGroup = getPermissionsForUserAnyGroup(gradebookId, userId); + List allPermsForUser = getPermissionsForUser(gradebookId, userId); + List permsForAnyGroupForCategories = getPermissionsForUserAnyGroupForCategory(gradebookId, userId, categoryIds); + List permsForUserAnyGroupAnyCategory = getPermissionsForUserAnyGroupAnyCategory(gradebookId, userId); + List permsForGroupsAnyCategory = getPermissionsForUserForGoupsAnyCategory(gradebookId, userId, groupIds); + List permsForUserForCategories = getPermissionsForUserForCategory(gradebookId, userId, categoryIds); + + Map> studentsMap = new HashMap<>(); + for (String studentId : studentIds) { + if (studentId != null) { + Map assignMap = getAvailableItemsForStudent(optGradebook.get(), + userId, studentId, sectionIdCourseSectionMap, + catIdCategoryMap, assignments, permsForUserAnyGroup, + allPermsForUser, permsForAnyGroupForCategories, + permsForUserAnyGroupAnyCategory, + permsForGroupsAnyCategory, permsForUserForCategories, + sectionIdStudentIdsMap); + studentsMap.put(studentId, assignMap); + } + } + return studentsMap; + } + + public Map> getAvailableItemsForStudents(String gradebookUid, String userId, List studentIds, List courseSections) throws IllegalArgumentException { + + if (gradebookUid == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getAvailableItemsForStudents"); + } + + Long gradebookId = getGradebook(gradebookUid).getId(); + return getAvailableItemsForStudents(gradebookId, userId, studentIds, courseSections); + } + + public Map getCourseGradePermission(Long gradebookId, String userId, List studentIds, List courseSections) throws IllegalArgumentException { + + if (gradebookId == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getCourseGradePermission"); + } + + if (studentIds == null) { + return Collections.emptyMap(); + } + + Map studentMap = new HashMap<>(); + Map> sectionIdStudentIdsMap = getSectionIdStudentIdsMap(courseSections, studentIds); + + List perms = getPermissionsForUserAnyGroupAnyCategory(gradebookId, userId); + Map studentMapForGroups = filterPermissionForGraderForAllStudent(perms, studentIds); + addToStudentMap(studentMap, studentMapForGroups); + + if (courseSections != null) { + List groupIds = courseSections.stream() + .filter(Objects::nonNull).map(grp -> grp.getUuid()).collect(Collectors.toList()); + + perms = getPermissionsForUserForGoupsAnyCategory(gradebookId, userId, groupIds); + studentMapForGroups = filterPermissionForGrader(perms, studentIds, sectionIdStudentIdsMap); + addToStudentMap(studentMap, studentMapForGroups); + + Optional optGradebook = gradingPersistenceManager.getGradebook(gradebookId); + + if (optGradebook.isEmpty()) { + log.warn("No gradebook for id {}", gradebookId); + return null; + } + + Gradebook gradebook = optGradebook.get(); + + if (gradebook != null && (gradebook.getCategoryType() == GradingCategoryType.ONLY_CATEGORY || + gradebook.getCategoryType() == GradingCategoryType.WEIGHTED_CATEGORY)) { + List cateList = gradingPersistenceManager.getCategoriesForGradebook(gradebookId); + + perms = getPermissionsForUserForGroup(gradebookId, userId, groupIds); + studentMapForGroups = filterForAllCategoryStudents(perms, studentIds, cateList, sectionIdStudentIdsMap); + addToStudentMap(studentMap, studentMapForGroups); + + final List cateIdList = cateList.stream() + .filter(Objects::nonNull).map(c -> c.getId()).collect(Collectors.toList()); + + perms = getPermissionsForUserAnyGroupForCategory(gradebookId, userId, cateIdList); + if (perms.size() > 0) { + studentMapForGroups = filterForAllCategoryStudentsAnyGroup(perms, courseSections, studentIds, cateList); + addToStudentMap(studentMap, studentMapForGroups); + } + } + } + + return studentMap; + } + + public Map getCourseGradePermission(String gradebookUid, String userId, List studentIds, List courseSections) throws IllegalArgumentException { + + if (gradebookUid == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getCourseGradePermission"); + } + + Long gradebookId = getGradebook(gradebookUid).getId(); + return getCourseGradePermission(gradebookId, userId, studentIds, courseSections); + } + + private Map filterForAllCategoryStudents(List perms, List studentIds, List cateList, Map> sectionIdStudentIdsMap) { + + if (sectionIdStudentIdsMap == null || studentIds == null || cateList == null) { + return Collections.emptyMap(); + } + + final List cateIdList = cateList.stream() + .filter(Objects::nonNull).map(c -> c.getId()).collect(Collectors.toList()); + + Map> studentCateMap = new HashMap<>(); + for (String studentId : studentIds) { + studentCateMap.put(studentId, new HashMap()); + if (studentId != null) { + for (Map.Entry> entry : sectionIdStudentIdsMap.entrySet()) { + String grpId = entry.getKey(); + + if (grpId != null) { + List grpMembers = sectionIdStudentIdsMap.get(grpId); + if (grpMembers != null && !grpMembers.isEmpty() && grpMembers.contains(studentId)) { + for (Permission perm : perms) { + if (perm != null && perm.getGroupId().equals(grpId) && perm.getCategoryId() != null && cateIdList.contains(perm.getCategoryId())) { + Map cateMap = studentCateMap.get(studentId); + if (cateMap.get(perm.getCategoryId()) == null || cateMap.get(perm.getCategoryId()).equals(GradingService.viewPermission)) { + cateMap.put(perm.getCategoryId(), perm.getFunctionName()); + } + studentCateMap.put(studentId, cateMap); + } + } + } + } + } + } + } + + Map studentPermissionMap = new HashMap<>(); + for (Entry> perEntry : studentCateMap.entrySet()) { + String studentId = perEntry.getKey(); + Map cateMap = perEntry.getValue(); + if (cateMap != null) { + for (Long existsCateId : cateIdList) { + if (existsCateId != null) { + boolean hasPermissionForCate = false; + String permission = null; + for (Entry entry : cateMap.entrySet()) { + Long cateId = entry.getKey(); + if (cateId.equals(existsCateId)) { + hasPermissionForCate = true; + permission = entry.getValue(); + break; + } + } + if (hasPermissionForCate && permission != null) { + if (studentPermissionMap.get(studentId) == null || studentPermissionMap.get(studentId).equals(GradingService.gradePermission)) { + studentPermissionMap.put(studentId, permission); + } + } else if (!hasPermissionForCate) { + if (studentPermissionMap.get(studentId) != null) { + studentPermissionMap.remove(studentId); + } + } + } + } + } + } + return studentPermissionMap; + } + + private Map filterForAllCategoryStudentsAnyGroup(List perms, List courseSections, List studentIds, List cateList) { + + if (courseSections == null || studentIds == null || cateList == null) { + return Collections.emptyMap(); + } + + Map cateMap = new HashMap<>(); + for (Category cate : cateList) { + if (cate != null) { + boolean permissionExistForCate = false; + for (Permission perm : perms) { + if (perm != null && perm.getCategoryId().equals(cate.getId())) { + if ((cateMap.get(cate.getId()) == null || cateMap.get(cate.getId()).equals(GradingService.viewPermission))) { + cateMap.put(cate.getId(), perm.getFunctionName()); + } + permissionExistForCate = true; + } + } + if (!permissionExistForCate) { + return new HashMap(); + } + } + } + + boolean view = false; + for (Long catId : cateMap.keySet()) { + String permission = cateMap.get(catId); + if (permission != null && permission.equals(GradingService.viewPermission)) { + view = true; + } + } + Map studentMap = new HashMap<>(); + for (String studentId : studentIds) { + if (view) { + studentMap.put(studentId, GradingService.viewPermission); + } else { + studentMap.put(studentId, GradingService.gradePermission); + } + } + + return studentMap; + } + + public List getViewableStudentsForUser(Long gradebookId, String userId, List studentIds, List sections) { + + if (gradebookId == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getAvailableItemsForStudent"); + } + + if (studentIds == null || studentIds.isEmpty()) { + return Collections.emptyList(); + } + + List permsForAnyGroup = getPermissionsForUserAnyGroup(gradebookId, userId); + if (!permsForAnyGroup.isEmpty()) { + return studentIds; + } + + Map> sectionIdStudentIdsMap = getSectionIdStudentIdsMap(sections, studentIds); + + if (sectionIdStudentIdsMap.isEmpty()) { + return Collections.emptyList(); + } + + // use a map to make sure the student ids are unique + Map studentMap = new HashMap<>(); + + // Next, check for permissions for specific sections + List groupIds = new ArrayList<>(sectionIdStudentIdsMap.keySet()); + List permsForGroupsAnyCategory = getPermissionsForUserForGroup(gradebookId, userId, groupIds); + + if (permsForGroupsAnyCategory.isEmpty()) { + return Collections.emptyList(); + } + + for (Permission perm : permsForGroupsAnyCategory) { + String groupId = perm.getGroupId(); + if (groupId != null) { + List sectionStudentIds = sectionIdStudentIdsMap.get(groupId); + if (sectionStudentIds != null && !sectionStudentIds.isEmpty()) { + sectionStudentIds.forEach(id -> studentMap.put(id, null)); + } + } + } + + return new ArrayList<>(studentMap.keySet()); + } + + public List getViewableStudentsForUser(String gradebookUid, String userId, List studentIds, List sections) { + + if (gradebookUid == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in GradebookPermissionServiceImpl.getViewableStudentsForUser"); + } + + Long gradebookId = getGradebook(gradebookUid).getId(); + + return getViewableStudentsForUser(gradebookId, userId, studentIds, sections); + + } + + /** + * Get a list of permissions defined for the given user based on section and role or all sections if allowed. + * This method checks realms permissions for role/section and is independent of the + * gb_permissions_t permissions. + * + * note: If user has the grade privilege, they are given the GraderPermission.VIEW_COURSE_GRADE permission to match + * GB classic functionality. This needs to be reviewed. + * + * @param userUuid + * @param siteId + * @param role user Role + * @return list of {@link org.sakaiproject.grading.api.PermissionDefinition PermissionDefinitions} or empty list if none + */ + public List getRealmsPermissionsForUser(String userUuid,String siteId, Role role) { + + List permissions = new ArrayList(); + + if (this.getGradingService().isUserAllowedToGrade(siteId,userUuid)) { + //FIXME:giving them view course grade (this needs to be reviewed!!), + //it appears in GB classic, User can view course grades if they have the ability to grade in realms + PermissionDefinition permDef = new PermissionDefinition(); + permDef.setFunctionName(GraderPermission.VIEW_COURSE_GRADE.toString()); + permDef.setUserId(userUuid); + permissions.add(permDef); + + if (this.getGradingService().isUserAllowedToGradeAll(siteId,userUuid)) { + permDef = new PermissionDefinition(); + permDef.setFunctionName(GraderPermission.GRADE.toString()); + permDef.setUserId(userUuid); + permissions.add(permDef); + } else { + //get list of sections belonging to user and set a PermissionDefinition for each one + //Didn't find a method that returned gradeable sections for a TA, only for the logged in user. + //grabbing list of sections for the site, if User is a member of the section and has privilege to + //grade their sections, they are given the grade permission. Seems straight forward?? + List sections = sectionAwareness.getSections(siteId); + + for (CourseSection section: sections) { + if (sectionAwareness.isSectionMemberInRole(section.getUuid(), userUuid,role)) { + //realms have no categories defined for grading, just perms and group id + permDef = new PermissionDefinition(); + permDef.setFunctionName(GraderPermission.GRADE.toString()); + permDef.setUserId(userUuid); + permDef.setGroupReference(section.getUuid()); + permissions.add(permDef); + } + } + } + } + + return permissions; + } + + public GradingService getGradingService() { + + return (GradingService) ComponentManager.get("org.sakaiproject.grading.api.GradingService"); + } + + private Map> getSectionIdStudentIdsMap(List courseSections, List studentIds) { + + if (courseSections == null) { + return Collections.>emptyMap(); + } + + Map> sectionIdStudentIdsMap = new HashMap<>(); + for (CourseSection section: courseSections) { + if (section != null) { + String sectionId = section.getUuid(); + List members = sectionAwareness.getSectionMembersInRole(sectionId, Role.STUDENT); + List sectionMembersFiltered = new ArrayList<>(); + if (!members.isEmpty()) { + for (EnrollmentRecord enr : members) { + String studentId = enr.getUser().getUserUid(); + if (studentIds.contains(studentId)) { + sectionMembersFiltered.add(studentId); + } + } + } + sectionIdStudentIdsMap.put(sectionId, sectionMembersFiltered); + } + } + return sectionIdStudentIdsMap; + } + + @Override + public List getPermissionsForUser(String gradebookUid, String userId) { + + Long gradebookId = getGradebook(gradebookUid).getId(); + + return getPermissionsForUser(gradebookId, userId).stream() + .map(this::toPermissionDefinition).collect(Collectors.toList()); + } + + @Override + public void updatePermissionsForUser(String gradebookUid, String userId, List permissionDefinitions) { + + Long gradebookId = getGradebook(gradebookUid).getId(); + + if (permissionDefinitions.isEmpty()) { + PermissionDefinition noPermDef = new PermissionDefinition(); + noPermDef.setFunctionName(GraderPermission.NONE.toString()); + noPermDef.setUserId(userId); + permissionDefinitions.add(noPermDef); + } + + //get the current list of permissions + final List currentPermissions = getPermissionsForUser(gradebookId, userId); + + //convert PermissionDefinition to Permission + final List newPermissions = new ArrayList<>(); + for (PermissionDefinition def: permissionDefinitions) { + if (!StringUtils.equalsIgnoreCase(def.getFunctionName(), GraderPermission.GRADE.toString()) + && !StringUtils.equalsIgnoreCase(def.getFunctionName(), GraderPermission.VIEW.toString()) + && !StringUtils.equalsIgnoreCase(def.getFunctionName(), GraderPermission.VIEW_COURSE_GRADE.toString()) + && !StringUtils.equalsIgnoreCase(def.getFunctionName(), GraderPermission.NONE.toString())) { + throw new IllegalArgumentException("Invalid function for permission definition: " + def.getFunctionName()); + } + + Permission permission = new Permission(); + permission.setCategoryId(def.getCategoryId()); + permission.setGradebookId(gradebookId); + permission.setGroupId(def.getGroupReference()); + permission.setFunctionName(def.getFunctionName()); + permission.setUserId(userId); + + newPermissions.add(permission); + } + + //Note: rather than iterate both lists and figure out the differences and add/update/delete as applicable, + //it is far simpler to just remove the existing permissions and add new ones in one transaction + currentPermissions.forEach(gradingPersistenceManager::deletePermission); + newPermissions.forEach(gradingPersistenceManager::savePermission); + } + + public void clearPermissionsForUser(String gradebookUid, String userId) { + + Long gradebookId = getGradebook(gradebookUid).getId(); + getPermissionsForUser(gradebookId, userId).forEach(gradingPersistenceManager::deletePermission); + } + + public boolean currentUserHasGraderPermissions(String gradebookUid) { + + List permissions = getGraderPermissionsForCurrentUser(gradebookUid); + return permissions != null && !permissions.isEmpty(); + } + + public boolean userHasGraderPermissions(String gradebookUid, String userId) { + + List permissions = getGraderPermissionsForUser(gradebookUid, userId); + return permissions != null && !permissions.isEmpty(); + } + + /** + * Maps a Permission to a PermissionDefinition + * Note that the persistent groupId is actually the group reference + * @param permission + * @return a {@link PermissionDefinition} + */ + private PermissionDefinition toPermissionDefinition(Permission permission) { + + PermissionDefinition rval = new PermissionDefinition(); + if (permission != null) { + rval.setId(permission.getId()); + rval.setUserId(permission.getUserId()); + rval.setCategoryId(permission.getCategoryId()); + rval.setFunctionName(permission.getFunctionName()); + rval.setGroupReference(permission.getGroupId()); + } + return rval; + } + + private List getPermissionsForUser(Long gradebookId, String userId) throws IllegalArgumentException { + + if (gradebookId == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in getPermissionsForUser"); + } + + return gradingPersistenceManager.getPermissionsForGradebookAndUser(gradebookId, userId); + } + + private List getPermissionsForUserAnyCategory(Long gradebookId, String userId) throws IllegalArgumentException { + + if (gradebookId == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) getPermissionsForUserAnyCategory"); + } + + return gradingPersistenceManager.getUncategorisedPermissionsForGradebookAndUserAndFunctions(gradebookId, userId, GraderPermission.getStandardPermissions()); + } + + private List getPermissionsForUserForCategory(Long gradebookId, String userId, List categoryIds) throws IllegalArgumentException { + + if (gradebookId == null || userId == null || categoryIds == null || categoryIds.isEmpty()) { + throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserForCategory"); + } + + return gradingPersistenceManager.getPermissionsForGradebookAndUserAndCategories(gradebookId, userId, categoryIds); + } + + private List getPermissionsForUserAnyGroup(Long gradebookId, String userId) throws IllegalArgumentException { + + if (gradebookId == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in getPermissionsForUserAnyGroup"); + } + + return gradingPersistenceManager.getUngroupedPermissionsForGradebookAndUserAndFunctions(gradebookId, userId, GraderPermission.getStandardPermissions()); + } + + private List getPermissionsForUserAnyGroupForCategory(Long gradebookId, String userId, List categoryIds) throws IllegalArgumentException { + + if (gradebookId == null || userId == null || categoryIds == null || categoryIds.isEmpty()) { + throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserAnyGroupForCategory"); + } + + return gradingPersistenceManager.getUngroupedPermissionsForGradebookAndUserAndCategories(gradebookId, userId, categoryIds); + } + + private List getPermissionsForUserAnyGroupAnyCategory(Long gradebookId, String userId) throws IllegalArgumentException { + + if (gradebookId == null || userId == null) { + throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserAnyGroupForCategory"); + } + + return gradingPersistenceManager.getPermissionsForGradebookAnyGroupAnyCategory(gradebookId, userId); + } + + private List getPermissionsForUserForGoupsAnyCategory(Long gradebookId, String userId, List groupIds) throws IllegalArgumentException { + + if (gradebookId == null || userId == null || groupIds == null || groupIds.isEmpty()) { + throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserForGoupsAnyCategory"); + } + + return gradingPersistenceManager.getUncategorisedPermissionsForGradebookAndGroups(gradebookId, userId, groupIds); + } + + private List getPermissionsForUserForGroup(Long gradebookId, String userId, List groupIds) throws IllegalArgumentException { + + if (gradebookId == null || userId == null || groupIds == null || groupIds.isEmpty()) { + throw new IllegalArgumentException("Null parameter(s) in BaseHibernateManager.getPermissionsForUserForGroup"); + } + + return gradingPersistenceManager.getPermissionsForGradebookAndGroups(gradebookId, userId, groupIds); + } + + private List getCategoriesWithAssignments(Long gradebookId) { + + List categoriesWithAssignments = new ArrayList<>(); + for (Category category : gradingPersistenceManager.getCategoriesForGradebook(gradebookId)) { + if (category != null) { + List assignments = gradingPersistenceManager.getAssignmentsForCategory(category.getId()); + category.setAssignmentList(assignments); + categoriesWithAssignments.add(category); + } + } + + return categoriesWithAssignments; + } + + private Gradebook getGradebook(String gradebookUid) { + + return gradingPersistenceManager.getGradebook(gradebookUid).orElse(null); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingPersistenceManagerImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingPersistenceManagerImpl.java new file mode 100644 index 000000000000..9380ab7f59cf --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingPersistenceManagerImpl.java @@ -0,0 +1,383 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.impl; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.sakaiproject.grading.api.GradingPersistenceManager; +import org.sakaiproject.grading.api.model.AssignmentGradeRecord; +import org.sakaiproject.grading.api.model.Category; +import org.sakaiproject.grading.api.model.Comment; +import org.sakaiproject.grading.api.model.CourseGrade; +import org.sakaiproject.grading.api.model.CourseGradeRecord; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradebookAssignment; +import org.sakaiproject.grading.api.model.GradebookProperty; +import org.sakaiproject.grading.api.model.GradeMapping; +import org.sakaiproject.grading.api.model.GradingEvent; +import org.sakaiproject.grading.api.model.GradingScale; +import org.sakaiproject.grading.api.model.LetterGradePercentMapping; +import org.sakaiproject.grading.api.model.Permission; + +import org.sakaiproject.grading.api.repository.AssignmentGradeRecordRepository; +import org.sakaiproject.grading.api.repository.CategoryRepository; +import org.sakaiproject.grading.api.repository.CommentRepository; +import org.sakaiproject.grading.api.repository.CourseGradeRepository; +import org.sakaiproject.grading.api.repository.CourseGradeRecordRepository; +import org.sakaiproject.grading.api.repository.GradebookAssignmentRepository; +import org.sakaiproject.grading.api.repository.GradebookRepository; +import org.sakaiproject.grading.api.repository.GradebookPropertyRepository; +import org.sakaiproject.grading.api.repository.GradeMappingRepository; +import org.sakaiproject.grading.api.repository.GradingEventRepository; +import org.sakaiproject.grading.api.repository.GradingScaleRepository; +import org.sakaiproject.grading.api.repository.LetterGradePercentMappingRepository; +import org.sakaiproject.grading.api.repository.PermissionRepository; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +public class GradingPersistenceManagerImpl implements GradingPersistenceManager { + + @Autowired protected AssignmentGradeRecordRepository assignmentGradeRecordRepository; + @Autowired protected CategoryRepository categoryRepository; + @Autowired protected CommentRepository commentRepository; + @Autowired protected CourseGradeRepository courseGradeRepository; + @Autowired protected CourseGradeRecordRepository courseGradeRecordRepository; + @Autowired protected GradebookAssignmentRepository gradebookAssignmentRepository; + @Autowired protected GradebookRepository gradebookRepository; + @Autowired protected GradebookPropertyRepository gradebookPropertyRepository; + @Autowired protected GradeMappingRepository gradeMappingRepository; + @Autowired protected GradingEventRepository gradingEventRepository; + @Autowired protected GradingScaleRepository gradingScaleRepository; + @Autowired protected LetterGradePercentMappingRepository letterGradePercentMappingRepository; + @Autowired protected PermissionRepository permissionRepository; + + public Gradebook saveGradebook(Gradebook gradebook) { + return gradebookRepository.save(gradebook); + } + + @Transactional + public void deleteGradebook(String gradebookUid) { + + Gradebook gradebook = gradebookRepository.findByUid(gradebookUid) + .orElseThrow(() -> new IllegalArgumentException("No gradebook with uid " + gradebookUid)); + Long gradebookId = gradebook.getId(); + + gradingEventRepository.deleteAll(gradingEventRepository.findByGradableObject_Gradebook_Uid(gradebookUid)); + + commentRepository.deleteAll(commentRepository.findByGradableObject_Gradebook_Uid(gradebookUid)); + + assignmentGradeRecordRepository.deleteAll(assignmentGradeRecordRepository.findByGradableObject_Gradebook_Uid(gradebookUid)); + + courseGradeRecordRepository.deleteAll(courseGradeRecordRepository.findByGradableObject_Gradebook_Uid(gradebookUid)); + + gradebookAssignmentRepository.deleteAll(gradebookAssignmentRepository.findByGradebook_Uid(gradebookUid)); + + courseGradeRepository.deleteAll(courseGradeRepository.findByGradebook_Uid(gradebookUid)); + + categoryRepository.deleteAll(categoryRepository.findByGradebook_Uid(gradebookUid)); + + gradeMappingRepository.deleteAll(gradeMappingRepository.findByGradebook_Uid(gradebookUid)); + + gradebookRepository.delete(gradebook); + } + + public Optional getGradebook(String gradebookUid) { + return gradebookRepository.findByUid(gradebookUid); + } + + public Optional getGradebook(Long gradebookId) { + return gradebookRepository.findById(gradebookId); + } + + public CourseGrade saveCourseGrade(CourseGrade courseGrade) { + return courseGradeRepository.save(courseGrade); + } + + public List getCourseGradesByGradebookId(Long gradebookId) { + return courseGradeRepository.findByGradebook_Id(gradebookId); + } + + public List getAvailableGradingScales() { + return gradingScaleRepository.findByUnavailable(false); + } + + public GradingScale saveGradingScale(GradingScale gradingScale) { + return gradingScaleRepository.save(gradingScale); + } + + public LetterGradePercentMapping saveLetterGradePercentMapping(LetterGradePercentMapping lgpm) { + return letterGradePercentMappingRepository.save(lgpm); + } + + public List getDefaultLetterGradePercentMappings() { + return letterGradePercentMappingRepository.findByMappingType(1); + } + + public Optional getLetterGradePercentMappingForGradebook(Long gradebookId) { + return letterGradePercentMappingRepository.findByGradebookIdAndMappingType(gradebookId, 2); + } + + public Optional getGradeMapping(Long id) { + return gradeMappingRepository.findById(id); + } + + public GradeMapping saveGradeMapping(GradeMapping gradeMapping) { + return gradeMappingRepository.save(gradeMapping); + } + + public List getOtherAvailableGradingScales(Set notTheseUids) { + return gradingScaleRepository.findByUnavailableAndUidNotIn(false, notTheseUids); + } + + public List getGradingScalesByUids(Set theseUids) { + return gradingScaleRepository.findByUidIn(theseUids); + } + + public GradebookAssignment saveAssignment(GradebookAssignment assignment) { + return gradebookAssignmentRepository.save(assignment); + } + + public Optional getAssignmentByNameAndGradebook(String name, String gradebookUid) { + return gradebookAssignmentRepository.findByNameAndGradebook_UidAndRemoved(name, gradebookUid, false); + } + + public List getAssignmentsForGradebook(Long gradebookId) { + return gradebookAssignmentRepository.findByGradebook_IdAndRemoved(gradebookId, false); + } + + public List getAssignmentsForCategory(Long categoryId) { + return gradebookAssignmentRepository.findByCategory_IdAndRemoved(categoryId, false); + } + + public List getCountedAssignmentsForGradebook(Long gradebookId) { + return gradebookAssignmentRepository.findByGradebook_IdAndRemovedAndNotCounted(gradebookId, false, false); + } + + public List getCountedAndGradedAssignmentsForGradebook(Long gradebookId) { + return gradebookAssignmentRepository.findByGradebook_IdAndRemovedAndNotCountedAndUngraded(gradebookId, false, false, false); + } + + public Optional getAssignmentByIdAndGradebook(Long id, String gradebookUid) { + return gradebookAssignmentRepository.findByIdAndGradebook_UidAndRemoved(id, gradebookUid, false); + } + + public Optional getAssignmentById(Long id) { + return gradebookAssignmentRepository.findById(id); + } + + public Long countAssignmentsByGradbookAndExternalId(String gradebookUid, String externalId) { + return gradebookAssignmentRepository.countByGradebook_UidAndExternalId(gradebookUid, externalId); + } + + public Long countAssignmentsByNameAndGradebookUid(String name, String gradebookUid) { + return gradebookAssignmentRepository.countByNameAndGradebook_UidAndRemoved(name, gradebookUid, false); + } + + public Long countDuplicateAssignments(GradebookAssignment assignment) { + + return gradebookAssignmentRepository.countByNameAndGradebookAndNotIdAndRemoved( + assignment.getName(), assignment.getGradebook(), assignment.getId(), false); + } + + public void deleteAssignment(GradebookAssignment assignment) { + gradebookAssignmentRepository.delete(assignment); + } + + public Optional getExternalAssignment(String gradebookUid, String externalId) { + return gradebookAssignmentRepository.findByGradebook_UidAndExternalId(gradebookUid, externalId); + } + + public GradebookAssignment saveGradebookAssignment(GradebookAssignment assignment) { + return gradebookAssignmentRepository.save(assignment); + } + + public Optional getInternalComment(String studentUid, String gradebookUid, Long assignmentId) { + + return commentRepository.findByStudentIdAndGradableObject_Gradebook_UidAndGradableObject_IdAndGradableObject_Removed( + studentUid, gradebookUid, assignmentId, false); + } + + public Comment saveComment(Comment comment) { + return commentRepository.save(comment); + } + + public List getCommentsForStudents(GradebookAssignment assignment, Collection studentIds) { + return commentRepository.findByGradableObjectAndStudentIdIn(assignment, studentIds); + } + + public int deleteCommentsForAssignment(GradebookAssignment assignment) { + return commentRepository.deleteByGradableObject(assignment); + } + + public Optional getCategory(Long categoryId) { + return categoryRepository.findById(categoryId); + } + + public List getCategoriesForGradebook(Long gradebookId) { + return categoryRepository.findByGradebook_IdAndRemoved(gradebookId, false); + } + + public boolean isCategoryDefined(String name, Gradebook gradebook) { + return categoryRepository.existsByNameAndGradebookAndRemoved(name, gradebook, false); + } + + public boolean existsDuplicateCategory(String name, Gradebook gradebook, Long id) { + return categoryRepository.existsByNameAndGradebookAndNotIdAndRemoved(name, gradebook, id, false); + } + + public Category saveCategory(Category category) { + return categoryRepository.save(category); + } + + public boolean isAssignmentDefined(Long id) { + return gradebookAssignmentRepository.existsByIdAndRemoved(id, false); + } + + public List getPermissionsForGradebookAndUser(Long gradebookId, String userId) { + return permissionRepository.findByGradebookIdAndUserId(gradebookId, userId); + } + + public List getPermissionsForGradebook(Long gradebookId) { + return permissionRepository.findByGradebookId(gradebookId); + } + + public List getPermissionsForGradebookAndUserAndCategories(Long gradebookId, String userId, List categoryIds) { + return permissionRepository.findByGradebookIdAndUserIdAndCategoryIdIn(gradebookId, userId, categoryIds); + } + + public List getUncategorisedPermissionsForGradebookAndUserAndFunctions(Long gradebookId, String userId, List functions) { + return permissionRepository.findByGradebookIdAndUserIdAndCategoryIdIsNullAndFunctionNameIn(gradebookId, userId, functions); + } + + public List getUngroupedPermissionsForGradebookAndUserAndFunctions(Long gradebookId, String userId, List functions) { + return permissionRepository.findByGradebookIdAndUserIdAndGroupIdIsNullAndFunctionNameIn(gradebookId, userId, functions); + } + + public List getUngroupedPermissionsForGradebookAndUserAndCategories(Long gradebookId, String userId, List categoryIds) { + return permissionRepository.findByGradebookIdAndUserIdAndGroupIdIsNullAndCategoryIdIn(gradebookId, userId, categoryIds); + } + + public List getPermissionsForGradebookAndCategories(Long gradebookId, List categoryIds) { + return permissionRepository.findByGradebookIdAndCategoryIdIn(gradebookId, categoryIds); + } + + public List getPermissionsForGradebookAnyGroupAnyCategory(Long gradebookId, String userId) { + return permissionRepository.findByGradebookIdAndUserIdAndCategoryIdIsNullAndGroupIdIsNull(gradebookId, userId); + } + + public List getUncategorisedPermissionsForGradebookAndGroups(Long gradebookId, String userId, List groupIds) { + return permissionRepository.findByGradebookIdAndUserIdAndCategoryIdIsNullAndGroupIdIn(gradebookId, userId, groupIds); + } + + public List getPermissionsForGradebookAndGroups(Long gradebookId, String userId, List groupIds) { + return permissionRepository.findByGradebookIdAndUserIdAndGroupIdIn(gradebookId, userId, groupIds); + } + + public Permission savePermission(Permission permission) { + return permissionRepository.save(permission); + } + + public void deletePermission(Permission permission) { + permissionRepository.delete(permission); + } + + public CourseGradeRecord saveCourseGradeRecord(CourseGradeRecord record) { + return courseGradeRecordRepository.save(record); + } + + public List getCourseGradeRecordsForCourseGrade(Long courseGradeId) { + return courseGradeRecordRepository.findByGradableObject_Id(courseGradeId); + } + + public List getCourseGradeOverrides(Gradebook gradebook) { + return courseGradeRecordRepository.findByGradableObject_GradebookAndEnteredGradeNotNull(gradebook); + } + + public Optional getCourseGradeRecord(Gradebook gradebook, String studentId) { + return courseGradeRecordRepository.findByGradableObject_GradebookAndStudentId(gradebook, studentId); + } + + public boolean hasCourseGradeRecordEntries(Long gradebookId, Set studentIds) { + return courseGradeRecordRepository.countByGradableObject_Gradebook_IdAndEnteredGradeNotNullAndStudentIdIn(gradebookId, studentIds) > 0L; + } + + public List getAllAssignmentGradeRecordsForGradebook(Long gradebookId) { + + return assignmentGradeRecordRepository + .findByGradableObject_Gradebook_IdAndGradableObject_RemovedOrderByPointsEarned(gradebookId, false); + } + + public List getAllAssignmentGradeRecordsForAssignment(Long assignmentId) { + + return assignmentGradeRecordRepository + .findByGradableObject_IdAndGradableObject_RemovedOrderByPointsEarned(assignmentId, false); + } + + public AssignmentGradeRecord getAssignmentGradeRecordForAssignmentAndStudent(Long assignmentId, String studentUid) { + return assignmentGradeRecordRepository.findByGradableObject_IdAndStudentId(assignmentId, studentUid).orElse(null); + } + + public List getAssignmentGradeRecordsForGradebookAndStudents(Long gradebookId, Collection studentIds) { + return assignmentGradeRecordRepository.findByGradableObject_Gradebook_IdAndGradableObject_RemovedAndStudentIdIn(gradebookId, false, studentIds); + } + + public List getAssignmentGradeRecordsForAssignmentAndStudents(GradebookAssignment assignment, Collection studentIds) { + return assignmentGradeRecordRepository.findByGradableObjectAndStudentIdIn(assignment, studentIds); + } + + public AssignmentGradeRecord saveAssignmentGradeRecord(AssignmentGradeRecord record) { + return assignmentGradeRecordRepository.save(record); + } + + public List getAssignmentGradeRecordsForAssignmentIdsAndStudentIds( + List gradableObjectIds, List studentIds) { + + return assignmentGradeRecordRepository.findByGradableObject_RemovedAndGradableObject_IdInAndStudentIdIn(false, gradableObjectIds, studentIds); + } + + public int deleteGradeRecordsForAssignment(GradebookAssignment assignment) { + return assignmentGradeRecordRepository.deleteByGradableObject(assignment); + } + + public GradingEvent saveGradingEvent(GradingEvent ge) { + return gradingEventRepository.save(ge); + } + + public List getGradingEventsForAssignment(Long assignmentId, String studentId) { + return gradingEventRepository.findByGradableObject_IdAndStudentIdOrderByDateGraded(assignmentId, studentId); + } + + public List getGradingEventsForAssignmentsSince(List assignmentIds, Date since) { + return gradingEventRepository.findByDateGreaterThanEqualAndGradableObject_IdIn(since, assignmentIds); + } + + public int deleteGradingEventsForAssignment(GradebookAssignment assignment) { + return gradingEventRepository.deleteByGradableObject(assignment); + } + + public Optional getGradebookProperty(String name) { + return gradebookPropertyRepository.findByName(name); + } + + public GradebookProperty saveGradebookProperty(GradebookProperty property) { + return gradebookPropertyRepository.save(property); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingServiceImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingServiceImpl.java new file mode 100644 index 000000000000..e07104780672 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/GradingServiceImpl.java @@ -0,0 +1,5444 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sakaiproject.grading.impl; + +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; + +import org.hibernate.StaleObjectStateException; + +import org.sakaiproject.authz.api.SecurityService; +import org.sakaiproject.component.api.ServerConfigurationService; +import org.sakaiproject.event.api.EventTrackingService; +import org.sakaiproject.grading.api.AssessmentNotFoundException; +import org.sakaiproject.grading.api.Assignment; +import org.sakaiproject.grading.api.AssignmentHasIllegalPointsException; +import org.sakaiproject.grading.api.CategoryDefinition; +import org.sakaiproject.grading.api.CategoryScoreData; +import org.sakaiproject.grading.api.CommentDefinition; +import org.sakaiproject.grading.api.ConflictingAssignmentNameException; +import org.sakaiproject.grading.api.ConflictingCategoryNameException; +import org.sakaiproject.grading.api.ConflictingExternalIdException; +import org.sakaiproject.grading.api.CourseGradeTransferBean; +import org.sakaiproject.grading.api.ExternalAssignmentProvider; +import org.sakaiproject.grading.api.ExternalAssignmentProviderCompat; +import org.sakaiproject.grading.api.GradeDefinition; +import org.sakaiproject.grading.api.GradeMappingDefinition; +import org.sakaiproject.grading.api.GradebookHelper; +import org.sakaiproject.grading.api.GradebookInformation; +import org.sakaiproject.grading.api.GradeType; +import org.sakaiproject.grading.api.GradingAuthz; +import org.sakaiproject.grading.api.GradingCategoryType; +import org.sakaiproject.grading.api.GradingConstants; +import org.sakaiproject.grading.api.GradingPermissionService; +import org.sakaiproject.grading.api.GradingPersistenceManager; +import org.sakaiproject.grading.api.GradingScaleDefinition; +import org.sakaiproject.grading.api.GradingSecurityException; +import org.sakaiproject.grading.api.GradingService; +import org.sakaiproject.grading.api.GradingEventStatus; +import org.sakaiproject.grading.api.InvalidCategoryException; +import org.sakaiproject.grading.api.InvalidGradeException; +import org.sakaiproject.grading.api.SortType; +import org.sakaiproject.grading.api.StaleObjectModificationException; +import org.sakaiproject.grading.api.UnmappableCourseGradeOverrideException; +import org.sakaiproject.grading.api.model.AbstractGradeRecord; +import org.sakaiproject.grading.api.model.AssignmentGradeRecord; +import org.sakaiproject.grading.api.model.Category; +import org.sakaiproject.grading.api.model.Comment; +import org.sakaiproject.grading.api.model.CourseGrade; +import org.sakaiproject.grading.api.model.CourseGradeRecord; +import org.sakaiproject.grading.api.model.GradableObject; +import org.sakaiproject.grading.api.model.GradeMapping; +import org.sakaiproject.grading.api.model.GradePointsMapping; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradebookAssignment; +import org.sakaiproject.grading.api.model.GradebookProperty; +import org.sakaiproject.grading.api.model.GradingEvent; +import org.sakaiproject.grading.api.model.GradingScale; +import org.sakaiproject.grading.api.model.LetterGradeMapping; +import org.sakaiproject.grading.api.model.LetterGradePercentMapping; +import org.sakaiproject.grading.api.model.LetterGradePlusMinusMapping; +import org.sakaiproject.grading.api.model.PassNotPassMapping; +import org.sakaiproject.hibernate.HibernateCriterionUtils; +import org.sakaiproject.section.api.SectionAwareness; +import org.sakaiproject.section.api.coursemanagement.CourseSection; +import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; +import org.sakaiproject.section.api.coursemanagement.User; +import org.sakaiproject.section.api.facade.Role; +import org.sakaiproject.site.api.Group; +import org.sakaiproject.site.api.Site; +import org.sakaiproject.site.api.SiteService; +import org.sakaiproject.tool.api.SessionManager; +import org.sakaiproject.tool.api.ToolManager; +import org.sakaiproject.grading.api.GradingAuthz; +import org.sakaiproject.util.ResourceLoader; + +import org.springframework.orm.hibernate5.HibernateOptimisticLockingFailureException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +/** + * A Hibernate implementation of GradingService. + */ +@Slf4j +@Getter +@Setter +public class GradingServiceImpl implements GradingService { + + public static final String UID_OF_DEFAULT_GRADING_SCALE_PROPERTY = "uidOfDefaultGradingScale"; + + public static final String PROP_COURSE_POINTS_DISPLAYED = "gradebook.coursepoints.displayed"; + public static final String PROP_COURSE_GRADE_DISPLAYED = "gradebook.coursegrade.displayed"; + public static final String PROP_ASSIGNMENTS_DISPLAYED = "gradebook.assignments.displayed"; + public static final String PROP_ASSIGNMENT_STATS_DISPLAYED = "gradebook.stats.assignments.displayed"; + public static final String PROP_COURSE_GRADE_STATS_DISPLAYED = "gradebook.stats.coursegrade.displayed"; + + @Autowired private EventTrackingService eventTrackingService; + @Autowired private GradingAuthz gradingAuthz; + @Autowired private GradingPermissionService gradingPermissionService; + @Autowired private GradingPersistenceManager gradingPersistenceManager; + @Autowired private ResourceLoader resourceLoader; + @Autowired private SiteService siteService; + @Autowired private SectionAwareness sectionAwareness; + @Autowired private SecurityService securityService; + @Autowired private SessionManager sessionManager; + @Autowired private ServerConfigurationService serverConfigurationService; + @Autowired private ToolManager toolManager; + + // Local cache of static-between-deployment properties. + private Map propertiesMap = new HashMap<>(); + + @Override + public boolean isAssignmentDefined(String gradebookUid, String assignmentName) { + + if (!isUserAbleToViewAssignments(gradebookUid)) { + log.warn("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to check for assignment {}", getUserUid(), gradebookUid, + assignmentName); + throw new GradingSecurityException(); + } + return getAssignmentWithoutStats(gradebookUid, assignmentName) != null; + } + + private boolean isUserAbleToViewAssignments(String gradebookUid) { + + return (gradingAuthz.isUserAbleToEditAssessments(gradebookUid) || gradingAuthz.isUserAbleToGrade(gradebookUid)); + } + + @Override + public boolean isUserAbleToGradeItemForStudent(String gradebookUid, Long itemId, String studentUid) { + + return gradingAuthz.isUserAbleToGradeItemForStudent(gradebookUid, itemId, studentUid); + } + + @Override + public boolean isUserAbleToViewItemForStudent(String gradebookUid, Long itemId, String studentUid) { + + return gradingAuthz.isUserAbleToViewItemForStudent(gradebookUid, itemId, studentUid); + } + + @Override + public String getGradeViewFunctionForUserForStudentForItem(String gradebookUid, Long itemId, String studentUid) { + + return gradingAuthz.getGradeViewFunctionForUserForStudentForItem(gradebookUid, itemId, studentUid); + } + + @Override + @Transactional(readOnly = true) + public List getAssignments(String gradebookUid) { + + return getAssignments(gradebookUid, SortType.SORT_BY_NONE); + } + + @Override + @Transactional(readOnly = true) + public List getAssignments(String gradebookUid, SortType sortBy) { + + if (!isUserAbleToViewAssignments(gradebookUid)) { + log.warn("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to get assignments list", getUserUid(), gradebookUid); + throw new GradingSecurityException(); + } + + Gradebook gradebook = getGradebook(gradebookUid); + // Determine whether this gradebook uses Categories Only or Weighted Categories by checking category type. + // We will avoid adding any legacy category information on the individual gb items if the instructor is no + // longer using categories in the gradebook. + final boolean gbUsesCategories = gradebook.getCategoryType() != GradingCategoryType.NO_CATEGORY; + + List internalAssignments = getAssignments(gradebook.getId()); + + return sortAssignments(internalAssignments, sortBy, true) + .stream().map(ga -> getAssignmentDefinition(ga, gbUsesCategories)).collect(Collectors.toList()); + } + + @Override + public Assignment getAssignment(String gradebookUid, Long assignmentId) throws AssessmentNotFoundException { + + if (assignmentId == null || gradebookUid == null) { + throw new IllegalArgumentException("null parameter passed to getAssignment. Values are assignmentId:" + + assignmentId + " gradebookUid:" + gradebookUid); + } + if (!isUserAbleToViewAssignments(gradebookUid) && !currentUserHasViewOwnGradesPerm(gradebookUid)) { + log.warn("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to get assignment with id {}", getUserUid(), gradebookUid, + assignmentId); + throw new GradingSecurityException(); + } + + GradebookAssignment assignment = getAssignmentWithoutStatsByID(gradebookUid, assignmentId); + + if (assignment == null) { + throw new AssessmentNotFoundException("No gradebook item exists with gradable object id = " + assignmentId); + } + + return getAssignmentDefinition(assignment); + } + + @Override + @Deprecated + public Assignment getAssignment(String gradebookUid, String assignmentName) throws AssessmentNotFoundException { + + if (assignmentName == null || gradebookUid == null) { + throw new IllegalArgumentException("null parameter passed to getAssignment. Values are assignmentName:" + + assignmentName + " gradebookUid:" + gradebookUid); + } + if (!isUserAbleToViewAssignments(gradebookUid) && !currentUserHasViewOwnGradesPerm(gradebookUid)) { + log.warn("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to get assignment {}", getUserUid(), gradebookUid, + assignmentName); + throw new GradingSecurityException(); + } + + GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentName); + + return assignment != null ? getAssignmentDefinition(assignment) : null; + } + + public Assignment getExternalAssignment(String gradebookUid, String externalId) { + + return getDbExternalAssignment(gradebookUid, externalId).map(this::getAssignmentDefinition) + .orElseThrow(() -> new IllegalArgumentException("Invalid gradebookUid or externalId")); + //return getAssignmentDefinition(getDbExternalAssignment(gradebookUid, externalId).get()); + } + + @Override + public Assignment getAssignmentByNameOrId(String gradebookUid, String assignmentName) throws AssessmentNotFoundException { + + Assignment assignment = null; + try { + assignment = getAssignment(gradebookUid, assignmentName); + } catch (AssessmentNotFoundException e) { + // Don't fail on this exception + log.debug("Assessment not found by name", e); + } + + if (assignment == null) { + // Try to get the assignment by id + if (NumberUtils.isCreatable(assignmentName)) { + final Long assignmentId = NumberUtils.toLong(assignmentName, -1L); + return getAssignment(gradebookUid, assignmentId); + } + } + return assignment; + } + + private Assignment getAssignmentDefinition(GradebookAssignment internalAssignment) { + + return getAssignmentDefinition(internalAssignment, true); + } + + private Assignment getAssignmentDefinition(GradebookAssignment internalAssignment, boolean gbUsesCategories) { + + Assignment assignmentDefinition = new Assignment(); + assignmentDefinition.setName(internalAssignment.getName()); + assignmentDefinition.setPoints(internalAssignment.getPointsPossible()); + assignmentDefinition.setDueDate(internalAssignment.getDueDate()); + assignmentDefinition.setCounted(internalAssignment.getCounted()); + assignmentDefinition.setExternallyMaintained(internalAssignment.getExternallyMaintained()); + assignmentDefinition.setExternalAppName(internalAssignment.getExternalAppName()); + assignmentDefinition.setExternalId(internalAssignment.getExternalId()); + assignmentDefinition.setExternalData(internalAssignment.getExternalData()); + assignmentDefinition.setReleased(internalAssignment.getReleased()); + assignmentDefinition.setId(internalAssignment.getId()); + assignmentDefinition.setExtraCredit(internalAssignment.getExtraCredit()); + if (gbUsesCategories && internalAssignment.getCategory() != null) { + assignmentDefinition.setCategoryName(internalAssignment.getCategory().getName()); + assignmentDefinition.setWeight(internalAssignment.getCategory().getWeight()); + assignmentDefinition.setCategoryExtraCredit(internalAssignment.getCategory().getExtraCredit()); + assignmentDefinition.setCategoryEqualWeight(internalAssignment.getCategory().getEqualWeightAssignments()); + assignmentDefinition.setCategoryId(internalAssignment.getCategory().getId()); + assignmentDefinition.setCategoryOrder(internalAssignment.getCategory().getCategoryOrder()); + } + assignmentDefinition.setUngraded(internalAssignment.getUngraded()); + assignmentDefinition.setSortOrder(internalAssignment.getSortOrder()); + assignmentDefinition.setCategorizedSortOrder(internalAssignment.getCategorizedSortOrder()); + + return assignmentDefinition; + } + + public Long createAssignment(Long gradebookId, String name, Double points, Date dueDate, Boolean isNotCounted, + Boolean isReleased, Boolean isExtraCredit, Integer sortOrder) + throws ConflictingAssignmentNameException, StaleObjectModificationException { + + return createNewAssignment(gradebookId, null, name, points, dueDate, isNotCounted, isReleased, isExtraCredit, sortOrder, null); + } + + public Long createAssignmentForCategory(Long gradebookId, Long categoryId, String name, Double points, Date dueDate, Boolean isNotCounted, + Boolean isReleased, Boolean isExtraCredit, Integer categorizedSortOrder) + throws ConflictingAssignmentNameException, StaleObjectModificationException, IllegalArgumentException { + + if (gradebookId == null || categoryId == null) { + throw new IllegalArgumentException("gradebookId or categoryId is null in BaseHibernateManager.createAssignmentForCategory"); + } + + return createNewAssignment(gradebookId, categoryId, name, points, dueDate, isNotCounted, isReleased, isExtraCredit, null, categorizedSortOrder); + } + + private Long createNewAssignment(final Long gradebookId, final Long categoryId, final String name, final Double points, final Date dueDate, final Boolean isNotCounted, + final Boolean isReleased, final Boolean isExtraCredit, final Integer sortOrder, final Integer categorizedSortOrder) + throws ConflictingAssignmentNameException, StaleObjectModificationException { + + GradebookAssignment asn = prepareNewAssignment(name, points, dueDate, isNotCounted, isReleased, isExtraCredit, sortOrder, categorizedSortOrder); + return saveNewAssignment(gradebookId, categoryId, asn); + } + + private GradebookAssignment prepareNewAssignment(final String name, final Double points, final Date dueDate, final Boolean isNotCounted, final Boolean isReleased, + final Boolean isExtraCredit, final Integer sortOrder, final Integer categorizedSortOrder) { + + // name cannot contain these special chars as they are reserved for special columns in import/export + String validatedName = GradebookHelper.validateGradeItemName(name); + + GradebookAssignment asn = new GradebookAssignment(); + asn.setName(validatedName); + asn.setPointsPossible(points); + asn.setDueDate(dueDate); + asn.setUngraded(false); + if (isNotCounted != null) { + asn.setNotCounted(isNotCounted.booleanValue()); + } + if (isExtraCredit != null) { + asn.setExtraCredit(isExtraCredit.booleanValue()); + } + if (isReleased != null) { + asn.setReleased(isReleased.booleanValue()); + } + if (sortOrder != null) { + asn.setSortOrder(sortOrder); + } + if (categorizedSortOrder != null) { + asn.setCategorizedSortOrder(categorizedSortOrder); + } + + return asn; + } + + private Long saveNewAssignment(Long gradebookId, Long categoryId, GradebookAssignment asn) throws ConflictingAssignmentNameException { + + loadAssignmentGradebookAndCategory(asn, gradebookId, categoryId); + + if (assignmentNameExists(asn.getName(), asn.getGradebook())) { + throw new ConflictingAssignmentNameException("You cannot save multiple assignments in a gradebook with the same name"); + } + + return gradingPersistenceManager.saveAssignment(asn).getId(); + } + + + + public void updateGradebook(final Gradebook gradebook) throws StaleObjectModificationException { + + // Get the gradebook and selected mapping from persistence + //final Gradebook gradebookFromPersistence = (Gradebook)session.load(gradebook.getClass(), gradebook.getId()); + final Gradebook gradebookFromPersistence = gradingPersistenceManager.getGradebook(gradebook.getId()).orElse(null); + final GradeMapping mappingFromPersistence = gradebookFromPersistence.getSelectedGradeMapping(); + + // If the mapping has changed, and there are explicitly entered + // course grade records, disallow this update. + if (!mappingFromPersistence.getId().equals(gradebook.getSelectedGradeMapping().getId())) { + if (hasExplicitlyEnteredCourseGradeRecords(gradebook.getId())) { + throw new IllegalStateException("Selected grade mapping can not be changed, since explicit course grades exist."); + } + } + + // Evict the persisted objects from the session and update the gradebook + // so the new grade mapping is used in the sort column update + //session.evict(mappingFromPersistence); + //for (final Object element : gradebookFromPersistence.getGradeMappings()) { + // session.evict(element); + //} + //session.evict(gradebookFromPersistence); + // TODO Adrian - 'this is a bit janky. I don't like this at all. + try { + gradingPersistenceManager.saveGradebook(gradebook); + } catch (final StaleObjectStateException e) { + throw new StaleObjectModificationException(e); + } + } + + public boolean hasExplicitlyEnteredCourseGradeRecords(final Long gradebookId) { + + final Set studentIds = getAllStudentUids(getGradebookUid(gradebookId)); + + if (studentIds.isEmpty()) { + return false; + } + + return gradingPersistenceManager.hasCourseGradeRecordEntries(gradebookId, studentIds); + } + + + @Override + public GradeDefinition getGradeDefinitionForStudentForItem(final String gradebookUid, final Long assignmentId, final String studentUid) { + + if (gradebookUid == null || assignmentId == null || studentUid == null) { + throw new IllegalArgumentException("Null paraemter passed to getGradeDefinitionForStudentForItem"); + } + + // studentId can be a groupId (from Assignments) + final boolean studentRequestingOwnScore = sessionManager.getCurrentSessionUserId().equals(studentUid) + || isCurrentUserFromGroup(gradebookUid, studentUid); + + final GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentId); + + if (assignment == null) { + throw new AssessmentNotFoundException( + "There is no assignment with the assignmentId " + assignmentId + " in gradebook " + gradebookUid); + } + + if (!studentRequestingOwnScore && !isUserAbleToViewItemForStudent(gradebookUid, assignment.getId(), studentUid)) { + log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to retrieve grade for student {} for assignment {}", + getUserUid(), gradebookUid, studentUid, assignmentId); + throw new GradingSecurityException(); + } + + final Gradebook gradebook = assignment.getGradebook(); + + final GradeDefinition gradeDef = new GradeDefinition(); + gradeDef.setStudentUid(studentUid); + gradeDef.setGradeEntryType(gradebook.getGradeType()); + gradeDef.setGradeReleased(assignment.getReleased()); + + // If this is the student, then the global setting needs to be enabled and the assignment needs to have + // been released. Return null score information if not released + if (studentRequestingOwnScore && (!gradebook.getAssignmentsDisplayed() || !assignment.getReleased())) { + gradeDef.setDateRecorded(null); + gradeDef.setGrade(null); + gradeDef.setGraderUid(null); + gradeDef.setGradeComment(null); + log.debug("Student {} in gradebook {} retrieving score for unreleased assignment {}", getUserUid(), gradebookUid, + assignment.getName()); + } else { + + final AssignmentGradeRecord gradeRecord = getAssignmentGradeRecord(assignment, studentUid); + final CommentDefinition gradeComment = getAssignmentScoreComment(gradebookUid, assignmentId, studentUid); + final String commentText = gradeComment != null ? gradeComment.getCommentText() : null; + log.debug("gradeRecord={}", gradeRecord); + + if (gradeRecord == null) { + gradeDef.setDateRecorded(null); + gradeDef.setGrade(null); + gradeDef.setGraderUid(null); + gradeDef.setGradeComment(commentText); + gradeDef.setExcused(false); + } else { + gradeDef.setDateRecorded(gradeRecord.getDateRecorded()); + gradeDef.setGraderUid(gradeRecord.getGraderId()); + gradeDef.setGradeComment(commentText); + + gradeDef.setExcused(gradeRecord.getExcludedFromGrade()); + + if (gradebook.getGradeType() == GradeType.LETTER) { + final List gradeList = new ArrayList<>(); + gradeList.add(gradeRecord); + convertPointsToLetterGrade(gradebook, gradeList); + final AssignmentGradeRecord gradeRec = gradeList.get(0); + if (gradeRec != null) { + gradeDef.setGrade(gradeRec.getLetterEarned()); + } + } else if (gradebook.getGradeType() == GradeType.PERCENTAGE) { + final Double percent = calculateEquivalentPercent(assignment.getPointsPossible(), + gradeRecord.getPointsEarned()); + if (percent != null) { + gradeDef.setGrade(percent.toString()); + } + } else { + if (gradeRecord.getPointsEarned() != null) { + gradeDef.setGrade(gradeRecord.getPointsEarned().toString()); + } + } + } + } + + log.debug("returning grade def for {}", studentUid); + return gradeDef; + } + + @Override + public GradebookInformation getGradebookInformation(final String gradebookUid) { + + if (gradebookUid == null) { + throw new IllegalArgumentException("null gradebookUid " + gradebookUid); + } + + if (!currentUserHasEditPerm(gradebookUid) && !currentUserHasGradingPerm(gradebookUid)) { + log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to access gb information", getUserUid(), gradebookUid); + throw new GradingSecurityException(); + } + + final Gradebook gradebook = getGradebook(gradebookUid); + if (gradebook == null) { + throw new IllegalArgumentException("Their is no gradbook associated with this Id: " + gradebookUid); + } + + final GradebookInformation rval = new GradebookInformation(); + + // add in all available grademappings for this gradebook + rval.setGradeMappings(getGradebookGradeMappings(gradebook.getGradeMappings())); + + // add in details about the selected one + final GradeMapping selectedGradeMapping = gradebook.getSelectedGradeMapping(); + if (selectedGradeMapping != null) { + + rval.setSelectedGradingScaleUid(selectedGradeMapping.getGradingScale().getUid()); + rval.setSelectedGradeMappingId(Long.toString(selectedGradeMapping.getId())); + + // note that these are not the DEFAULT bottom percents but the configured ones per gradebook + Map gradeMap = selectedGradeMapping.getGradeMap(); + gradeMap = GradeMappingDefinition.sortGradeMapping(gradeMap); + rval.setSelectedGradingScaleBottomPercents(gradeMap); + rval.setGradeScale(selectedGradeMapping.getGradingScale().getName()); + } + + rval.setGradeType(gradebook.getGradeType()); + rval.setCategoryType(gradebook.getCategoryType()); + rval.setDisplayReleasedGradeItemsToStudents(gradebook.getAssignmentsDisplayed()); + + // add in the category definitions + rval.setCategories(getCategoryDefinitions(gradebookUid)); + + // add in the course grade display settings + rval.setCourseGradeDisplayed(gradebook.getCourseGradeDisplayed()); + rval.setCourseLetterGradeDisplayed(gradebook.getCourseLetterGradeDisplayed()); + rval.setCoursePointsDisplayed(gradebook.getCoursePointsDisplayed()); + rval.setCourseAverageDisplayed(gradebook.getCourseAverageDisplayed()); + + // add in stats display settings + rval.setAssignmentStatsDisplayed(gradebook.getAssignmentStatsDisplayed()); + rval.setCourseGradeStatsDisplayed(gradebook.getCourseGradeStatsDisplayed()); + + // add in compare grades with classmates settings + rval.setAllowStudentsToCompareGrades(gradebook.getAllowStudentsToCompareGrades()); + rval.setComparingDisplayStudentNames(gradebook.getComparingDisplayStudentNames()); + rval.setComparingDisplayStudentSurnames(gradebook.getComparingDisplayStudentSurnames()); + rval.setComparingDisplayTeacherComments(gradebook.getComparingDisplayTeacherComments()); + rval.setComparingIncludeAllGrades(gradebook.getComparingIncludeAllGrades()); + rval.setComparingRandomizeDisplayedData(gradebook.getComparingRandomizeDisplayedData()); + + return rval; + } + + @Override + public Map transferGradebook(final GradebookInformation gradebookInformation, + final List assignments, final String toGradebookUid, final String fromContext) { + + final Map transversalMap = new HashMap<>(); + + final Gradebook gradebook = getGradebook(toGradebookUid); + + gradebook.setCategoryType(gradebookInformation.getCategoryType()); + gradebook.setGradeType(gradebookInformation.getGradeType()); + gradebook.setAssignmentStatsDisplayed(gradebookInformation.getAssignmentStatsDisplayed()); + gradebook.setCourseGradeStatsDisplayed(gradebookInformation.getCourseGradeStatsDisplayed()); + gradebook.setAssignmentsDisplayed(gradebookInformation.getDisplayReleasedGradeItemsToStudents()); + gradebook.setCourseGradeDisplayed(gradebookInformation.getCourseGradeDisplayed()); + gradebook.setAllowStudentsToCompareGrades(gradebookInformation.getAllowStudentsToCompareGrades()); + gradebook.setComparingDisplayStudentNames(gradebookInformation.getComparingDisplayStudentNames()); + gradebook.setComparingDisplayStudentSurnames(gradebookInformation.getComparingDisplayStudentSurnames()); + gradebook.setComparingDisplayTeacherComments(gradebookInformation.getComparingDisplayTeacherComments()); + gradebook.setComparingIncludeAllGrades(gradebookInformation.getComparingIncludeAllGrades()); + gradebook.setComparingRandomizeDisplayedData(gradebookInformation.getComparingRandomizeDisplayedData()); + gradebook.setCourseLetterGradeDisplayed(gradebookInformation.getCourseLetterGradeDisplayed()); + gradebook.setCoursePointsDisplayed(gradebookInformation.getCoursePointsDisplayed()); + gradebook.setCourseAverageDisplayed(gradebookInformation.getCourseAverageDisplayed()); + + updateGradebook(gradebook); + + // all categories that we need to end up with + final List categories = gradebookInformation.getCategories(); + + // filter out externally managed assignments. These are never imported. + assignments.removeIf(a -> a.getExternallyMaintained()); + + // this map holds the names of categories that have been created in the site to the category ids + // and is updated as we go along + // likewise for list of assignments + final Map categoriesCreated = new HashMap<>(); + final List assignmentsCreated = new ArrayList<>(); + + if (!categories.isEmpty() && gradebookInformation.getCategoryType() != GradingCategoryType.NO_CATEGORY) { + + // migrate the categories with assignments + categories.forEach(c -> { + + assignments.forEach(a -> { + + if (StringUtils.equals(c.getName(), a.getCategoryName())) { + + if (!categoriesCreated.containsKey(c.getName())) { + + // create category + Long categoryId = null; + try { + categoryId = createCategory(gradebook.getId(), c.getName(), c.getWeight(), c.getDropLowest(), + c.getDropHighest(), c.getKeepHighest(), c.getExtraCredit(), c.getEqualWeight(), c.getCategoryOrder()); + } catch (final ConflictingCategoryNameException e) { + // category already exists. Could be from a merge. + log.info("Category: {} already exists in target site. Skipping creation.", c.getName()); + } + + if (categoryId == null) { + // couldn't create so look up the id in the target site + final List existingCategories = getCategoryDefinitions(gradebook.getUid()); + categoryId = existingCategories.stream().filter(e -> StringUtils.equals(e.getName(), c.getName())) + .findFirst().get().getId(); + } + // record that we have created this category + categoriesCreated.put(c.getName(), categoryId); + + } + + // create the assignment for the current category + try { + Long newId = createAssignmentForCategory(gradebook.getId(), categoriesCreated.get(c.getName()), a.getName(), a.getPoints(), + a.getDueDate(), !a.getCounted(), a.getReleased(), a.getExtraCredit(), a.getCategorizedSortOrder()); + transversalMap.put("gb/"+a.getId(),"gb/"+newId); + } catch (final ConflictingAssignmentNameException e) { + // assignment already exists. Could be from a merge. + log.info("GradebookAssignment: {} already exists in target site. Skipping creation.", a.getName()); + } catch (final Exception ex) { + log.warn("GradebookAssignment: exception {} trying to create {} in target site. Skipping creation.", ex.getMessage(), a.getName()); + } + + // record that we have created this assignment + assignmentsCreated.add(a.getName()); + } + }); + }); + + // create any remaining categories that have no assignments + categories.removeIf(c -> categoriesCreated.containsKey(c.getName())); + categories.forEach(c -> { + try { + createCategory(gradebook.getId(), c.getName(), c.getWeight(), c.getDropLowest(), c.getDropHighest(), c.getKeepHighest(), + c.getExtraCredit(), c.getEqualWeight(), c.getCategoryOrder()); + } catch (final ConflictingCategoryNameException e) { + // category already exists. Could be from a merge. + log.info("Category: {} already exists in target site. Skipping creation.", c.getName()); + } + }); + } + + // create any remaining assignments that have no categories + assignments.removeIf(a -> assignmentsCreated.contains(a.getName())); + assignments.forEach(a -> { + + try { + Long newId = createAssignment(gradebook.getId(), a.getName(), a.getPoints(), a.getDueDate(), !a.getCounted(), a.getReleased(), a.getExtraCredit(), a.getSortOrder()); + transversalMap.put("gb/"+a.getId(),"gb/"+newId); + } catch (final ConflictingAssignmentNameException e) { + // assignment already exists. Could be from a merge. + log.info("GradebookAssignment: {} already exists in target site. Skipping creation.", a.getName()); + } catch (final Exception ex) { + log.warn("GradebookAssignment: exception {} trying to create {} in target site. Skipping creation.", ex.getMessage(), a.getName()); + } + }); + + // Carry over the old gradebook's selected grading scheme if possible. + final String fromGradingScaleUid = gradebookInformation.getSelectedGradingScaleUid(); + + MERGE_GRADE_MAPPING: if (!StringUtils.isEmpty(fromGradingScaleUid)) { + for (final GradeMapping gradeMapping : gradebook.getGradeMappings()) { + if (gradeMapping.getGradingScale().getUid().equals(fromGradingScaleUid)) { + // We have a match. Now make sure that the grades are as expected. + final Map inputGradePercents = gradebookInformation.getSelectedGradingScaleBottomPercents(); + final Set gradeCodes = inputGradePercents.keySet(); + + // If the grades dont map one-to-one, clear out the destination site's existing map + if (!gradeCodes.containsAll(gradeMapping.getGradeMap().keySet())) { + gradeMapping.getGradeMap().clear(); + } + + // Modify the existing grade-to-percentage map. + for (String gradeCode : gradeCodes) { + gradeMapping.getGradeMap().put(gradeCode, inputGradePercents.get(gradeCode)); + } + gradebook.setSelectedGradeMapping(gradeMapping); + updateGradebook(gradebook); + log.info("Merge to gradebook {} updated grade mapping", toGradebookUid); + + break MERGE_GRADE_MAPPING; + } + } + // Did not find a matching grading scale. + log.info("Merge to gradebook {} skipped grade mapping change because grading scale {} is not defined", toGradebookUid, + fromGradingScaleUid); + } + return transversalMap; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void removeAssignment(Long assignmentId) throws StaleObjectModificationException { + + Optional optAsn = gradingPersistenceManager.getAssignmentById(assignmentId); + if (optAsn.isPresent()) { + optAsn.get().setRemoved(true); + Gradebook gradebook = optAsn.get().getGradebook(); + gradingPersistenceManager.saveAssignment(optAsn.get()); + log.info("GradebookAssignment {} has been removed from {}", optAsn.get().getName(), gradebook); + } else { + log.warn("No assignment for id {}", assignmentId); + } + } + + @Override + public Long addAssignment(final String gradebookUid, Assignment assignmentDefinition) { + + if (!gradingAuthz.isUserAbleToEditAssessments(gradebookUid)) { + log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to add an assignment", getUserUid(), gradebookUid); + throw new GradingSecurityException(); + } + + final String validatedName = GradebookHelper.validateAssignmentNameAndPoints(assignmentDefinition); + + final Gradebook gradebook = getGradebook(gradebookUid); + + // if attaching to category + if (assignmentDefinition.getCategoryId() != null) { + return createAssignmentForCategory(gradebook.getId(), assignmentDefinition.getCategoryId(), validatedName, + assignmentDefinition.getPoints(), assignmentDefinition.getDueDate(), !assignmentDefinition.getCounted(), assignmentDefinition.getReleased(), + assignmentDefinition.getExtraCredit(), assignmentDefinition.getCategorizedSortOrder()); + } + + return createAssignment(gradebook.getId(), validatedName, assignmentDefinition.getPoints(), assignmentDefinition.getDueDate(), + !assignmentDefinition.getCounted(), assignmentDefinition.getReleased(), assignmentDefinition.getExtraCredit(), assignmentDefinition.getSortOrder()); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void updateAssignment(final String gradebookUid, final Long assignmentId, final Assignment assignmentDefinition) { + + if (!gradingAuthz.isUserAbleToEditAssessments(gradebookUid)) { + log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to change the definition of assignment {}", getUserUid(), + gradebookUid, assignmentId); + throw new GradingSecurityException(); + } + + final String validatedName = GradebookHelper.validateAssignmentNameAndPoints(assignmentDefinition); + + final Gradebook gradebook = this.getGradebook(gradebookUid); + + final GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentId); + if (assignment == null) { + throw new AssessmentNotFoundException( + "There is no assignment with id " + assignmentId + " in gradebook " + gradebookUid); + } + + // check if we need to scale the grades + boolean scaleGrades = false; + final Double originalPointsPossible = assignment.getPointsPossible(); + if (gradebook.getGradeType() == GradeType.PERCENTAGE + && !assignment.getPointsPossible().equals(assignmentDefinition.getPoints())) { + scaleGrades = true; + } + + if (gradebook.getGradeType() == GradeType.POINTS && assignmentDefinition.getScaleGrades()) { + scaleGrades = true; + } + + // external assessments are supported, but not these fields + if (!assignmentDefinition.getExternallyMaintained()) { + assignment.setName(validatedName); + assignment.setPointsPossible(assignmentDefinition.getPoints()); + assignment.setDueDate(assignmentDefinition.getDueDate()); + } + assignment.setExtraCredit(assignmentDefinition.getExtraCredit()); + assignment.setCounted(assignmentDefinition.getCounted()); + assignment.setReleased(assignmentDefinition.getReleased()); + + assignment.setExternalAppName(assignmentDefinition.getExternalAppName()); + assignment.setExternallyMaintained(assignmentDefinition.getExternallyMaintained()); + assignment.setExternalId(assignmentDefinition.getExternalId()); + assignment.setExternalData(assignmentDefinition.getExternalData()); + + // if we have a category, get it and set it + // otherwise clear it fully + if (assignmentDefinition.getCategoryId() != null) { + final Category cat = gradingPersistenceManager.getCategory(assignmentDefinition.getCategoryId()).orElse(null); + assignment.setCategory(cat); + } else { + assignment.setCategory(null); + } + + updateAssignment(assignment); + + if (scaleGrades) { + scaleGrades(gradebook, assignment, originalPointsPossible); + } + } + + private CourseGrade getCourseGrade(Long gradebookId) { + return gradingPersistenceManager.getCourseGradesByGradebookId(gradebookId).get(0); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private List getPointsEarnedCourseGradeRecords(final CourseGrade courseGrade, final Collection studentUids) { + + if (studentUids == null || studentUids.isEmpty()) { + log.info("Returning no grade records for an empty collection of student UIDs"); + return Collections.emptyList(); + } + + List unfilteredRecords = gradingPersistenceManager.getCourseGradeRecordsForCourseGrade(courseGrade.getId()); + final List records = filterAndPopulateCourseGradeRecordsByStudents(courseGrade, unfilteredRecords, studentUids); + + final Long gradebookId = courseGrade.getGradebook().getId(); + final Gradebook gradebook = getGradebook(gradebookId); + final List cates = getCategories(gradebookId); + + // get all of the AssignmentGradeRecords here to avoid repeated db calls + final Map> gradeRecMap = getGradeRecordMapForStudents(gradebookId, studentUids); + + // get all of the counted assignments + final List countedAssigns = getCountedAssignments(gradebookId) + .stream().filter(a -> a.isIncludedInCalculations()).collect(Collectors.toList()); + // double totalPointsPossible = getTotalPointsInternal(gradebookId, session); + // if (log.isDebugEnabled()) log.debug("Total points = " + totalPointsPossible); + + for (CourseGradeRecord cgr : records) { + // double totalPointsEarned = getTotalPointsEarnedInternal(gradebookId, cgr.getStudentId(), session); + final List studentGradeRecs = gradeRecMap.get(cgr.getStudentId()); + + applyDropScores(studentGradeRecs, gradebook.getCategoryType()); + final List totalEarned = getTotalPointsEarnedInternal(cgr.getStudentId(), gradebook, cates, studentGradeRecs, + countedAssigns); + final double totalPointsEarned = totalEarned.get(0); + final double literalTotalPointsEarned = totalEarned.get(1); + final double extraPointsEarned = totalEarned.get(2); + final double totalPointsPossible = getTotalPointsInternal(gradebook, cates, cgr.getStudentId(), studentGradeRecs, + countedAssigns, false); + cgr.initNonpersistentFields(totalPointsPossible, totalPointsEarned, literalTotalPointsEarned, extraPointsEarned); + log.debug("Points earned = {}", cgr.getPointsEarned()); + log.debug("Points possible = {}", cgr.getTotalPointsPossible()); + } + + return records; + } + + private List filterAndPopulateCourseGradeRecordsByStudents(CourseGrade courseGrade, Collection gradeRecords, Collection studentUids) { + + final List filteredRecords = new ArrayList<>(); + final Set missingStudents = new HashSet<>(studentUids); + for (CourseGradeRecord cgr : gradeRecords) { + if (studentUids.contains(cgr.getStudentId())) { + filteredRecords.add(cgr); + missingStudents.remove(cgr.getStudentId()); + } + } + missingStudents.forEach(id -> filteredRecords.add(new CourseGradeRecord(courseGrade, id))); + return filteredRecords; + } + + private double getTotalPointsInternal(final Gradebook gradebook, final List categories, final String studentId, + final List studentGradeRecs, final List countedAssigns, + final boolean literalTotal) { + + final GradeType gbGradeType = gradebook.getGradeType(); + if (gbGradeType != GradeType.POINTS && gbGradeType != GradeType.PERCENTAGE) { + log.error("Wrong grade type in GradebookCalculationImpl.getTotalPointsInternal"); + return -1; + } + + if (studentGradeRecs == null || countedAssigns == null) { + log.debug("Returning 0 from getTotalPointsInternal since studentGradeRecs or countedAssigns was null"); + return 0; + } + + double totalPointsPossible = 0; + + final HashSet countedSet = new HashSet<>(countedAssigns); + + // we need to filter this list to identify only "counted" grade recs + final List countedGradeRecs = new ArrayList<>(); + for (AssignmentGradeRecord gradeRec : studentGradeRecs) { + GradebookAssignment assign = gradeRec.getAssignment(); + boolean extraCredit = assign.getExtraCredit(); + if (gradebook.getCategoryType() != GradingCategoryType.NO_CATEGORY && assign.getCategory() != null + && assign.getCategory().getExtraCredit()) { + extraCredit = true; + } + + boolean excused = BooleanUtils.toBoolean(gradeRec.getExcludedFromGrade()); + if (assign.getCounted() && !assign.getUngraded() && !assign.getRemoved() && countedSet.contains(assign) && + assign.getPointsPossible() != null && assign.getPointsPossible() > 0 && !gradeRec.getDroppedFromGrade() && !extraCredit + && !excused) { + countedGradeRecs.add(gradeRec); + } + } + + final Set assignmentsTaken = new HashSet<>(); + final Set categoryTaken = new HashSet<>(); + for (final AssignmentGradeRecord gradeRec : countedGradeRecs) { + if (gradeRec.getPointsEarned() != null && !gradeRec.getPointsEarned().equals("")) { + final Double pointsEarned = gradeRec.getPointsEarned(); + final GradebookAssignment go = gradeRec.getAssignment(); + if (pointsEarned != null) { + if (gradebook.getCategoryType() == GradingCategoryType.NO_CATEGORY) { + assignmentsTaken.add(go.getId()); + } else if ((gradebook.getCategoryType() == GradingCategoryType.ONLY_CATEGORY || gradebook + .getCategoryType() == GradingCategoryType.WEIGHTED_CATEGORY) + && go != null && categories != null) { + // assignmentsTaken.add(go.getId()); + // } + // else if (gradebook.getCategoryType() == GradingCategoryType.WEIGHTED_CATEGORY && go != null && + // categories != null) + // { + for (Category cate : categories) { + if (cate != null && !cate.getRemoved() && go.getCategory() != null + && cate.getId().equals(go.getCategory().getId()) + && ((cate.getExtraCredit() != null && !cate.getExtraCredit()) || cate.getExtraCredit() == null)) { + assignmentsTaken.add(go.getId()); + categoryTaken.add(cate.getId()); + break; + } + } + } + } + } + } + + if (!assignmentsTaken.isEmpty()) { + if (!literalTotal && gradebook.getCategoryType() == GradingCategoryType.WEIGHTED_CATEGORY) { + for (Category cate : categories) { + if (cate != null && !cate.getRemoved() && categoryTaken.contains(cate.getId())) { + totalPointsPossible += cate.getWeight(); + } + } + return totalPointsPossible; + } + final Iterator assignmentIter = countedAssigns.iterator(); + while (assignmentIter.hasNext()) { + final GradebookAssignment asn = (GradebookAssignment) assignmentIter.next(); + if (asn != null) { + final Double pointsPossible = asn.getPointsPossible(); + + if (gradebook.getCategoryType() == GradingCategoryType.NO_CATEGORY + && assignmentsTaken.contains(asn.getId())) { + totalPointsPossible += pointsPossible; + } else if (gradebook.getCategoryType() == GradingCategoryType.ONLY_CATEGORY + && assignmentsTaken.contains(asn.getId())) { + totalPointsPossible += pointsPossible; + } else if (literalTotal && gradebook.getCategoryType() == GradingCategoryType.WEIGHTED_CATEGORY + && assignmentsTaken.contains(asn.getId())) { + totalPointsPossible += pointsPossible; + } + } + } + } else { + totalPointsPossible = -1; + } + + return totalPointsPossible; + } + + private List getTotalPointsEarnedInternal(final String studentId, final Gradebook gradebook, final List categories, + final List gradeRecs, final List countedAssigns) { + + final GradeType gbGradeType = gradebook.getGradeType(); + if (gbGradeType != GradeType.POINTS && gbGradeType != GradeType.PERCENTAGE) { + log.error("Wrong grade type in GradebookCalculationImpl.getTotalPointsEarnedInternal"); + return Collections.emptyList(); + } + + if (gradeRecs == null || countedAssigns == null) { + log.debug("getTotalPointsEarnedInternal for studentId={} returning 0 because null gradeRecs or countedAssigns", studentId); + List returnList = new ArrayList<>(); + returnList.add(new Double(0)); + returnList.add(new Double(0)); + returnList.add(new Double(0)); // 3rd one is for the pre-adjusted course grade + return returnList; + } + + BigDecimal totalPointsEarned = new BigDecimal(0); + BigDecimal extraPointsEarned = new BigDecimal(0); + BigDecimal literalTotalPointsEarned = new BigDecimal(0d); + + final Map cateScoreMap = new HashMap<>(); + final Map cateTotalScoreMap = new HashMap<>(); + final Set assignmentsTaken = new HashSet<>(); + + for (final AssignmentGradeRecord gradeRec : gradeRecs) { + final boolean excused = BooleanUtils.toBoolean(gradeRec.getExcludedFromGrade()); + + if (gradeRec.getPointsEarned() != null && !gradeRec.getPointsEarned().equals("") && !gradeRec.getDroppedFromGrade()) { + final GradebookAssignment go = gradeRec.getAssignment(); + if (go.isIncludedInCalculations() && countedAssigns.contains(go)) { + BigDecimal pointsEarned = BigDecimal.valueOf(gradeRec.getPointsEarned()); + final BigDecimal pointsPossible = BigDecimal.valueOf(go.getPointsPossible()); + + // if (gbGradeType == GradeType.POINTS) + // { + if (gradebook.getCategoryType() == GradingCategoryType.NO_CATEGORY) { + if (!excused) { + totalPointsEarned = totalPointsEarned.add(pointsEarned, GradingService.MATH_CONTEXT); + literalTotalPointsEarned = pointsEarned.add(literalTotalPointsEarned, GradingService.MATH_CONTEXT); + assignmentsTaken.add(go.getId()); + } + } else if (gradebook.getCategoryType() == GradingCategoryType.ONLY_CATEGORY && go != null) { + if (!excused) { + totalPointsEarned = totalPointsEarned.add(pointsEarned, GradingService.MATH_CONTEXT); + literalTotalPointsEarned = pointsEarned.add(literalTotalPointsEarned, GradingService.MATH_CONTEXT); + assignmentsTaken.add(go.getId()); + } + } else if (gradebook.getCategoryType() == GradingCategoryType.WEIGHTED_CATEGORY && go != null + && categories != null) { + for (Category cate : categories) { + if (cate != null && !cate.getRemoved() && go.getCategory() != null + && cate.getId().equals(go.getCategory().getId())) { + if (!excused) { + assignmentsTaken.add(go.getId()); + literalTotalPointsEarned = pointsEarned.add(literalTotalPointsEarned, GradingService.MATH_CONTEXT); + + // If category is equal weight, manipulate points to be the average + if (cate.getEqualWeightAssignments()) { + pointsEarned = pointsEarned.divide(pointsPossible, GradingService.MATH_CONTEXT); + } + + if (cateScoreMap.get(cate.getId()) != null) { + cateScoreMap.put(cate.getId(), ((BigDecimal)cateScoreMap.get(cate.getId())).add(pointsEarned, GradingService.MATH_CONTEXT)); + } else { + cateScoreMap.put(cate.getId(), pointsEarned); + } + } + break; + } + } + } + } + } + } + + if (categories.size() > 0 && gradebook.getCategoryType() == GradingCategoryType.WEIGHTED_CATEGORY) { + for (GradebookAssignment asgn : countedAssigns) { + BigDecimal pointsPossible = new BigDecimal(asgn.getPointsPossible()); + + if (assignmentsTaken.contains(asgn.getId())) { + for (Category cate : categories) { + if (cate != null && !cate.getRemoved() && asgn.getCategory() != null + && cate.getId().equals(asgn.getCategory().getId()) && !asgn.getExtraCredit()) { + + // If it's equal-weight category, just want to divide averages by number of items + if (cate.getEqualWeightAssignments()) { + pointsPossible = new BigDecimal("1"); + } + + if (cateTotalScoreMap.get(cate.getId()) == null) { + cateTotalScoreMap.put(cate.getId(), pointsPossible); + } else { + cateTotalScoreMap.put(cate.getId(), + ((BigDecimal) cateTotalScoreMap.get(cate.getId())).add(pointsPossible)); + } + } + } + } + } + } + + if (assignmentsTaken.isEmpty()) { + totalPointsEarned = new BigDecimal(-1); + } + + if (gradebook.getCategoryType() == GradingCategoryType.WEIGHTED_CATEGORY) { + for (Category cate : categories) { + if (cate != null && !cate.getRemoved() && cateScoreMap.get(cate.getId()) != null + && cateTotalScoreMap.get(cate.getId()) != null) { + if (cate.getExtraCredit()) { + extraPointsEarned = extraPointsEarned.add(((BigDecimal) cateScoreMap.get(cate.getId())).multiply(new BigDecimal(cate.getWeight()), GradingService.MATH_CONTEXT) + .divide((BigDecimal) cateTotalScoreMap.get(cate.getId()), GradingService.MATH_CONTEXT)); + } + else { + totalPointsEarned = totalPointsEarned.add(((BigDecimal) cateScoreMap.get(cate.getId())).multiply(new BigDecimal(cate.getWeight()), GradingService.MATH_CONTEXT) + .divide((BigDecimal) cateTotalScoreMap.get(cate.getId()), GradingService.MATH_CONTEXT)); + } + } + } + } + + log.debug("getTotalPointsEarnedInternal for studentId={} returning {}", studentId, totalPointsEarned); + List returnList = new ArrayList<>(); + returnList.add(totalPointsEarned.doubleValue()); + returnList.add(literalTotalPointsEarned.doubleValue()); + returnList.add(extraPointsEarned.doubleValue()); + + return returnList; + } + + /** + * Internal method to get a gradebook based on its id. + * + * @param id + * @return + * + * NOTE: When the UI changes, this is to be turned private again + */ + public Gradebook getGradebook(Long id) { + + return gradingPersistenceManager.getGradebook(id).orElse(null); + } + + private List getAssignmentsCounted(Long gradebookId) { + + return gradingPersistenceManager.getCountedAssignmentsForGradebook(gradebookId); + } + + @Override + public boolean checkStudentsNotSubmitted(String gradebookUid) { + + final Gradebook gradebook = getGradebook(gradebookUid); + final Set studentUids = getAllStudentUids(getGradebookUid(gradebook.getId())); + if (gradebook.getCategoryType() == GradingCategoryType.NO_CATEGORY + || gradebook.getCategoryType() == GradingCategoryType.ONLY_CATEGORY) { + + List filteredAssigns = getAssignments(gradebook.getId(), SortType.SORT_BY_SORTING, true) + .stream().filter(a -> a.getCounted() && !a.getUngraded()).collect(Collectors.toList()); + + final List records = getAllAssignmentGradeRecords(gradebook.getId(), studentUids); + final List filteredRecords = new ArrayList<>(); + for (AssignmentGradeRecord agr : records) { + if (!agr.isCourseGradeRecord() && agr.getAssignment().getCounted() && !agr.getAssignment().getUngraded()) { + if (agr.getPointsEarned() == null) { + return true; + } + filteredRecords.add(agr); + } + } + + return filteredRecords.size() < (filteredAssigns.size() * studentUids.size()); + } else { + final List assigns = getAssignments(gradebook.getId(), SortType.SORT_BY_SORTING, true); + final List records = getAllAssignmentGradeRecords(gradebook.getId(), studentUids); + + final Set filteredAssigns = new HashSet<>(); + for (GradebookAssignment assign : assigns) { + if (assign != null && assign.getCounted() && !assign.getUngraded()) { + if (assign.getCategory() != null && !assign.getCategory().getRemoved()) { + filteredAssigns.add(assign.getId()); + } + } + } + + final List filteredRecords = new ArrayList<>(); + for (AssignmentGradeRecord agr : records) { + if (filteredAssigns.contains(agr.getAssignment().getId()) && !agr.isCourseGradeRecord()) { + if (agr.getPointsEarned() == null) { + return true; + } + filteredRecords.add(agr); + } + } + + return filteredRecords.size() < filteredAssigns.size() * studentUids.size(); + } + } + + /** + * Get all assignment grade records for the given students + * + * @param gradebookId + * @param studentUids + * @return + * + * NOTE When the UI changes, this needs to be made private again + */ + public List getAllAssignmentGradeRecords(Long gradebookId, Collection studentUids) { + + if (studentUids.isEmpty()) { + // If there are no enrollments, no need to execute the query. + log.info("No enrollments were specified. Returning an empty List of grade records"); + return Collections.emptyList(); + } else { + List unfilteredRecords = gradingPersistenceManager.getAllAssignmentGradeRecordsForGradebook(gradebookId); + return filterGradeRecordsByStudents(unfilteredRecords, studentUids); + } + } + + private List getAllAssignmentGradeRecordsForGbItem(Long gradableObjectId, Collection studentUids) { + + if (studentUids.isEmpty()) { + // If there are no enrollments, no need to execute the query. + log.info("No enrollments were specified. Returning an empty List of grade records"); + return Collections.emptyList(); + } else { + List unfilteredRecords = gradingPersistenceManager.getAllAssignmentGradeRecordsForAssignment(gradableObjectId); + return filterGradeRecordsByStudents(unfilteredRecords, studentUids); + } + } + + /** + * Gets all AssignmentGradeRecords on the gradableObjectIds limited to students specified by studentUids + */ + private List getAllAssignmentGradeRecordsForGbItems(final List gradableObjectIds, final List studentUids) { + + final List gradeRecords = new ArrayList<>(); + if (studentUids.isEmpty()) { + // If there are no enrollments, no need to execute the query. + log.debug("No enrollments were specified. Returning an empty List of grade records"); + return gradeRecords; + } + /* + * Watch out for Oracle's "in" limit. Ignoring oracle, the query would be: + * "from AssignmentGradeRecord as agr where agr.gradableObject.removed = false and agr.gradableObject.id in (:gradableObjectIds) and agr.studentId in (:studentUids)" + * Note: the order is not important. The calling methods will iterate over all entries and add them to a map. We could have + * made this method return a map, but we'd have to iterate over the items in order to add them to the map anyway. That would + * be a waste of a loop that the calling method could use to perform additional tasks. + */ + // For Oracle, iterate over gbItems 1000 at a time (sympathies to whoever needs to query grades for a thousand gbItems) + int minGbo = 0; + int maxGbo = Math.min(gradableObjectIds.size(), 1000); + while (minGbo < gradableObjectIds.size()) { + // For Oracle, iterate over students 1000 at a time + int minStudent = 0; + int maxStudent = Math.min(studentUids.size(), 1000); + while (minStudent < studentUids.size()) { + List subRecords + = gradingPersistenceManager.getAssignmentGradeRecordsForAssignmentIdsAndStudentIds( + gradableObjectIds.subList(minGbo, maxGbo), studentUids.subList(minStudent, maxStudent)); + // Add the query results to our overall results (in case there's over a thousand things) + gradeRecords.addAll(subRecords); + minStudent += 1000; + maxStudent = Math.min(studentUids.size(), minStudent + 1000); + } + minGbo += 1000; + maxGbo = Math.min(gradableObjectIds.size(), minGbo + 1000); + } + return gradeRecords; + } + + /** + * Get a list of assignments, sorted + * + * @param gradebookId + * @param sortBy + * @param ascending + * @return + * + * NOTE: When the UI changes, this needs to go back to private + */ + private List getAssignments(Long gradebookId, SortType sortBy, boolean ascending) { + + return sortAssignments(getAssignments(gradebookId), sortBy, ascending); + } + + /** + * Sort the list of (internal) assignments by the given criteria + * + * @param assignments + * @param sortBy + * @param ascending + */ + private List sortAssignments(final List assignments, SortType sortBy, final boolean ascending) { + + // note, this is duplicated in the tool GradebookManagerHibernateImpl class + Comparator comp; + + if (sortBy == null) { + sortBy = SortType.SORT_BY_SORTING; // default + } + + switch (sortBy) { + + case SORT_BY_NONE: + return assignments; // no sorting + case SORT_BY_NAME: + comp = GradableObject.nameComparator; + break; + case SORT_BY_DATE: + comp = GradableObject.dateComparator; + break; + case SORT_BY_MEAN: + comp = GradableObject.meanComparator; + break; + case SORT_BY_POINTS: + comp = GradebookAssignment.pointsComparator; + break; + case SORT_BY_RELEASED: + comp = GradebookAssignment.releasedComparator; + break; + case SORT_BY_COUNTED: + comp = GradebookAssignment.countedComparator; + break; + case SORT_BY_EDITOR: + comp = GradebookAssignment.gradeEditorComparator; + break; + case SORT_BY_SORTING: + comp = GradableObject.sortingComparator; + break; + case SORT_BY_CATEGORY: + comp = GradebookAssignment.categoryComparator; + break; + default: + comp = GradableObject.defaultComparator; + } + + Collections.sort(assignments, comp); + if (!ascending) { + Collections.reverse(assignments); + } + log.debug("sortAssignments: ordering by {} ({}), ascending={}", sortBy, comp, ascending); + return assignments; + } + + /* + * (non-Javadoc) + * + * @see org.sakaiproject.grading.api.GradingService#getViewableAssignmentsForCurrentUser(java.lang.String) + */ + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public List getViewableAssignmentsForCurrentUser(String gradebookUid) { + + return getViewableAssignmentsForCurrentUser(gradebookUid, SortType.SORT_BY_SORTING); + } + + /* + * (non-Javadoc) + * + * @see org.sakaiproject.grading.api.GradingService#getViewableAssignmentsForCurrentUser(java.lang.String, java.) + */ + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public List getViewableAssignmentsForCurrentUser(final String gradebookUid, final SortType sortBy) { + + if (!gradingAuthz.isUserAbleToGradeAll(gradebookUid) + && !gradingAuthz.isUserAbleToGrade(gradebookUid) + && !gradingAuthz.isUserAbleToViewOwnGrades(gradebookUid)) { + return Collections.emptyList(); + } + + List viewableAssignments = new ArrayList<>(); + final LinkedHashSet assignmentsToReturn = new LinkedHashSet<>(); + + final Gradebook gradebook = getGradebook(gradebookUid); + + // will send back all assignments if user can grade all + if (gradingAuthz.isUserAbleToGradeAll(gradebookUid)) { + viewableAssignments = getAssignments(gradebook.getId(), sortBy, true); + } else if (gradingAuthz.isUserAbleToGrade(gradebookUid)) { + // if user can grade and doesn't have grader perm restrictions, they + // may view all assigns + if (!gradingPermissionService.currentUserHasGraderPermissions(gradebookUid)) { + viewableAssignments = getAssignments(gradebook.getId(), sortBy, true); + } else { + // this user has grader perms, so we need to filter the items returned + // if this gradebook has categories enabled, we need to check for category-specific restrictions + if (gradebook.getCategoryType() == GradingCategoryType.NO_CATEGORY) { + assignmentsToReturn.addAll(getAssignments(gradebookUid, sortBy)); + } else { + final String userUid = getUserUid(); + if (gradingPermissionService.getPermissionForUserForAllAssignment(gradebook.getId(), userUid)) { + assignmentsToReturn.addAll(getAssignments(gradebookUid, sortBy)); + } else { + final List assignments = getAssignments(gradebookUid, sortBy); + final List categoryIds = ((List) getCategories(gradebook.getId())).stream().map(Category::getId) + .collect(Collectors.toList()); + // categories are enabled, so we need to check the category restrictions + if (!categoryIds.isEmpty()) { + List viewableCategoryIds = gradingPermissionService.getCategoriesForUser(gradebook.getId(), + userUid, categoryIds); + for (Assignment assignment : assignments) { + if (assignment.getCategoryId() != null && viewableCategoryIds.contains(assignment.getCategoryId())) { + assignmentsToReturn.add(assignment); + } + } + } + } + } + } + } else if (gradingAuthz.isUserAbleToViewOwnGrades(gradebookUid)) { + // if user is just a student, we need to filter out unreleased items + final List allAssigns = getAssignments(gradebook.getId(), sortBy, true); + if (allAssigns != null) { + for (final Iterator aIter = allAssigns.iterator(); aIter.hasNext();) { + final GradebookAssignment assign = (GradebookAssignment) aIter.next(); + if (assign != null && assign.getReleased()) { + viewableAssignments.add(assign); + } + } + } + } + + // Now we need to convert these to the assignment template objects + if (viewableAssignments != null && !viewableAssignments.isEmpty()) { + final boolean gbUsesCategories = gradebook.getCategoryType() != GradingCategoryType.NO_CATEGORY; + for (final Object element : viewableAssignments) { + final GradebookAssignment assignment = (GradebookAssignment) element; + assignmentsToReturn.add(getAssignmentDefinition(assignment, gbUsesCategories)); + } + } + + return new ArrayList<>(assignmentsToReturn); + } + + @Override + public Map getViewableStudentsForItemForCurrentUser(String gradebookUid, Long gradableObjectId) { + + String userUid = sessionManager.getCurrentSessionUserId(); + return getViewableStudentsForItemForUser(userUid, gradebookUid, gradableObjectId); + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Map getViewableStudentsForItemForUser(String userUid, String gradebookUid, Long gradableObjectId) { + + if (gradebookUid == null || gradableObjectId == null || userUid == null) { + throw new IllegalArgumentException("null gradebookUid or gradableObjectId or " + + "userId passed to getViewableStudentsForUserForItem." + + " gradebookUid: " + gradebookUid + " gradableObjectId:" + + gradableObjectId + " userId: " + userUid); + } + + if (!this.gradingAuthz.isUserAbleToGrade(gradebookUid, userUid)) { + return new HashMap<>(); + } + + final GradebookAssignment gradebookItem = getAssignmentWithoutStatsByID(gradebookUid, gradableObjectId); + + if (gradebookItem == null) { + log.debug("The gradebook item does not exist, so returning empty set"); + return new HashMap(); + } + + final Long categoryId = gradebookItem.getCategory() == null ? null : gradebookItem.getCategory().getId(); + + final Map enrRecFunctionMap = this.gradingAuthz.findMatchingEnrollmentsForItemForUser(userUid, gradebookUid, + categoryId, getGradebook(gradebookUid).getCategoryType(), null, null); + if (enrRecFunctionMap == null) { + return new HashMap(); + } + + final Map studentIdFunctionMap = new HashMap(); + for (final Entry entry : enrRecFunctionMap.entrySet()) { + final EnrollmentRecord enr = entry.getKey(); + if (enr != null && enrRecFunctionMap.get(enr) != null) { + studentIdFunctionMap.put(enr.getUser().getUserUid(), entry.getValue()); + } + } + return studentIdFunctionMap; + } + + @Override + public boolean isGradableObjectDefined(final Long gradableObjectId) { + + if (gradableObjectId == null) { + throw new IllegalArgumentException("null gradableObjectId passed to isGradableObjectDefined"); + } + + return gradingPersistenceManager.isAssignmentDefined(gradableObjectId); + } + + @Override + public Map getViewableSectionUuidToNameMap(String gradebookUid) { + + if (gradebookUid == null) { + throw new IllegalArgumentException("Null gradebookUid passed to getViewableSectionIdToNameMap"); + } + + return gradingAuthz.getViewableSections(gradebookUid).stream().filter(Objects::nonNull) + .collect(Collectors.toMap(s -> s.getUuid(), s -> s.getTitle())); + } + + @Override + public boolean currentUserHasGradeAllPerm(final String gradebookUid) { + + return this.gradingAuthz.isUserAbleToGradeAll(gradebookUid); + } + + @Override + public boolean isUserAllowedToGradeAll(final String gradebookUid, final String userUid) { + + return this.gradingAuthz.isUserAbleToGradeAll(gradebookUid, userUid); + } + + @Override + public boolean currentUserHasGradingPerm(String gradebookUid) { + + return gradingAuthz.isUserAbleToGrade(gradebookUid); + } + + @Override + public boolean isUserAllowedToGrade(final String gradebookUid, final String userUid) { + + return this.gradingAuthz.isUserAbleToGrade(gradebookUid, userUid); + } + + @Override + public boolean currentUserHasEditPerm(final String gradebookUid) { + + return this.gradingAuthz.isUserAbleToEditAssessments(gradebookUid); + } + + @Override + public boolean currentUserHasViewOwnGradesPerm(final String gradebookUid) { + + return this.gradingAuthz.isUserAbleToViewOwnGrades(gradebookUid); + } + + @Override + public boolean currentUserHasViewStudentNumbersPerm(final String gradebookUid) { + + return this.gradingAuthz.isUserAbleToViewStudentNumbers(gradebookUid); + } + + @Override + public List getGradesForStudentsForItem(final String gradebookUid, final Long gradableObjectId, + final List studentIds) { + + if (gradableObjectId == null) { + throw new IllegalArgumentException("null gradableObjectId passed to getGradesForStudentsForItem"); + } + + if (studentIds == null || studentIds.isEmpty()) { + return Collections.emptyList(); + } + + final List studentGrades = new ArrayList<>(); + + // first, we need to make sure the current user is authorized to view the + // grades for all of the requested students + final GradebookAssignment gbItem = getAssignmentWithoutStatsByID(gradebookUid, gradableObjectId); + + if (gbItem != null) { + final Gradebook gradebook = gbItem.getGradebook(); + + if (!this.gradingAuthz.isUserAbleToGrade(gradebook.getUid())) { + log.error( + "User {} attempted to access grade information without permission in gb {} using gradebookService.getGradesForStudentsForItem", + sessionManager.getCurrentSessionUserId(), gradebook.getUid()); + throw new GradingSecurityException(); + } + + final Long categoryId = gbItem.getCategory() != null ? gbItem.getCategory().getId() : null; + final Map enrRecFunctionMap = this.gradingAuthz.findMatchingEnrollmentsForItem(gradebook.getUid(), categoryId, + gradebook.getCategoryType(), null, null); + final Set enrRecs = enrRecFunctionMap.keySet(); + final Map studentIdEnrRecMap = new HashMap(); + if (enrRecs != null) { + for (final Iterator enrIter = enrRecs.iterator(); enrIter.hasNext();) { + final EnrollmentRecord enr = (EnrollmentRecord) enrIter.next(); + if (enr != null) { + studentIdEnrRecMap.put(enr.getUser().getUserUid(), enr); + } + } + } + + // filter the provided studentIds if user doesn't have permissions + studentIds.removeIf(studentId -> { + return !studentIdEnrRecMap.containsKey(studentId); + }); + + // retrieve the grading comments for all of the students + final List commentRecs = getComments(gbItem, studentIds); + final Map studentIdCommentTextMap = new HashMap(); + if (commentRecs != null) { + for (final Comment comment : commentRecs) { + if (comment != null) { + studentIdCommentTextMap.put(comment.getStudentId(), comment.getCommentText()); + } + } + } + + // now, we can populate the grade information + final List studentsWithGradeRec = new ArrayList<>(); + final List gradeRecs = getAllAssignmentGradeRecordsForGbItem(gradableObjectId, studentIds); + if (gradeRecs != null) { + if (gradebook.getGradeType() == GradeType.LETTER) { + convertPointsToLetterGrade(gradebook, gradeRecs); + } else if (gradebook.getGradeType() == GradeType.PERCENTAGE) { + convertPointsToPercentage(gradebook, gradeRecs); + } + + for (final Object element : gradeRecs) { + final AssignmentGradeRecord agr = (AssignmentGradeRecord) element; + if (agr != null) { + final String commentText = studentIdCommentTextMap.get(agr.getStudentId()); + final GradeDefinition gradeDef = convertGradeRecordToGradeDefinition(agr, gbItem, gradebook, commentText); + + studentGrades.add(gradeDef); + studentsWithGradeRec.add(agr.getStudentId()); + } + } + + // if student has a comment but no grade add an empty grade definition with the comment + if (studentsWithGradeRec.size() < studentIds.size()) { + for (final String studentId : studentIdCommentTextMap.keySet()) { + if (!studentsWithGradeRec.contains(studentId)) { + final String comment = studentIdCommentTextMap.get(studentId); + final AssignmentGradeRecord emptyGradeRecord = new AssignmentGradeRecord(gbItem, studentId, null); + final GradeDefinition gradeDef = convertGradeRecordToGradeDefinition(emptyGradeRecord, gbItem, gradebook, + comment); + studentGrades.add(gradeDef); + } + } + } + } + } + + return studentGrades; + } + + @Override + public Map> getGradesWithoutCommentsForStudentsForItems(final String gradebookUid, + final List gradableObjectIds, final List studentIds) { + + if (gradableObjectIds == null || gradableObjectIds.isEmpty()) { + throw new IllegalArgumentException("null or empty gradableObjectIds passed to getGradesWithoutCommentsForStudentsForItems"); + } + + if (!this.gradingAuthz.isUserAbleToGrade(gradebookUid)) { + throw new GradingSecurityException(); + } + + final Map> gradesMap = new HashMap<>(); + if (studentIds == null || studentIds.isEmpty()) { + // We could populate the map with (gboId : new ArrayList()), but it's cheaper to allow get(gboId) to return null. + return gradesMap; + } + + // Get all the grades for the gradableObjectIds + final List gradeRecords = getAllAssignmentGradeRecordsForGbItems(gradableObjectIds, studentIds); + // AssignmentGradeRecord is not in the API. So we need to convert grade records into GradeDefinition objects. + // GradeDefinitions are not tied to their gbos, so we need to return a map associating them back to their gbos + final List gradeDefinitions = new ArrayList<>(); + for (final AssignmentGradeRecord gradeRecord : gradeRecords) { + final GradebookAssignment gbo = (GradebookAssignment) gradeRecord.getGradableObject(); + final Long gboId = gbo.getId(); + final Gradebook gradebook = gbo.getGradebook(); + if (!gradebookUid.equals(gradebook.getUid())) { + // The user is authorized against gradebookUid, but we have grades for another gradebook. + // This is an authorization issue caused by gradableObjectIds violating the method contract. + throw new IllegalArgumentException("gradableObjectIds must belong to grades within this gradebook"); + } + + final GradeDefinition gradeDef = convertGradeRecordToGradeDefinition(gradeRecord, gbo, gradebook, null); + + List gradeList = gradesMap.get(gboId); + if (gradeList == null) { + gradeList = new ArrayList<>(); + gradesMap.put(gboId, gradeList); + } + gradeList.add(gradeDef); + } + + return gradesMap; + } + + /** + * Converts an AssignmentGradeRecord into a GradeDefinition object. + * + * @param gradeRecord + * @param gbo + * @param gradebook + * @param commentText - goes into the GradeComment attribute. Will be omitted if null + * @return a GradeDefinition object whose attributes match the passed in gradeRecord + */ + private GradeDefinition convertGradeRecordToGradeDefinition(final AssignmentGradeRecord gradeRecord, final GradebookAssignment gbo, + final Gradebook gradebook, final String commentText) { + final GradeDefinition gradeDef = new GradeDefinition(); + gradeDef.setStudentUid(gradeRecord.getStudentId()); + gradeDef.setGraderUid(gradeRecord.getGraderId()); + gradeDef.setDateRecorded(gradeRecord.getDateRecorded()); + final GradeType gradeEntryType = gradebook.getGradeType(); + gradeDef.setGradeEntryType(gradeEntryType); + String grade = null; + if (gradeEntryType == GradeType.LETTER) { + grade = gradeRecord.getLetterEarned(); + } else if (gradeEntryType == GradeType.PERCENTAGE) { + final Double percentEarned = gradeRecord.getPercentEarned(); + grade = percentEarned != null ? percentEarned.toString() : null; + } else { + final Double pointsEarned = gradeRecord.getPointsEarned(); + grade = pointsEarned != null ? pointsEarned.toString() : null; + } + gradeDef.setGrade(grade); + gradeDef.setGradeReleased(gradebook.getAssignmentsDisplayed() && gbo.getReleased()); + + if (commentText != null) { + gradeDef.setGradeComment(commentText); + } + + gradeDef.setExcused(gradeRecord.getExcludedFromGrade()); + + return gradeDef; + } + + @Override + public boolean isGradeValid(String gradebookUuid, String grade) { + + if (gradebookUuid == null) { + throw new IllegalArgumentException("Null gradebookUuid passed to isGradeValid"); + } + + Gradebook gradebook = getGradebook(gradebookUuid); + GradeType gradeType = getGradebook(gradebookUuid).getGradeType(); + LetterGradePercentMapping mapping = null; + if (gradeType == GradeType.LETTER) { + mapping = getLetterGradePercentMapping(gradebook); + } + + return isGradeValid(grade, gradeType, mapping); + } + + @Override + public boolean isValidNumericGrade(final String grade) { + boolean gradeIsValid = false; + + try { + final NumberFormat nbFormat = NumberFormat.getInstance(resourceLoader.getLocale()); + final Double gradeAsDouble = nbFormat.parse(grade).doubleValue(); + final String decSeparator = ((DecimalFormat) nbFormat).getDecimalFormatSymbols().getDecimalSeparator() + ""; + + // grade must be greater than or equal to 0 + if (gradeAsDouble >= 0) { + final String[] splitOnDecimal = grade.split("\\" + decSeparator); + // check that there are no more than 2 decimal places + if (splitOnDecimal == null) { + gradeIsValid = true; + + // check for a valid score matching ##########.## + // where integer is maximum of 10 integers in length + // and maximum of 2 decimal places + } else if (grade.matches("[0-9]{0,10}(\\" + decSeparator + "[0-9]{0,2})?")) { + gradeIsValid = true; + } + } + } catch (NumberFormatException | ParseException nfe) { + log.debug("Passed grade is not a numeric value"); + } + + return gradeIsValid; + } + + private boolean isGradeValid(final String grade, final GradeType gradeEntryType, final LetterGradePercentMapping gradeMapping) { + + boolean gradeIsValid = false; + + if (grade == null || "".equals(grade)) { + + gradeIsValid = true; + + } else { + + if (gradeEntryType == GradeType.POINTS || + gradeEntryType == GradeType.PERCENTAGE) { + try { + final NumberFormat nbFormat = NumberFormat.getInstance(resourceLoader.getLocale()); + final Double gradeAsDouble = nbFormat.parse(grade).doubleValue(); + final String decSeparator = ((DecimalFormat) nbFormat).getDecimalFormatSymbols().getDecimalSeparator() + ""; + // grade must be greater than or equal to 0 + if (gradeAsDouble >= 0) { + final String[] splitOnDecimal = grade.split("\\" + decSeparator); + // check that there are no more than 2 decimal places + if (splitOnDecimal == null) { + gradeIsValid = true; + + // check for a valid score matching ##########.## + // where integer is maximum of 10 integers in length + // and maximum of 2 decimal places + } else if (grade.matches("[0-9]{0,10}(\\" + decSeparator + "[0-9]{0,2})?")) { + gradeIsValid = true; + } + } + } catch (NumberFormatException | ParseException nfe) { + log.debug("Passed grade is not a numeric value"); + } + + } else if (gradeEntryType == GradeType.LETTER) { + if (gradeMapping == null) { + throw new IllegalArgumentException("Null mapping passed to isGradeValid for a letter grade-based gradeook"); + } + + final String standardizedGrade = gradeMapping.standardizeInputGrade(grade); + if (standardizedGrade != null) { + gradeIsValid = true; + } + } else { + throw new IllegalArgumentException("Invalid gradeEntryType passed to isGradeValid"); + } + } + + return gradeIsValid; + } + + @Override + public List identifyStudentsWithInvalidGrades(final String gradebookUid, final Map studentIdToGradeMap) { + + if (gradebookUid == null) { + throw new IllegalArgumentException("null gradebookUid passed to identifyStudentsWithInvalidGrades"); + } + + final List studentsWithInvalidGrade = new ArrayList<>(); + + if (studentIdToGradeMap != null) { + Gradebook gradebook = getGradebook(gradebookUid); + GradeType gradeType = gradebook.getGradeType(); + + LetterGradePercentMapping gradeMapping = null; + if (gradeType == GradeType.LETTER) { + gradeMapping = getLetterGradePercentMapping(gradebook); + } + + for (final String studentId : studentIdToGradeMap.keySet()) { + final String grade = studentIdToGradeMap.get(studentId); + if (!isGradeValid(grade, gradeType, gradeMapping)) { + studentsWithInvalidGrade.add(studentId); + } + } + } + return studentsWithInvalidGrade; + } + + @Override + @Transactional + public void saveGradeAndCommentForStudent(final String gradebookUid, final Long gradableObjectId, final String studentUid, + final String grade, final String comment) { + + if (gradebookUid == null || gradableObjectId == null || studentUid == null) { + throw new IllegalArgumentException( + "Null gradebookUid or gradableObjectId or studentUid passed to saveGradeAndCommentForStudent"); + } + + final GradeDefinition gradeDef = new GradeDefinition(); + gradeDef.setStudentUid(studentUid); + gradeDef.setGrade(grade); + gradeDef.setGradeComment(comment); + + final List gradeDefList = new ArrayList<>(); + gradeDefList.add(gradeDef); + + final GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, gradableObjectId); + + final AssignmentGradeRecord record = getAssignmentGradeRecord(assignment, studentUid); + if (record != null) { + gradeDef.setExcused(BooleanUtils.toBoolean(record.getExcludedFromGrade())); + } else { + gradeDef.setExcused(false); + } + saveGradesAndComments(gradebookUid, gradableObjectId, gradeDefList); + } + + @Override + public void saveGradeAndExcuseForStudent(final String gradebookUid, final Long gradableObjectId, final String studentUid, + final String grade, final boolean excuse) { + if (gradebookUid == null || gradableObjectId == null || studentUid == null) { + throw new IllegalArgumentException( + "Null gradebookUid, gradeableObjectId, or studentUid passed to saveGradeAndExcuseForStudent"); + } + + final GradeDefinition gradeDef = new GradeDefinition(); + gradeDef.setStudentUid(studentUid); + gradeDef.setGrade(grade); + gradeDef.setExcused(excuse); + + // Lookup any existing comments and set the text so that they don't get wiped out on a save + CommentDefinition gradeComment = getAssignmentScoreComment(gradebookUid, gradableObjectId, studentUid); + if (gradeComment != null) { + gradeDef.setGradeComment(gradeComment.getCommentText()); + } + + final List gradeDefList = new ArrayList<>(); + gradeDefList.add(gradeDef); + + saveGradesAndComments(gradebookUid, gradableObjectId, gradeDefList); + } + + /** + * Gets the course grade record for a student, or null if it does not yet exist. + * + * @param studentId The student ID + * @return The course grade record for the student + */ + private CourseGradeRecord getCourseGradeRecord(final Gradebook gradebook, final String studentId) { + return gradingPersistenceManager.getCourseGradeRecord(gradebook, studentId).orElse(null); + } + + + @Override + public boolean getIsAssignmentExcused(final String gradebookUid, final Long assignmentId, final String studentUid) throws AssessmentNotFoundException { + + if (gradebookUid == null || assignmentId == null || studentUid == null) { + throw new IllegalArgumentException("null parameter passed to getAssignmentScoreComment. Values are gradebookUid:" + gradebookUid + " assignmentId:" + assignmentId + " studentUid:"+ studentUid); + } + GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentId); + AssignmentGradeRecord agr = getAssignmentGradeRecord(assignment, studentUid); + + if (agr == null) { + return false; + }else{ + return agr.getExcludedFromGrade(); + } + } + + @Override + @Transactional + public void saveGradesAndComments(final String gradebookUid, final Long gradableObjectId, final List gradeDefList) { + + if (gradebookUid == null || gradableObjectId == null) { + throw new IllegalArgumentException("Null gradebookUid or gradableObjectId passed to saveGradesAndComments"); + } + + if (CollectionUtils.isEmpty(gradeDefList)) { + return; + } + + final GradebookAssignment assignment = getAssignmentWithoutStatsByID(gradebookUid, gradableObjectId); + if (assignment == null) { + throw new AssessmentNotFoundException("No gradebook item exists with gradable object id = " + gradableObjectId); + } + + if (!currentUserHasGradingPerm(gradebookUid)) { + log.warn("User attempted to save grades and comments without authorization"); + throw new GradingSecurityException(); + } + + // identify all of the students being updated first + final Map studentIdGradeDefMap = new HashMap<>(); + final Map studentIdToGradeMap = new HashMap<>(); + + for (final GradeDefinition gradeDef : gradeDefList) { + studentIdGradeDefMap.put(gradeDef.getStudentUid(), gradeDef); + studentIdToGradeMap.put(gradeDef.getStudentUid(), gradeDef.getGrade()); + } + + /* + * TODO: this check may be unnecessary if we're validating grades in the first step of the grade import wizard BUT, this can + * only be removed if the only place it's used is in the grade import (other places may not perform the grade validation prior + * to calling this + */ + // Check for invalid grades + final List invalidStudentUUIDs = identifyStudentsWithInvalidGrades(gradebookUid, studentIdToGradeMap); + if (CollectionUtils.isNotEmpty(invalidStudentUUIDs)) { + throw new InvalidGradeException( + "At least one grade passed to be updated is " + "invalid. No grades or comments were updated."); + } + + // Retrieve all existing grade records for the given students and assignment + final List existingGradeRecords = getAllAssignmentGradeRecordsForGbItem(gradableObjectId, + studentIdGradeDefMap.keySet()); + final Map studentIdGradeRecordMap = new HashMap<>(); + if (CollectionUtils.isNotEmpty(existingGradeRecords)) { + for (final AssignmentGradeRecord agr : existingGradeRecords) { + studentIdGradeRecordMap.put(agr.getStudentId(), agr); + } + } + + // Retrieve all existing comments for the given students and assignment + final List existingComments = getComments(assignment, studentIdGradeDefMap.keySet()); + final Map studentIdCommentMap = new HashMap<>(); + if (CollectionUtils.isNotEmpty(existingComments)) { + for (final Comment comment : existingComments) { + studentIdCommentMap.put(comment.getStudentId(), comment); + } + } + + Gradebook gradebook = getGradebook(gradebookUid); + + final boolean userHasGradeAllPerm = currentUserHasGradeAllPerm(gradebookUid); + final String graderId = sessionManager.getCurrentSessionUserId(); + final Date now = new Date(); + LetterGradePercentMapping mapping = null; + if (gradebook.getGradeType() == GradeType.LETTER) { + mapping = getLetterGradePercentMapping(gradebook); + } + + // Don't use a HashSet because you may have multiple Comments with null ID and the same comment at this point. + // The Comment object defines objects as equal if they have the same ID, comment text, and gradebook item. The + // only difference may be the student IDs + final List commentsToUpdate = new ArrayList<>(); + final Set eventsToAdd = new HashSet<>(); + final Set gradeRecordsToUpdate = new HashSet<>(); + for (final GradeDefinition gradeDef : gradeDefList) { + final String studentId = gradeDef.getStudentUid(); + + // use the grader ID from the definition if it is not null, otherwise use the current user ID + final String graderUid = gradeDef.getGraderUid() != null ? gradeDef.getGraderUid() : graderId; + // use the grade date from the definition if it is not null, otherwise use the current date + final Date gradedDate = gradeDef.getDateRecorded() != null ? gradeDef.getDateRecorded() : now; + + final boolean excuse = gradeDef.isExcused(); + + // check specific grading privileges if user does not have + // grade all perm + if (!userHasGradeAllPerm) { + if (!isUserAbleToGradeItemForStudent(gradebookUid, gradableObjectId, studentId)) { + log.warn("User {} attempted to save a grade for {} without authorization", graderId, studentId); + throw new GradingSecurityException(); + } + } + // Determine if the AssignmentGradeRecord needs to be updated + final String newGrade = StringUtils.trimToEmpty(gradeDef.getGrade()); + final Double convertedGrade = convertInputGradeToPoints(gradebook.getGradeType(), mapping, assignment.getPointsPossible(), + newGrade); + AssignmentGradeRecord gradeRec = studentIdGradeRecordMap.get(studentId); + boolean currentExcuse; + if (gradeRec == null) { + currentExcuse = false; + } else { + currentExcuse = BooleanUtils.toBoolean(gradeRec.getExcludedFromGrade()); + } + + if (gradeRec != null) { + final Double pointsEarned = gradeRec.getPointsEarned(); + if ((convertedGrade == null && pointsEarned != null) + || (convertedGrade != null && pointsEarned == null) + || (convertedGrade != null && pointsEarned != null && !convertedGrade.equals(pointsEarned)) + || (excuse != currentExcuse)) { + + gradeRec.setPointsEarned(convertedGrade); + gradeRec.setGraderId(graderUid); + gradeRec.setDateRecorded(gradedDate); + gradeRec.setExcludedFromGrade(excuse); + gradeRecordsToUpdate.add(gradeRec); + + // Add a GradingEvent, which stores the actual input grade rather than the converted one + final GradingEvent event = new GradingEvent(assignment, graderId, studentId, newGrade); + if (excuse != currentExcuse) { + event.setStatus(excuse ? + GradingEventStatus.GRADE_EXCLUDED : + GradingEventStatus.GRADE_INCLUDED); + } + eventsToAdd.add(event); + } + } else { + // if the grade is something other than null, add a new AGR + if (StringUtils.isNotBlank(newGrade) && (StringUtils.isNotBlank(gradeDef.getGrade()) || excuse != currentExcuse)) { + gradeRec = new AssignmentGradeRecord(assignment, studentId, convertedGrade); + gradeRec.setGraderId(graderUid); + gradeRec.setDateRecorded(gradedDate); + gradeRecordsToUpdate.add(gradeRec); + gradeRec.setExcludedFromGrade(excuse); + + // Add a GradingEvent, which stores the actual input grade rather than the converted one + final GradingEvent event = new GradingEvent(assignment, graderId, studentId, newGrade); + if (excuse != currentExcuse) { + event.setStatus(excuse ? + GradingEventStatus.GRADE_EXCLUDED: + GradingEventStatus.GRADE_INCLUDED); + } + eventsToAdd.add(event); + } + } + // Determine if the Comment needs to be updated + Comment comment = studentIdCommentMap.get(studentId); + final String newCommentText = StringUtils.trimToEmpty(gradeDef.getGradeComment()); + if (comment != null) { + final String existingCommentText = StringUtils.trimToEmpty(comment.getCommentText()); + final boolean existingCommentTextIsEmpty = existingCommentText.isEmpty(); + final boolean newCommentTextIsEmpty = newCommentText.isEmpty(); + if ((existingCommentTextIsEmpty && !newCommentTextIsEmpty) + || (!existingCommentTextIsEmpty && newCommentTextIsEmpty) + || (!existingCommentTextIsEmpty && !newCommentTextIsEmpty && !newCommentText.equals(existingCommentText))) { + comment.setCommentText(newCommentText); + comment.setGraderId(graderId); + comment.setDateRecorded(gradedDate); + commentsToUpdate.add(comment); + } + } else { + // If the comment is something other than null, add a new Comment + if (!newCommentText.isEmpty()) { + comment = new Comment(studentId, newCommentText, assignment); + comment.setGraderId(graderId); + comment.setDateRecorded(gradedDate); + commentsToUpdate.add(comment); + } + } + } + + // Save or update the necessary items + try { + gradeRecordsToUpdate.forEach(gradingPersistenceManager::saveAssignmentGradeRecord); + commentsToUpdate.forEach(gradingPersistenceManager::saveComment); + eventsToAdd.forEach(gradingPersistenceManager::saveGradingEvent); + } catch (final HibernateOptimisticLockingFailureException | StaleObjectStateException holfe) { + // TODO: Adrian How janky is this? + log.info("An optimistic locking failure occurred while attempting to save scores and comments for gb Item {}", gradableObjectId); + throw new StaleObjectModificationException(holfe); + } + } + + /** + * Helper method to retrieve Assignment by ID without stats for the given gradebook. Reduces code duplication in several areas. + * + * @param gradebookUID + * @param gradeableObjectID + * @return + */ + private GradebookAssignment getAssignmentWithoutStatsByID(String gradebookUID, Long gradeableObjectID) { + + return getAssignmentWithoutStats(gradebookUID, gradeableObjectID); + } + + /** + * + * @param gradeEntryType + * @param mapping + * @param gbItemPointsPossible + * @param grade + * @return given a generic String grade, converts it to the equivalent Double point value that will be stored in the db based upon the + * gradebook's grade entry type + */ + private Double convertInputGradeToPoints(final GradeType gradeEntryType, final LetterGradePercentMapping mapping, + final Double gbItemPointsPossible, final String grade) throws InvalidGradeException { + + if (StringUtils.isBlank(grade)) { + return null; + } + + Double convertedValue = null; + if (gradeEntryType == GradeType.POINTS) { + try { + final NumberFormat nbFormat = NumberFormat.getInstance(resourceLoader.getLocale()); + final Double pointValue = nbFormat.parse(grade).doubleValue(); + convertedValue = pointValue; + } catch (NumberFormatException | ParseException nfe) { + throw new InvalidGradeException("Invalid grade passed to convertInputGradeToPoints"); + } + } else if (gradeEntryType == GradeType.PERCENTAGE || + gradeEntryType == GradeType.LETTER) { + + // for letter or %-based grading, we need to calculate the equivalent point value + if (gbItemPointsPossible == null) { + throw new IllegalArgumentException("Null points possible passed" + + " to convertInputGradeToPoints for letter or % based grading"); + } + + Double percentage = null; + if (gradeEntryType == GradeType.LETTER) { + if (mapping == null) { + throw new IllegalArgumentException("No mapping passed to convertInputGradeToPoints for a letter-based gb"); + } + + if (mapping.getGradeMap() != null) { + // standardize the grade mapping + final String standardizedGrade = mapping.standardizeInputGrade(grade); + percentage = mapping.getValue(standardizedGrade); + if (percentage == null) { + throw new IllegalArgumentException("Invalid grade passed to convertInputGradeToPoints"); + } + } + } else { + try { + final NumberFormat nbFormat = NumberFormat.getInstance(resourceLoader.getLocale()); + percentage = nbFormat.parse(grade).doubleValue(); + } catch (NumberFormatException | ParseException nfe) { + throw new IllegalArgumentException("Invalid % grade passed to convertInputGradeToPoints"); + } + } + + convertedValue = calculateEquivalentPointValueForPercent(gbItemPointsPossible, percentage); + + } else { + throw new InvalidGradeException("invalid grade entry type passed to convertInputGradeToPoints"); + } + + return convertedValue; + } + + @Override + public GradeType getGradeEntryType(String gradebookUid) { + + if (gradebookUid == null) { + throw new IllegalArgumentException("null gradebookUid passed to getGradeEntryType"); + } + + return getGradebook(gradebookUid).getGradeType(); + } + + @Override + public Map getEnteredCourseGrade(final String gradebookUid) { + + final Gradebook thisGradebook = getGradebook(gradebookUid); + + final Long gradebookId = thisGradebook.getId(); + final CourseGrade courseGrade = getCourseGrade(gradebookId); + + Map enrollmentMap; + + final Map viewableEnrollmentsMap + = gradingAuthz.findMatchingEnrollmentsForViewableCourseGrade(gradebookUid, thisGradebook.getCategoryType(), null, null); + + enrollmentMap = new HashMap(); + + final Map enrollmentMapUid = new HashMap(); + for (final Iterator iter = viewableEnrollmentsMap.keySet().iterator(); iter.hasNext();) { + final EnrollmentRecord enr = (EnrollmentRecord) iter.next(); + enrollmentMap.put(enr.getUser().getUserUid(), enr); + enrollmentMapUid.put(enr.getUser().getUserUid(), enr); + } + + final List unfilteredRecords + = gradingPersistenceManager.getCourseGradeRecordsForCourseGrade(courseGrade.getId()); + + final List records = filterAndPopulateCourseGradeRecordsByStudents(courseGrade, unfilteredRecords, enrollmentMap.keySet()); + + final Map returnMap = new HashMap(); + + for (CourseGradeRecord cgr : records) { + if (cgr.getEnteredGrade() != null && !cgr.getEnteredGrade().equalsIgnoreCase("")) { + final EnrollmentRecord enr = (EnrollmentRecord) enrollmentMapUid.get(cgr.getStudentId()); + if (enr != null) { + returnMap.put(enr.getUser().getDisplayId(), cgr.getEnteredGrade()); + } + } + } + + return returnMap; + } + + @Override + public String getAssignmentScoreString(String gradebookUid, Long assignmentId, String studentUid) + throws AssessmentNotFoundException { + + final boolean studentRequestingOwnScore = sessionManager.getCurrentSessionUserId().equals(studentUid); + + if (gradebookUid == null || assignmentId == null || studentUid == null) { + throw new IllegalArgumentException("null parameter passed to getAssignment. Values are gradebookUid:" + + gradebookUid + " assignmentId:" + assignmentId + " studentUid:" + studentUid); + } + + Double assignmentScore = null; + + final GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentId); + if (assignment == null) { + throw new AssessmentNotFoundException( + "There is no assignment with id " + assignmentId + " in gradebook " + gradebookUid); + } + + if (!studentRequestingOwnScore && !isUserAbleToViewItemForStudent(gradebookUid, assignmentId, studentUid)) { + log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to retrieve grade for student {} for assignment {}", + getUserUid(), gradebookUid, studentUid, assignment.getName()); + throw new GradingSecurityException(); + } + + // If this is the student, then the assignment needs to have + // been released. + if (studentRequestingOwnScore && !assignment.getReleased()) { + log.error("AUTHORIZATION FAILURE: Student {} in gradebook {} attempted to retrieve score for unreleased assignment {}", + getUserUid(), gradebookUid, assignment.getName()); + throw new GradingSecurityException(); + } + + final AssignmentGradeRecord gradeRecord = getAssignmentGradeRecord(assignment, studentUid); + log.debug("gradeRecord={}", gradeRecord); + if (gradeRecord != null) { + assignmentScore = gradeRecord.getPointsEarned(); + } + + // TODO: when ungraded items is considered, change column to ungraded-grade + // its possible that the assignment score is null + if (assignmentScore == null) { + return null; + } + + // avoid scientific notation on large scores by using a formatter + final NumberFormat numberFormat = NumberFormat.getInstance(resourceLoader.getLocale()); + final DecimalFormat df = (DecimalFormat) numberFormat; + df.setGroupingUsed(false); + + log.debug("assignment score before formatting: {}", assignmentScore); + + String formatted = df.format(assignmentScore); + log.debug("assignment score after formatting: {}", formatted); + return formatted; + } + + @Override + public String getAssignmentScoreString(final String gradebookUid, final String assignmentName, final String studentUid) + throws AssessmentNotFoundException { + + if (gradebookUid == null || assignmentName == null || studentUid == null) { + throw new IllegalArgumentException("null parameter passed to getAssignment. Values are gradebookUid:" + + gradebookUid + " assignmentName:" + assignmentName + " studentUid:" + studentUid); + } + + final GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentName); + + if (assignment == null) { + throw new AssessmentNotFoundException("There is no assignment with name " + assignmentName + " in gradebook " + gradebookUid); + } + + return getAssignmentScoreString(gradebookUid, assignment.getId(), studentUid); + } + + @Override + public String getAssignmentScoreStringByNameOrId(final String gradebookUid, final String assignmentName, final String studentUid) + throws AssessmentNotFoundException { + String score = null; + try { + score = getAssignmentScoreString(gradebookUid, assignmentName, studentUid); + } catch (final AssessmentNotFoundException e) { + // Don't fail on this exception + log.debug("Assessment not found by name", e); + } catch (final GradingSecurityException gse) { + log.warn("User {} does not have permission to retrieve score for assignment {}", studentUid, assignmentName, gse); + return null; + } + + if (score == null) { + // Try to get the assignment by id + if (NumberUtils.isCreatable(assignmentName)) { + final Long assignmentId = NumberUtils.toLong(assignmentName, -1L); + try { + score = getAssignmentScoreString(gradebookUid, assignmentId, studentUid); + } catch (AssessmentNotFoundException anfe) { + log.debug("Assessment could not be found for gradebook id {} and assignment id {} and student id {}", gradebookUid, assignmentName, studentUid); + } + } + } + return score; + } + + @Override + public void setAssignmentScoreString(String gradebookUid, Long assignmentId, String studentUid, String score, String clientServiceDescription) + throws AssessmentNotFoundException { + + final GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentId); + if (assignment == null) { + throw new AssessmentNotFoundException( + "There is no assignment with id " + assignmentId + " in gradebook " + gradebookUid); + } + if (assignment.getExternallyMaintained()) { + log.error( + "AUTHORIZATION FAILURE: User {} in gradebook {} attempted to grade externally maintained assignment {} from {}", + getUserUid(), gradebookUid, assignmentId, clientServiceDescription); + throw new GradingSecurityException(); + } + + if (!isUserAbleToGradeItemForStudent(gradebookUid, assignment.getId(), studentUid)) { + log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to grade student {} from {} for item {}", + getUserUid(), gradebookUid, studentUid, clientServiceDescription, assignmentId); + throw new GradingSecurityException(); + } + + final Date now = new Date(); + final String graderId = sessionManager.getCurrentSessionUserId(); + AssignmentGradeRecord gradeRecord = getAssignmentGradeRecord(assignment, studentUid); + if (gradeRecord == null) { + // Creating a new grade record. + gradeRecord = new AssignmentGradeRecord(assignment, studentUid, convertStringToDouble(score)); + // TODO: test if it's ungraded item or not. if yes, set ungraded grade for this record. if not, need validation?? + } else { + // TODO: test if it's ungraded item or not. if yes, set ungraded grade for this record. if not, need validation?? + gradeRecord.setPointsEarned(convertStringToDouble(score)); + } + gradeRecord.setGraderId(graderId); + gradeRecord.setDateRecorded(now); + gradingPersistenceManager.saveAssignmentGradeRecord(gradeRecord); + + gradingPersistenceManager.saveGradingEvent(new GradingEvent(assignment, graderId, studentUid, score)); + + // Post an event in SAKAI_EVENT table + postUpdateGradeEvent(gradebookUid, assignment.getName(), studentUid, convertStringToDouble(score)); + + log.debug("Score updated in gradebookUid={}, assignmentId={} by userUid={} from client={}, new score={}", gradebookUid, assignmentId, getUserUid() + , clientServiceDescription, score); + } + + @Override + public void setAssignmentScoreString(String gradebookUid, String assignmentName, String studentUid, String score, String clientServiceDescription) + throws AssessmentNotFoundException { + + GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentName); + + if (assignment == null) { + throw new AssessmentNotFoundException("There is no assignment with name " + assignmentName + " in gradebook " + gradebookUid); + } + + setAssignmentScoreString(gradebookUid, assignment.getId(), studentUid, score, clientServiceDescription); + } + + @Override + public void finalizeGrades(final String gradebookUid) { + + if (!gradingAuthz.isUserAbleToGradeAll(gradebookUid)) { + log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to finalize grades", getUserUid(), gradebookUid); + throw new GradingSecurityException(); + } + finalizeNullGradeRecords(getGradebook(gradebookUid)); + } + + @Override + public String getLowestPossibleGradeForGbItem(final String gradebookUid, final Long gradebookItemId) { + + if (gradebookUid == null || gradebookItemId == null) { + throw new IllegalArgumentException("Null gradebookUid and/or gradebookItemId " + + "passed to getLowestPossibleGradeForGbItem. gradebookUid:" + + gradebookUid + " gradebookItemId:" + gradebookItemId); + } + + final GradebookAssignment gbItem = getAssignmentWithoutStatsByID(gradebookUid, gradebookItemId); + + if (gbItem == null) { + throw new AssessmentNotFoundException("No gradebook item found with id " + gradebookItemId); + } + + final Gradebook gradebook = gbItem.getGradebook(); + + // double check that user has some permission to access gb items in this site + if (!isUserAbleToViewAssignments(gradebookUid) && !currentUserHasViewOwnGradesPerm(gradebookUid)) { + throw new GradingSecurityException(); + } + + String lowestPossibleGrade = null; + + if (gbItem.getUngraded()) { + lowestPossibleGrade = null; + } else if (gradebook.getGradeType() == GradeType.PERCENTAGE || + gradebook.getGradeType() == GradeType.POINTS) { + lowestPossibleGrade = "0"; + } else if (gbItem.getGradebook().getGradeType() == GradeType.LETTER) { + final LetterGradePercentMapping mapping = getLetterGradePercentMapping(gradebook); + lowestPossibleGrade = mapping.getGrade(0d); + } + + return lowestPossibleGrade; + } + + @Override + public List getCategoryDefinitions(String gradebookUid) { + + if (gradebookUid == null) { + throw new IllegalArgumentException("Null gradebookUid passed to getCategoryDefinitions"); + } + + if (!isUserAbleToViewAssignments(gradebookUid)) { + log.warn("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to retrieve all categories without permission", getUserUid(), + gradebookUid); + throw new GradingSecurityException(); + } + + return getCategories(getGradebook(gradebookUid).getId()) + .stream().map(this::getCategoryDefinition).collect(Collectors.toList()); + } + + private CategoryDefinition getCategoryDefinition(final Category category) { + + final CategoryDefinition categoryDef = new CategoryDefinition(); + if (category != null) { + categoryDef.setId(category.getId()); + categoryDef.setName(category.getName()); + categoryDef.setWeight(category.getWeight()); + categoryDef.setDropLowest(category.getDropLowest()); + categoryDef.setDropHighest(category.getDropHighest()); + categoryDef.setKeepHighest(category.getKeepHighest()); + categoryDef.setAssignmentList(getAssignments(category.getGradebook().getUid(), category.getName())); + categoryDef.setDropKeepEnabled(category.isDropScores()); + categoryDef.setExtraCredit(category.getExtraCredit()); + categoryDef.setEqualWeight(category.getEqualWeightAssignments()); + categoryDef.setCategoryOrder(category.getCategoryOrder()); + } + + return categoryDef; + } + + /** + * + * @param gradebookId + * @param studentUids + * @return a map of studentUid to a list of that student's AssignmentGradeRecords for the given studentUids list in the given gradebook. + * the grade records are all recs for assignments that are not removed and have a points possible > 0 + */ + private Map> getGradeRecordMapForStudents(Long gradebookId, Collection studentUids) { + + final Map> filteredGradeRecs = new HashMap<>(); + final List allGradeRecs = gradingPersistenceManager.getAssignmentGradeRecordsForGradebookAndStudents(gradebookId, studentUids); + + for (final AssignmentGradeRecord gradeRec : allGradeRecs) { + if (studentUids.contains(gradeRec.getStudentId())) { + final String studentId = gradeRec.getStudentId(); + List gradeRecList = filteredGradeRecs.get(studentId); + if (gradeRecList == null) { + gradeRecList = new ArrayList<>(); + gradeRecList.add(gradeRec); + filteredGradeRecs.put(studentId, gradeRecList); + } else { + gradeRecList.add(gradeRec); + filteredGradeRecs.put(studentId, gradeRecList); + } + } + } + + return filteredGradeRecs; + } + + /** + * + * @param session + * @param gradebookId + * @return a list of Assignments that have not been removed, are "counted", graded, and have a points possible > 0 + */ + private List getCountedAssignments(Long gradebookId) { + + List assignList = new ArrayList<>(); + + List results = gradingPersistenceManager.getCountedAndGradedAssignmentsForGradebook(gradebookId); + + if (results != null) { + // making sure there's no invalid points possible for normal assignments + for (final GradebookAssignment a : results) { + + if (a.getPointsPossible() != null && a.getPointsPossible() > 0) { + assignList.add(a); + } + } + } + + return assignList; + } + + /** + * set the droppedFromGrade attribute of each of the n highest and the n lowest scores of a student based on the assignment's category + * + * @param gradeRecords + * + * NOTE: When the UI changes, this needs to be made private again + */ + public void applyDropScores(final Collection gradeRecords, GradingCategoryType categoryType) { + + if (gradeRecords == null || gradeRecords.size() < 1) { + return; + } + final long start = System.currentTimeMillis(); + + final Set studentIds = new HashSet<>(); + final List categories = new ArrayList<>(); + final Map> gradeRecordMap = new HashMap<>(); + for (final AssignmentGradeRecord gradeRecord : gradeRecords) { + + if (gradeRecord == null || gradeRecord.getPointsEarned() == null) { + // don't consider grades that have null pointsEarned (this occurs when a + // previously entered score for an assignment is removed; record stays in + // database) + continue; + } + + // reset + gradeRecord.setDroppedFromGrade(false); + + if (categoryType == GradingCategoryType.NO_CATEGORY) { + continue; + } + + final GradebookAssignment assignment = gradeRecord.getAssignment(); + if (assignment.getUngraded() // GradeType.LETTER + || assignment.getNotCounted() // don't consider grades that are not counted toward course grade + || assignment.getItemType().equals(GradebookAssignment.item_type_adjustment) + || assignment.getRemoved()) { + continue; + } + // get all the students represented + final String studentId = gradeRecord.getStudentId(); + studentIds.add(studentId); + // get all the categories represented + final Category cat = gradeRecord.getAssignment().getCategory(); + if (cat != null) { + if (!categories.contains(cat)) { + categories.add(cat); + } + List gradeRecordsByCatAndStudent = gradeRecordMap.get(studentId + cat.getId()); + if (gradeRecordsByCatAndStudent == null) { + gradeRecordsByCatAndStudent = new ArrayList<>(); + gradeRecordsByCatAndStudent.add(gradeRecord); + gradeRecordMap.put(studentId + cat.getId(), gradeRecordsByCatAndStudent); + } else { + gradeRecordsByCatAndStudent.add(gradeRecord); + } + } + } + + if (categories.size() < 1 || categoryType == GradingCategoryType.NO_CATEGORY) { + return; + } + for (final Category cat : categories) { + final Integer dropHighest = cat.getDropHighest(); + Integer dropLowest = cat.getDropLowest(); + final Integer keepHighest = cat.getKeepHighest(); + final Long catId = cat.getId(); + + if ((dropHighest != null && dropHighest > 0) || (dropLowest != null && dropLowest > 0) + || (keepHighest != null && keepHighest > 0)) { + + for (final String studentId : studentIds) { + // get the student's gradeRecords for this category + final List gradesByCategory = new ArrayList<>(); + final List gradeRecordsByCatAndStudent = gradeRecordMap.get(studentId + cat.getId()); + if (gradeRecordsByCatAndStudent != null) { + for (AssignmentGradeRecord agr : gradeRecordsByCatAndStudent) { + if (!BooleanUtils.toBoolean(agr.getExcludedFromGrade())) { + gradesByCategory.add(agr); + } + } + + final int numGrades = gradesByCategory.size(); + + if (dropHighest > 0 && numGrades > dropHighest + dropLowest) { + for (int i = 0; i < dropHighest; i++) { + final AssignmentGradeRecord highest = Collections.max(gradesByCategory, + AssignmentGradeRecord.numericComparator); + highest.setDroppedFromGrade(true); + gradesByCategory.remove(highest); + log.debug("dropHighest applied to {}", highest); + } + } + + if (keepHighest > 0 && numGrades > (gradesByCategory.size() - keepHighest)) { + dropLowest = gradesByCategory.size() - keepHighest; + } + + if (dropLowest > 0 && numGrades > dropLowest + dropHighest) { + for (int i = 0; i < dropLowest; i++) { + AssignmentGradeRecord lowest = Collections.min(gradesByCategory, + AssignmentGradeRecord.numericComparator); + lowest.setDroppedFromGrade(true); + gradesByCategory.remove(lowest); + log.debug("dropLowest applied to {}", lowest); + } + } + } + } + log.debug("processed {} student in category {}", studentIds.size(), cat.getId()); + } + } + + log.debug("GradebookManager.applyDropScores took {} millis to execute", (System.currentTimeMillis() - start)); + } + + @Override + public PointsPossibleValidation isPointsPossibleValid(String gradebookUid, Assignment gradebookItem, Double pointsPossible) { + + if (gradebookUid == null) { + throw new IllegalArgumentException("Null gradebookUid passed to isPointsPossibleValid"); + } + if (gradebookItem == null) { + throw new IllegalArgumentException("Null gradebookItem passed to isPointsPossibleValid"); + } + + // At this time, all gradebook items follow the same business rules for + // points possible (aka relative weight in % gradebooks) so special logic + // using the properties of the gradebook item is unnecessary. + // In the future, we will have the flexibility to change + // that behavior without changing the method signature + + // the points possible must be a non-null value greater than 0 with + // no more than 2 decimal places + + if (pointsPossible == null) { + return PointsPossibleValidation.INVALID_NULL_VALUE; + } + + if (pointsPossible <= 0) { + return PointsPossibleValidation.INVALID_NUMERIC_VALUE; + } + // ensure there are no more than 2 decimal places + BigDecimal bd = new BigDecimal(pointsPossible); + bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP); // Two decimal places + double roundedVal = bd.doubleValue(); + double diff = pointsPossible - roundedVal; + if (diff != 0) { + return PointsPossibleValidation.INVALID_DECIMAL; + } + + return PointsPossibleValidation.VALID; + } + + /** + * + * @param doubleAsString + * @return a locale-aware Double value representation of the given String + * @throws ParseException + */ + private Double convertStringToDouble(final String doubleAsString) { + + if (StringUtils.isBlank(doubleAsString)) { + return null; + } + + Double scoreAsDouble = null; + try { + NumberFormat numberFormat = NumberFormat.getInstance(resourceLoader.getLocale()); + Number numericScore = numberFormat.parse(doubleAsString.trim()); + return numericScore.doubleValue(); + } catch (final ParseException e) { + log.error("Failed to convert {}: {}", doubleAsString, e.toString()); + return null; + } + } + + /** + * Get a list of assignments in the gradebook attached to the given category. Note that each assignment only knows the category by name. + * + *

+ * Note also that this is different to {@link BaseHibernateManager#getAssignmentsForCategory(Long)} because this method returns the + * shared GradebookAssignment object. + * + * @param gradebookUid + * @param categoryName + * @return + */ + private List getAssignments(String gradebookUid, String categoryName) { + + return getAssignments(gradebookUid).stream() + .filter(a -> StringUtils.equals(a.getCategoryName(), categoryName)) + .collect(Collectors.toList()); + } + + /** + * Post an event to Sakai's event table + * + * @param gradebookUid + * @param assignmentName + * @param studentUid + * @param pointsEarned + * @return + */ + private void postUpdateGradeEvent(String gradebookUid, String assignmentName, String studentUid, Double pointsEarned) { + + postEvent("gradebook.updateItemScore", + "/gradebook/" + gradebookUid + "/" + assignmentName + "/" + studentUid + "/" + pointsEarned + "/student"); + } + + /** + * Retrieves the calculated average course grade. + */ + @Override + @Transactional + public String getAverageCourseGrade(final String gradebookUid) { + + if (gradebookUid == null) { + throw new IllegalArgumentException("Null gradebookUid passed to getAverageCourseGrade"); + } + // Check user has permission to invoke method. + if (!currentUserHasGradeAllPerm(gradebookUid)) { + StringBuilder sb = new StringBuilder() + .append("User ") + .append(sessionManager.getCurrentSessionUserId()) + .append(" attempted to access the average course grade without permission in gb ") + .append(gradebookUid) + .append(" using gradebookService.getAverageCourseGrade"); + throw new GradingSecurityException(sb.toString()); + } + + String courseGradeLetter = null; + Gradebook gradebook = getGradebook(gradebookUid); + if (gradebook != null) { + CourseGrade courseGrade = getCourseGrade(gradebook.getId()); + Set studentUids = getAllStudentUids(gradebookUid); + // This call handles the complex rules of which assignments and grades to include in the calculation + List courseGradeRecs = getPointsEarnedCourseGradeRecords(courseGrade, studentUids); + if (courseGrade != null) { + // Calculate the course mean grade whether the student grade was manually entered or auto-calculated. + courseGrade.calculateStatistics(courseGradeRecs, studentUids.size()); + if (courseGrade.getMean() != null) { + courseGradeLetter = gradebook.getSelectedGradeMapping().getMappedGrade(courseGrade.getMean()); + } + } + + } + return courseGradeLetter; + } + + /** + * Updates the order of an assignment + * + * @see GradingService.updateAssignmentOrder(java.lang.String gradebookUid, java.lang.Long assignmentId, java.lang.Integer order) + */ + @Override + @Transactional + public void updateAssignmentOrder(final String gradebookUid, final Long assignmentId, Integer order) { + + if (!gradingAuthz.isUserAbleToEditAssessments(gradebookUid)) { + log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to change the order of assignment {}", getUserUid(), + gradebookUid, assignmentId); + throw new GradingSecurityException(); + } + + if (order == null) { + throw new IllegalArgumentException("Order cannot be null"); + } + + final Long gradebookId = getGradebook(gradebookUid).getId(); + + // get all assignments for this gradebook + final List assignments = getAssignments(gradebookId, SortType.SORT_BY_SORTING, true); + + // find the assignment + Optional optTarget = assignments.stream().filter(a -> a.getId().equals(assignmentId)).findAny(); + + if (!optTarget.isPresent()) { + throw new IllegalArgumentException("No assignment for id " + assignmentId); + } + + GradebookAssignment target = optTarget.get(); + + // adjust order to be within bounds + if (order < 0) { + order = 0; + } else if (order > assignments.size()) { + order = assignments.size(); + } + + // add the assignment to the list via a 'pad, remove, add' approach + assignments.add(null); // ensure size remains the same for the remove + assignments.remove(target); // remove item + assignments.add(order, target); // add at ordered position, will shuffle others along + + // the assignments are now in the correct order within the list, we just need to update the sort order for each one + // create a new list for the assignments we need to update in the database + final List assignmentsToUpdate = new ArrayList<>(); + + int i = 0; + for (final GradebookAssignment a : assignments) { + + // skip if null + if (a == null) { + continue; + } + + // if the sort order is not the same as the counter, update the order and add to the other list + // this allows us to skip items that have not had their position changed and saves some db work later on + // sort order may be null if never previously sorted, so give it the current index + if (a.getSortOrder() == null || !a.getSortOrder().equals(i)) { + a.setSortOrder(i); + assignmentsToUpdate.add(a); + } + + i++; + } + + // do the updates + assignmentsToUpdate.forEach(this::updateAssignment); + } + + /** + * {@inheritDoc} + */ + @Override + public List getGradingEvents(final String studentId, final long assignmentId) { + + log.debug("getGradingEvents called for studentId: {}", studentId); + + if (studentId == null) { + log.debug("No student id was specified. Returning an empty GradingEvents list"); + return Collections.emptyList(); + } + + return gradingPersistenceManager.getGradingEventsForAssignment(assignmentId, studentId); + } + + @Override + @Transactional(readOnly = true) + public Optional calculateCategoryScore(final Object gradebook, final String studentUuid, + final CategoryDefinition category, final List categoryAssignments, + final Map gradeMap, final boolean includeNonReleasedItems) { + + final Gradebook gb = (Gradebook) gradebook; + + // used for translating letter grades + final Map gradingSchema = gb.getSelectedGradeMapping().getGradeMap(); + + // collect the data and turn it into a list of AssignmentGradeRecords + // this is the info that is compatible with both applyDropScores and the calculateCategoryScore method + final List gradeRecords = new ArrayList<>(); + for (final Assignment assignment : categoryAssignments) { + + final Long assignmentId = assignment.getId(); + + final String rawGrade = gradeMap.get(assignmentId); + final Double pointsPossible = assignment.getPoints(); + Double grade; + + // determine the grade we should be using depending on the grading type + if (gb.getGradeType() == GradeType.PERCENTAGE) { + grade = calculateEquivalentPointValueForPercent(pointsPossible, NumberUtils.createDouble(rawGrade)); + } else if (gb.getGradeType() == GradeType.LETTER) { + grade = gradingSchema.get(rawGrade); + } else { + grade = NumberUtils.createDouble(rawGrade); + } + + // recreate the category (required fields only) + final Category c = new Category(); + c.setId(category.getId()); + c.setDropHighest(category.getDropHighest()); + c.setDropLowest(category.getDropLowest()); + c.setKeepHighest(category.getKeepHighest()); + c.setEqualWeightAssignments(category.getEqualWeight()); + + // recreate the assignment (required fields only) + final GradebookAssignment a = new GradebookAssignment(); + a.setPointsPossible(assignment.getPoints()); + a.setUngraded(assignment.getUngraded()); + a.setCounted(assignment.getCounted()); + a.setExtraCredit(assignment.getExtraCredit()); + a.setReleased(assignment.getReleased()); + a.setRemoved(false); // shared.GradebookAssignment doesn't include removed so this will always be false + a.setGradebook(gb); + a.setCategory(c); + a.setId(assignment.getId()); // store the id so we can find out later which grades were dropped, if any + + // create the AGR + final AssignmentGradeRecord gradeRecord = new AssignmentGradeRecord(a, studentUuid, grade); + + if (!a.getNotCounted()) { + gradeRecords.add(gradeRecord); + } + } + + return calculateCategoryScore(studentUuid, category.getId(), gradeRecords, includeNonReleasedItems, gb.getCategoryType(), category.getEqualWeight()); + } + + + @Override + public Optional calculateCategoryScore(Long gradebookId, String studentUuid, Long categoryId, + boolean includeNonReleasedItems, GradingCategoryType categoryType, Boolean equalWeightAssignments) { + + // get all grade records for the student + Map> gradeRecMap = getGradeRecordMapForStudents(gradebookId, Collections.singletonList(studentUuid)); + + // apply the settings + List gradeRecords = gradeRecMap.get(studentUuid); + + return calculateCategoryScore(studentUuid, categoryId, gradeRecords, includeNonReleasedItems, categoryType, equalWeightAssignments); + } + + /** + * Does the heavy lifting for the category calculations. Requires the List of AssignmentGradeRecord so that we can applyDropScores. + * + * @param studentUuid the student uuid + * @param categoryId the category id we are interested in + * @param gradeRecords all grade records for the student + * @return + */ + private Optional calculateCategoryScore(final String studentUuid, final Long categoryId, + final List gradeRecords, final boolean includeNonReleasedItems, final GradingCategoryType categoryType, Boolean equalWeightAssignments) { + + // validate + if (gradeRecords == null) { + log.debug("No grade records for student: {}. Nothing to do.", studentUuid); + return Optional.empty(); + } + + if (categoryId == null) { + log.debug("No category supplied, nothing to do."); + return Optional.empty(); + } + + // setup + int numScored = 0; + int numOfAssignments = 0; + BigDecimal totalEarned = new BigDecimal("0"); + BigDecimal totalEarnedMean = new BigDecimal("0"); + BigDecimal totalPossible = new BigDecimal("0"); + + // apply any drop/keep settings for this category + applyDropScores(gradeRecords, categoryType); + + // find the records marked as dropped (highest/lowest) before continuing, + // as gradeRecords will be modified in place after this and these records will be removed + final List droppedItemIds = gradeRecords.stream() + .filter(AssignmentGradeRecord::getDroppedFromGrade) + .map(agr -> agr.getAssignment().getId()) + .collect(Collectors.toList()); + + // Since all gradeRecords for the student are passed in, not just for this category, + // plus they may not meet the criteria for including in the calculation, + // this list is filtered down according to the following rules: + // Rule 1. remove gradeRecords that don't match the given category + // Rule 2. the assignment must have points to be assigned + // Rule 3. there is a non blank grade for the student + // Rule 4. the assignment is included in course grade calculations + // Rule 5. the assignment is released to the student (instructor gets to see category grade regardless of release status; student does not) + // Rule 6. the grade is not dropped from the calc + // Rule 7. extra credit items have their grade value counted only. Their total points possible does not apply to the calculations + log.debug("categoryId: {}", categoryId); + + gradeRecords.removeIf(gradeRecord -> { + + final GradebookAssignment assignment = gradeRecord.getAssignment(); + + // remove if not for this category (rule 1) + if (assignment.getCategory() == null) { + return true; + } + if (categoryId.longValue() != assignment.getCategory().getId().longValue()) { + return true; + } + + final boolean excluded = BooleanUtils.toBoolean(gradeRecord.getExcludedFromGrade()); + // remove if the assignment/graderecord doesn't meet the criteria for the calculation (rule 2-6) + if (excluded || assignment.getPointsPossible() == null || gradeRecord.getPointsEarned() == null || !assignment.getCounted() + || (!assignment.getReleased() && !includeNonReleasedItems) || gradeRecord.getDroppedFromGrade()) { + return true; + } + + return false; + }); + + log.debug("gradeRecords.size(): {}", gradeRecords.size()); + + // pre-calculation + // Rule 1. If category only has a single EC item, don't try to calculate category total. + if (gradeRecords.size() == 1 && gradeRecords.get(0).getAssignment().getExtraCredit()) { + return Optional.empty(); + } + + // iterate the filtered list and set the variables for the calculation + for (AssignmentGradeRecord gradeRecord : gradeRecords) { + + GradebookAssignment assignment = gradeRecord.getAssignment(); + BigDecimal possiblePoints = new BigDecimal(assignment.getPointsPossible().toString()); + + // EC item, don't count points possible + if (!assignment.getExtraCredit()) { + totalPossible = totalPossible.add(possiblePoints); + numOfAssignments++; + numScored++; + } + + // sanitise grade, null values to "0"; + String gradeString = (gradeRecord.getPointsEarned() != null) ? String.valueOf(gradeRecord.getPointsEarned()) : "0"; + BigDecimal grade = new BigDecimal(gradeString); + + // update total points earned + totalEarned = totalEarned.add(grade); + + // keep running total of averages in case the category is equal weighted + try { + totalEarnedMean + = totalEarnedMean.add(grade.divide(possiblePoints, GradingService.MATH_CONTEXT)); + } catch (ArithmeticException ae) { + totalEarnedMean = totalEarnedMean.add(new BigDecimal("0")); + } + } + + if (numScored == 0 || numOfAssignments == 0 || totalPossible.doubleValue() == 0) { + return Optional.empty(); + } + + BigDecimal mean = totalEarned.divide(new BigDecimal(numScored), GradingService.MATH_CONTEXT) + .divide((totalPossible.divide(new BigDecimal(numOfAssignments), GradingService.MATH_CONTEXT)), + GradingService.MATH_CONTEXT) + .multiply(new BigDecimal("100")); + + if (equalWeightAssignments == null) { + Category category = getCategory(categoryId); + equalWeightAssignments = category.getEqualWeightAssignments(); + } + if (equalWeightAssignments) { + mean = totalEarnedMean.divide(new BigDecimal(numScored), GradingService.MATH_CONTEXT).multiply(new BigDecimal("100")); + } + + return Optional.of(new CategoryScoreData(mean.doubleValue(), droppedItemIds)); + } + + @Override + public CourseGradeTransferBean getCourseGradeForStudent(String gradebookUid, String userUuid) { + return this.getCourseGradeForStudents(gradebookUid, Collections.singletonList(userUuid)).get(userUuid); + } + + @Override + public Map getCourseGradeForStudents(String gradebookUid, List userUuids) { + + try { + GradeMapping gradeMap = getGradebook(gradebookUid).getSelectedGradeMapping(); + return getCourseGradeForStudents(gradebookUid, userUuids, gradeMap.getGradeMap()); + } catch (Exception e) { + log.error("Error in getCourseGradeForStudents : {}", e.toString()); + return Collections.emptyMap(); + } + } + + @Override + @Transactional + public Map getCourseGradeForStudents(final String gradebookUid, + final List userUuids, final Map gradeMap) { + + final Map rval = new HashMap<>(); + + try { + final Gradebook gradebook = getGradebook(gradebookUid); + + // if not released, and not instructor or TA, don't do any work + // note that this will return a course grade for Instructor and TA even if not released, see SAK-30119 + if (!gradebook.getCourseGradeDisplayed() && !(currentUserHasEditPerm(gradebookUid) || currentUserHasGradingPerm(gradebookUid))) { + return rval; + } + + final List assignments = getAssignmentsCounted(gradebook.getId()); + + // this takes care of drop/keep scores + final List gradeRecords = getPointsEarnedCourseGradeRecords(getCourseGrade(gradebook.getId()), userUuids); + + // gradeMap MUST be sorted for the grade mapping to apply correctly + final Map sortedGradeMap = GradeMappingDefinition.sortGradeMapping(gradeMap); + + gradeRecords.forEach(gr -> { + + final CourseGradeTransferBean cg = new CourseGradeTransferBean(); + + // ID of the course grade item + cg.setId(gr.getCourseGrade().getId()); + + // set entered grade + cg.setEnteredGrade(gr.getEnteredGrade()); + + // set date recorded + cg.setDateRecorded(gr.getDateRecorded()); + + // set entered points + cg.setEnteredPoints(gr.getEnteredPoints()); + + if (!assignments.isEmpty()) { + + boolean showCalculatedGrade = serverConfigurationService.getBoolean("gradebook.coursegrade.showCalculatedGrade", true); + + // calculated grade + // may be null if no grade entries to calculate + Double calculatedGrade = showCalculatedGrade == true ? gr.getAutoCalculatedGrade() : gr.getEnteredPoints(); + + if (calculatedGrade == null) { + calculatedGrade = gr.getAutoCalculatedGrade(); + } + + if (calculatedGrade != null) { + cg.setCalculatedGrade(calculatedGrade.toString()); + + // SAK-33997 Adjust the rounding of the calculated grade so we get the appropriate + // grade mapping + BigDecimal bd = new BigDecimal(calculatedGrade) + .setScale(10, RoundingMode.HALF_UP) + .setScale(2, RoundingMode.HALF_UP); + calculatedGrade = bd.doubleValue(); + } + + // mapped grade + final String mappedGrade = GradeMapping.getMappedGrade(sortedGradeMap, calculatedGrade); + log.debug("calculatedGrade: {} -> mappedGrade: {}", calculatedGrade, mappedGrade); + cg.setMappedGrade(mappedGrade); + + // points + cg.setPointsEarned(gr.getPointsEarned()); // synonymous with gradeRecord.getCalculatedPointsEarned() + cg.setTotalPointsPossible(gr.getTotalPointsPossible()); + } + rval.put(gr.getStudentId(), cg); + }); + } catch (final Exception e) { + log.error("Error in getCourseGradeForStudents: {}", e.toString()); + } + return rval; + } + + @Override + public List getViewableSections(final String gradebookUid) { + + return gradingAuthz.getViewableSections(gradebookUid); + } + + @Override + public void updateGradebookSettings(final String gradebookUid, final GradebookInformation gbInfo) { + + if (gradebookUid == null) { + throw new IllegalArgumentException("null gradebookUid " + gradebookUid); + } + + // must be instructor type person + if (!currentUserHasEditPerm(gradebookUid)) { + log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to edit gb information", getUserUid(), gradebookUid); + throw new GradingSecurityException("You do not have permission to edit gradebook information in site " + gradebookUid); + } + + final Gradebook gradebook = getGradebook(gradebookUid); + if (gradebook == null) { + throw new IllegalArgumentException("There is no gradebook associated with this id: " + gradebookUid); + } + + final Map bottomPercents = gbInfo.getSelectedGradingScaleBottomPercents(); + + // Before we do any work, check if any existing course grade overrides might be left in an unmappable state + final List courseGradeOverrides = gradingPersistenceManager.getCourseGradeOverrides(gradebook); + courseGradeOverrides.forEach(cgr -> { + + if (!bottomPercents.containsKey(cgr.getEnteredGrade())) { + throw new UnmappableCourseGradeOverrideException( + "The grading schema could not be updated as it would leave some course grade overrides in an unmappable state."); + } + }); + + // iterate all available grademappings for this gradebook and set the one that we have the ID and bottomPercents for + final Set gradeMappings = gradebook.getGradeMappings(); + gradeMappings.forEach(gradeMapping -> { + + if (StringUtils.equals(Long.toString(gradeMapping.getId()), gbInfo.getSelectedGradeMappingId())) { + gradebook.setSelectedGradeMapping(gradeMapping); + + // update the map values + updateGradeMapping(gradeMapping.getId(), bottomPercents); + } + }); + + // set grade type, but only if sakai.property is true OR user is admin + final boolean gradeTypeAvailForNonAdmins = serverConfigurationService.getBoolean("gradebook.settings.gradeEntry.showToNonAdmins", true); + if (gradeTypeAvailForNonAdmins || securityService.isSuperUser()) { + gradebook.setGradeType(gbInfo.getGradeType()); + } + + // set category type + gradebook.setCategoryType(gbInfo.getCategoryType()); + + // set display release items to students + gradebook.setAssignmentsDisplayed(gbInfo.getDisplayReleasedGradeItemsToStudents()); + + // set course grade display settings + gradebook.setCourseGradeDisplayed(gbInfo.getCourseGradeDisplayed()); + gradebook.setCourseLetterGradeDisplayed(gbInfo.getCourseLetterGradeDisplayed()); + gradebook.setCoursePointsDisplayed(gbInfo.getCoursePointsDisplayed()); + gradebook.setCourseAverageDisplayed(gbInfo.getCourseAverageDisplayed()); + + // set stats display settings + gradebook.setAssignmentStatsDisplayed(gbInfo.getAssignmentStatsDisplayed()); + gradebook.setCourseGradeStatsDisplayed(gbInfo.getCourseGradeStatsDisplayed()); + + // set allow students to compare grades + gradebook.setAllowStudentsToCompareGrades(gbInfo.getAllowStudentsToCompareGrades()); + gradebook.setComparingDisplayStudentNames(gbInfo.getComparingDisplayStudentNames()); + gradebook.setComparingDisplayStudentSurnames(gbInfo.getComparingDisplayStudentSurnames()); + gradebook.setComparingDisplayTeacherComments(gbInfo.getComparingDisplayTeacherComments()); + gradebook.setComparingIncludeAllGrades(gbInfo.getComparingIncludeAllGrades()); + gradebook.setComparingRandomizeDisplayedData(gbInfo.getComparingRandomizeDisplayedData()); + + final List newCategoryDefinitions = gbInfo.getCategories(); + + // if we have categories and they are weighted, check the weightings sum up to 100% (or 1 since it's a fraction) + if (gradebook.getCategoryType() == GradingCategoryType.WEIGHTED_CATEGORY) { + double totalWeight = 0; + for (CategoryDefinition newDef : newCategoryDefinitions) { + + if (newDef.getWeight() == null) { + throw new IllegalArgumentException("No weight specified for a category, but weightings enabled"); + } + + totalWeight += newDef.getWeight(); + } + if (Math.rint(totalWeight) != 1) { + throw new IllegalArgumentException("Weightings for the categories do not equal 100%"); + } + } + + // get current categories and build a mapping list of Category.id to Category + final List currentCategories = getCategories(gradebook.getId()); + final Map currentCategoryMap + = currentCategories.stream().collect(Collectors.toMap(c -> c.getId(), c -> c)); + + // compare current list with given list, add/update/remove as required + // Rules: + // If category does not have an ID it is new; add these later after all removals have been processed + // If category has an ID it is to be updated. Update and remove from currentCategoryMap. + // Any categories remaining in currentCategoryMap are to be removed. + // Sort by category order as we resequence the order values to avoid gaps + Collections.sort(newCategoryDefinitions, CategoryDefinition.orderComparator); + final Map newCategories = new HashMap<>(); + int categoryIndex = 0; + for (final CategoryDefinition newDef : newCategoryDefinitions) { + + // preprocessing and validation + // Rule 1: If category has no name, it is to be removed/skipped + // Note that we no longer set weights to 0 even if unweighted category type selected. The weights are not considered if its not + // a weighted category type + // so this allows us to switch back and forth between types without losing information + + if (StringUtils.isBlank(newDef.getName())) { + continue; + } + + if (newDef.getId() == null) { + // new + newCategories.put(newDef, categoryIndex); + categoryIndex++; + } else { + // existing + final Category existing = currentCategoryMap.get(newDef.getId()); + existing.setName(newDef.getName()); + existing.setWeight(newDef.getWeight()); + existing.setDropLowest(newDef.getDropLowest()); + existing.setDropHighest(newDef.getDropHighest()); + existing.setKeepHighest(newDef.getKeepHighest()); + existing.setExtraCredit(newDef.getExtraCredit()); + existing.setEqualWeightAssignments(newDef.getEqualWeight()); + existing.setCategoryOrder(categoryIndex); + updateCategory(existing); + + // remove from currentCategoryMap so we know not to delete it + currentCategoryMap.remove(newDef.getId()); + + categoryIndex++; + } + + } + + // handle deletes + // anything left in currentCategoryMap was not included in the new list, delete them + currentCategoryMap.keySet().forEach(this::removeCategory); + + // Handle the additions + for (final Entry entry : newCategories.entrySet()) { + final CategoryDefinition newCat = entry.getKey(); + this.createCategory(gradebook.getId(), newCat.getName(), newCat.getWeight(), newCat.getDropLowest(), + newCat.getDropHighest(), newCat.getKeepHighest(), newCat.getExtraCredit(), newCat.getEqualWeight(), entry.getValue()); + } + + // if weighted categories, all uncategorised assignments are to be removed from course grade calcs + if (gradebook.getCategoryType() == GradingCategoryType.WEIGHTED_CATEGORY) { + excludeUncategorisedItemsFromCourseGradeCalculations(gradebook); + } + + // persist + updateGradebook(gradebook); + + } + + @Override + public Set getGradebookGradeMappings(Long gradebookId) { + + Optional optGradebook = gradingPersistenceManager.getGradebook(gradebookId); + + if (optGradebook.isPresent()) { + return optGradebook.get().getGradeMappings(); + } else { + log.warn("No gradebook for id {}", gradebookId); + return null; + } + } + + @Override + public Set getGradebookGradeMappings(String gradebookUid) { + + final Long gradebookId = getGradebook(gradebookUid).getId(); + return this.getGradebookGradeMappings(gradebookId); + } + + @Override + public void updateCourseGradeForStudent(final String gradebookUid, final String studentUuid, final String grade, final String gradeScale) { + + // must be instructor type person + if (!currentUserHasEditPerm(gradebookUid)) { + log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to update course grade for student: {}", getUserUid(), + gradebookUid, studentUuid); + throw new GradingSecurityException("You do not have permission to update course grades in " + gradebookUid); + } + + final Gradebook gradebook = getGradebook(gradebookUid); + if (gradebook == null) { + throw new IllegalArgumentException("There is no gradebook associated with this id: " + gradebookUid); + } + + // get course grade for the student + CourseGradeRecord courseGradeRecord = getCourseGradeRecord(gradebook, studentUuid); + + // if user doesn't have an entered course grade, we need to find the course grade and create a record + if (courseGradeRecord == null) { + + final CourseGrade courseGrade = getCourseGrade(gradebook.getId()); + + courseGradeRecord = new CourseGradeRecord(courseGrade, studentUuid); + courseGradeRecord.setGraderId(getUserUid()); + + } else { + // if passed in grade override is same as existing grade override, nothing to do + if (StringUtils.equals(courseGradeRecord.getEnteredGrade(), gradeScale) && Double.compare(courseGradeRecord.getEnteredPoints(), Double.parseDouble(grade)) == 0) { + return; + } + } + + // set the grade override + courseGradeRecord.setEnteredGrade(gradeScale); + if (grade == null) { + courseGradeRecord.setEnteredPoints(null); + } else { + courseGradeRecord.setEnteredPoints(Double.parseDouble(grade)); + } + // record the last grade override date + courseGradeRecord.setDateRecorded(new Date()); + + // create a grading event + final GradingEvent gradingEvent = new GradingEvent(courseGradeRecord.getCourseGrade(), getUserUid(), studentUuid, courseGradeRecord.getEnteredGrade()); + + gradingPersistenceManager.saveCourseGradeRecord(courseGradeRecord); + gradingPersistenceManager.saveGradingEvent(gradingEvent); + } + + /** + * Map a set of GradeMapping to a list of GradeMappingDefinition + * + * @param gradeMappings set of GradeMapping + * @return list of GradeMappingDefinition + */ + private List getGradebookGradeMappings(final Set gradeMappings) { + final List rval = new ArrayList<>(); + + for (final GradeMapping mapping : gradeMappings) { + rval.add(new GradeMappingDefinition(mapping.getId(), mapping.getName(), + GradeMappingDefinition.sortGradeMapping(mapping.getGradeMap()), + GradeMappingDefinition.sortGradeMapping(mapping.getDefaultBottomPercents()))); + } + return rval; + + } + + /** + * Updates the categorized order of an assignment + * + * @see GradingService.updateAssignmentCategorizedOrder(java.lang.String gradebookUid, java.lang.Long assignmentId, java.lang.Integer + * order) + */ + @Override + public void updateAssignmentCategorizedOrder(final String gradebookUid, final Long categoryId, final Long assignmentId, Integer order) { + + if (!gradingAuthz.isUserAbleToEditAssessments(gradebookUid)) { + log.error("AUTHORIZATION FAILURE: User {} in gradebook {} attempted to change the order of assignment {}", getUserUid(), + gradebookUid, assignmentId); + throw new GradingSecurityException(); + } + + if (order == null) { + throw new IllegalArgumentException("Categorized Order cannot be null"); + } + + final Long gradebookId = getGradebook(gradebookUid).getId(); + + // get all assignments for this gradebook + final List assignments = getAssignments(gradebookId, SortType.SORT_BY_CATEGORY, true); + final List assignmentsInNewCategory = new ArrayList<>(); + for (final GradebookAssignment assignment : assignments) { + if (assignment.getCategory() == null) { + if (categoryId == null) { + assignmentsInNewCategory.add(assignment); + } + } else if (assignment.getCategory().getId().equals(categoryId)) { + assignmentsInNewCategory.add(assignment); + } + } + + // adjust order to be within bounds + if (order < 0) { + order = 0; + } else if (order > assignmentsInNewCategory.size()) { + order = assignmentsInNewCategory.size(); + } + + // find the assignment + GradebookAssignment target = null; + for (final GradebookAssignment a : assignmentsInNewCategory) { + if (a.getId().equals(assignmentId)) { + target = a; + break; + } + } + + // add the assignment to the list via a 'pad, remove, add' approach + assignmentsInNewCategory.add(null); // ensure size remains the same for the remove + assignmentsInNewCategory.remove(target); // remove item + assignmentsInNewCategory.add(order, target); // add at ordered position, will shuffle others along + + // the assignments are now in the correct order within the list, we just need to update the sort order for each one + // create a new list for the assignments we need to update in the database + final List assignmentsToUpdate = new ArrayList<>(); + + int i = 0; + for (final GradebookAssignment a : assignmentsInNewCategory) { + + // skip if null + if (a == null) { + continue; + } + + // if the sort order is not the same as the counter, update the order and add to the other list + // this allows us to skip items that have not had their position changed and saves some db work later on + // sort order may be null if never previously sorted, so give it the current index + if (a.getCategorizedSortOrder() == null || !a.getCategorizedSortOrder().equals(i)) { + a.setCategorizedSortOrder(i); + assignmentsToUpdate.add(a); + } + + i++; + } + + assignmentsToUpdate.forEach(this::updateAssignment); + + } + + /** + * Return the grade changes made since a given time + * + * @param assignmentIds ids of assignments to check + * @param since timestamp from which to check for changes + * @return set of changes made + */ + @Override + public List getGradingEvents(List assignmentIds, Date since) { + + if (assignmentIds == null || assignmentIds.isEmpty() || since == null) { + return Collections.emptyList(); + } + + return gradingPersistenceManager.getGradingEventsForAssignmentsSince(assignmentIds, since); + } + + /** + * Update the persistent grade points for an assignment when the total points is changed. + * + * @param gradebook the gradebook + * @param assignment assignment with original total point value + */ + private void scaleGrades(final Gradebook gradebook, final GradebookAssignment assignment, + final Double originalPointsPossible) { + + if (gradebook == null || assignment == null || assignment.getPointsPossible() == null) { + throw new IllegalArgumentException("null values found in convertGradePointsForUpdatedTotalPoints."); + } + + final List studentUids = getStudentsForGradebook(gradebook); + final List gradeRecords = getAllAssignmentGradeRecordsForGbItem(assignment.getId(), studentUids); + final Set eventsToAdd = new HashSet<>(); + final String currentUserUid = sessionManager.getCurrentSessionUserId(); + + // scale for total points changed when on percentage grading + if (gradebook.getGradeType() == GradeType.PERCENTAGE && assignment.getPointsPossible() != null) { + + log.debug("Scaling percentage grades"); + + for (final AssignmentGradeRecord gr : gradeRecords) { + if (gr.getPointsEarned() != null) { + final BigDecimal scoreAsPercentage = (new BigDecimal(gr.getPointsEarned()) + .divide(new BigDecimal(originalPointsPossible), GradingService.MATH_CONTEXT)) + .multiply(new BigDecimal(100)); + + final BigDecimal scaledScore = new BigDecimal(calculateEquivalentPointValueForPercent(assignment.getPointsPossible(), + scoreAsPercentage.doubleValue()), GradingService.MATH_CONTEXT).setScale(2, RoundingMode.HALF_UP); + + log.debug("scoreAsPercentage: {}, scaledScore: {}", scoreAsPercentage, scaledScore); + + gr.setPointsEarned(scaledScore.doubleValue()); + eventsToAdd.add(new GradingEvent(assignment, currentUserUid, gr.getStudentId(), scaledScore)); + } + } + } + else if (gradebook.getGradeType() == GradeType.POINTS && assignment.getPointsPossible() != null) { + + log.debug("Scaling point grades"); + + final BigDecimal previous = new BigDecimal(originalPointsPossible); + final BigDecimal current = new BigDecimal(assignment.getPointsPossible()); + final BigDecimal factor = current.divide(previous, GradingService.MATH_CONTEXT); + + log.debug("previous points possible: {}, current points possible: {}, factor: {}", previous, current, factor); + + for (final AssignmentGradeRecord gr : gradeRecords) { + if (gr.getPointsEarned() != null) { + + final BigDecimal currentGrade = new BigDecimal(gr.getPointsEarned(), GradingService.MATH_CONTEXT); + final BigDecimal scaledGrade = currentGrade.multiply(factor, GradingService.MATH_CONTEXT).setScale(2, RoundingMode.HALF_UP); + + log.debug("currentGrade: {}, scaledGrade: {}", currentGrade, scaledGrade); + + gr.setPointsEarned(scaledGrade.doubleValue()); + DecimalFormat df = (DecimalFormat)NumberFormat.getNumberInstance((resourceLoader).getLocale()); + df.setGroupingUsed(false); + String pointsLocale = df.format(scaledGrade); + eventsToAdd.add(new GradingEvent(assignment, currentUserUid, gr.getStudentId(), pointsLocale)); + } + } + } + + // save all + gradeRecords.forEach(gradingPersistenceManager::saveAssignmentGradeRecord); + + // Insert the new grading events (GradeRecord) + eventsToAdd.forEach(gradingPersistenceManager::saveGradingEvent); + } + + /** + * Get the list of students for the given gradebook + * + * @param gradebook the gradebook for the site + * @return a list of uuids for the students + */ + private List getStudentsForGradebook(Gradebook gradebook) { + + final List enrolments = sectionAwareness.getSiteMembersInRole(gradebook.getUid(), Role.STUDENT); + + final List rval = enrolments.stream() + .map(EnrollmentRecord::getUser) + .map(User::getUserUid) + .collect(Collectors.toList()); + + return rval; + } + + private boolean isCurrentUserFromGroup(final String gradebookUid, final String studentId) { + + boolean isFromGroup = false; + try { + final Site s = this.siteService.getSite(gradebookUid); + final Group g = s.getGroup(studentId); + isFromGroup = (g != null) && (g.getMember(sessionManager.getCurrentSessionUserId()) != null); + } catch (final Exception e) { + // Id not found + log.error("Error in isCurrentUserFromGroup: ", e); + } + return isFromGroup; + } + + /** + * Updates all uncategorised items to exclude them from the course grade calcs + * + * @param gradebook + */ + private void excludeUncategorisedItemsFromCourseGradeCalculations(final Gradebook gradebook) { + final List allAssignments = getAssignments(gradebook.getId()); + + final List assignments = allAssignments.stream().filter(a -> a.getCategory() == null) + .collect(Collectors.toList()); + assignments.forEach(a -> { + a.setCounted(false); + gradingPersistenceManager.saveAssignment(a); + }); + } + + + private ConcurrentHashMap externalProviders = new ConcurrentHashMap(); + + // Mapping of providers to their getAllExternalAssignments(String gradebookUid) methods, + // used to allow the method to be called on providers not declaring the Compat interface. + // This is to allow the same code to be used on 2.9 and beyond, where the secondary interface + // may be removed, without build profiles. + private final ConcurrentHashMap providerMethods = new ConcurrentHashMap(); + + /** + * Property in sakai.properties used to allow this service to update scores in the db every time the update method is called. By + * default, scores are only updated if the score is different than what is currently in the db. + */ + public static final String UPDATE_SAME_SCORE_PROP = "gradebook.externalAssessments.updateSameScore"; + public static final boolean UPDATE_SAME_SCORE_PROP_DEFAULT = false; + + public ConcurrentMap getExternalAssignmentProviders() { + if (this.externalProviders == null) { + this.externalProviders = new ConcurrentHashMap<>(0); + } + return this.externalProviders; + } + + @Override + public void registerExternalAssignmentProvider(final ExternalAssignmentProvider provider) { + if (provider == null) { + throw new IllegalArgumentException("provider cannot be null"); + } else { + getExternalAssignmentProviders().put(provider.getAppKey(), provider); + + // Try to duck-type the provider so it doesn't have to declare the Compat interface. + // TODO: Remove this handling once the Compat interface has been merged or the issue is otherwise resolved. + if (!(provider instanceof ExternalAssignmentProviderCompat)) { + try { + final Method m = provider.getClass().getDeclaredMethod("getAllExternalAssignments", String.class); + if (m.getReturnType().equals(List.class)) { + this.providerMethods.put(provider, m); + } + } catch (final Exception e) { + log.warn("ExternalAssignmentProvider [" + provider.getAppKey() + " / " + provider.getClass().toString() + + "] does not implement getAllExternalAssignments. It will not be able to exclude items from student views/grades. " + + "See the ExternalAssignmentProviderCompat interface and SAK-23733 for details."); + } + } + } + } + + @Override + public void unregisterExternalAssignmentProvider(final String providerAppKey) { + if (providerAppKey == null || "".equals(providerAppKey)) { + throw new IllegalArgumentException("providerAppKey must be set"); + } else if (getExternalAssignmentProviders().containsKey(providerAppKey)) { + final ExternalAssignmentProvider provider = getExternalAssignmentProviders().get(providerAppKey); + this.providerMethods.remove(provider); + getExternalAssignmentProviders().remove(providerAppKey); + } + } + + public void init() { + log.debug("INIT"); + } + + public void destroy() { + log.debug("DESTROY"); + if (this.externalProviders != null) { + this.externalProviders.clear(); + this.externalProviders = null; + } + } + + @Override + public void addExternalAssessment(final String gradebookUid, final String externalId, final String externalUrl, + final String title, final double points, final Date dueDate, final String externalServiceDescription, String externalData) + throws ConflictingAssignmentNameException, ConflictingExternalIdException { + + // Ensure that the required strings are not empty + if (StringUtils.trimToNull(externalServiceDescription) == null || + StringUtils.trimToNull(externalId) == null || + StringUtils.trimToNull(title) == null) { + throw new RuntimeException("External service description, externalId, and title must not be empty"); + } + + // Ensure that points is > zero + if (points <= 0) { + throw new AssignmentHasIllegalPointsException("Points must be > 0"); + } + + // Ensure that the assessment name is unique within this gradebook + if (isAssignmentDefined(gradebookUid, title)) { + throw new ConflictingAssignmentNameException("An assignment with that name already exists in gradebook uid=" + gradebookUid); + } + + + // name cannot contain these chars as they are reserved for special columns in import/export + GradebookHelper.validateGradeItemName(title); + + // Ensure that the externalId is unique within this gradebook + final Long conflicts = gradingPersistenceManager.countAssignmentsByGradbookAndExternalId(gradebookUid, externalId); + + if (conflicts.intValue() > 0) { + throw new ConflictingExternalIdException( + "An external assessment with ID=" + externalId + " already exists in gradebook uid=" + gradebookUid); + } + + // Get the gradebook + final Gradebook gradebook = getGradebook(gradebookUid); + + // Create the external assignment + final GradebookAssignment asn = new GradebookAssignment(gradebook, title, Double.valueOf(points), dueDate); + asn.setExternallyMaintained(true); + asn.setExternalId(externalId); + asn.setExternalInstructorLink(externalUrl); + asn.setExternalStudentLink(externalUrl); + asn.setExternalAppName(externalServiceDescription); + asn.setExternalData(externalData); + // set released to be true to support selective release + asn.setReleased(true); + asn.setUngraded(false); + + gradingPersistenceManager.saveAssignment(asn); + + log.info("External assessment added to gradebookUid={}, externalId={} by userUid={} from externalApp={}", gradebookUid, externalId, + getUserUid(), externalServiceDescription); + } + + @Override + public void updateExternalAssessment(String gradebookUid, String externalId, String externalUrl, + String externalData, String title, double points, Date dueDate) + throws AssessmentNotFoundException, AssignmentHasIllegalPointsException { + + final Optional optAsn = getDbExternalAssignment(gradebookUid, externalId); + + if (optAsn.isEmpty()) { + throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); + } + + GradebookAssignment asn = optAsn.get(); + + // Ensure that points is > zero + if (points <= 0) { + throw new AssignmentHasIllegalPointsException("Points must be > 0"); + } + + // Ensure that the required strings are not empty + if (StringUtils.trimToNull(externalId) == null || + StringUtils.trimToNull(title) == null) { + throw new RuntimeException("ExternalId, and title must not be empty"); + } + + // name cannot contain these chars as they are reserved for special columns in import/export + GradebookHelper.validateGradeItemName(title); + + asn.setExternalInstructorLink(externalUrl); + asn.setExternalStudentLink(externalUrl); + asn.setExternalData(externalData); + asn.setName(title); + asn.setDueDate(dueDate); + // support selective release + asn.setReleased(BooleanUtils.isTrue(asn.getReleased())); + asn.setPointsPossible(Double.valueOf(points)); + gradingPersistenceManager.saveAssignment(asn); + log.info("External assessment updated in gradebookUid={}, externalId={} by userUid={}", gradebookUid, externalId, + getUserUid()); + } + + @Override + @Transactional + public void removeExternalAssignment(String gradebookUid, String externalId) throws AssessmentNotFoundException { + + // Get the external assignment + final Optional optAsn = getDbExternalAssignment(gradebookUid, externalId); + if (optAsn.isEmpty()) { + throw new AssessmentNotFoundException("There is no external assessment id=" + externalId + " in gradebook uid=" + gradebookUid); + } + + GradebookAssignment asn = optAsn.get(); + + int numDeleted = gradingPersistenceManager.deleteGradingEventsForAssignment(asn); + log.debug("Deleted {} records from gb_grading_event_t", numDeleted); + + numDeleted = gradingPersistenceManager.deleteGradeRecordsForAssignment(asn); + log.info("Deleted {} externally defined scores", numDeleted); + + numDeleted = gradingPersistenceManager.deleteCommentsForAssignment(asn); + log.info("Deleted {} externally defined comments", numDeleted); + + // Delete the assessment. + gradingPersistenceManager.deleteAssignment(asn); + + log.info("External assessment removed from gradebookUid={}, externalId={} by userUid={}", gradebookUid, externalId, getUserUid()); + } + + private Optional getDbExternalAssignment(String gradebookUid, String externalId) { + + if (externalId == null) { + log.warn("null externalId supplied to getDbExternalAssignment. Returning empty ..."); + return Optional.empty(); + } + + return gradingPersistenceManager.getExternalAssignment(gradebookUid, externalId); + } + + @Override + public void updateExternalAssessmentComments(String gradebookUid, String externalId, + Map studentUidsToComments) throws AssessmentNotFoundException { + + Optional optAsn = getDbExternalAssignment(gradebookUid, externalId); + if (optAsn.isEmpty()) { + throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); + } + Set studentIds = studentUidsToComments.keySet(); + if (studentIds.isEmpty()) { + return; + } + + GradebookAssignment asn = optAsn.get(); + + List existingScores + = gradingPersistenceManager.getAssignmentGradeRecordsForAssignmentAndStudents(asn, studentIds); + + Set changedStudents = new HashSet<>(); + for (AssignmentGradeRecord agr : existingScores) { + String studentUid = agr.getStudentId(); + + // Try to reduce data contention by only updating when a score + // has changed or property has been set forcing a db update every time. + boolean alwaysUpdate = isUpdateSameScore(); + + CommentDefinition gradeComment = getAssignmentScoreComment(gradebookUid, asn.getId(), studentUid); + String oldComment = gradeComment != null ? gradeComment.getCommentText() : null; + String newComment = studentUidsToComments.get(studentUid); + + if (alwaysUpdate || (newComment != null && !newComment.equals(oldComment)) || (newComment == null && oldComment != null)) { + changedStudents.add(studentUid); + setAssignmentScoreComment(gradebookUid, asn.getId(), studentUid, newComment); + } + } + + log.debug("updateExternalAssessmentScores sent {} records, actually changed {}", studentIds.size(), changedStudents.size()); + } + + @Override + public void updateExternalAssessmentScores(final String gradebookUid, final String externalId, + final Map studentUidsToScores) throws AssessmentNotFoundException { + + final Optional optAssignment = getDbExternalAssignment(gradebookUid, externalId); + if (optAssignment.isEmpty()) { + throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); + } + final Set studentIds = studentUidsToScores.keySet(); + if (studentIds.isEmpty()) { + return; + } + + GradebookAssignment assignment = optAssignment.get(); + + final Date now = new Date(); + final String graderId = getUserUid(); + + final List existingScores + = gradingPersistenceManager.getAssignmentGradeRecordsForAssignmentAndStudents(assignment, studentIds); + + final Set previouslyUnscoredStudents = new HashSet<>(studentIds); + final Set changedStudents = new HashSet<>(); + for (final AssignmentGradeRecord agr : existingScores) { + final String studentUid = agr.getStudentId(); + previouslyUnscoredStudents.remove(studentUid); + + // Try to reduce data contention by only updating when a score + // has changed or property has been set forcing a db update every time. + final boolean alwaysUpdate = isUpdateSameScore(); + + final Double oldPointsEarned = agr.getPointsEarned(); + final Double newPointsEarned = studentUidsToScores.get(studentUid); + if (alwaysUpdate || (newPointsEarned != null && !newPointsEarned.equals(oldPointsEarned)) + || (newPointsEarned == null && oldPointsEarned != null)) { + agr.setDateRecorded(now); + agr.setGraderId(graderId); + agr.setPointsEarned(newPointsEarned); + gradingPersistenceManager.saveAssignmentGradeRecord(agr); + changedStudents.add(studentUid); + postUpdateGradeEvent(gradebookUid, assignment.getName(), studentUid, newPointsEarned); + } + } + for (String studentUid : previouslyUnscoredStudents) { + // Don't save unnecessary null scores. + Double newPointsEarned = studentUidsToScores.get(studentUid); + if (newPointsEarned != null) { + AssignmentGradeRecord agr = new AssignmentGradeRecord(assignment, studentUid, newPointsEarned); + agr.setDateRecorded(now); + agr.setGraderId(graderId); + gradingPersistenceManager.saveAssignmentGradeRecord(agr); + changedStudents.add(studentUid); + postUpdateGradeEvent(gradebookUid, assignment.getName(), studentUid, newPointsEarned); + } + } + + log.debug("updateExternalAssessmentScores sent {} records, actually changed {}", studentIds.size(), changedStudents.size()); + } + + @Override + public void updateExternalAssessmentScoresString(final String gradebookUid, final String externalId, + final Map studentUidsToScores) throws AssessmentNotFoundException { + + final Optional optAssignment = getDbExternalAssignment(gradebookUid, externalId); + if (optAssignment.isEmpty()) { + throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); + } + GradebookAssignment assignment = optAssignment.get(); + final Set studentIds = studentUidsToScores.keySet(); + if (studentIds.isEmpty()) { + return; + } + final Date now = new Date(); + final String graderId = getUserUid(); + + List existingScores + = gradingPersistenceManager.getAssignmentGradeRecordsForAssignmentAndStudents(assignment, studentIds); + + final Set previouslyUnscoredStudents = new HashSet<>(studentIds); + final Set changedStudents = new HashSet<>(); + for (final AssignmentGradeRecord agr : existingScores) { + final String studentUid = agr.getStudentId(); + previouslyUnscoredStudents.remove(studentUid); + + // Try to reduce data contention by only updating when a score + // has changed or property has been set forcing a db update every time. + final boolean alwaysUpdate = isUpdateSameScore(); + + // TODO: for ungraded items, needs to set ungraded-grades later... + final Double oldPointsEarned = agr.getPointsEarned(); + final String newPointsEarnedString = studentUidsToScores.get(studentUid); + final Double newPointsEarned = (newPointsEarnedString == null) ? null : convertStringToDouble(newPointsEarnedString); + if (alwaysUpdate || (newPointsEarned != null && !newPointsEarned.equals(oldPointsEarned)) + || (newPointsEarned == null && oldPointsEarned != null)) { + agr.setDateRecorded(now); + agr.setGraderId(graderId); + if (newPointsEarned != null) { + agr.setPointsEarned(newPointsEarned); + } else { + agr.setPointsEarned(null); + } + gradingPersistenceManager.saveAssignmentGradeRecord(agr); + changedStudents.add(studentUid); + postUpdateGradeEvent(gradebookUid, assignment.getName(), studentUid, newPointsEarned); + } + } + for (String studentUid : previouslyUnscoredStudents) { + // Don't save unnecessary null scores. + String newPointsEarned = studentUidsToScores.get(studentUid); + if (newPointsEarned != null) { + AssignmentGradeRecord agr = new AssignmentGradeRecord(assignment, studentUid, + convertStringToDouble(newPointsEarned)); + agr.setDateRecorded(now); + agr.setGraderId(graderId); + gradingPersistenceManager.saveAssignmentGradeRecord(agr); + changedStudents.add(studentUid); + postUpdateGradeEvent(gradebookUid, assignment.getName(), studentUid, convertStringToDouble(newPointsEarned)); + } + } + + log.debug("updateExternalAssessmentScores sent {} records, actually changed {}", studentIds.size(), changedStudents.size()); + } + + @Override + public boolean isExternalAssignmentDefined(String gradebookUid, String externalId) { + // SAK-19668 + return getDbExternalAssignment(gradebookUid, externalId).isPresent(); + } + + @Override + public boolean isExternalAssignmentGrouped(final String gradebookUid, final String externalId) { + + // SAK-19668 + final Optional optAssignment = getDbExternalAssignment(gradebookUid, externalId); + // If we check all available providers for an existing, externally maintained assignment + // and none manage it, return false since grouping is the outlier case and all items + // showed for all users until the 2.9 release. + boolean result = false; + boolean providerResponded = false; + if (optAssignment.isEmpty()) { + log.info("No assignment found for external assignment check: gradebookUid=" + gradebookUid + ", externalId=" + externalId); + } else { + GradebookAssignment assignment = optAssignment.get(); + for (final ExternalAssignmentProvider provider : getExternalAssignmentProviders().values()) { + if (provider.isAssignmentDefined(assignment.getExternalAppName(), externalId)) { + providerResponded = true; + result = result || provider.isAssignmentGrouped(externalId); + } + } + } + return result || !providerResponded; + } + + @Override + public boolean isExternalAssignmentVisible(final String gradebookUid, final String externalId, final String userId) { + + // SAK-19668 + final Optional optAssignment = getDbExternalAssignment(gradebookUid, externalId); + // If we check all available providers for an existing, externally maintained assignment + // and none manage it, assume that it should be visible. This matches the pre-2.9 behavior + // when a provider is not implemented to handle the assignment. Also, any provider that + // returns true will allow access (logical OR of responses). + boolean result = false; + boolean providerResponded = false; + if (optAssignment.isEmpty()) { + log.info("No assignment found for external assignment check: gradebookUid=" + gradebookUid + ", externalId=" + externalId); + } else { + GradebookAssignment assignment = optAssignment.get(); + for (final ExternalAssignmentProvider provider : getExternalAssignmentProviders().values()) { + if (provider.isAssignmentDefined(assignment.getExternalAppName(), externalId)) { + providerResponded = true; + result = result || provider.isAssignmentVisible(externalId, userId); + } + } + } + return result || !providerResponded; + } + + @Override + public Map getExternalAssignmentsForCurrentUser(final String gradebookUid) { + + final Map visibleAssignments = new HashMap<>(); + final Set providedAssignments = getProvidedExternalAssignments(gradebookUid); + + for (final ExternalAssignmentProvider provider : getExternalAssignmentProviders().values()) { + final String appKey = provider.getAppKey(); + final List assignments = provider.getExternalAssignmentsForCurrentUser(gradebookUid); + for (final String externalId : assignments) { + visibleAssignments.put(externalId, appKey); + } + } + + // We include those items that the gradebook has marked as externally maintained, but no provider has + // identified as items under its authority. This maintains the behavior prior to the grouping support + // introduced for the 2.9 release (SAK-11485 and SAK-19688), where a tool that does not have a provider + // implemented does not have its items filtered for student views and grading. + final List gbAssignments = getViewableAssignmentsForCurrentUser(gradebookUid); + for (final org.sakaiproject.grading.api.Assignment assignment : gbAssignments) { + final String id = assignment.getExternalId(); + if (assignment.getExternallyMaintained() && !providedAssignments.contains(id) && !visibleAssignments.containsKey(id)) { + log.debug("External assignment in gradebook [{}] is not handled by a provider; ID: {}", gradebookUid, id); + visibleAssignments.put(id, null); + } + } + + return visibleAssignments; + } + + private Set getProvidedExternalAssignments(final String gradebookUid) { + final Set allAssignments = new HashSet<>(); + for (final ExternalAssignmentProvider provider : getExternalAssignmentProviders().values()) { + // TODO: This is a temporary cast; if this method proves to be the right fit + // and perform well enough, it will be moved to the regular interface. + if (provider instanceof ExternalAssignmentProviderCompat) { + allAssignments.addAll( + ((ExternalAssignmentProviderCompat) provider).getAllExternalAssignments(gradebookUid)); + } else if (this.providerMethods.containsKey(provider)) { + final Method m = this.providerMethods.get(provider); + try { + @SuppressWarnings("unchecked") + final List reflectedAssignments = (List) m.invoke(provider, gradebookUid); + allAssignments.addAll(reflectedAssignments); + } catch (final Exception e) { + log.debug("Exception calling getAllExternalAssignments", e); + } + } + } + return allAssignments; + } + + @Override + public Map> getVisibleExternalAssignments(final String gradebookUid, final Collection studentIds) { + + final Set providedAssignments = getProvidedExternalAssignments(gradebookUid); + + final Map> visible = new HashMap<>(); + for (final String studentId : studentIds) { + visible.put(studentId, new HashSet()); + } + + for (final ExternalAssignmentProvider provider : getExternalAssignmentProviders().values()) { + // SAK-24407 - Some tools modify this set so we can't pass it. I considered making it an unmodifableCollection but that would + // require changing a number of tools + final Set studentIdsCopy = new HashSet<>(studentIds); + final Map> externals = provider.getAllExternalAssignments(gradebookUid, (studentIdsCopy)); + for (final String studentId : externals.keySet()) { + if (visible.containsKey(studentId)) { + visible.get(studentId).addAll(externals.get(studentId)); + } + } + } + + // SAK-23733 - This covers a tricky case where items that the gradebook thinks are external + // but are not reported by any provider should be included for everyone. This is + // to accommodate tools that use the external assessment mechanisms but have not + // implemented an ExternalAssignmentProvider. + List allAssignments = getViewableAssignmentsForCurrentUser(gradebookUid); + for (Assignment assignment : allAssignments) { + String id = assignment.getExternalId(); + if (assignment.getExternallyMaintained() && !providedAssignments.contains(id)) { + for (String studentId : visible.keySet()) { + visible.get(studentId).add(id); + } + } + } + + return visible.keySet().stream() + .collect(Collectors.toMap(k -> k, k -> new ArrayList(visible.get(k)))); + } + + @Override + public void setExternalAssessmentToGradebookAssignment(final String gradebookUid, final String externalId) { + + final Optional optAssignment = getDbExternalAssignment(gradebookUid, externalId); + if (optAssignment.isEmpty()) { + throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); + } + GradebookAssignment assignment = optAssignment.get(); + assignment.setExternalAppName(null); + assignment.setExternalId(null); + assignment.setExternalInstructorLink(null); + assignment.setExternalStudentLink(null); + assignment.setExternalData(null); + assignment.setExternallyMaintained(false); + gradingPersistenceManager.saveAssignment(assignment); + log.info("Externally-managed assignment {} moved to Gradebook management in gradebookUid={} by userUid={}", externalId, + gradebookUid, getUserUid()); + } + + /** + * Wrapper created when category was added for assignments tool + */ + @Override + public void addExternalAssessment(String gradebookUid, String externalId, String externalUrl, String title, Double points, + Date dueDate, String externalServiceDescription, String externalData, Boolean ungraded) + throws ConflictingAssignmentNameException, ConflictingExternalIdException, AssignmentHasIllegalPointsException { + + addExternalAssessment(gradebookUid, externalId, externalUrl, title, points, dueDate, externalServiceDescription, externalData, ungraded, null); + } + + @Override + public void addExternalAssessment(final String gradebookUid, final String externalId, final String externalUrl, final String title, final Double points, + final Date dueDate, final String externalServiceDescription, String externalData, final Boolean ungraded, final Long categoryId) + throws ConflictingAssignmentNameException, ConflictingExternalIdException, AssignmentHasIllegalPointsException { + // Ensure that the required strings are not empty + if (StringUtils.trimToNull(externalServiceDescription) == null || + StringUtils.trimToNull(externalId) == null || + StringUtils.trimToNull(title) == null) { + throw new RuntimeException("External service description, externalId, and title must not be empty"); + } + + // Ensure that points is > zero + if ((ungraded != null && !ungraded.booleanValue() && (points == null || points.doubleValue() <= 0)) + || (ungraded == null && (points == null || points.doubleValue() <= 0))) { + throw new AssignmentHasIllegalPointsException("Points can't be null or Points must be > 0"); + } + + // Ensure that the assessment name is unique within this gradebook + if (isAssignmentDefined(gradebookUid, title)) { + throw new ConflictingAssignmentNameException("An assignment with that name already exists in gradebook uid=" + gradebookUid); + } + + // name cannot contain these chars as they are reserved for special columns in import/export + GradebookHelper.validateGradeItemName(title); + + // Ensure that the externalId is unique within this gradebook + Long conflicts = gradingPersistenceManager.countAssignmentsByGradbookAndExternalId(gradebookUid, externalId); + if (conflicts > 0L) { + throw new ConflictingExternalIdException( + "An external assessment with that ID already exists in gradebook uid=" + gradebookUid); + } + + // Get the gradebook + final Gradebook gradebook = getGradebook(gradebookUid); + + // if a category was indicated, double check that it is valid + Category persistedCategory = null; + if (categoryId != null) { + persistedCategory = getCategory(categoryId); + if (persistedCategory.isDropScores() && !persistedCategory.getEqualWeightAssignments()) { + List thisCategoryAssignments = getAssignmentsForCategory(categoryId); + for (GradebookAssignment thisAssignment : thisCategoryAssignments) { + if (!Objects.equals(thisAssignment.getPointsPossible(), points)) { + String errorMessage = "Assignment points mismatch the selected Gradebook Category (" + + thisAssignment.getPointsPossible().toString() + ") and cannot be added to Gradebook )"; + throw new InvalidCategoryException(errorMessage); + } + } + } + if (persistedCategory == null || persistedCategory.getRemoved() || + !persistedCategory.getGradebook().getId().equals(gradebook.getId())) { + throw new InvalidCategoryException("The category with id " + categoryId + + " is not valid for gradebook " + gradebook.getUid()); + } + } + + // Create the external assignment + final GradebookAssignment asn = new GradebookAssignment(gradebook, title, points, dueDate); + asn.setExternallyMaintained(true); + asn.setExternalId(externalId); + asn.setExternalInstructorLink(externalUrl); + asn.setExternalStudentLink(externalUrl); + asn.setExternalAppName(externalServiceDescription); + asn.setExternalData(externalData); + if (persistedCategory != null) { + asn.setCategory(persistedCategory); + } + // set released to be true to support selective release + asn.setReleased(true); + if (ungraded != null) { + asn.setUngraded(ungraded); + } else { + asn.setUngraded(false); + } + + gradingPersistenceManager.saveGradebookAssignment(asn); + + log.info("External assessment added to gradebookUid={}, externalId={} by userUid={} from externalApp={}", gradebookUid, externalId, + getUserUid(), externalServiceDescription); + } + + @Override + public void updateExternalAssessment(final String gradebookUid, final String externalId, final String externalUrl, String externalData, final String title, + final Double points, final Date dueDate, final Boolean ungraded) + throws AssessmentNotFoundException, ConflictingAssignmentNameException, AssignmentHasIllegalPointsException { + final Optional optAsn = getDbExternalAssignment(gradebookUid, externalId); + + if (optAsn.isEmpty()) { + throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); + } + + GradebookAssignment asn = optAsn.get(); + + // Ensure that points is > zero + if ((ungraded != null && !ungraded.booleanValue() && (points == null || points.doubleValue() <= 0)) + || (ungraded == null && (points == null || points.doubleValue() <= 0))) { + throw new AssignmentHasIllegalPointsException("Points can't be null or Points must be > 0"); + } + + // Ensure that the required strings are not empty + if (StringUtils.trimToNull(externalId) == null || + StringUtils.trimToNull(title) == null) { + throw new RuntimeException("ExternalId, and title must not be empty"); + } + + // name cannot contain these chars as they are reserved for special columns in import/export + GradebookHelper.validateGradeItemName(title); + + asn.setExternalInstructorLink(externalUrl); + asn.setExternalStudentLink(externalUrl); + asn.setExternalData(externalData); + asn.setName(title); + asn.setDueDate(dueDate); + // support selective release + asn.setReleased(BooleanUtils.isTrue(asn.getReleased())); + asn.setPointsPossible(points); + if (ungraded != null) { + asn.setUngraded(ungraded.booleanValue()); + } else { + asn.setUngraded(false); + } + gradingPersistenceManager.saveGradebookAssignment(asn); + + log.info("External assessment updated in gradebookUid={}, externalId={} by userUid={}", gradebookUid, externalId, getUserUid()); + } + + @Override + public void updateExternalAssessmentComment(final String gradebookUid, final String externalId, final String studentUid, + final String comment) + throws AssessmentNotFoundException { + + final Optional optAsn = getDbExternalAssignment(gradebookUid, externalId); + + if (optAsn.isEmpty()) { + throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); + } + + GradebookAssignment asn = optAsn.get(); + + log.debug("BEGIN: Update 1 score for gradebookUid={}, external assessment={} from {}", gradebookUid, externalId, + asn.getExternalAppName()); + + // Try to reduce data contention by only updating when the + // score has actually changed or property has been set forcing a db update every time. + final boolean alwaysUpdate = isUpdateSameScore(); + + final CommentDefinition gradeComment = getAssignmentScoreComment(gradebookUid, asn.getId(), studentUid); + final String oldComment = gradeComment != null ? gradeComment.getCommentText() : null; + + if (alwaysUpdate || (comment != null && !comment.equals(oldComment)) || + (comment == null && oldComment != null)) { + if (comment != null) { + setAssignmentScoreComment(gradebookUid, asn.getId(), studentUid, comment); + } else { + setAssignmentScoreComment(gradebookUid, asn.getId(), studentUid, null); + } + log.debug("updateExternalAssessmentComment: grade record saved"); + } else { + log.debug("Ignoring updateExternalAssessmentComment, since the new comment is the same as the old"); + } + log.debug("END: Update 1 score for gradebookUid={}, external assessment={} from {}", gradebookUid, externalId, + asn.getExternalAppName()); + log.debug("External assessment comment updated in gradebookUid={}, externalId={} by userUid={}, new score={}", gradebookUid, + externalId, getUserUid(), comment); + } + + @Override + public void updateExternalAssessmentScore(final String gradebookUid, final String externalId, final String studentUid, + final String points) + throws AssessmentNotFoundException { + final Optional optAsn = getDbExternalAssignment(gradebookUid, externalId); + + if (optAsn.isEmpty()) { + throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUid); + } + + GradebookAssignment asn = optAsn.get(); + + log.debug("BEGIN: Update 1 score for gradebookUid={}, external assessment={} from {}", gradebookUid, externalId, + asn.getExternalAppName()); + + final Date now = new Date(); + + AssignmentGradeRecord agr = getAssignmentGradeRecord(asn, studentUid); + + // Try to reduce data contention by only updating when the + // score has actually changed or property has been set forcing a db update every time. + final boolean alwaysUpdate = isUpdateSameScore(); + + // TODO: for ungraded items, needs to set ungraded-grades later... + final Double oldPointsEarned = (agr == null) ? null : agr.getPointsEarned(); + final Double newPointsEarned = (points == null) ? null : convertStringToDouble(points); + if (alwaysUpdate || (newPointsEarned != null && !newPointsEarned.equals(oldPointsEarned)) || + (newPointsEarned == null && oldPointsEarned != null)) { + if (agr == null) { + if (newPointsEarned != null) { + agr = new AssignmentGradeRecord(asn, studentUid, Double.valueOf(newPointsEarned)); + } else { + agr = new AssignmentGradeRecord(asn, studentUid, null); + } + } else { + if (newPointsEarned != null) { + agr.setPointsEarned(Double.valueOf(newPointsEarned)); + } else { + agr.setPointsEarned(null); + } + } + + agr.setDateRecorded(now); + agr.setGraderId(getUserUid()); + log.debug("About to save AssignmentGradeRecord id={}, version={}, studenttId={}, pointsEarned={}", agr.getId(), + agr.getVersion(), agr.getStudentId(), agr.getPointsEarned()); + gradingPersistenceManager.saveAssignmentGradeRecord(agr); + + // Sync database. + postUpdateGradeEvent(gradebookUid, asn.getName(), studentUid, newPointsEarned); + } else { + log.debug("Ignoring updateExternalAssessmentScore, since the new points value is the same as the old"); + } + + log.debug("END: Update 1 score for gradebookUid={}, external assessment={} from {}", gradebookUid, externalId, + asn.getExternalAppName()); + log.debug("External assessment score updated in gradebookUid={}, externalId={} by userUid={}, new score={}", gradebookUid, + externalId, getUserUid(), points); + } + + /** + * + * @param s the string we want to convert to a double + * @return a locale-aware Double value representation of the given String + * @throws ParseException + */ + /* + private Double convertStringToDouble(final String s) { + Double scoreAsDouble = null; + String doubleAsString = s; + if (doubleAsString != null && !"".equals(doubleAsString)) { + try { + // check if grade uses a comma as separator because of number format and change to a comma y the external app sends a point + // as separator + final DecimalFormat dcformat = (DecimalFormat) getNumberFormat(); + final String decSeparator = dcformat.getDecimalFormatSymbols().getDecimalSeparator() + ""; + if (",".equals(decSeparator)) { + doubleAsString = doubleAsString.replace(".", ","); + } + final Number numericScore = getNumberFormat().parse(doubleAsString.trim()); + scoreAsDouble = numericScore.doubleValue(); + } catch (final ParseException e) { + log.error(e.getMessage()); + } + } + + return scoreAsDouble; + } + */ + + private NumberFormat getNumberFormat() { + return NumberFormat.getInstance(resourceLoader.getLocale()); + } + + @Override + public Long getExternalAssessmentCategoryId(final String gradebookUId, final String externalId) { + Long categoryId = null; + final Optional optAssignment = getDbExternalAssignment(gradebookUId, externalId); + if (optAssignment.isEmpty()) { + throw new AssessmentNotFoundException("There is no assessment id=" + externalId + " in gradebook uid=" + gradebookUId); + } + GradebookAssignment assignment = optAssignment.get(); + if (assignment.getCategory() != null) { + categoryId = assignment.getCategory().getId(); + } + return categoryId; + } + + /** + * Determines whether to update a grade record when there have been no changes. This is useful when we need to update only + * gb_grade_record_t's 'DATE_RECORDED' field for instance. Generally uses the sakai.property + * 'gradebook.externalAssessments.updateSameScore', but a site property by the same name can override it. That is to say, the site + * property is checked first, and if it is not present, the sakai.property is used. + */ + private boolean isUpdateSameScore() { + String siteProperty = null; + try { + final String siteId = this.toolManager.getCurrentPlacement().getContext(); + final Site site = this.siteService.getSite(siteId); + siteProperty = site.getProperties().getProperty(UPDATE_SAME_SCORE_PROP); + } catch (final Exception e) { + // Can't access site property. Leave it set to null + } + + // Site property override not set. Use setting in sakai.properties + if (siteProperty == null) { + return this.serverConfigurationService.getBoolean(UPDATE_SAME_SCORE_PROP, UPDATE_SAME_SCORE_PROP_DEFAULT); + } + + return Boolean.TRUE.toString().equals(siteProperty); + } + + @Override + public boolean isCategoriesEnabled(String gradebookUid) { + + return getGradebook(gradebookUid).getCategoryType() != GradingCategoryType.NO_CATEGORY; + } + + @Override + @Transactional + public Gradebook addGradebook(final String uid) { + + log.debug("Adding gradebook uid={} by userUid={}", uid, getUserUid()); + + createDefaultLetterGradeMapping(getHardDefaultLetterMapping()); + + // Get available grade mapping templates. + List gradingScales = gradingPersistenceManager.getAvailableGradingScales(); + + // The application won't be able to run without grade mapping + // templates, so if for some reason none have been defined yet, + // do that now. + if (gradingScales.isEmpty()) { + log.info("No Grading Scale defined yet. This is probably because you have upgraded or you are working with a new database. Default grading scales will be created. Any customized system-wide grade mappings you may have defined in previous versions will have to be reconfigured."); + gradingScales = addDefaultGradingScales(); + } + + // Create and save the gradebook + final Gradebook gradebook = new Gradebook(); + gradebook.setName(uid); + gradebook.setUid(uid); + gradingPersistenceManager.saveGradebook(gradebook); + + // Create the course grade for the gradebook + final CourseGrade cg = new CourseGrade(); + cg.setGradebook(gradebook); + gradingPersistenceManager.saveCourseGrade(cg); + + // According to the specification, Display GradebookAssignment Grades is + // on by default, and Display course grade is off. But can be overridden via properties + + Boolean propAssignmentsDisplayed = this.serverConfigurationService.getBoolean(PROP_ASSIGNMENTS_DISPLAYED,true); + gradebook.setAssignmentsDisplayed(propAssignmentsDisplayed); + + Boolean propCourseGradeDisplayed = this.serverConfigurationService.getBoolean(PROP_COURSE_GRADE_DISPLAYED,false); + gradebook.setCourseGradeDisplayed(propCourseGradeDisplayed); + + Boolean propCoursePointsDisplayed = this.serverConfigurationService.getBoolean(PROP_COURSE_POINTS_DISPLAYED,false); + gradebook.setCoursePointsDisplayed(propCoursePointsDisplayed); + + final String defaultScaleUid = getPropertyValue(UID_OF_DEFAULT_GRADING_SCALE_PROPERTY); + + // Add and save grade mappings based on the templates. + GradeMapping defaultGradeMapping = null; + Set gradeMappings = new HashSet<>(); + for (GradingScale gradingScale : gradingScales) { + GradeMapping gradeMapping = new GradeMapping(gradingScale); + gradeMapping.setGradebook(gradebook); + gradingPersistenceManager.saveGradeMapping(gradeMapping); + gradeMappings.add(gradeMapping); + if (gradingScale.getUid().equals(defaultScaleUid)) { + defaultGradeMapping = gradeMapping; + } + } + + // Check for null default. + if (defaultGradeMapping == null) { + defaultGradeMapping = gradeMappings.iterator().next(); + if (log.isWarnEnabled()) { + log.warn("No default GradeMapping found for new Gradebook={}; will set default to {}", + gradebook.getUid(), defaultGradeMapping.getName()); + } + } + gradebook.setSelectedGradeMapping(defaultGradeMapping); + + // The Hibernate mapping as of Sakai 2.2 makes this next + // call meaningless when it comes to persisting changes at + // the end of the transaction. It is, however, needed for + // the mappings to be seen while the transaction remains + // uncommitted. + gradebook.setGradeMappings(gradeMappings); + + gradebook.setGradeType(GradeType.POINTS); + gradebook.setCategoryType(GradingCategoryType.NO_CATEGORY); + + //SAK-29740 make backwards compatible + gradebook.setCourseLetterGradeDisplayed(true); + gradebook.setCourseAverageDisplayed(true); + + // SAK-33855 turn on stats for new gradebooks + final Boolean propAssignmentStatsDisplayed = this.serverConfigurationService.getBoolean(PROP_ASSIGNMENT_STATS_DISPLAYED, true); + gradebook.setAssignmentStatsDisplayed(propAssignmentStatsDisplayed); + + final Boolean propCourseGradeStatsDisplayed = this.serverConfigurationService.getBoolean(PROP_COURSE_GRADE_STATS_DISPLAYED, true); + gradebook.setCourseGradeStatsDisplayed(propCourseGradeStatsDisplayed); + + // Update the gradebook with the new selected grade mapping + return gradingPersistenceManager.saveGradebook(gradebook); + } + + private List addDefaultGradingScales() { + + final List gradingScales = new ArrayList<>(); + + // Base the default set of templates on the old + // statically defined GradeMapping classes. + final GradeMapping[] oldGradeMappings = { + new LetterGradeMapping(), + new LetterGradePlusMinusMapping(), + new PassNotPassMapping(), + new GradePointsMapping() + }; + + for (final GradeMapping sampleMapping : oldGradeMappings) { + sampleMapping.setDefaultValues(); + GradingScale gradingScale = new GradingScale(); + String uid = sampleMapping.getClass().getName(); + uid = uid.substring(uid.lastIndexOf('.') + 1); + gradingScale.setUid(uid); + gradingScale.setUnavailable(false); + gradingScale.setName(sampleMapping.getName()); + gradingScale.setGrades(new ArrayList<>(sampleMapping.getGrades())); + gradingScale.setDefaultBottomPercents(new HashMap<>(sampleMapping.getGradeMap())); + gradingPersistenceManager.saveGradingScale(gradingScale); + log.info("Added Grade Mapping " + gradingScale.getUid()); + gradingScales.add(gradingScale); + } + setDefaultGradingScale("LetterGradePlusMinusMapping"); + return gradingScales; + } + + @Override + public void setAvailableGradingScales(Collection gradingScaleDefinitions) { + mergeGradeMappings(gradingScaleDefinitions); + } + + @Override + public void saveGradeMappingToGradebook(String scaleUuid, String gradebookUid) { + + List gradingScales = gradingPersistenceManager.getAvailableGradingScales(); + + for (GradingScale gradingScale : gradingScales) { + if (gradingScale.getUid().equals(scaleUuid)) { + GradeMapping gradeMapping = new GradeMapping(gradingScale); + Gradebook gradebookToSet = getGradebook(gradebookUid); + gradeMapping.setGradebook(gradebookToSet); + gradingPersistenceManager.saveGradeMapping(gradeMapping); + } + } + } + + @Override + public List getAvailableGradingScales() { + + List gradingScales = gradingPersistenceManager.getAvailableGradingScales(); + + // The application won't be able to run without grade mapping + // templates, so if for some reason none have been defined yet, + // do that now. + if (gradingScales.isEmpty()) { + log.info("No Grading Scale defined yet. This is probably because you have upgraded or you are working with a new database. Default grading scales will be created. Any customized system-wide grade mappings you may have defined in previous versions will have to be reconfigured."); + gradingScales = addDefaultGradingScales(); + } + return gradingScales; + } + + @Override + public List getAvailableGradingScaleDefinitions() { + final List gradingScales = getAvailableGradingScales(); + + final List rval = new ArrayList<>(); + for (final GradingScale gradingScale: gradingScales) { + rval.add(gradingScale.toGradingScaleDefinition()); + } + return rval; + } + + @Override + public void setDefaultGradingScale(final String uid) { + setPropertyValue(UID_OF_DEFAULT_GRADING_SCALE_PROPERTY, uid); + } + + private void copyDefinitionToScale(final GradingScaleDefinition bean, final GradingScale gradingScale) { + + gradingScale.setUnavailable(false); + gradingScale.setName(bean.getName()); + gradingScale.setGrades(bean.getGrades()); + Map defaultBottomPercents = new HashMap<>(); + Iterator gradesIter = bean.getGrades().iterator(); + Iterator defaultBottomPercentsIter = bean.getDefaultBottomPercentsAsList().iterator(); + while (gradesIter.hasNext() && defaultBottomPercentsIter.hasNext()) { + String grade = (String)gradesIter.next(); + Double value = (Double)defaultBottomPercentsIter.next(); + defaultBottomPercents.put(grade, value); + } + gradingScale.setDefaultBottomPercents(defaultBottomPercents); + } + + private void mergeGradeMappings(final Collection gradingScaleDefinitions) { + + final Map newMappingDefinitionsMap = new HashMap<>(); + final Set uidsToSet = new HashSet<>(); + for (GradingScaleDefinition bean : gradingScaleDefinitions) { + newMappingDefinitionsMap.put(bean.getUid(), bean); + uidsToSet.add(bean.getUid()); + } + + // Until we move to Hibernate 3 syntax, we need to update one record at a time. + // Toggle any scales that are no longer specified. + List gmtList = gradingPersistenceManager.getOtherAvailableGradingScales(uidsToSet); + for (GradingScale gradingScale : gmtList) { + gradingScale.setUnavailable(true); + gradingPersistenceManager.saveGradingScale(gradingScale); + log.info("Set Grading Scale {} unavailable", gradingScale.getUid()); + } + + // Modify any specified scales that already exist. + //q = session.createQuery("from GradingScale as gradingScale where gradingScale.uid in (:uidList)"); + gmtList = gradingPersistenceManager.getGradingScalesByUids(uidsToSet); + for (GradingScale gradingScale : gmtList) { + copyDefinitionToScale(newMappingDefinitionsMap.get(gradingScale.getUid()), gradingScale); + uidsToSet.remove(gradingScale.getUid()); + gradingPersistenceManager.saveGradingScale(gradingScale); + log.info("Updated Grading Scale {}", gradingScale.getUid()); + } + + // Add any new scales. + for (final String uid : uidsToSet) { + final GradingScale gradingScale = new GradingScale(); + gradingScale.setUid(uid); + final GradingScaleDefinition bean = newMappingDefinitionsMap.get(uid); + copyDefinitionToScale(bean, gradingScale); + gradingPersistenceManager.saveGradingScale(gradingScale); + log.info("Added Grading Scale {}", gradingScale.getUid()); + } + } + + @Override + public void deleteGradebook(String gradebookUid) { + + log.debug("Deleting gradebook uid={} by userUid={}", gradebookUid, getUserUid()); + + //Gradebook gradebook = gradingPersistencegetGradebook(uid); + gradingPersistenceManager.deleteGradebook(gradebookUid); + } + + @Override + public void updateGradeMapping(Long gradeMappingId, Map gradeMap) { + + Optional optGradeMapping = gradingPersistenceManager.getGradeMapping(gradeMappingId); + + if (optGradeMapping.isPresent()) { + optGradeMapping.get().setGradeMap(gradeMap); + gradingPersistenceManager.saveGradeMapping(optGradeMapping.get()); + } else { + log.warn("No grade mapping for id {}", gradeMappingId); + } + } + + public CommentDefinition getAssignmentScoreComment(String gradebookUid, Long assignmentId, String studentUid) throws AssessmentNotFoundException { + + if (StringUtils.isBlank(gradebookUid) || assignmentId == null || StringUtils.isBlank(studentUid)) { + throw new IllegalArgumentException("gradebookUid, assignmentId and studentUid must be valid."); + } + + final GradebookAssignment assignment = getAssignmentWithoutStats(gradebookUid, assignmentId); + if (assignment == null) { + throw new AssessmentNotFoundException("There is no assignmentId " + assignmentId + " for gradebookUid " + gradebookUid); + } + + CommentDefinition commentDefinition = null; + final Optional optComment = gradingPersistenceManager.getInternalComment(studentUid, gradebookUid, assignmentId); + if (optComment.isPresent()) { + Comment comment = optComment.get(); + commentDefinition = new CommentDefinition(); + commentDefinition.setAssignmentName(assignment.getName()); + commentDefinition.setCommentText(comment.getCommentText()); + commentDefinition.setDateRecorded(comment.getDateRecorded()); + commentDefinition.setGraderUid(comment.getGraderId()); + commentDefinition.setStudentUid(comment.getStudentId()); + } + return commentDefinition; + } + + public void setAssignmentScoreComment(String gradebookUid, Long assignmentId, String studentUid, String commentText) throws AssessmentNotFoundException { + + if (StringUtils.isBlank(gradebookUid) || assignmentId == null || StringUtils.isBlank(studentUid) || StringUtils.isBlank(commentText)) { + throw new IllegalArgumentException("gradebookUid, assignmentId, studentUid and commentText must be valid."); + } + + final Optional optComment = gradingPersistenceManager.getInternalComment(studentUid, gradebookUid, assignmentId); + Comment comment = null; + if (optComment.isEmpty()) { + comment = new Comment(studentUid, commentText, getAssignmentWithoutStats(gradebookUid, assignmentId)); + } else { + comment = optComment.get(); + comment.setCommentText(commentText); + } + comment.setGraderId(sessionManager.getCurrentSessionUserId()); + comment.setDateRecorded(new Date()); + gradingPersistenceManager.saveComment(comment); + } + + public Gradebook getGradebook(String uid) { + + return gradingPersistenceManager.getGradebook(uid).orElseGet(() -> addGradebook(uid)); + } + + private List getAssignments(Long gradebookId) { + return gradingPersistenceManager.getAssignmentsForGradebook(gradebookId); + } + + public String getGradebookUid(final Long id) { + + Optional optGradebook = gradingPersistenceManager.getGradebook(id); + if (optGradebook.isPresent()) { + return optGradebook.get().getUid(); + } else { + log.warn("No gradebook for id {}", id); + return null; + } + } + + @Deprecated + private GradebookAssignment getAssignmentWithoutStats(String gradebookUid, String assignmentName) { + return gradingPersistenceManager.getAssignmentByNameAndGradebook(assignmentName, gradebookUid).orElse(null); + } + + private GradebookAssignment getAssignmentWithoutStats(String gradebookUid, Long assignmentId) { + return gradingPersistenceManager.getAssignmentByIdAndGradebook(assignmentId, gradebookUid).orElse(null); + } + + private void updateAssignment(final GradebookAssignment assignment) throws ConflictingAssignmentNameException { + // Ensure that we don't have the assignment in the session, since + // we need to compare the existing one in the db to our edited assignment + //final Session session = getSessionFactory().getCurrentSession(); + //session.evict(assignment); + + //final GradebookAssignment asnFromDb = (GradebookAssignment) session.load(GradebookAssignment.class, assignment.getId()); + + final Long count = gradingPersistenceManager.countDuplicateAssignments(assignment); + + if (count > 0) { + throw new ConflictingAssignmentNameException("You can not save multiple assignments in a gradebook with the same name"); + } + + //session.evict(asnFromDb); + gradingPersistenceManager.saveAssignment(assignment); + //session.update(assignment); + } + + private AssignmentGradeRecord getAssignmentGradeRecord(GradebookAssignment assignment, String studentUid) { + return gradingPersistenceManager.getAssignmentGradeRecordForAssignmentAndStudent(assignment.getId(), studentUid); + } + + public void postEvent(final String event, final String objectReference) { + this.eventTrackingService.post(this.eventTrackingService.newEvent(event, objectReference, true)); + } + + public List getCategories(Long gradebookId) { + return gradingPersistenceManager.getCategoriesForGradebook(gradebookId); + } + + public List getAssignmentsForCategory(Long categoryId) { + return gradingPersistenceManager.getAssignmentsForCategory(categoryId); + } + + public Category getCategory(Long categoryId) { + return gradingPersistenceManager.getCategory(categoryId).orElse(null); + } + + public void updateCategory(final Category category) throws ConflictingCategoryNameException, StaleObjectModificationException { + //session.evict(category); + final Category persistentCat = gradingPersistenceManager.getCategory(category.getId()).orElse(null); + + if (gradingPersistenceManager.existsDuplicateCategory(category.getName(), category.getGradebook(), category.getId())) { + throw new ConflictingCategoryNameException("You can not save multiple category in a gradebook with the same name"); + } + if (category.getWeight().doubleValue() > 1 || category.getWeight().doubleValue() < 0) { + throw new IllegalArgumentException("weight for category is greater than 1 or less than 0 in updateCategory of BaseHibernateManager"); + } + //session.evict(persistentCat); + gradingPersistenceManager.saveCategory(category); + } + + public void removeCategory(Long categoryId) throws StaleObjectModificationException{ + + Optional optCategory = gradingPersistenceManager.getCategory(categoryId); + + if (optCategory.isPresent()) { + + getAssignmentsForCategory(optCategory.get().getId()).forEach(assignment -> { + + assignment.setCategory(null); + updateAssignment(assignment); + }); + + optCategory.get().setRemoved(true); + gradingPersistenceManager.saveCategory(optCategory.get()); + } else { + log.warn("No category for id {}", categoryId); + } + } + + private Optional getDefaultLetterGradePercentMapping() { + + List mappings = gradingPersistenceManager.getDefaultLetterGradePercentMappings(); + + if (mappings.size() == 0) { + log.info("Default letter grade mapping hasn't been created in DB in BaseHibernateManager.getDefaultLetterGradePercentMapping"); + return Optional.empty(); + } + + if (mappings.size() > 1) { + log.error("Duplicate default letter grade mapping was created in DB in BaseHibernateManager.getDefaultLetterGradePercentMapping"); + return Optional.empty(); + } + + return Optional.of(mappings.get(0)); + } + + private void createOrUpdateDefaultLetterGradePercentMapping(final Map gradeMap) { + + if (gradeMap == null) { + throw new IllegalArgumentException("gradeMap is null in BaseHibernateManager.createOrUpdateDefaultLetterGradePercentMapping"); + } + + Optional lgpm = getDefaultLetterGradePercentMapping(); + + if (lgpm.isPresent()) { + updateDefaultLetterGradePercentMapping(gradeMap, lgpm.get()); + } else { + createDefaultLetterGradePercentMapping(gradeMap); + } + } + + private void updateDefaultLetterGradePercentMapping(final Map gradeMap, final LetterGradePercentMapping lgpm) { + + if (gradeMap.keySet().size() != GradingService.validLetterGrade.length) { + throw new IllegalArgumentException("gradeMap doesn't have right size in BaseHibernateManager.updateDefaultLetterGradePercentMapping"); + } + + if (!validateLetterGradeMapping(gradeMap)) { + throw new IllegalArgumentException("gradeMap contains invalid letter in BaseHibernateManager.updateDefaultLetterGradePercentMapping"); + } + + lgpm.setGradeMap(gradeMap); + gradingPersistenceManager.saveLetterGradePercentMapping(lgpm); + } + + public void createDefaultLetterGradePercentMapping(final Map gradeMap) { + + if (getDefaultLetterGradePercentMapping().isPresent()) { + throw new IllegalArgumentException("gradeMap has already been created in BaseHibernateManager.createDefaultLetterGradePercentMapping"); + } + + if (gradeMap == null) { + throw new IllegalArgumentException("gradeMap is null in BaseHibernateManager.createDefaultLetterGradePercentMapping"); + } + + final Set keySet = gradeMap.keySet(); + + if (keySet.size() != GradingService.validLetterGrade.length) { + throw new IllegalArgumentException("gradeMap doesn't have right size in BaseHibernateManager.createDefaultLetterGradePercentMapping"); + } + + if (!validateLetterGradeMapping(gradeMap)) { + throw new IllegalArgumentException("gradeMap contains invalid letter in BaseHibernateManager.createDefaultLetterGradePercentMapping"); + } + + final LetterGradePercentMapping lgpm = new LetterGradePercentMapping(); + final Map saveMap = new HashMap<>(gradeMap); + lgpm.setGradeMap(saveMap); + lgpm.setMappingType(1); + gradingPersistenceManager.saveLetterGradePercentMapping(lgpm); + } + + public LetterGradePercentMapping getLetterGradePercentMapping(Gradebook gradebook) { + + Optional optMapping + = gradingPersistenceManager.getLetterGradePercentMappingForGradebook(gradebook.getId()); + + if (optMapping.isEmpty()) { + LetterGradePercentMapping lgpm = getDefaultLetterGradePercentMapping().get(); + LetterGradePercentMapping returnLgpm = new LetterGradePercentMapping(); + returnLgpm.setGradebookId(gradebook.getId()); + returnLgpm.setGradeMap(lgpm.getGradeMap()); + returnLgpm.setMappingType(2); + return returnLgpm; + } else { + return optMapping.get(); + } + } + + /** + * this method is different with getLetterGradePercentMapping - + * it returns null if no mapping exists for gradebook instead of + * returning default mapping. + */ + private LetterGradePercentMapping getLetterGradePercentMappingForGradebook(Gradebook gradebook) { + return gradingPersistenceManager.getLetterGradePercentMappingForGradebook(gradebook.getId()).orElse(null); + } + + public void saveOrUpdateLetterGradePercentMapping(final Map gradeMap, final Gradebook gradebook) { + + if (gradeMap == null) { + throw new IllegalArgumentException("gradeMap is null in BaseHibernateManager.saveOrUpdateLetterGradePercentMapping"); + } + + final LetterGradePercentMapping lgpm = getLetterGradePercentMappingForGradebook(gradebook); + + if (lgpm == null) { + final Set keySet = gradeMap.keySet(); + + if (keySet.size() != GradingService.validLetterGrade.length) { //we only consider letter grade with -/+ now. + throw new IllegalArgumentException("gradeMap doesn't have right size in BaseHibernateManager.saveOrUpdateLetterGradePercentMapping"); + } + if (!validateLetterGradeMapping(gradeMap)) { + throw new IllegalArgumentException("gradeMap contains invalid letter in BaseHibernateManager.saveOrUpdateLetterGradePercentMapping"); + } + + final LetterGradePercentMapping lgpm1 = new LetterGradePercentMapping(); + final Map saveMap = new HashMap<>(gradeMap); + lgpm1.setGradeMap(saveMap); + lgpm1.setGradebookId(gradebook.getId()); + lgpm1.setMappingType(2); + gradingPersistenceManager.saveLetterGradePercentMapping(lgpm1); + } + else + { + udpateLetterGradePercentMapping(gradeMap, gradebook); + } + } + + private void udpateLetterGradePercentMapping(final Map gradeMap, final Gradebook gradebook) { + + final LetterGradePercentMapping lgpm = getLetterGradePercentMapping(gradebook); + + if (lgpm == null) { + throw new IllegalArgumentException("LetterGradePercentMapping is null in BaseHibernateManager.updateLetterGradePercentMapping"); + } + if (gradeMap == null) { + throw new IllegalArgumentException("gradeMap is null in BaseHibernateManager.updateLetterGradePercentMapping"); + } + final Set keySet = gradeMap.keySet(); + + if (keySet.size() != GradingService.validLetterGrade.length) { //we only consider letter grade with -/+ now. + throw new IllegalArgumentException("gradeMap doesn't have right size in BaseHibernateManager.udpateLetterGradePercentMapping"); + } + if (validateLetterGradeMapping(gradeMap) == false) { + throw new IllegalArgumentException("gradeMap contains invalid letter in BaseHibernateManager.udpateLetterGradePercentMapping"); + } + final Map saveMap = new HashMap<>(gradeMap); + lgpm.setGradeMap(saveMap); + gradingPersistenceManager.saveLetterGradePercentMapping(lgpm); + } + + private boolean validateLetterGradeMapping(final Map gradeMap) { + + for (final String key : gradeMap.keySet()) { + boolean validLetter = false; + for (final String element : GradingService.validLetterGrade) { + if (key.equalsIgnoreCase(element)) { + validLetter = true; + break; + } + } + if (!validLetter) { + return false; + } + } + return true; + } + + public Long createUngradedAssignment(final Long gradebookId, final String name, final Date dueDate, final Boolean isNotCounted, + final Boolean isReleased) throws ConflictingAssignmentNameException, StaleObjectModificationException { + final Gradebook gb = gradingPersistenceManager.getGradebook(gradebookId).orElse(null);; + + // trim the name before validation + final String trimmedName = StringUtils.trimToEmpty(name); + + if (assignmentNameExists(trimmedName, gb)) { + throw new ConflictingAssignmentNameException("You can not save multiple assignments in a gradebook with the same name"); + } + + final GradebookAssignment asn = new GradebookAssignment(); + asn.setGradebook(gb); + asn.setName(trimmedName); + asn.setDueDate(dueDate); + asn.setUngraded(true); + if (isNotCounted != null) { + asn.setNotCounted(isNotCounted); + } + if (isReleased != null) { + asn.setReleased(isReleased); + } + + return gradingPersistenceManager.saveAssignment(asn).getId(); + } + + /** + * + * @param id + * @return the GradebookAssignment object with the given id + */ + public GradebookAssignment getAssignment(Long id) { + return gradingPersistenceManager.getAssignmentById(id).orElse(null); + } + + private void createDefaultLetterGradeMapping(final Map gradeMap) { + + if (getDefaultLetterGradePercentMapping().isEmpty()) { + final Set keySet = gradeMap.keySet(); + + if (keySet.size() != GradingService.validLetterGrade.length) { + throw new IllegalArgumentException("gradeMap doesn't have right size in BaseHibernateManager.createDefaultLetterGradePercentMapping"); + } + + if (!validateLetterGradeMapping(gradeMap)) { + throw new IllegalArgumentException("gradeMap contains invalid letter in BaseHibernateManager.createDefaultLetterGradePercentMapping"); + } + + final LetterGradePercentMapping lgpm = new LetterGradePercentMapping(); + gradingPersistenceManager.saveLetterGradePercentMapping(lgpm); + final Map saveMap = new HashMap(); + for (final Iterator iter = gradeMap.keySet().iterator(); iter.hasNext();) + { + final String key = (String) iter.next(); + saveMap.put(key, gradeMap.get(key)); + } + if (lgpm != null) + { + lgpm.setGradeMap(saveMap); + lgpm.setMappingType(1); + gradingPersistenceManager.saveLetterGradePercentMapping(lgpm); + } + } + } + + private Map getHardDefaultLetterMapping() { + + final Map gradeMap = new HashMap<>(); + gradeMap.put("A+", Double.valueOf(100)); + gradeMap.put("A", Double.valueOf(95)); + gradeMap.put("A-", Double.valueOf(90)); + gradeMap.put("B+", Double.valueOf(87)); + gradeMap.put("B", Double.valueOf(83)); + gradeMap.put("B-", Double.valueOf(80)); + gradeMap.put("C+", Double.valueOf(77)); + gradeMap.put("C", Double.valueOf(73)); + gradeMap.put("C-", Double.valueOf(70)); + gradeMap.put("D+", Double.valueOf(67)); + gradeMap.put("D", Double.valueOf(63)); + gradeMap.put("D-", Double.valueOf(60)); + gradeMap.put("F", Double.valueOf(0.0)); + + return gradeMap; + } + + private void finalizeNullGradeRecords(final Gradebook gradebook) { + + final Set studentUids = getAllStudentUids(gradebook.getUid()); + final Date now = new Date(); + final String graderId = sessionManager.getCurrentSessionUserId(); + + final List countedAssignments + = gradingPersistenceManager.getCountedAndGradedAssignmentsForGradebook(gradebook.getId()); + + final Map> visible = getVisibleExternalAssignments(gradebook, studentUids, countedAssignments); + + for (final GradebookAssignment assignment : countedAssignments) { + final List scoredGradeRecords + = gradingPersistenceManager.getAllAssignmentGradeRecordsForAssignment(assignment.getId()); + + final Map studentToGradeRecordMap = new HashMap<>(); + for (final AssignmentGradeRecord scoredGradeRecord : scoredGradeRecords) { + studentToGradeRecordMap.put(scoredGradeRecord.getStudentId(), scoredGradeRecord); + } + + for (String studentUid : studentUids) { + // SAK-11485 - We don't want to add scores for those grouped activities + // that this student should not see or be scored on. + if (assignment.getExternallyMaintained() && (!visible.containsKey(studentUid) || !visible.get(studentUid).contains(assignment))) { + continue; + } + AssignmentGradeRecord gradeRecord = studentToGradeRecordMap.get(studentUid); + if (gradeRecord != null) { + if (gradeRecord.getPointsEarned() == null) { + gradeRecord.setPointsEarned(0d); + } else { + continue; + } + } else { + gradeRecord = new AssignmentGradeRecord(assignment, studentUid, 0d); + } + gradeRecord.setGraderId(graderId); + gradeRecord.setDateRecorded(now); + gradingPersistenceManager.saveAssignmentGradeRecord(gradeRecord); + gradingPersistenceManager.saveGradingEvent(new GradingEvent(assignment, graderId, studentUid, gradeRecord.getPointsEarned())); + } + } + } + + private Map> getVisibleExternalAssignments(final Gradebook gradebook, final Collection studentIds, final List assignments) { + + final String gradebookUid = gradebook.getUid(); + final Map> allExternals = getVisibleExternalAssignments(gradebookUid, studentIds); + final Map allRequested = new HashMap(); + + for (final GradebookAssignment a : assignments) { + if (a.getExternallyMaintained()) { + allRequested.put(a.getExternalId(), a); + } + } + + final Map> visible = new HashMap>(); + for (final String studentId : allExternals.keySet()) { + if (studentIds.contains(studentId)) { + final Set studentAssignments = new HashSet(); + for (final String assignmentId : allExternals.get(studentId)) { + if (allRequested.containsKey(assignmentId)) { + studentAssignments.add(allRequested.get(assignmentId)); + } + } + visible.put(studentId, studentAssignments); + } + } + return visible; + } + + private String getPropertyValue(final String name) { + + // TODO: ADRIAN should be caching these like this? + String value = this.propertiesMap.get(name); + if (value == null) { + final Optional property = gradingPersistenceManager.getGradebookProperty(name); + if (property.isPresent()) { + value = property.get().getValue(); + this.propertiesMap.put(name, value); + } + } + return value; + } + + private void setPropertyValue(final String name, final String value) { + + GradebookProperty property + = gradingPersistenceManager.getGradebookProperty(name).orElse(new GradebookProperty(name)); + property.setValue(value); + gradingPersistenceManager.saveGradebookProperty(property); + this.propertiesMap.put(name, value); + } + + /** + * Oracle has a low limit on the maximum length of a parameter list + * in SQL queries of the form "WHERE tbl.col IN (:paramList)". + * Since enrollment lists can sometimes be very long, we've replaced + * such queries with full selects followed by filtering. This helper + * method filters out unwanted grade records. (Typically they're not + * wanted because they're either no longer officially enrolled in the + * course or they're not members of the selected section.) + */ + private List filterGradeRecordsByStudents(final Collection gradeRecords, final Collection studentUids) { + + final List filteredRecords = new ArrayList(); + for (final Iterator iter = gradeRecords.iterator(); iter.hasNext(); ) { + final AbstractGradeRecord agr = (AbstractGradeRecord)iter.next(); + if (studentUids.contains(agr.getStudentId())) { + filteredRecords.add(agr); + } + } + return filteredRecords; + } + + private Set getAllStudentUids(final String gradebookUid) { + + final List enrollments = sectionAwareness.getSiteMembersInRole(gradebookUid, Role.STUDENT); + return enrollments.stream().map(e -> e.getUser().getUserUid()).collect(Collectors.toSet()); + } + + + private String getUserUid() { + return sessionManager.getCurrentSessionUserId(); + } + + private void loadAssignmentGradebookAndCategory(GradebookAssignment asn, Long gradebookId, Long categoryId) { + + Gradebook gb = gradingPersistenceManager.getGradebook(gradebookId).get(); + asn.setGradebook(gb); + if (categoryId != null) { + asn.setCategory(gradingPersistenceManager.getCategory(categoryId).orElse(null)); + } + } + + private Long createCategory(Long gradebookId, String name, Double weight, Integer drop_lowest, + Integer dropHighest, Integer keepHighest, Boolean is_extra_credit, Boolean is_equal_weight) { + return createCategory(gradebookId, name, weight, drop_lowest, dropHighest, keepHighest, is_extra_credit, is_equal_weight, null); + } + + private Long createCategory(Long gradebookId, String name, Double weight, Integer drop_lowest, + Integer dropHighest, Integer keepHighest, Boolean is_extra_credit, Boolean is_equal_weight, + Integer categoryOrder) throws ConflictingCategoryNameException, StaleObjectModificationException { + + //final Gradebook gb = (Gradebook)session.load(Gradebook.class, gradebookId); + final Optional optGb = gradingPersistenceManager.getGradebook(gradebookId); + + final Gradebook gb = optGb.get(); + + if (gradingPersistenceManager.isCategoryDefined(name, gb)) { + throw new ConflictingCategoryNameException("You can not save multiple categories in a gradebook with the same name"); + } + if (weight > 1 || weight < 0) { + throw new IllegalArgumentException("weight for category is greater than 1 or less than 0 in createCategory of BaseHibernateManager"); + } + if (((drop_lowest != null && drop_lowest > 0) || (dropHighest!=null && dropHighest > 0)) && (keepHighest!=null && keepHighest > 0)) { + throw new IllegalArgumentException("a combination of positive values for keepHighest and either drop_lowest or dropHighest occurred in createCategory of BaseHibernateManager"); + } + + final Category ca = new Category(); + ca.setGradebook(gb); + ca.setName(name); + ca.setWeight(weight); + ca.setDropLowest(drop_lowest); + ca.setDropHighest(dropHighest); + ca.setKeepHighest(keepHighest); + //ca.setItemValue(itemValue); + ca.setRemoved(false); + ca.setExtraCredit(is_extra_credit); + ca.setEqualWeightAssignments(is_equal_weight); + ca.setCategoryOrder(categoryOrder); + + return gradingPersistenceManager.saveCategory(ca).getId(); + } + + private Double calculateEquivalentPointValueForPercent(final Double doublePointsPossible, final Double doublePercentEarned) { + + if (doublePointsPossible == null || doublePercentEarned == null) { + return null; + } + + final BigDecimal pointsPossible = new BigDecimal(doublePointsPossible.toString()); + final BigDecimal percentEarned = new BigDecimal(doublePercentEarned.toString()); + final BigDecimal equivPoints = pointsPossible.multiply(percentEarned.divide(new BigDecimal("100"), GradingConstants.MATH_CONTEXT)); + return equivPoints.doubleValue(); + } + + private List getComments(GradebookAssignment assignment, Collection studentIds) { + + if (studentIds.isEmpty()) { + return Collections.emptyList(); + } + + return gradingPersistenceManager.getCommentsForStudents(assignment, studentIds); + } + + /** + * Converts points to letter grade for the given AssignmentGradeRecords + * @param gradebook + * @param studentRecordsFromDB + * @return + */ + private void convertPointsToLetterGrade(Gradebook gradebook, List studentRecordsFromDB) { + + LetterGradePercentMapping lgpm = getLetterGradePercentMapping(gradebook); + for (AssignmentGradeRecord agr : studentRecordsFromDB) { + if (agr != null) { + Double pointsPossible = agr.getAssignment().getPointsPossible(); + // TODO Adrian: What is this about? Weirddddddd + agr.setDateRecorded(agr.getDateRecorded()); + agr.setGraderId(agr.getGraderId()); + if (pointsPossible == null || agr.getPointsEarned() == null) { + agr.setLetterEarned(null); + } else { + String letterGrade = lgpm.getGrade(calculateEquivalentPercent(pointsPossible, agr.getPointsEarned())); + agr.setLetterEarned(letterGrade); + } + } + } + } + + /** + * + * @param name the assignment name (will not be trimmed) + * @param gradebook the gradebook to check + * @return true if an assignment with the given name already exists in this gradebook. + */ + private boolean assignmentNameExists(String name, Gradebook gradebook) { + + return gradingPersistenceManager.countAssignmentsByNameAndGradebookUid(name, gradebook.getUid()) > 0L; + } + + /** + * + * @param doublePointsPossible + * @param doublePointsEarned + * @return the % equivalent for the given points possible and points earned + */ + private Double calculateEquivalentPercent(final Double doublePointsPossible, final Double doublePointsEarned) { + + if (doublePointsEarned == null || doublePointsPossible == null) { + return null; + } + + // scale to handle points stored as repeating decimals + final BigDecimal pointsEarned = new BigDecimal(doublePointsEarned.toString()); + final BigDecimal pointsPossible = new BigDecimal(doublePointsPossible.toString()); + + // Avoid dividing by zero + if (pointsEarned.compareTo(BigDecimal.ZERO) == 0 || pointsPossible.compareTo(BigDecimal.ZERO) == 0) { + return new Double(0); + } + + final BigDecimal equivPercent = pointsEarned.divide(pointsPossible, GradingConstants.MATH_CONTEXT).multiply(new BigDecimal("100")); + return Double.valueOf(equivPercent.doubleValue()); + + } + + /** + * Converts points to percentage for the given AssignmentGradeRecords + * @param gradebook + * @param studentRecordsFromDB + * @return + */ + private void convertPointsToPercentage(Gradebook gradebook, List studentRecordsFromDB) { + + for (AssignmentGradeRecord agr : studentRecordsFromDB) { + if (agr != null) { + final Double pointsPossible = agr.getAssignment().getPointsPossible(); + if (pointsPossible == null || agr.getPointsEarned() == null) { + agr.setPercentEarned(null); + } else { + agr.setDateRecorded(agr.getDateRecorded()); + agr.setGraderId(agr.getGraderId()); + agr.setPercentEarned(calculateEquivalentPercent(pointsPossible, agr.getPointsEarned())); + } + } + } + } +} diff --git a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/VersionedExternalizable.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/VersionedExternalizable.java similarity index 53% rename from edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/VersionedExternalizable.java rename to gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/VersionedExternalizable.java index 632c767dff06..2c658fc08f97 100644 --- a/edu-services/gradebook-service/impl/src/java/org/sakaiproject/component/gradebook/VersionedExternalizable.java +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/VersionedExternalizable.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.sakaiproject.component.gradebook; +package org.sakaiproject.grading.impl; import java.io.Externalizable; import java.io.IOException; @@ -56,12 +56,12 @@ */ @Deprecated public abstract class VersionedExternalizable implements Externalizable { - public static String VERSION_ATTRIBUTE = "externalizableVersion"; + public static String VERSION_ATTRIBUTE = "externalizableVersion"; - /** - * @return non-null archivable version identifier for the object definition - */ - public abstract String getExternalizableVersion(); + /** + * @return non-null archivable version identifier for the object definition + */ + public abstract String getExternalizableVersion(); /** * This XStream converter stores the externalizable version of the @@ -70,65 +70,65 @@ public abstract class VersionedExternalizable implements Externalizable { * don't try to reconstitute XML corresponding to anything but the current * version. */ - public static class Converter extends AbstractReflectionConverter { - public Converter(final Mapper mapper, final ReflectionProvider reflectionProvider) { - super(mapper, reflectionProvider); - } + public static class Converter extends AbstractReflectionConverter { + public Converter(final Mapper mapper, final ReflectionProvider reflectionProvider) { + super(mapper, reflectionProvider); + } - @Override - public boolean canConvert(final Class type) { + @Override + public boolean canConvert(final Class type) { return VersionedExternalizable.class.isAssignableFrom(type); } - @Override - public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) { - writer.addAttribute(VERSION_ATTRIBUTE, ((VersionedExternalizable) source).getExternalizableVersion()); - super.marshal(source, writer, context); + @Override + public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) { + writer.addAttribute(VERSION_ATTRIBUTE, ((VersionedExternalizable) source).getExternalizableVersion()); + super.marshal(source, writer, context); } - @Override - public Object doUnmarshal(final Object result, final HierarchicalStreamReader reader, final UnmarshallingContext context) { - final String currentVersion = ((VersionedExternalizable) result).getExternalizableVersion(); - final String oldVersion = reader.getAttribute(VERSION_ATTRIBUTE); - if ((oldVersion == null) || !currentVersion.equals(oldVersion)) { - // This is one place we might put a version translation method in the future.... - throw new ConversionException("Cannot convert " + result + " from version " + oldVersion + " to version " + currentVersion); - } - return super.doUnmarshal(result, reader, context); + @Override + public Object doUnmarshal(final Object result, final HierarchicalStreamReader reader, final UnmarshallingContext context) { + final String currentVersion = ((VersionedExternalizable) result).getExternalizableVersion(); + final String oldVersion = reader.getAttribute(VERSION_ATTRIBUTE); + if ((oldVersion == null) || !currentVersion.equals(oldVersion)) { + // This is one place we might put a version translation method in the future.... + throw new ConversionException("Cannot convert " + result + " from version " + oldVersion + " to version " + currentVersion); + } + return super.doUnmarshal(result, reader, context); } } - protected static XStream getXStream() { - final XStream xstream = new XStream(new DomDriver()); // does not require XPP3 library - xstream.registerConverter(new Converter(xstream.getMapper(), xstream.getReflectionProvider())); - return xstream; - } + protected static XStream getXStream() { + final XStream xstream = new XStream(new DomDriver()); // does not require XPP3 library + xstream.registerConverter(new Converter(xstream.getMapper(), xstream.getReflectionProvider())); + return xstream; + } - @Override - public void readExternal(final ObjectInput inputStream) throws IOException, ClassNotFoundException { - getXStream().fromXML(inputStream.readUTF(), this); - } + @Override + public void readExternal(final ObjectInput inputStream) throws IOException, ClassNotFoundException { + getXStream().fromXML(inputStream.readUTF(), this); + } - @Override - public void writeExternal(final ObjectOutput outputStream) throws IOException { - outputStream.writeUTF(getXStream().toXML(this)); - } + @Override + public void writeExternal(final ObjectOutput outputStream) throws IOException { + outputStream.writeUTF(getXStream().toXML(this)); + } - /** - * @param obj the Java object (usually a subclass of VersionedExternalizable) to describe - * as XML - * @return XML describing the object - */ - public static String toXml(final Object obj) { - return getXStream().toXML(obj); - } + /** + * @param obj the Java object (usually a subclass of VersionedExternalizable) to describe + * as XML + * @return XML describing the object + */ + public static String toXml(final Object obj) { + return getXStream().toXML(obj); + } - /** - * @param xmlString XML string (presumably created by this class) describing a Java object - * @return the Java object it describes - */ - public static Object fromXml(final String xmlString) { - return getXStream().fromXML(xmlString); - } + /** + * @param xmlString XML string (presumably created by this class) describing a Java object + * @return the Java object it describes + */ + public static Object fromXml(final String xmlString) { + return getXStream().fromXML(xmlString); + } } diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/] b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/] new file mode 100644 index 000000000000..b6ef07055bd5 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/] @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang3.StringUtils; + +import org.hibernate.HibernateException; +import org.hibernate.Session; + +import org.sakaiproject.component.cover.ComponentManager; +import org.sakaiproject.grading.api.GraderPermission; +import org.sakaiproject.grading.api.GradingPersistenceManager; +import org.sakaiproject.grading.api.GradingService; +import org.sakaiproject.grading.api.GradingCategoryType; +import org.sakaiproject.grading.api.PermissionDefinition; +import org.sakaiproject.grading.api.model.GradebookAssignment; +import org.sakaiproject.grading.api.model.Category; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.Permission; +import org.sakaiproject.section.api.SectionAwareness; +import org.sakaiproject.section.api.coursemanagement.CourseSection; +import org.sakaiproject.section.api.coursemanagement.EnrollmentRecord; +import org.sakaiproject.section.api.facade.Role; +import org.sakaiproject.tool.api.SessionManager; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.orm.hibernate5.HibernateCallback; + +public class GradingPersistenceManagerImpl implements GradingPersistenceManager { +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/AssignmentGradeRecordRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/AssignmentGradeRecordRepositoryImpl.java new file mode 100644 index 000000000000..d4879e0b71b5 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/AssignmentGradeRecordRepositoryImpl.java @@ -0,0 +1,161 @@ +package org.sakaiproject.grading.impl.repository; + +import org.hibernate.Session; +import org.hibernate.criterion.Restrictions; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import org.springframework.transaction.annotation.Transactional; + +import org.sakaiproject.grading.api.model.AssignmentGradeRecord; +import org.sakaiproject.grading.api.model.GradableObject; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradebookAssignment; +import org.sakaiproject.grading.api.repository.AssignmentGradeRecordRepository; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaDelete; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Root; + +import org.sakaiproject.hibernate.HibernateCriterionUtils; + +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +public class AssignmentGradeRecordRepositoryImpl extends SpringCrudRepositoryImpl implements AssignmentGradeRecordRepository { + + @Transactional(readOnly = true) + public List findByGradableObject_Gradebook_IdAndGradableObject_RemovedOrderByPointsEarned(Long gradebookId, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(AssignmentGradeRecord.class); + Root agr = query.from(AssignmentGradeRecord.class); + Join go = agr.join("gradableObject"); + Join gb = go.join("gradebook"); + query.where(cb.and(cb.equal(gb.get("id"), gradebookId), cb.equal(go.get("removed"), removed))) + .orderBy(cb.asc(agr.get("pointsEarned"))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradableObject_IdAndGradableObject_RemovedOrderByPointsEarned(Long gradableObjectId, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(AssignmentGradeRecord.class); + Root agr = query.from(AssignmentGradeRecord.class); + Join go = agr.join("gradableObject"); + query.where(cb.and(cb.equal(go.get("id"), gradableObjectId), cb.equal(go.get("removed"), removed))) + .orderBy(cb.asc(agr.get("pointsEarned"))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public Optional findByGradableObject_IdAndStudentId(Long gradableObjectId, String studentId) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(AssignmentGradeRecord.class); + Root agr = query.from(AssignmentGradeRecord.class); + Join go = agr.join("gradableObject"); + query.where(cb.and(cb.equal(go.get("id"), gradableObjectId), cb.equal(agr.get("studentId"), studentId))); + return session.createQuery(query).uniqueResultOptional(); + } + + @Transactional(readOnly = true) + public List findByGradableObject_Gradebook_Id(Long gradebookId) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(AssignmentGradeRecord.class); + Root agr = query.from(AssignmentGradeRecord.class); + Join gb = agr.join("gradableObject").join("gradebook"); + query.where(cb.equal(gb.get("id"), gradebookId)); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradableObject_Gradebook_Uid(String gradebookUid) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(AssignmentGradeRecord.class); + Root agr = query.from(AssignmentGradeRecord.class); + Join gb = agr.join("gradableObject").join("gradebook"); + query.where(cb.equal(gb.get("uid"), gradebookUid)); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradableObject_RemovedAndGradableObject_IdInAndStudentIdIn(Boolean removed, List gradableObjectIds, List studentIds) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(AssignmentGradeRecord.class); + Root agr = query.from(AssignmentGradeRecord.class); + Join go = agr.join("gradableObject"); + query.where(cb.and(cb.equal(go.get("removed"), removed)), + go.get("id").in(gradableObjectIds), + agr.get("studentId").in(studentIds)); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradableObject_Gradebook_IdAndGradableObject_RemovedAndStudentIdIn(Long gradebookId, Boolean removed, Collection studentIds) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(AssignmentGradeRecord.class); + Root agr = query.from(AssignmentGradeRecord.class); + Join go = agr.join("gradableObject"); + Join gb = go.join("gradebook"); + query.where(cb.and(cb.equal(gb.get("id"), gradebookId), cb.equal(go.get("removed"), false), agr.get("studentId").in(studentIds))); + return session.createQuery(query).list(); + + /* + return (List) sessionFactory.getCurrentSession() + .createCriteria(AssignmentGradeRecord.class) + .createAlias("gradableObject", "go") + .createAlias("gradableObject.gradebook", "gb") + .add(Restrictions.equal("gb.id", gradebookId)) + .add(Restrictions.equal("go.removed", false)) + .add(HibernateCriterionUtils.CriterionInRestrictionSplitter("studentId", studentIds)) + .list(); + */ + } + + @Transactional(readOnly = true) + public List findByGradableObjectAndStudentIdIn(GradebookAssignment assignment, Collection studentIds) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(AssignmentGradeRecord.class); + Root agr = query.from(AssignmentGradeRecord.class); + query.where(cb.and(cb.equal(agr.get("gradableObject"), assignment), agr.get("studendId").in(studentIds))); + return session.createQuery(query).list(); + + /* + return (List) sessionFactory.getCurrentSession() + .createCriteria(AssignmentGradeRecord.class) + .add(Restrictions.equal("gradableObject", assignment)) + .add(HibernateCriterionUtils.CriterionInRestrictionSplitter("studentId", studentIds)) + .list(); + */ + } + + + @Transactional + public int deleteByGradableObject(GradebookAssignment assignment) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaDelete delete = cb.createCriteriaDelete(AssignmentGradeRecord.class); + Root agr = delete.from(AssignmentGradeRecord.class); + delete.where(cb.equal(agr.get("gradableObject"), assignment)); + return session.createQuery(delete).executeUpdate(); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CategoryRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CategoryRepositoryImpl.java new file mode 100644 index 000000000000..40b9926cdac5 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CategoryRepositoryImpl.java @@ -0,0 +1,73 @@ +package org.sakaiproject.grading.impl.repository; + +import java.util.List; + +import org.hibernate.Session; + +import org.springframework.transaction.annotation.Transactional; + +import org.sakaiproject.grading.api.model.Category; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.repository.CategoryRepository; +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Root; + +public class CategoryRepositoryImpl extends SpringCrudRepositoryImpl implements CategoryRepository { + + @Transactional(readOnly = true) + public List findByGradebook_IdAndRemoved(Long gradebookId, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Category.class); + Root cat = query.from(Category.class); + Join gb = cat.join("gradebook"); + query.where(cb.and(cb.equal(cat.get("removed"), removed), cb.equal(gb.get("id"), gradebookId))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public boolean existsByNameAndGradebookAndRemoved(String name, Gradebook gradebook, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Long.class); + Root cat = query.from(Category.class); + query.select(cb.count(cat)) + .where(cb.and(cb.equal(cat.get("name"), name), + cb.equal(cat.get("gradebook"), gradebook), + cb.equal(cat.get("removed"), removed))); + return session.createQuery(query).getSingleResult() > 0L; + } + + @Transactional(readOnly = true) + public boolean existsByNameAndGradebookAndNotIdAndRemoved(String name, Gradebook gradebook, Long id, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Long.class); + Root cat = query.from(Category.class); + query.select(cb.count(cat)) + .where(cb.and(cb.equal(cat.get("name"), name), + cb.equal(cat.get("gradebook"), gradebook), + cb.equal(cat.get("removed"), removed), + cb.notEqual(cat.get("id"), id))); + return session.createQuery(query).getSingleResult() > 0L; + } + + @Transactional(readOnly = true) + public List findByGradebook_Uid(String gradebookUid) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Category.class); + Join gb = query.from(Category.class).join("gradebook"); + query.where(cb.equal(gb.get("uid"), gradebookUid)); + return session.createQuery(query).list(); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CommentRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CommentRepositoryImpl.java new file mode 100644 index 000000000000..aa05367ba7a4 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CommentRepositoryImpl.java @@ -0,0 +1,75 @@ +package org.sakaiproject.grading.impl.repository; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import org.hibernate.Session; +import org.hibernate.criterion.Restrictions; + +import org.springframework.transaction.annotation.Transactional; + +import org.sakaiproject.grading.api.model.Comment; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradableObject; +import org.sakaiproject.grading.api.model.GradebookAssignment; +import org.sakaiproject.grading.api.repository.CommentRepository; +import org.sakaiproject.hibernate.HibernateCriterionUtils; +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaDelete; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Root; + +public class CommentRepositoryImpl extends SpringCrudRepositoryImpl implements CommentRepository { + + @Transactional(readOnly = true) + public Optional findByStudentIdAndGradableObject_Gradebook_UidAndGradableObject_IdAndGradableObject_Removed( + String studentUid, String gradebookUid, Long assignmentId, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Comment.class); + Root comment = query.from(Comment.class); + Join go = comment.join("gradableObject"); + Join gb = go.join("gradebook"); + query.where(cb.and(cb.equal(comment.get("studentId"), studentUid), + cb.equal(gb.get("uid"), gradebookUid), + cb.equal(go.get("id"), assignmentId))); + return session.createQuery(query).uniqueResultOptional(); + } + + @Transactional(readOnly = true) + public List findByGradableObjectAndStudentIdIn(GradebookAssignment assignment, Collection studentIds) { + + return (List) sessionFactory.getCurrentSession() + .createCriteria(Comment.class) + .add(Restrictions.eq("gradableObject", assignment)) + .add(HibernateCriterionUtils.CriterionInRestrictionSplitter("studentId", studentIds)) + .list(); + } + + @Transactional(readOnly = true) + public List findByGradableObject_Gradebook_Uid(String gradebookUid) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Comment.class); + Root comment = query.from(Comment.class); + Join gb = comment.join("gradableObject").join("gradebook"); + return session.createQuery(query.where(cb.equal(gb.get("uid"), gradebookUid))).list(); + } + + @Transactional + public int deleteByGradableObject(GradebookAssignment assignment) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaDelete delete = cb.createCriteriaDelete(Comment.class); + Root comment = delete.from(Comment.class); + delete.where(cb.equal(comment.get("gradableObject"), assignment)); + return session.createQuery(delete).executeUpdate(); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CourseGradeRecordRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CourseGradeRecordRepositoryImpl.java new file mode 100644 index 000000000000..97c45204a80f --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CourseGradeRecordRepositoryImpl.java @@ -0,0 +1,100 @@ +package org.sakaiproject.grading.impl.repository; + +import org.hibernate.Session; +import org.hibernate.criterion.Projections; +import org.hibernate.criterion.Restrictions; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Root; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.springframework.transaction.annotation.Transactional; + +import org.sakaiproject.grading.api.model.CourseGradeRecord; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradableObject; +import org.sakaiproject.grading.api.repository.CourseGradeRecordRepository; +import org.sakaiproject.hibernate.HibernateCriterionUtils; +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +public class CourseGradeRecordRepositoryImpl extends SpringCrudRepositoryImpl implements CourseGradeRecordRepository { + + @Transactional(readOnly = true) + public List findByGradableObject_Id(Long id) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(CourseGradeRecord.class); + Join go = query.from(CourseGradeRecord.class).join("gradableObject"); + query.where(cb.equal(go.get("id"), id)); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradableObject_GradebookAndEnteredGradeNotNull(Gradebook gradebook) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(CourseGradeRecord.class); + Root cgr = query.from(CourseGradeRecord.class); + Join go = cgr.join("gradableObject"); + query.where(cb.and(cb.equal(go.get("gradebook"), gradebook), cb.isNotNull(cgr.get("enteredGrade")))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public Optional findByGradableObject_GradebookAndStudentId(Gradebook gradebook, String studentId) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(CourseGradeRecord.class); + Root cgr = query.from(CourseGradeRecord.class); + Join go = cgr.join("gradableObject"); + query.where(cb.and(cb.equal(cgr.get("studentId"), studentId), + cb.equal(go.get("gradebook"), gradebook))); + return session.createQuery(query).uniqueResultOptional(); + } + + @Transactional(readOnly = true) + public Long countByGradableObject_Gradebook_IdAndEnteredGradeNotNullAndStudentIdIn(Long gradebookId, Set studentIds) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Long.class); + Root cgr = query.from(CourseGradeRecord.class); + Join gb = cgr.join("gradableObject").join("gradebook"); + query.select(cb.count(cgr)) + .where(cb.and(cb.equal(gb.get("id"), gradebookId), + cb.isNotNull(cgr.get("enteredGrade")), + cgr.get("studentId").in(studentIds))); + return session.createQuery(query).uniqueResult(); + + /* + return (Long) sessionFactory.getCurrentSession().createCriteria(CourseGradeRecord.class) + .createAlias("gradableObject", "go") + .createAlias("go.gradebook", "gb") + .add(Restrictions.eq("gb.id", gradebookId)) + .add(Restrictions.isNotNull("enteredGrade")) + .add(HibernateCriterionUtils.CriterionInRestrictionSplitter("studentId", studentIds)) + .setProjection(Projections.rowCount()) + .uniqueResult(); + */ + } + + @Transactional(readOnly = true) + public List findByGradableObject_Gradebook_Uid(String gradebookUid) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(CourseGradeRecord.class); + Root cgr = query.from(CourseGradeRecord.class); + Join gb = cgr.join("gradableObject").join("gradebook"); + query.where(cb.equal(gb.get("uid"), gradebookUid)); + return session.createQuery(query).list(); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CourseGradeRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CourseGradeRepositoryImpl.java new file mode 100644 index 000000000000..e54f47a71776 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/CourseGradeRepositoryImpl.java @@ -0,0 +1,41 @@ +package org.sakaiproject.grading.impl.repository; + +import org.hibernate.Session; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; + +import java.util.List; + +import org.springframework.transaction.annotation.Transactional; + +import org.sakaiproject.grading.api.model.CourseGrade; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.repository.CourseGradeRepository; +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +public class CourseGradeRepositoryImpl extends SpringCrudRepositoryImpl implements CourseGradeRepository { + + @Transactional(readOnly = true) + public List findByGradebook_Id(Long gradebookId) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(CourseGrade.class); + Join gb = query.from(CourseGrade.class).join("gradebook"); + query.where(cb.equal(gb.get("id"), gradebookId)); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradebook_Uid(String gradebookUid) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(CourseGrade.class); + Join gb = query.from(CourseGrade.class).join("gradebook"); + query.where(cb.equal(gb.get("uid"), gradebookUid)); + return session.createQuery(query).list(); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradeMappingRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradeMappingRepositoryImpl.java new file mode 100644 index 000000000000..0a1326b08ccd --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradeMappingRepositoryImpl.java @@ -0,0 +1,33 @@ +package org.sakaiproject.grading.impl.repository; + +import java.util.List; + +import org.springframework.transaction.annotation.Transactional; + +import org.hibernate.Session; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaDelete; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Root; + +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradeMapping; +import org.sakaiproject.grading.api.repository.GradeMappingRepository; +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +public class GradeMappingRepositoryImpl extends SpringCrudRepositoryImpl implements GradeMappingRepository { + + @Transactional(readOnly = true) + public List findByGradebook_Uid(String gradebookUid) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradeMapping.class); + Join gb = query.from(GradeMapping.class).join("gradebook"); + query.where(cb.equal(gb.get("uid"), gradebookUid)); + return session.createQuery(query).list(); + } + +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradebookAssignmentRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradebookAssignmentRepositoryImpl.java new file mode 100644 index 000000000000..df6e6d8247af --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradebookAssignmentRepositoryImpl.java @@ -0,0 +1,186 @@ +package org.sakaiproject.grading.impl.repository; + +import org.hibernate.Session; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Root; + +import java.util.List; +import java.util.Optional; + +import org.springframework.transaction.annotation.Transactional; + +import org.sakaiproject.grading.api.model.Category; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradebookAssignment; +import org.sakaiproject.grading.api.repository.GradebookAssignmentRepository; +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +public class GradebookAssignmentRepositoryImpl extends SpringCrudRepositoryImpl implements GradebookAssignmentRepository { + + @Transactional(readOnly = true) + public Optional findByNameAndGradebook_UidAndRemoved(String name, String gradebookUid, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradebookAssignment.class); + Root ga = query.from(GradebookAssignment.class); + Join gb = ga.join("gradebook"); + query.where(cb.and(cb.equal(ga.get("name"), name), + cb.equal(gb.get("uid"), gradebookUid), + cb.equal(ga.get("removed"), removed))); + return session.createQuery(query).uniqueResultOptional(); + } + + @Transactional(readOnly = true) + public List findByCategory_IdAndRemoved(Long categoryId, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradebookAssignment.class); + Root ga = query.from(GradebookAssignment.class); + Join cat = ga.join("category"); + query.where(cb.and(cb.equal(cat.get("id"), categoryId), + cb.equal(ga.get("removed"), removed))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public Optional findByIdAndGradebook_UidAndRemoved(Long id, String gradebookUid, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradebookAssignment.class); + Root ga = query.from(GradebookAssignment.class); + Join gb = ga.join("gradebook"); + query.where(cb.and(cb.equal(ga.get("id"), id), + cb.equal(gb.get("uid"), gradebookUid), + cb.equal(ga.get("removed"), removed))); + return session.createQuery(query).uniqueResultOptional(); + } + + @Transactional(readOnly = true) + public List findByGradebook_IdAndRemoved(Long gradebookId, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradebookAssignment.class); + Root ga = query.from(GradebookAssignment.class); + Join gb = ga.join("gradebook"); + query.where(cb.and(cb.equal(gb.get("id"), gradebookId), + cb.equal(ga.get("removed"), removed))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradebook_IdAndRemovedAndNotCounted(Long gradebookId, Boolean removed, Boolean notCounted) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradebookAssignment.class); + Root ga = query.from(GradebookAssignment.class); + Join gb = ga.join("gradebook"); + query.where(cb.and(cb.equal(gb.get("id"), gradebookId), + cb.equal(ga.get("removed"), removed), + cb.equal(ga.get("notCounted"), notCounted))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradebook_IdAndRemovedAndNotCountedAndUngraded(Long gradebookId, Boolean removed, Boolean notCounted, Boolean ungraded) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradebookAssignment.class); + Root ga = query.from(GradebookAssignment.class); + Join gb = ga.join("gradebook"); + query.where(cb.and(cb.equal(gb.get("id"), gradebookId), + cb.equal(ga.get("removed"), removed), + cb.equal(ga.get("notCounted"), notCounted), + cb.equal(ga.get("ungraded"), ungraded))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public Optional findByGradebook_UidAndExternalId(String gradebookUid, String externalId) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradebookAssignment.class); + Root ga = query.from(GradebookAssignment.class); + Join gb = ga.join("gradebook"); + query.where(cb.and(cb.equal(gb.get("uid"), gradebookUid), + cb.equal(ga.get("externalId"), externalId))); + return session.createQuery(query).uniqueResultOptional(); + } + + @Transactional(readOnly = true) + public Long countByGradebook_UidAndExternalId(String gradebookUid, String externalId) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Long.class); + Root ga = query.from(GradebookAssignment.class); + Join gb = ga.join("gradebook"); + query.select(cb.count(ga)) + .where(cb.and(cb.equal(gb.get("uid"), gradebookUid), + cb.equal(ga.get("externalId"), externalId))); + return session.createQuery(query).uniqueResult(); + } + + @Transactional(readOnly = true) + public Long countByNameAndGradebook_UidAndRemoved(String name, String gradebookUid, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Long.class); + Root ga = query.from(GradebookAssignment.class); + Join gb = ga.join("gradebook"); + query.select(cb.count(ga)) + .where(cb.and(cb.equal(gb.get("uid"), gradebookUid), + cb.equal(ga.get("name"), name), + cb.equal(ga.get("removed"), removed))); + return session.createQuery(query).uniqueResult(); + } + + + @Transactional(readOnly = true) + public Long countByNameAndGradebookAndNotIdAndRemoved(String name, Gradebook gradebook, Long id, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Long.class); + Root ga = query.from(GradebookAssignment.class); + query.select(cb.count(ga)) + .where(cb.and(cb.equal(ga.get("name"), name), + cb.equal(ga.get("gradebook"), gradebook), + cb.notEqual(ga.get("id"), id), + cb.equal(ga.get("removed"), removed))); + return session.createQuery(query).uniqueResult(); + } + + @Transactional(readOnly = true) + public boolean existsByIdAndRemoved(Long id, Boolean removed) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Long.class); + Root ga = query.from(GradebookAssignment.class); + query.select(cb.count(ga)) + .where(cb.and(cb.equal(ga.get("id"), id), cb.equal(ga.get("removed"), removed))); + return session.createQuery(query).uniqueResult() == 1L; + } + + @Transactional(readOnly = true) + public List findByGradebook_Uid(String gradebookUid) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradebookAssignment.class); + Join gb = query.from(GradebookAssignment.class).join("gradebook"); + query.where(cb.equal(gb.get("uid"), gradebookUid)); + return session.createQuery(query).list(); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradebookPropertyRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradebookPropertyRepositoryImpl.java new file mode 100644 index 000000000000..207207721a20 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradebookPropertyRepositoryImpl.java @@ -0,0 +1,26 @@ +package org.sakaiproject.grading.impl.repository; + +import org.hibernate.Session; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import java.util.Optional; + +import org.sakaiproject.grading.api.model.GradebookProperty; +import org.sakaiproject.grading.api.repository.GradebookPropertyRepository; +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +public class GradebookPropertyRepositoryImpl extends SpringCrudRepositoryImpl implements GradebookPropertyRepository { + + public Optional findByName(String name) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradebookProperty.class); + Root prop = query.from(GradebookProperty.class); + query.where(cb.equal(prop.get("name"), name)); + return session.createQuery(query).uniqueResultOptional(); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradebookRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradebookRepositoryImpl.java new file mode 100644 index 000000000000..3e645ea8fbb6 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradebookRepositoryImpl.java @@ -0,0 +1,41 @@ +package org.sakaiproject.grading.impl.repository; + +import org.hibernate.Session; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaDelete; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import java.util.Optional; + +import org.springframework.transaction.annotation.Transactional; + +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.repository.GradebookRepository; +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +public class GradebookRepositoryImpl extends SpringCrudRepositoryImpl implements GradebookRepository { + + @Transactional(readOnly = true) + public Optional findByUid(String uid) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Gradebook.class); + Root gradebook = query.from(Gradebook.class); + query.where(cb.equal(gradebook.get("uid"), uid)); + return session.createQuery(query).uniqueResultOptional(); + } + + @Transactional + public int deleteByUid(String uid) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaDelete delete = cb.createCriteriaDelete(Gradebook.class); + Root gradebook = delete.from(Gradebook.class); + delete.where(cb.equal(gradebook.get("uid"), uid)); + return session.createQuery(delete).executeUpdate(); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradingEventRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradingEventRepositoryImpl.java new file mode 100644 index 000000000000..82781f683ff0 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradingEventRepositoryImpl.java @@ -0,0 +1,84 @@ +package org.sakaiproject.grading.impl.repository; + +import java.util.Date; +import java.util.List; + +import org.hibernate.Session; +import org.hibernate.criterion.Restrictions; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaDelete; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Root; + +import org.springframework.transaction.annotation.Transactional; + +import org.sakaiproject.grading.api.model.GradableObject; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradebookAssignment; +import org.sakaiproject.grading.api.model.GradingEvent; +import org.sakaiproject.grading.api.repository.GradingEventRepository; +import org.sakaiproject.hibernate.HibernateCriterionUtils; +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +public class GradingEventRepositoryImpl extends SpringCrudRepositoryImpl implements GradingEventRepository { + + @Transactional(readOnly = true) + public List findByGradableObject_Gradebook_Uid(String gradebookUid) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradingEvent.class); + Root ge = query.from(GradingEvent.class); + Join gb = ge.join("gradableObject").join("gradebook"); + query.where(cb.equal(gb.get("uid"), gradebookUid)); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradableObject_IdAndStudentIdOrderByDateGraded(Long assignmentId, String studentId) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradingEvent.class); + Root ge = query.from(GradingEvent.class); + Join go = ge.join("gradableObject"); + query.where(cb.and(cb.equal(go.get("id"), assignmentId), cb.equal(ge.get("studentId"), studentId))) + .orderBy(cb.desc(ge.get("dateGraded"))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByDateGreaterThanEqualAndGradableObject_IdIn(Date since, List assignmentIds) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradingEvent.class); + Root ge = query.from(GradingEvent.class); + Join go = ge.join("gradableObject"); + query.where(cb.and(cb.greaterThanOrEqualTo(ge.get("dateGraded"), since), go.get("id").in(assignmentIds))); + return session.createQuery(query).list(); + + /* + return (List) sessionFactory.getCurrentSession() + .createCriteria(GradingEvent.class) + .createAlias("gradableObject", "go") + .add(Restrictions.and( + Restrictions.ge("dateGraded", since), + HibernateCriterionUtils.CriterionInRestrictionSplitter("go.id", assignmentIds))) + .list(); + */ + } + + @Transactional + public int deleteByGradableObject(GradebookAssignment assignment) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaDelete delete = cb.createCriteriaDelete(GradingEvent.class); + Root ge = delete.from(GradingEvent.class); + delete.where(cb.equal(ge.get("gradableObject"), assignment)); + return session.createQuery(delete).executeUpdate(); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradingScaleRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradingScaleRepositoryImpl.java new file mode 100644 index 000000000000..af65ea1037c4 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/GradingScaleRepositoryImpl.java @@ -0,0 +1,53 @@ +package org.sakaiproject.grading.impl.repository; + +import org.hibernate.Session; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import java.util.List; +import java.util.Set; + +import org.springframework.transaction.annotation.Transactional; + +import org.sakaiproject.grading.api.model.GradingScale; +import org.sakaiproject.grading.api.repository.GradingScaleRepository; +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +public class GradingScaleRepositoryImpl extends SpringCrudRepositoryImpl implements GradingScaleRepository { + + @Transactional(readOnly = true) + public List findByUnavailable(Boolean unavailable) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradingScale.class); + Root gs = query.from(GradingScale.class); + query.where(cb.equal(gs.get("unavailable"), unavailable)); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByUnavailableAndUidNotIn(Boolean unavailable, Set notTheseUids) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradingScale.class); + Root gs = query.from(GradingScale.class); + query.where(cb.and(cb.equal(gs.get("unavailable"), unavailable), + cb.not(gs.get("uid").in(notTheseUids)))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByUidIn(Set theseUids) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(GradingScale.class); + Root gs = query.from(GradingScale.class); + query.where(gs.get("uid").in(theseUids)); + return session.createQuery(query).list(); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/LetterGradePercentMappingRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/LetterGradePercentMappingRepositoryImpl.java new file mode 100644 index 000000000000..47e542363971 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/LetterGradePercentMappingRepositoryImpl.java @@ -0,0 +1,41 @@ +package org.sakaiproject.grading.impl.repository; + +import org.hibernate.Session; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import java.util.List; +import java.util.Optional; + +import org.springframework.transaction.annotation.Transactional; + +import org.sakaiproject.grading.api.model.LetterGradePercentMapping; +import org.sakaiproject.grading.api.repository.LetterGradePercentMappingRepository; +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +public class LetterGradePercentMappingRepositoryImpl extends SpringCrudRepositoryImpl implements LetterGradePercentMappingRepository { + + @Transactional(readOnly = true) + public List findByMappingType(Integer mappingType) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(LetterGradePercentMapping.class); + Root pm = query.from(LetterGradePercentMapping.class); + query.where(cb.equal(pm.get("mappingType"), mappingType)); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public Optional findByGradebookIdAndMappingType(Long gradebookId, Integer mappingType) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(LetterGradePercentMapping.class); + Root pm = query.from(LetterGradePercentMapping.class); + query.where(cb.and(cb.equal(pm.get("gradebookId"), gradebookId), cb.equal(pm.get("mappingType"), mappingType))); + return session.createQuery(query).uniqueResultOptional(); + } +} diff --git a/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/PermissionRepositoryImpl.java b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/PermissionRepositoryImpl.java new file mode 100644 index 000000000000..e8134aa742e2 --- /dev/null +++ b/gradebookng/impl/src/main/java/org/sakaiproject/grading/impl/repository/PermissionRepositoryImpl.java @@ -0,0 +1,148 @@ +package org.sakaiproject.grading.impl.repository; + +import java.util.List; + +import org.hibernate.Session; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import org.springframework.transaction.annotation.Transactional; + +import org.sakaiproject.grading.api.model.Permission; +import org.sakaiproject.grading.api.repository.PermissionRepository; +import org.sakaiproject.springframework.data.SpringCrudRepositoryImpl; + +public class PermissionRepositoryImpl extends SpringCrudRepositoryImpl implements PermissionRepository { + + @Transactional(readOnly = true) + public List findByGradebookId(Long gradebookId) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Permission.class); + Root p = query.from(Permission.class); + query.where(cb.equal(p.get("gradebookId"), gradebookId)); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradebookIdAndUserId(Long gradebookId, String userId) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Permission.class); + Root p = query.from(Permission.class); + query.where(cb.and(cb.equal(p.get("gradebookId"), gradebookId), + cb.equal(p.get("userId"), userId))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradebookIdAndUserIdAndCategoryIdIn(Long gradebookId, String userId, List categoryIds) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Permission.class); + Root p = query.from(Permission.class); + query.where(cb.and(cb.equal(p.get("gradebookId"), gradebookId), + cb.equal(p.get("userId"), userId), + p.get("categoryId").in(categoryIds))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradebookIdAndUserIdAndCategoryIdIsNullAndFunctionNameIn(Long gradebookId, String userId, List functionNames) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Permission.class); + Root p = query.from(Permission.class); + query.where(cb.and(cb.equal(p.get("gradebookId"), gradebookId), + cb.equal(p.get("userId"), userId), + cb.isNull(p.get("categoryId")), + p.get("functionName").in(functionNames))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradebookIdAndUserIdAndGroupIdIsNullAndFunctionNameIn(Long gradebookId, String userId, List functionNames) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Permission.class); + Root p = query.from(Permission.class); + query.where(cb.and(cb.equal(p.get("gradebookId"), gradebookId), + cb.equal(p.get("userId"), userId), + cb.isNull(p.get("groupId")), + p.get("functionName").in(functionNames))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradebookIdAndUserIdAndGroupIdIsNullAndCategoryIdIn(Long gradebookId, String userId, List categoryIds) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Permission.class); + Root p = query.from(Permission.class); + query.where(cb.and(cb.equal(p.get("gradebookId"), gradebookId), + cb.equal(p.get("userId"), userId), + cb.isNull(p.get("groupId")), + p.get("categoryId").in(categoryIds))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradebookIdAndCategoryIdIn(Long gradebookId, List categoryIds) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Permission.class); + Root p = query.from(Permission.class); + query.where(cb.and(cb.equal(p.get("gradebookId"), gradebookId), p.get("categoryId").in(categoryIds))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradebookIdAndUserIdAndCategoryIdIsNullAndGroupIdIsNull(Long gradebookId, String userId) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Permission.class); + Root p = query.from(Permission.class); + query.where(cb.and(cb.equal(p.get("gradebookId"), gradebookId), + cb.equal(p.get("userId"), userId), + cb.isNull(p.get("categoryId")), + cb.isNull(p.get("groupId")))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradebookIdAndUserIdAndCategoryIdIsNullAndGroupIdIn(Long gradebookId, String userId, List groupIds) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Permission.class); + Root p = query.from(Permission.class); + query.where(cb.and(cb.equal(p.get("gradebookId"), gradebookId), + cb.equal(p.get("userId"), userId), + cb.isNull(p.get("categoryId")), + p.get("groupId").in(groupIds))); + return session.createQuery(query).list(); + } + + @Transactional(readOnly = true) + public List findByGradebookIdAndUserIdAndGroupIdIn(Long gradebookId, String userId, List groupIds) { + + Session session = sessionFactory.getCurrentSession(); + CriteriaBuilder cb = session.getCriteriaBuilder(); + CriteriaQuery query = cb.createQuery(Permission.class); + Root p = query.from(Permission.class); + query.where(cb.and(cb.equal(p.get("gradebookId"), gradebookId), + cb.equal(p.get("userId"), userId), + p.get("groupId").in(groupIds))); + return session.createQuery(query).list(); + } +} diff --git a/gradebookng/impl/src/main/webapp/WEB-INF/components.xml b/gradebookng/impl/src/main/webapp/WEB-INF/components.xml new file mode 100644 index 000000000000..f76794fd1ab1 --- /dev/null +++ b/gradebookng/impl/src/main/webapp/WEB-INF/components.xml @@ -0,0 +1,115 @@ + + + + + + + + + + org.sakaiproject.grading.api.model.Gradebook + org.sakaiproject.grading.api.model.GradableObject + org.sakaiproject.grading.api.model.GradebookAssignment + org.sakaiproject.grading.api.model.AssignmentGradeRecord + org.sakaiproject.grading.api.model.CourseGradeRecord + org.sakaiproject.grading.api.model.GradingEvent + org.sakaiproject.grading.api.model.GradingScale + org.sakaiproject.grading.api.model.GradebookProperty + org.sakaiproject.grading.api.model.GradeMapping + org.sakaiproject.grading.api.model.CourseGrade + org.sakaiproject.grading.api.model.Spreadsheet + org.sakaiproject.grading.api.model.Comment + org.sakaiproject.grading.api.model.Category + org.sakaiproject.grading.api.model.LetterGradePercentMapping + org.sakaiproject.grading.api.model.Permission + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java b/gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java new file mode 100644 index 000000000000..9c5e4d862a6b --- /dev/null +++ b/gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingServiceTests.java @@ -0,0 +1,693 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.impl.test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import org.sakaiproject.authz.api.SecurityService; +import org.sakaiproject.grading.api.Assignment; +import org.sakaiproject.grading.api.AssessmentNotFoundException; +import org.sakaiproject.grading.api.CategoryDefinition; +import org.sakaiproject.grading.api.CommentDefinition; +import org.sakaiproject.grading.api.CourseGradeTransferBean; +import org.sakaiproject.grading.api.GradebookInformation; +import org.sakaiproject.grading.api.GradeDefinition; +import org.sakaiproject.grading.api.GradeType; +import org.sakaiproject.grading.api.GradingAuthz; +import org.sakaiproject.grading.api.GradingSecurityException; +import org.sakaiproject.grading.api.GradingService; +import org.sakaiproject.grading.api.model.CourseGrade; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradingEvent; +import org.sakaiproject.grading.api.model.LetterGradePercentMapping; +import org.sakaiproject.grading.api.repository.CourseGradeRepository; +import org.sakaiproject.grading.api.repository.LetterGradePercentMappingRepository; +import org.sakaiproject.grading.impl.GradingServiceImpl; +import org.sakaiproject.site.api.Site; +import org.sakaiproject.site.api.SiteService; +import org.sakaiproject.tool.api.SessionManager; +import org.sakaiproject.user.api.User; +import org.sakaiproject.user.api.UserDirectoryService; +import org.sakaiproject.user.api.UserNotDefinedException; +import org.sakaiproject.util.ResourceLoader; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.util.AopTestUtils; + +import static org.mockito.Mockito.*; + +import lombok.extern.slf4j.Slf4j; + +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@Slf4j +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = {GradingTestConfiguration.class}) +public class GradingServiceTests extends AbstractTransactionalJUnit4SpringContextTests { + + @Autowired private CourseGradeRepository courseGradeRepository; + @Autowired private GradingService gradingService; + @Autowired private LetterGradePercentMappingRepository letterGradePercentMappingRepository; + @Autowired private SecurityService securityService; + @Autowired private SessionManager sessionManager; + @Autowired private SiteService siteService; + @Autowired private UserDirectoryService userDirectoryService; + + private ResourceLoader resourceLoader; + + String instructor = "instructor"; + User instructorUser = null; + String user1 = "user1"; + User user1User = null; + String user2 = "user2"; + User user2User = null; + + String siteId = "xyz"; + + String cat1Name = "Category One"; + String cat2Name = "Category Two"; + + Double ass1Points = 15D; + String ass1Name = "Assignment One"; + + Double ass2Points = 7D; + String ass2Name = "Assignment Two"; + + Assignment ass1 = null; + Assignment ass2 = null; + + @Before + public void setup() { + + reset(sessionManager); + reset(securityService); + reset(userDirectoryService); + + ass1 = new Assignment(); + ass1.setPoints(ass1Points); + ass1.setName(ass1Name); + ass1.setUngraded(false); + ass1.setDueDate(new Date()); + ass1.setCounted(true); + + ass2 = new Assignment(); + ass2.setPoints(ass2Points); + ass2.setName(ass2Name); + ass2.setUngraded(false); + ass2.setDueDate(new Date()); + + instructorUser = mock(User.class); + when(instructorUser.getDisplayName()).thenReturn(instructor); + + user1User = mock(User.class); + when(user1User.getDisplayName()).thenReturn(user1); + + user2User = mock(User.class); + when(user2User.getDisplayName()).thenReturn(user2); + + when(siteService.siteReference(siteId)).thenReturn("/site/" + siteId); + + resourceLoader = mock(ResourceLoader.class); + when(resourceLoader.getLocale()).thenReturn(Locale.ENGLISH); + ((GradingServiceImpl) AopTestUtils.getTargetObject(gradingService)).setResourceLoader(resourceLoader); + } + + @Test + public void addGradebook() { + + Gradebook gradebook = createGradebook(); + assertEquals(siteId, gradebook.getUid()); + assertEquals(siteId, gradebook.getName()); + + List courseGrades = courseGradeRepository.findByGradebook_Id(gradebook.getId()); + assertEquals(1, courseGrades.size()); + + List mappings = letterGradePercentMappingRepository.findByMappingType(1); + assertEquals(1, mappings.size()); + } + + @Test + public void deleteGradebook() { + + Gradebook gradebook = createGradebook(); + //try { + gradingService.deleteGradebook(gradebook.getUid()); + //} catch (Exception e) { + // e.printStackTrace(); + //} + } + + @Test + public void addAssignment() { + + Gradebook gradebook = createGradebook(); + assertEquals(siteId, gradebook.getUid()); + + switchToInstructor(); + + when(securityService.unlock(GradingAuthz.PERMISSION_EDIT_ASSIGNMENTS, "/site/" + siteId)).thenReturn(false); + + assertThrows(GradingSecurityException.class, () -> gradingService.addAssignment(gradebook.getUid(), ass1)); + + when(securityService.unlock(GradingAuthz.PERMISSION_EDIT_ASSIGNMENTS, "/site/" + siteId)).thenReturn(true); + //when(siteService.siteReference(gradebook.getUid())).thenReturn("/site/" + gradebook.getUid()); + + gradingService.addAssignment(gradebook.getUid(), ass1); + + List assignments = gradingService.getAssignments(gradebook.getUid()); + assertEquals(1, assignments.size()); + assertEquals(ass1Name, assignments.get(0).getName()); + assertEquals(ass1Points, assignments.get(0).getPoints()); + assertFalse(assignments.get(0).getUngraded()); + } + + @Test + public void getAssignments() { + + Gradebook gradebook = createGradebook(); + Long id = createAssignment1(gradebook); + List assignments = gradingService.getAssignments(gradebook.getUid()); + assertEquals(1, assignments.size()); + Long id2 = createAssignment2(gradebook); + assignments = gradingService.getAssignments(gradebook.getUid()); + assertEquals(2, assignments.size()); + } + + @Test + public void addAndUpdateExternalAssessment() { + + Gradebook gradebook = createGradebook(); + switchToUser1(); + + String externalId = "bf3eeca2-1b97-4ead-b605-a8b50a0c6950"; + String title = "External One"; + Double points = 55.3D; + Date dueDate = new Date(); + String description = "The Sakai assignments tool"; + + assertThrows(GradingSecurityException.class, + () -> gradingService.addExternalAssessment("none", externalId, "http://eggs.com", + title, points, dueDate, description, "data", false)); + + switchToInstructor(); + + gradingService.addExternalAssessment(gradebook.getUid(), externalId, "http://eggs.com", title, points, dueDate, description, "data", false); + Assignment assignment = gradingService.getExternalAssignment(gradebook.getUid(), externalId); + + assertEquals(title, assignment.getName()); + assertEquals(points, assignment.getPoints()); + assertEquals(dueDate, assignment.getDueDate()); + assertEquals(description, assignment.getExternalAppName()); + assertTrue(assignment.getExternallyMaintained()); + + String newTitle = "New Title"; + Double newPoints = 23.2D; + + gradingService.updateExternalAssessment(gradebook.getUid(), externalId, "http://eggs.com", "data", newTitle, newPoints, assignment.getDueDate()); + + assignment = gradingService.getExternalAssignment(gradebook.getUid(), externalId); + assertEquals(newTitle, assignment.getName()); + assertEquals(newPoints, assignment.getPoints()); + assertEquals(dueDate, assignment.getDueDate()); + assertEquals(description, assignment.getExternalAppName()); + assertTrue(assignment.getExternallyMaintained()); + } + + @Test + public void currentUserHasGradingPerm() { + + Gradebook gradebook = createGradebook(); + switchToUser1(); + assertFalse(gradingService.currentUserHasGradingPerm(gradebook.getUid())); + switchToInstructor(); + when(securityService.unlock(GradingAuthz.PERMISSION_GRADE_ALL, "/site/" + siteId)).thenReturn(true); + assertTrue(gradingService.currentUserHasGradingPerm(gradebook.getUid())); + } + + @Test + public void removeAssignment() { + + Gradebook gradebook = createGradebook(); + + Long id = createAssignment1(gradebook); + + gradingService.removeAssignment(id); + assertEquals(0, gradingService.getAssignments(gradebook.getUid()).size()); + } + + @Test + public void removeExternalAssignment() { + + Gradebook gradebook = createGradebook(); + String externalId = "xyz"; + String externalUrl = "http://xyz.com"; + String title = "External Assignment"; + Double points = 32.0D; + Date dueDate = Date.from(Instant.now().plus(24, ChronoUnit.HOURS)); + String externalServiceDescription = "test"; + + switchToInstructor(); + + gradingService.addExternalAssessment(gradebook.getUid(), externalId, externalUrl, + title, points, dueDate, externalServiceDescription, null, false); + + Assignment assignment = gradingService.getExternalAssignment(gradebook.getUid(), externalId); + + assertNotNull(assignment); + + gradingService.removeExternalAssignment(gradebook.getUid(), externalId); + + assertThrows(IllegalArgumentException.class, () -> gradingService.getExternalAssignment(gradebook.getUid(), externalId)); + } + + @Test + public void updateAssignment() { + + Gradebook gradebook = createGradebook(); + + Long id = createAssignment1(gradebook); + Assignment updated = gradingService.getAssignment(gradebook.getUid(), id); + updated.setExternallyMaintained(true); + updated.setName("Changed Name"); + updated.setPoints(80D); + updated.setDueDate(new Date()); + gradingService.updateAssignment(gradebook.getUid(), id, updated); + updated = gradingService.getAssignment(gradebook.getUid(), id); + // You can't change the name, points or due date of externally maintained assignments + assertEquals(ass1Name, updated.getName()); + // You can't change the points of externally maintained assignments + assertEquals(ass1.getPoints(), updated.getPoints()); + assertEquals(ass1.getDueDate(), updated.getDueDate()); + } + + @Test + public void isAssignmentDefined() { + + Gradebook gradebook = createGradebook(); + when(securityService.unlock(GradingAuthz.PERMISSION_EDIT_ASSIGNMENTS, "/site/" + gradebook.getUid())).thenReturn(false); + when(securityService.unlock(GradingAuthz.PERMISSION_GRADE_ALL, "/site/" + gradebook.getUid())).thenReturn(false); + assertThrows(GradingSecurityException.class, () -> gradingService.isAssignmentDefined(gradebook.getUid(), ass1Name)); + when(securityService.unlock(GradingAuthz.PERMISSION_EDIT_ASSIGNMENTS, "/site/" + gradebook.getUid())).thenReturn(true); + assertFalse(gradingService.isAssignmentDefined(gradebook.getUid(), ass1Name)); + Long id = createAssignment1(gradebook); + assertTrue(gradingService.isAssignmentDefined(gradebook.getUid(), ass1Name)); + } + + @Test + public void setAssignmentScoreString() { + + Gradebook gradebook = createGradebook(); + assertThrows(AssessmentNotFoundException.class, () -> gradingService.setAssignmentScoreString(gradebook.getUid(), ass1Name, user1, "43.0", "")); + Long id = createAssignment1(gradebook); + + gradingService.setAssignmentScoreString(gradebook.getUid(), ass1Name, user1, "43.0", ""); + assertEquals("43", gradingService.getAssignmentScoreString(gradebook.getUid(), ass1Name, user1)); + + gradingService.setAssignmentScoreString(gradebook.getUid(), ass1Name, user1, "27.5", ""); + assertEquals("27.5", gradingService.getAssignmentScoreString(gradebook.getUid(), ass1Name, user1)); + } + + @Test + public void saveGradesAndComments() { + + Gradebook gradebook = createGradebook(); + Long id = createAssignment1(gradebook); + + GradeDefinition def1 = new GradeDefinition(); + def1.setStudentUid(user1); + def1.setGraderUid(instructor); + def1.setDateRecorded(new Date()); + def1.setGrade("16.4"); + def1.setGradeComment("Great"); + def1.setGradeEntryType(GradeType.POINTS); + + GradeDefinition def2 = new GradeDefinition(); + def2.setStudentUid(user2); + def2.setGraderUid(instructor); + def2.setDateRecorded(new Date()); + def2.setGrade("12.5"); + def2.setGradeComment("Good"); + def2.setGradeEntryType(GradeType.POINTS); + + gradingService.saveGradesAndComments(gradebook.getUid(), id, List.of(def1, def2)); + + List gradingEvents = gradingService.getGradingEvents(user1, id); + assertEquals(1, gradingEvents.size()); + + gradingEvents = gradingService.getGradingEvents(user2, id); + assertEquals(1, gradingEvents.size()); + } + + @Test + public void getAverageCourseGrade() { + + Gradebook gradebook = createGradebook(); + String average = gradingService.getAverageCourseGrade(gradebook.getUid()); + assertNull(average); + + Long assId = createAssignment1(gradebook); + + String grade = "3.7"; + String comment = "Rather shoddy"; + + gradingService.saveGradeAndCommentForStudent(gradebook.getUid(), assId, user1, grade, comment); + + average = gradingService.getAverageCourseGrade(gradebook.getUid()); + //assertEquals("3.7", average); + } + + @Test + public void createGradebookWithCategories() { + + switchToInstructor(); + + gradingService.addGradebook(siteId); + Gradebook gradebook = gradingService.getGradebook(siteId); + + addCategories(gradebook); + + GradebookInformation gradebookInformation = gradingService.getGradebookInformation(siteId); + + List categories = gradebookInformation.getCategories(); + assertEquals(2, categories.size()); + } + + @Test + public void setAssignmentScoreComment() { + + assertThrows(IllegalArgumentException.class, () -> gradingService.setAssignmentScoreComment(null, null, user1, "Great!")); + + Gradebook gradebook = createGradebook(); + + Long ass1Id = createAssignment1(gradebook); + + gradingService.setAssignmentScoreComment(gradebook.getUid(), ass1Id, user1, "Great!"); + + CommentDefinition commentDefinition = gradingService.getAssignmentScoreComment(gradebook.getUid(), ass1Id, user1); + assertEquals("Great!", commentDefinition.getCommentText()); + } + + @Test + public void getAssignmentByNameOrId() { + + Gradebook gradebook = createGradebook(); + + Assignment ass = gradingService.getAssignmentByNameOrId(gradebook.getUid(), "none"); + assertNull(ass); + + Long ass1Id = createAssignment1(gradebook); + + ass = gradingService.getAssignmentByNameOrId(gradebook.getUid(), ass1Id.toString()); + assertNotNull(ass); + } + + @Test + public void getCategoryDefinitions() { + + switchToInstructor(); + + gradingService.addGradebook(siteId); + Gradebook gradebook = gradingService.getGradebook(siteId); + List cats = gradingService.getCategoryDefinitions(gradebook.getUid()); + assertEquals(0, cats.size()); + + addCategories(gradebook); + + cats = gradingService.getCategoryDefinitions(gradebook.getUid()); + assertEquals(2, cats.size()); + } + + @Test + public void getViewableAssignmentsForCurrentUser() { + + Gradebook gradebook = createGradebook(); + Long id = createAssignment1(gradebook); + List assignments = gradingService.getViewableAssignmentsForCurrentUser(gradebook.getUid()); + assertEquals(1, assignments.size()); + } + + @Test + public void isPointsPossibleValid() { + + assertThrows(IllegalArgumentException.class, () -> gradingService.isPointsPossibleValid(null, null, null)); + + Gradebook gradebook = createGradebook(); + Long id = createAssignment1(gradebook); + Assignment updated = gradingService.getAssignment(gradebook.getUid(), id); + assertEquals(GradingService.PointsPossibleValidation.INVALID_NUMERIC_VALUE, gradingService.isPointsPossibleValid(gradebook.getUid(), updated, 0D)); + assertEquals(GradingService.PointsPossibleValidation.INVALID_NULL_VALUE, gradingService.isPointsPossibleValid(gradebook.getUid(), updated, null)); + assertEquals(GradingService.PointsPossibleValidation.INVALID_DECIMAL, gradingService.isPointsPossibleValid(gradebook.getUid(), updated, 2.344D)); + assertEquals(GradingService.PointsPossibleValidation.VALID, gradingService.isPointsPossibleValid(gradebook.getUid(), updated, 2.34D)); + } + + @Test + public void isUserAbleToGradeItemForStudent() { + + Gradebook gradebook = createGradebook(); + Long id = createAssignment1(gradebook); + switchToUser1(); + assertFalse(gradingService.isUserAbleToGradeItemForStudent(gradebook.getUid(), id, user2)); + when(securityService.unlock(GradingAuthz.PERMISSION_GRADE_ALL, "/site/" + gradebook.getUid())).thenReturn(true); + assertTrue(gradingService.isUserAbleToGradeItemForStudent(gradebook.getUid(), id, user2)); + } + + @Test + public void isUserAbleToViewItemForStudent() { + + Gradebook gradebook = createGradebook(); + Long id = createAssignment1(gradebook); + when(securityService.unlock(GradingAuthz.PERMISSION_GRADE_ALL, "/site/" + gradebook.getUid())).thenReturn(false); + assertFalse(gradingService.isUserAbleToViewItemForStudent(gradebook.getUid(), id, user2)); + when(securityService.unlock(GradingAuthz.PERMISSION_GRADE_ALL, "/site/" + gradebook.getUid())).thenReturn(true); + assertTrue(gradingService.isUserAbleToViewItemForStudent(gradebook.getUid(), id, user2)); + + switchToUser1(); + when(securityService.unlock(GradingAuthz.PERMISSION_GRADE_ALL, "/site/" + gradebook.getUid())).thenReturn(false); + assertFalse(gradingService.isUserAbleToViewItemForStudent(gradebook.getUid(), id, user2)); + when(securityService.unlock(GradingAuthz.PERMISSION_GRADE_ALL, "/site/" + gradebook.getUid())).thenReturn(true); + assertTrue(gradingService.isUserAbleToViewItemForStudent(gradebook.getUid(), id, user2)); + } + + @Test + public void getGradeViewFunctionForUserForStudentForItem() { + + assertThrows(IllegalArgumentException.class, () -> gradingService.getGradeViewFunctionForUserForStudentForItem(null, null, null)); + + Gradebook gradebook = createGradebook(); + Long id = createAssignment1(gradebook); + + String perm = gradingService.getGradeViewFunctionForUserForStudentForItem(gradebook.getUid(), id, user1); + + assertEquals(GradingService.gradePermission, perm); + + switchToUser1(); + + perm = gradingService.getGradeViewFunctionForUserForStudentForItem(gradebook.getUid(), id, user1); + + assertEquals(null, perm); + } + + @Test + public void getPointsEarnedCourseGradeRecords() { + } + + @Test + public void saveGradeAndCommentForStudent() { + + Gradebook gradebook = createGradebook(); + Long assId = createAssignment1(gradebook); + + String grade = "3.7"; + String comment = "Rather shoddy"; + + gradingService.saveGradeAndCommentForStudent(gradebook.getUid(), assId, user1, grade, comment); + + Site site = mock(Site.class); + when(site.getGroup(user1)).thenReturn(null); + try { + when(siteService.getSite(gradebook.getUid())).thenReturn(site); + } catch (Exception e) { + } + + GradeDefinition gradeDef = gradingService.getGradeDefinitionForStudentForItem(gradebook.getUid(), assId, user1); + + assertEquals(grade, gradeDef.getGrade()); + assertEquals(comment, gradeDef.getGradeComment()); + } + + @Test + public void getCourseGradeForStudents() { + + Gradebook gradebook = createGradebook(); + Long assId = createAssignment1(gradebook); + + Map gradeMapping = new HashMap<>(); + gradeMapping.put(user1, 3.0D); + + Map grades = gradingService.getCourseGradeForStudents(gradebook.getUid(), Arrays.asList(user1), gradeMapping); + assertEquals(1, grades.size()); + + String grade = "3.0"; + String comment = "Rather shoddy"; + + gradingService.saveGradeAndCommentForStudent(gradebook.getUid(), assId, user1, grade, comment); + grades = gradingService.getCourseGradeForStudents(gradebook.getUid(), Arrays.asList(user1), gradeMapping); + assertEquals(1, grades.size()); + assertEquals("20.0", grades.get(user1).getCalculatedGrade()); + } + + @Test + public void getGradesWithoutCommentsForStudentsForItems() { + + Gradebook gradebook = createGradebook(); + Long assId = createAssignment1(gradebook); + + String grade = "3.7"; + String comment = "Rather shoddy"; + + Map gradeMapping = new HashMap<>(); + gradeMapping.put(user1, 3.7D); + + gradingService.saveGradeAndCommentForStudent(gradebook.getUid(), assId, user1, grade, comment); + + assertThrows(IllegalArgumentException.class, () -> gradingService.getGradesWithoutCommentsForStudentsForItems(gradebook.getUid(), null, null)); + + switchToUser2(); + + assertThrows(GradingSecurityException.class, () -> gradingService.getGradesWithoutCommentsForStudentsForItems(gradebook.getUid(), List.of(assId), List.of(user1))); + + switchToInstructor(); + + Map> gradeMap = gradingService.getGradesWithoutCommentsForStudentsForItems(gradebook.getUid(), List.of(assId), List.of(user1)); + + // The keys should be the assignment ids + assertTrue(gradeMap.keySet().contains(assId)); + + List defs = gradeMap.get(assId); + assertEquals(1, defs.size()); + assertEquals(grade, defs.get(0).getGrade()); + assertEquals(user1, defs.get(0).getStudentUid()); + assertEquals(instructor, defs.get(0).getGraderUid()); + assertEquals(GradeType.POINTS, defs.get(0).getGradeEntryType()); + assertNull(defs.get(0).getGradeComment()); + } + + private Long createAssignment1(Gradebook gradebook) { + + when(securityService.unlock(GradingAuthz.PERMISSION_EDIT_ASSIGNMENTS, "/site/" + gradebook.getUid())).thenReturn(true); + //when(siteService.siteReference(gradebook.getUid())).thenReturn("/site/" + gradebook.getUid()); + + return gradingService.addAssignment(gradebook.getUid(), ass1); + } + + private Long createAssignment2(Gradebook gradebook) { + + when(securityService.unlock(GradingAuthz.PERMISSION_EDIT_ASSIGNMENTS, "/site/" + gradebook.getUid())).thenReturn(true); + //when(siteService.siteReference(gradebook.getUid())).thenReturn("/site/" + gradebook.getUid()); + + return gradingService.addAssignment(gradebook.getUid(), ass2); + } + + + private void addCategories(Gradebook gradebook) { + + GradebookInformation gradebookInformation = gradingService.getGradebookInformation(gradebook.getUid()); + + var cd1 = new CategoryDefinition(); + cd1.setName(cat1Name); + cd1.setExtraCredit(false); + cd1.setEqualWeight(false); + cd1.setWeight(Double.valueOf(0)); + cd1.setAssignmentList(Collections.emptyList()); + cd1.setDropHighest(0); + cd1.setDropLowest(0); + cd1.setKeepHighest(0); + + var cd2 = new CategoryDefinition(); + cd2.setName(cat2Name); + cd2.setExtraCredit(false); + cd2.setEqualWeight(false); + cd2.setWeight(Double.valueOf(0)); + cd2.setAssignmentList(Collections.emptyList()); + cd2.setDropHighest(0); + cd2.setDropLowest(0); + cd2.setKeepHighest(0); + + var cats = new ArrayList(); + cats.add(cd1); + cats.add(cd2); + + gradebookInformation.setCategories(cats); + + gradingService.updateGradebookSettings(gradebook.getUid(), gradebookInformation); + + } + + private Gradebook createGradebook() { + + switchToInstructor(); + + return gradingService.addGradebook(siteId); + } + + private void switchToInstructor() { + + when(sessionManager.getCurrentSessionUserId()).thenReturn(instructor); + when(securityService.unlock(GradingAuthz.PERMISSION_EDIT_ASSIGNMENTS, "/site/" + siteId)).thenReturn(true); + when(securityService.unlock(GradingAuthz.PERMISSION_GRADE_ALL, "/site/" + siteId)).thenReturn(true); + try { + when(userDirectoryService.getUser(instructor)).thenReturn(instructorUser); + } catch (UserNotDefinedException unde) { + } + } + + private void switchToUser1() { + + when(sessionManager.getCurrentSessionUserId()).thenReturn(user1); + when(securityService.unlock(GradingAuthz.PERMISSION_EDIT_ASSIGNMENTS, "/site/" + siteId)).thenReturn(false); + when(securityService.unlock(GradingAuthz.PERMISSION_GRADE_ALL, "/site/" + siteId)).thenReturn(false); + try { + when(userDirectoryService.getUser(user1)).thenReturn(user1User); + } catch (UserNotDefinedException unde) { + } + } + + private void switchToUser2() { + + when(sessionManager.getCurrentSessionUserId()).thenReturn(user2); + when(securityService.unlock(GradingAuthz.PERMISSION_EDIT_ASSIGNMENTS, "/site/" + siteId)).thenReturn(false); + when(securityService.unlock(GradingAuthz.PERMISSION_GRADE_ALL, "/site/" + siteId)).thenReturn(false); + try { + when(userDirectoryService.getUser(user2)).thenReturn(user2User); + } catch (UserNotDefinedException unde) { + } + } +} diff --git a/gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingTestConfiguration.java b/gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingTestConfiguration.java new file mode 100644 index 000000000000..e8371cd4c2e4 --- /dev/null +++ b/gradebookng/impl/src/test/java/org/sakaiproject/grading/impl/test/GradingTestConfiguration.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2003-2017 The Apereo Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://opensource.org/licenses/ecl2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sakaiproject.grading.impl.test; + +import static org.mockito.Mockito.mock; + +import org.sakaiproject.authz.api.FunctionManager; +import org.sakaiproject.event.api.EventTrackingService; +import org.sakaiproject.grading.api.repository.GradebookRepository; +import org.sakaiproject.section.api.SectionAwareness; +import org.sakaiproject.springframework.orm.hibernate.AdditionalHibernateMappings; +import org.sakaiproject.test.SakaiTestConfiguration; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; +import org.springframework.context.annotation.PropertySource; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import lombok.Getter; + +@Configuration +@EnableTransactionManagement +@ImportResource("classpath:/WEB-INF/components.xml") +@PropertySource("classpath:/hibernate.properties") +public class GradingTestConfiguration extends SakaiTestConfiguration { + + @Autowired + @Qualifier("org.sakaiproject.springframework.orm.hibernate.impl.AdditionalHibernateMappings.grading") + @Getter + private AdditionalHibernateMappings additionalHibernateMappings; + + @Bean(name = "org.sakaiproject.section.api.SectionAwareness") + public SectionAwareness sectionAwareness() { + return mock(SectionAwareness.class); + } + + @Bean(name = "org.sakaiproject.event.api.EventTrackingService") + public EventTrackingService eventTrackingService() { + return mock(EventTrackingService.class); + } + + @Bean(name = "org.sakaiproject.authz.api.FunctionManager") + public FunctionManager functionManager() { + return mock(FunctionManager.class); + } + + /* + @Bean(name = "org.sakaiproject.grading.api.repository.GradebookRepository") + public GradebookRepository gradebookRepository() { + return mock(GradebookRepository.class); + } + */ +} diff --git a/gradebookng/impl/src/test/resources/hibernate.properties b/gradebookng/impl/src/test/resources/hibernate.properties new file mode 100644 index 000000000000..0eeb7092d362 --- /dev/null +++ b/gradebookng/impl/src/test/resources/hibernate.properties @@ -0,0 +1,20 @@ +# Base Hibernate settings +hibernate.show_sql=false +hibernate.hbm2ddl.auto=create-only + +# Connection definition to the HSQLDB database +hibernate.connection.driver_class=org.hsqldb.jdbcDriver +hibernate.connection.url=jdbc:hsqldb:mem:test +hibernate.dialect=org.hibernate.dialect.HSQLDialect +hibernate.cache.use_second_level_cache=false +hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext +# NOTE getConnection(username, password) will throw an SQLFeatureNotSupportedException on the HikariDataSource +#hibernate.connection.username=sa +#hibernate.connection.password= + +#hibernate.connection.driver_class=com.mysql.jdbc.Driver +#hibernate.connection.url=jdbc:mysql://localhost:3306/sakai?useUnicode=true&characterEncoding=UTF-8 +#hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect +#hibernate.connection.username=sakai +#hibernate.connection.password=sakai + diff --git a/gradebookng/pom.xml b/gradebookng/pom.xml index e58f453958e2..602397e741bd 100644 --- a/gradebookng/pom.xml +++ b/gradebookng/pom.xml @@ -37,20 +37,13 @@ + api bundle + impl tool - - - org.apache.maven.plugins - maven-eclipse-plugin - 2.9 - - true - true - - - + src/main/java + src/test/java diff --git a/gradebookng/tool/pom.xml b/gradebookng/tool/pom.xml index 64653ebf0e63..7d429366d379 100644 --- a/gradebookng/tool/pom.xml +++ b/gradebookng/tool/pom.xml @@ -55,12 +55,8 @@ sakai-kernel-util - org.sakaiproject.edu-services.gradebook - gradebook-service-api - - - org.sakaiproject.edu-services.gradebook - gradebook-service-hibernate + org.sakaiproject.grading + sakai-grading-api org.sakaiproject.edu-services.course-management @@ -162,6 +158,9 @@ + src/java + src/webapp + src/test diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/CourseGradeComparator.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/CourseGradeComparator.java index 0820d3b3dc88..d3edb95a7c0e 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/CourseGradeComparator.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/CourseGradeComparator.java @@ -22,8 +22,8 @@ import org.apache.commons.lang3.builder.CompareToBuilder; import org.sakaiproject.gradebookng.business.model.GbStudentGradeInfo; -import org.sakaiproject.service.gradebook.shared.CourseGrade; -import org.sakaiproject.service.gradebook.shared.GradebookInformation; +import org.sakaiproject.grading.api.CourseGradeTransferBean; +import org.sakaiproject.grading.api.GradebookInformation; /** * Comparator class for sorting by course grade, first by the letter grade's index in the gradebook's grading scale and then by the number @@ -48,8 +48,8 @@ public int compare(final String a, final String b) { @Override public int compare(final GbStudentGradeInfo g1, final GbStudentGradeInfo g2) { - final CourseGrade cg1 = g1.getCourseGrade().getCourseGrade(); - final CourseGrade cg2 = g2.getCourseGrade().getCourseGrade(); + final CourseGradeTransferBean cg1 = g1.getCourseGrade(); + final CourseGradeTransferBean cg2 = g2.getCourseGrade(); String letterGrade1 = cg1.getMappedGrade(); if (cg1.getEnteredGrade() != null) { diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GbCategoryType.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GbCategoryType.java deleted file mode 100644 index 34ea4d2ac7e5..000000000000 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GbCategoryType.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) 2003-2016 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.gradebookng.business; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents the category types allowed in the gradebook. Must be kept in sync with GradebookService if that ever changes. - * - * @author Steve Swinsburg (steve.swinsburg@gmail.com) - * - */ -public enum GbCategoryType { - - NO_CATEGORY(1), - ONLY_CATEGORY(2), - WEIGHTED_CATEGORY(3); - - private int value; - - GbCategoryType(final int value) { - this.value = value; - } - - /** - * Get the value for the type - * - * @return - */ - public int getValue() { - return this.value; - } - - // also need to maintain a map of the types so we can lookup the enum based on type - private static Map map = new HashMap(); - - static { - for (final GbCategoryType type : GbCategoryType.values()) { - map.put(type.value, type); - } - } - - public static GbCategoryType valueOf(final int value) { - return map.get(value); - } - -} diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GradebookNgBusinessService.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GradebookNgBusinessService.java index 508a9f4fe1b7..35a14cdcba17 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GradebookNgBusinessService.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/GradebookNgBusinessService.java @@ -79,27 +79,28 @@ import org.sakaiproject.gradebookng.business.util.FormatHelper; import org.sakaiproject.gradebookng.business.util.GbStopWatch; import org.sakaiproject.gradebookng.tool.model.GradebookUiSettings; +import org.sakaiproject.grading.api.GradingConstants; +import org.sakaiproject.grading.api.GradingService; import org.sakaiproject.rubrics.logic.RubricsConstants; import org.sakaiproject.rubrics.logic.RubricsService; import org.sakaiproject.section.api.SectionManager; -import org.sakaiproject.service.gradebook.shared.AssessmentNotFoundException; -import org.sakaiproject.service.gradebook.shared.Assignment; -import org.sakaiproject.service.gradebook.shared.CategoryDefinition; -import org.sakaiproject.service.gradebook.shared.CategoryScoreData; -import org.sakaiproject.service.gradebook.shared.CommentDefinition; -import org.sakaiproject.service.gradebook.shared.CourseGrade; -import org.sakaiproject.service.gradebook.shared.GradeDefinition; -import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService; -import org.sakaiproject.service.gradebook.shared.GradebookFrameworkService; -import org.sakaiproject.service.gradebook.shared.GradebookInformation; -import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException; -import org.sakaiproject.service.gradebook.shared.GradebookPermissionService; -import org.sakaiproject.service.gradebook.shared.GradebookService; -import org.sakaiproject.service.gradebook.shared.GraderPermission; -import org.sakaiproject.service.gradebook.shared.GradingType; -import org.sakaiproject.service.gradebook.shared.InvalidGradeException; -import org.sakaiproject.service.gradebook.shared.PermissionDefinition; -import org.sakaiproject.service.gradebook.shared.SortType; +import org.sakaiproject.grading.api.AssessmentNotFoundException; +import org.sakaiproject.grading.api.Assignment; +import org.sakaiproject.grading.api.CategoryDefinition; +import org.sakaiproject.grading.api.CategoryScoreData; +import org.sakaiproject.grading.api.CommentDefinition; +import org.sakaiproject.grading.api.CourseGradeTransferBean; +import org.sakaiproject.grading.api.GradeDefinition; +import org.sakaiproject.grading.api.GradebookInformation; +import org.sakaiproject.grading.api.GraderPermission; +import org.sakaiproject.grading.api.GradingCategoryType; +import org.sakaiproject.grading.api.GradingPermissionService; +import org.sakaiproject.grading.api.GradeType; +import org.sakaiproject.grading.api.InvalidGradeException; +import org.sakaiproject.grading.api.PermissionDefinition; +import org.sakaiproject.grading.api.SortType; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.GradingEvent; import org.sakaiproject.site.api.Group; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.SiteService; @@ -108,8 +109,6 @@ import org.sakaiproject.tasks.api.TaskService; import org.sakaiproject.time.api.UserTimeService; import org.sakaiproject.tool.api.ToolManager; -import org.sakaiproject.tool.gradebook.Gradebook; -import org.sakaiproject.tool.gradebook.GradingEvent; import org.sakaiproject.user.api.CandidateDetailProvider; import org.sakaiproject.user.api.Preferences; import org.sakaiproject.user.api.PreferencesEdit; @@ -151,16 +150,10 @@ public class GradebookNgBusinessService { private ToolManager toolManager; @Setter - private GradebookService gradebookService; + private GradingService gradingService; @Setter - private GradebookPermissionService gradebookPermissionService; - - @Setter - private GradebookFrameworkService gradebookFrameworkService; - - @Setter - private GradebookExternalAssessmentService gradebookExternalAssessmentService; + private GradingPermissionService gradingPermissionService; @Setter private PreferencesService preferencesService; @@ -245,7 +238,7 @@ public List getGradeableUsers(final String siteId, final GbGroup groupFi } // note that this list MUST exclude TAs as it is checked in the - // GradebookService and will throw a SecurityException if invalid + // GradingService and will throw a SecurityException if invalid // users are provided Site site = siteService.getSite(givenSiteId); final Set userUuids = site.getUsersIsAllowed(GbRole.STUDENT.getValue()); @@ -284,7 +277,7 @@ public List getGradeableUsers(final String siteId, final GbGroup groupFi final Gradebook gradebook = this.getGradebook(givenSiteId); // get list of sections and groups this TA has access to - final List courseSections = this.gradebookService.getViewableSections(gradebook.getUid()); + final List courseSections = this.gradingService.getViewableSections(gradebook.getUid()); //for each section TA has access to, grab student Id's List viewableStudents = new ArrayList(); @@ -405,20 +398,9 @@ public Gradebook getGradebook() { * @param siteId the siteId * @return the gradebook for the site */ - private Gradebook getGradebook(final String siteId) { - Gradebook gradebook = null; - try { - gradebook = (Gradebook) this.gradebookService.getGradebook(siteId); - } catch (final GradebookNotFoundException e) { - log.debug("Request made for inaccessible, adding gradebookUid={}", siteId); - this.gradebookFrameworkService.addGradebook(siteId, siteId); - try { - gradebook = (Gradebook) this.gradebookService.getGradebook(siteId); - } catch (final GradebookNotFoundException e2) { - log.error("Request made and could not add inaccessible gradebookUid={}", siteId); - } - } - return gradebook; + private Gradebook getGradebook(String siteId) { + + return gradingService.getGradebook(siteId); } /** @@ -436,7 +418,7 @@ public List getGradebookAssignments() { * @param siteId * @return a list of assignments or empty list if none/no gradebook */ - public List getGradebookAssignments(final String siteId) { + public List getGradebookAssignments(String siteId) { return getGradebookAssignments(siteId, SortType.SORT_BY_SORTING); } @@ -489,9 +471,9 @@ public List getGradebookAssignmentsForStudent(final String studentUu final Iterator iter = assignments.iterator(); while (iter.hasNext()) { final Assignment a = iter.next(); - if (a.isExternallyMaintained()) { - if (this.gradebookExternalAssessmentService.isExternalAssignmentGrouped(gradebook.getUid(), a.getExternalId()) && - !this.gradebookExternalAssessmentService.isExternalAssignmentVisible(gradebook.getUid(), a.getExternalId(), + if (a.getExternallyMaintained()) { + if (this.gradingService.isExternalAssignmentGrouped(gradebook.getUid(), a.getExternalId()) && + !this.gradingService.isExternalAssignmentVisible(gradebook.getUid(), a.getExternalId(), studentUuid)) { iter.remove(); } @@ -514,8 +496,9 @@ public List getGradebookAssignments(final String siteId, final SortT if (gradebook != null) { // applies permissions (both student and TA) and default sort is // SORT_BY_SORTING - assignments.addAll(this.gradebookService.getViewableAssignmentsForCurrentUser(gradebook.getUid(), sortBy)); + assignments.addAll(this.gradingService.getViewableAssignmentsForCurrentUser(gradebook.getUid(), sortBy)); } + log.debug("Retrieved {} assignments", assignments.size()); return assignments; } @@ -544,7 +527,7 @@ public List getGradebookCategories(final String siteId) { } if (categoriesAreEnabled()) { - rval = this.gradebookService.getCategoryDefinitions(gradebook.getUid()); + rval = this.gradingService.getCategoryDefinitions(gradebook.getUid()); } GbRole role; @@ -570,7 +553,7 @@ public List getGradebookCategories(final String siteId) { } // get a list of category ids the user can actually view - List viewableCategoryIds = this.gradebookPermissionService + List viewableCategoryIds = this.gradingPermissionService .getCategoriesForUser(gradebook.getId(), user.getId(), allCategoryIds); //FIXME: this is a hack to implement the old style realms checks. The above method only checks the gb_permission_t table and not realms @@ -622,7 +605,7 @@ public List getGradebookCategoriesForStudent(String studentU SecurityAdvisor gbAdvisor = (String userId, String function, String reference) -> "gradebook.gradeAll".equals(function) ? SecurityAdvice.ALLOWED : SecurityAdvice.PASS; securityService.pushAdvisor(gbAdvisor); - List catDefs = gradebookService.getCategoryDefinitions(getGradebook().getUid()); + List catDefs = gradingService.getCategoryDefinitions(getGradebook().getUid()); securityService.popAdvisor(gbAdvisor); // filter out the categories that don't match the categories of the viewable assignments @@ -636,7 +619,7 @@ public List getGradebookCategoriesForStudent(String studentU * @param siteId siteId to get course grades for * @return the map of course grades for students, key = studentUuid, value = course grade, or an empty map */ - public Map getCourseGrades(final String siteId) { + public Map getCourseGrades(final String siteId) { final Gradebook gradebook = this.getGradebook(siteId); final List studentUuids = this.getGradeableUsers(siteId); return this.getCourseGrades(gradebook, studentUuids, null); @@ -649,7 +632,7 @@ public Map getCourseGrades(final String siteId) { * @param schema grading schema mapping * @return the map of course grades for students, key = studentUuid, value = course grade, or an empty map */ - public Map getCourseGrades(final String siteId, final Map schema) { + public Map getCourseGrades(final String siteId, final Map schema) { final Gradebook gradebook = this.getGradebook(siteId); final List studentUuids = this.getGradeableUsers(siteId); return this.getCourseGrades(gradebook, studentUuids, schema); @@ -662,7 +645,7 @@ public Map getCourseGrades(final String siteId, final Map getCourseGrades(final List studentUuids) { + public Map getCourseGrades(final List studentUuids) { final Gradebook gradebook = this.getGradebook(); return this.getCourseGrades(gradebook, studentUuids, null); } @@ -675,13 +658,13 @@ public Map getCourseGrades(final List studentUuids) * @param gradeMap the grade mapping to use. This should be left blank if you are displaying grades to students so that the currently persisted value is used. * @return the map of course grades for students, key = studentUuid, value = course grade, or an empty map */ - private Map getCourseGrades(final Gradebook gradebook, final List studentUuids, final Map gradeMap) { - Map rval = new HashMap<>(); + private Map getCourseGrades(final Gradebook gradebook, final List studentUuids, final Map gradeMap) { + Map rval = new HashMap<>(); if (gradebook != null) { if(gradeMap != null) { - rval = this.gradebookService.getCourseGradeForStudents(gradebook.getUid(), studentUuids, gradeMap); + rval = this.gradingService.getCourseGradeForStudents(gradebook.getUid(), studentUuids, gradeMap); } else { - rval = this.gradebookService.getCourseGradeForStudents(gradebook.getUid(), studentUuids); + rval = this.gradingService.getCourseGradeForStudents(gradebook.getUid(), studentUuids); } } return rval; @@ -693,10 +676,10 @@ private Map getCourseGrades(final Gradebook gradebook, fina * @param studentUuid * @return coursegrade. May have null fields if the coursegrade has not been released */ - public CourseGrade getCourseGrade(final String studentUuid) { + public CourseGradeTransferBean getCourseGrade(final String studentUuid) { final Gradebook gradebook = this.getGradebook(); - final CourseGrade courseGrade = this.gradebookService.getCourseGradeForStudent(gradebook.getUid(), studentUuid); + final CourseGradeTransferBean courseGrade = this.gradingService.getCourseGradeForStudent(gradebook.getUid(), studentUuid); // handle the special case in the gradebook service where totalPointsPossible = -1 if (courseGrade != null && (courseGrade.getTotalPointsPossible() == null || courseGrade.getTotalPointsPossible() == -1)) { @@ -734,7 +717,7 @@ public GradeSaveResponse saveGrade(final Long assignmentId, final String student } // get current grade - final String storedGrade = this.gradebookService.getAssignmentScoreString(gradebook.getUid(), assignmentId, + final String storedGrade = this.gradingService.getAssignmentScoreString(gradebook.getUid(), assignmentId, studentUuid); // get assignment config @@ -742,7 +725,7 @@ public GradeSaveResponse saveGrade(final Long assignmentId, final String student final Double maxPoints = assignment.getPoints(); // check what grading mode we are in - final GradingType gradingType = GradingType.valueOf(gradebook.getGrade_type()); + final GradeType gradingType = gradebook.getGradeType(); // if percentage entry type, reformat the grades, otherwise use points as is String newGradeAdjusted = newGrade; @@ -760,7 +743,7 @@ public GradeSaveResponse saveGrade(final Long assignmentId, final String student ",".equals(formattedText.getDecimalSeparator()) ? "," : "."); } - if (gradingType == GradingType.PERCENTAGE) { + if (gradingType == GradeType.PERCENTAGE) { // the passed in grades represents a percentage so the number needs to be adjusted back to points Double newGradePercentage = new Double("0.0"); @@ -859,13 +842,13 @@ public GradeSaveResponse saveGrade(final Long assignmentId, final String student try { // note, you must pass in the comment or it will be nulled out by the GB service // also, must pass in the raw grade as the service does conversions between percentage etc - this.gradebookService.saveGradeAndCommentForStudent(gradebook.getUid(), assignmentId, studentUuid, + this.gradingService.saveGradeAndCommentForStudent(gradebook.getUid(), assignmentId, studentUuid, newGrade, comment); if (rval == null) { // if we don't have some other warning, it was all OK rval = GradeSaveResponse.OK; } - } catch (InvalidGradeException | GradebookNotFoundException | AssessmentNotFoundException e) { + } catch (InvalidGradeException | AssessmentNotFoundException e) { log.error("An error occurred saving the grade. {}: {}", e.getClass(), e.getMessage()); rval = GradeSaveResponse.ERROR; } @@ -881,9 +864,9 @@ public GradeSaveResponse saveGradesAndCommentsForImport(final Gradebook gradeboo } try { - gradebookService.saveGradesAndComments(gradebook.getUid(), assignment.getId(), gradeDefList); + gradingService.saveGradesAndComments(gradebook.getUid(), assignment.getId(), gradeDefList); return GradeSaveResponse.OK; - } catch (InvalidGradeException | GradebookNotFoundException | AssessmentNotFoundException e) { + } catch (InvalidGradeException | AssessmentNotFoundException e) { log.error("An error occurred saving the grade. {}: {}", e.getClass(), e.getMessage()); return GradeSaveResponse.ERROR; } @@ -903,7 +886,7 @@ public GradeSaveResponse saveExcuse(final Long assignmentId, final String studen } // get current grade - final String storedGrade = this.gradebookService.getAssignmentScoreString(gradebook.getUid(), assignmentId, + final String storedGrade = this.gradingService.getAssignmentScoreString(gradebook.getUid(), assignmentId, studentUuid); GradeSaveResponse rval = null; @@ -911,14 +894,14 @@ public GradeSaveResponse saveExcuse(final Long assignmentId, final String studen // save try { //must pass in the raw grade as the service does conversions between percentage etc - this.gradebookService.saveGradeAndExcuseForStudent(gradebook.getUid(), assignmentId, studentUuid, + this.gradingService.saveGradeAndExcuseForStudent(gradebook.getUid(), assignmentId, studentUuid, storedGrade, excuse); if (rval == null) { // if we don't have some other warning, it was all OK rval = GradeSaveResponse.OK; } - } catch (InvalidGradeException | GradebookNotFoundException | AssessmentNotFoundException e) { + } catch (InvalidGradeException | AssessmentNotFoundException e) { log.error("An error occurred saving the excuse. " + e.getClass() + ": " + e.getMessage()); rval = GradeSaveResponse.ERROR; } @@ -962,7 +945,7 @@ public HashMap buildHasAssociatedRubricMap(final List map = new HashMap(); for (Assignment assignment : assignments) { String externalAppName = assignment.getExternalAppName(); - if(assignment.isExternallyMaintained()) { + if(assignment.getExternallyMaintained()) { boolean hasAssociatedRubric = StringUtils.equals(externalAppName, toolManager.getLocalizedToolProperty("sakai.assignment", "title")) ? rubricsService.hasAssociatedRubric(externalAppName, assignment.getExternalId()) : false; map.put(assignment.getExternalId(), hasAssociatedRubric); } else { @@ -1090,7 +1073,7 @@ public List buildGradeMatrixForImportExport(final List buildMatrixForGradeComparison(Assignment assignment, GradingType gradingType, GradebookInformation settings){ + public List buildMatrixForGradeComparison(Assignment assignment, GradeType gradingType, GradebookInformation settings){ // Only return the list if the feature is activated boolean serverPropertyOn = serverConfigService.getConfig( SAK_PROP_ALLOW_STUDENTS_TO_COMPARE_GRADES, @@ -1105,14 +1088,14 @@ public List buildMatrixForGradeComparison(Assignment assi String userEid = getCurrentUser().getEid(); boolean isComparingAndDisplayingFullName = settings - .isComparingDisplayStudentNames() && + .getComparingDisplayStudentNames() && settings - .isComparingDisplayStudentSurnames(); + .getComparingDisplayStudentSurnames(); boolean isComparingOrDisplayingFullName = settings - .isComparingDisplayStudentNames() || + .getComparingDisplayStudentNames() || settings - .isComparingDisplayStudentSurnames(); + .getComparingDisplayStudentSurnames(); // Add advisor to retrieve the grades as student SecurityAdvisor advisor = null; @@ -1124,22 +1107,22 @@ public List buildMatrixForGradeComparison(Assignment assi if(isComparingOrDisplayingFullName){ String studentDisplayName = String.format( "%s%s%s", - settings.isComparingDisplayStudentNames() ? el.getStudentFirstName() : "", + settings.getComparingDisplayStudentNames() ? el.getStudentFirstName() : "", isComparingAndDisplayingFullName ? " " : "", - settings.isComparingDisplayStudentSurnames()? el.getStudentLastName() : "" + settings.getComparingDisplayStudentSurnames()? el.getStudentLastName() : "" ); el.setStudentDisplayName(studentDisplayName); } el.setIsCurrentUser(userEid.equals(el.getEid())); el.setGrade(FormatHelper.formatGrade(el.getGrade()) + ( - GradingType.PERCENTAGE.equals(gradingType) ? "%" : "" + GradeType.PERCENTAGE == gradingType ? "%" : "" )); return el; }) .collect(Collectors.toList()); - if(settings.isComparingRandomizeDisplayedData()){ + if(settings.getComparingRandomizeDisplayedData()){ Collections.shuffle(data); } return data; @@ -1274,7 +1257,7 @@ public List getGbUsersForUiSettings(List userUuids, GradebookUiS public void putCourseGradesInMatrix(Map matrix, List gbStudents, List studentUuids, Gradebook gradebook, GbRole role, boolean isCourseGradeVisible, GradebookUiSettings settings) { // Get the course grades - final Map courseGrades = getCourseGrades(studentUuids); + final Map courseGrades = getCourseGrades(studentUuids); // Setup the course grade formatter // TODO we want the override except in certain cases. Can we hard code this? @@ -1286,8 +1269,8 @@ public void putCourseGradesInMatrix(Map matrix, List // Add the course grade, including the display String uid = student.getUserUuid(); - final CourseGrade courseGrade = courseGrades.get(uid); - final GbCourseGrade gbCourseGrade = new GbCourseGrade(courseGrades.get(uid)); + final CourseGradeTransferBean courseGrade = courseGrades.get(uid); + final CourseGradeTransferBean gbCourseGrade = courseGrades.get(uid); gbCourseGrade.setDisplayString(courseGradeFormatter.format(courseGrade)); sg.setCourseGrade(gbCourseGrade); @@ -1383,7 +1366,7 @@ public void putAssignmentsAndCategoryItemsInMatrix(Map defs = this.gradebookService.getGradesForStudentsForItem(gradebook.getUid(), assignment.getId(), studentUuids); + final List defs = this.gradingService.getGradesForStudentsForItem(gradebook.getUid(), assignment.getId(), studentUuids); // iterate the definitions returned and update the record for each // student with the grades @@ -1426,7 +1409,7 @@ public void putAssignmentsAndCategoryItemsInMatrix(Map categoryScore = gradebookService.calculateCategoryScore(gradebook, + final Optional categoryScore = gradingService.calculateCategoryScore(gradebook, student.getUserUuid(), category, category.getAssignmentList(), gradeMap, (role == GbRole.TA || role == GbRole.INSTRUCTOR)); categoryScore.ifPresent(data -> { for (Long item : gradeMap.keySet()) { @@ -1455,7 +1438,7 @@ public void putAssignmentsAndCategoryItemsInMatrix(Map !StringUtils.equalsIgnoreCase(GraderPermission.GRADE.toString(), permission.getFunction())); + permissions.removeIf(permission -> !StringUtils.equalsIgnoreCase(GraderPermission.GRADE.toString(), permission.getFunctionName())); log.debug("Filtered permissions: {}", permissions.size()); @@ -1574,7 +1557,7 @@ public void putAssignmentsInMatrixForExport(Map matr for (final Assignment assignment : assignments) { // get grades - final List defs = this.gradebookService.getGradesForStudentsForItem(gradebook.getUid(), assignment.getId(), studentUuids); + final List defs = this.gradingService.getGradesForStudentsForItem(gradebook.getUid(), assignment.getId(), studentUuids); // iterate the definitions returned and update the record for each // student with the grades @@ -1604,7 +1587,7 @@ public void putAssignmentsInMatrixForExport(Map matr // only need to process this if some are defined // again only concerned with grade permission, so parse the list to // remove those that aren't GRADE - permissions.removeIf(permission -> !StringUtils.equalsIgnoreCase(GraderPermission.GRADE.toString(), permission.getFunction())); + permissions.removeIf(permission -> !StringUtils.equalsIgnoreCase(GraderPermission.GRADE.toString(), permission.getFunctionName())); log.debug("Filtered permissions: {}", permissions.size()); @@ -1793,7 +1776,7 @@ public List getSiteSectionsAndGroups() { // get the ones the TA can actually view // note that if a group is empty, it will not be included. - List viewableGroupIds = this.gradebookPermissionService + List viewableGroupIds = this.gradingPermissionService .getViewableGroupsForUser(gradebook.getId(), user.getId(), allGroupIds); //FIXME: Another realms hack. The above method only returns groups from gb_permission_t. If this list is empty, @@ -1869,7 +1852,7 @@ private List getSiteSectionsAndGroups(final String siteId) { // get the ones the TA can actually view // note that if a group is empty, it will not be included. - List viewableGroupIds = this.gradebookPermissionService + List viewableGroupIds = this.gradingPermissionService .getViewableGroupsForUser(gradebook.getId(), user.getId(), allGroupIds); //FIXME: Another realms hack. The above method only returns groups from gb_permission_t. If this list is empty, @@ -1968,11 +1951,11 @@ public Long addAssignment(final Assignment assignment) { if (gradebook != null) { final String gradebookId = gradebook.getUid(); - final Long assignmentId = this.gradebookService.addAssignment(gradebookId, assignment); + final Long assignmentId = this.gradingService.addAssignment(gradebookId, assignment); // Force the assignment to sit at the end of the list if (assignment.getSortOrder() == null) { - final List allAssignments = this.gradebookService.getAssignments(gradebookId); + final List allAssignments = this.gradingService.getAssignments(gradebookId); int nextSortOrder = allAssignments.size(); for (final Assignment anotherAssignment : allAssignments) { if (anotherAssignment.getSortOrder() != null && anotherAssignment.getSortOrder() >= nextSortOrder) { @@ -1988,18 +1971,17 @@ public Long addAssignment(final Assignment assignment) { EventHelper.postAddAssignmentEvent(gradebook, assignmentId, assignment, getUserRoleOrNone()); - // Create the task if it is released - if(assignment.isReleased()) { - String reference = GradebookService.REFERENCE_ROOT + Entity.SEPARATOR + "a" + Entity.SEPARATOR + getCurrentSiteId() + Entity.SEPARATOR + assignmentId; - Task task = new Task(); - task.setSiteId(getCurrentSiteId()); - task.setReference(reference); - task.setSystem(true); - task.setDescription(assignment.getName()); - task.setDue((assignment.getDueDate() == null) ? null : assignment.getDueDate().toInstant()); - Set users = new HashSet<>(this.getGradeableUsers()); - taskService.createTask(task, users, Priorities.HIGH); - } + if (assignment.getReleased()) { + String reference = GradingConstants.REFERENCE_ROOT + Entity.SEPARATOR + "a" + Entity.SEPARATOR + getCurrentSiteId() + Entity.SEPARATOR + assignmentId; + Task task = new Task(); + task.setSiteId(getCurrentSiteId()); + task.setReference(reference); + task.setSystem(true); + task.setDescription(assignment.getName()); + task.setDue((assignment.getDueDate() == null) ? null : assignment.getDueDate().toInstant()); + Set users = new HashSet<>(this.getGradeableUsers()); + taskService.createTask(task, users, Priorities.HIGH); + } return assignmentId; @@ -2030,7 +2012,7 @@ public void updateAssignmentOrder(final long assignmentId, final int order) { public void updateAssignmentOrder(final String siteId, final long assignmentId, final int order) { final Gradebook gradebook = this.getGradebook(siteId); - this.gradebookService.updateAssignmentOrder(gradebook.getUid(), assignmentId, order); + this.gradingService.updateAssignmentOrder(gradebook.getUid(), assignmentId, order); } /** @@ -2067,14 +2049,14 @@ public void updateAssignmentCategorizedOrder(final String siteId, final long ass return; } - final Gradebook gradebook = (Gradebook) this.gradebookService.getGradebook(siteId); + final Gradebook gradebook = (Gradebook) this.gradingService.getGradebook(siteId); if (gradebook == null) { log.error(String.format("Gradebook not in site %s", siteId)); return; } - final Assignment assignmentToMove = this.gradebookService.getAssignment(gradebook.getUid(), assignmentId); + final Assignment assignmentToMove = this.gradingService.getAssignment(gradebook.getUid(), assignmentId); if (assignmentToMove == null) { // TODO Handle assignment not in gradebook @@ -2096,7 +2078,7 @@ public void updateAssignmentCategorizedOrder(final String siteId, final long ass */ private void updateAssignmentCategorizedOrder(final String gradebookId, final Long categoryId, final Long assignmentId, final int order) { - this.gradebookService.updateAssignmentCategorizedOrder(gradebookId, categoryId, assignmentId, order); + this.gradingService.updateAssignmentCategorizedOrder(gradebookId, categoryId, assignmentId, order); } /** @@ -2112,10 +2094,11 @@ public List getEditingNotifications(final String gradebookUid, fina final List rval = new ArrayList<>(); - final List assignments = this.gradebookService.getViewableAssignmentsForCurrentUser(gradebookUid, + final List assignments = this.gradingService.getViewableAssignmentsForCurrentUser(gradebookUid, SortType.SORT_BY_SORTING); + log.debug("Retrieved {} assignments", assignments.size()); final List assignmentIds = assignments.stream().map(a -> a.getId()).collect(Collectors.toList()); - final List events = this.gradebookService.getGradingEvents(assignmentIds, since); + final List events = this.gradingService.getGradingEvents(assignmentIds, since); // keep a hash of all users so we don't have to hit the service each time final Map users = new HashMap<>(); @@ -2159,7 +2142,7 @@ public Assignment getAssignment(final long assignmentId) { public Assignment getAssignment(final String siteId, final long assignmentId) { final Gradebook gradebook = getGradebook(siteId); if (gradebook != null) { - return this.gradebookService.getAssignment(gradebook.getUid(), assignmentId); + return this.gradingService.getAssignment(gradebook.getUid(), assignmentId); } return null; } @@ -2187,7 +2170,7 @@ public Assignment getAssignment(final String assignmentName) { public Assignment getAssignment(final String siteId, final String assignmentName) { final Gradebook gradebook = getGradebook(siteId); if (gradebook != null) { - return this.gradebookService.getAssignment(gradebook.getUid(), assignmentName); + return this.gradingService.getAssignment(gradebook.getUid(), assignmentName); } return null; } @@ -2207,7 +2190,7 @@ public int getAssignmentSortOrder(final long assignmentId) { final Gradebook gradebook = getGradebook(siteId); if (gradebook != null) { - final Assignment assignment = this.gradebookService.getAssignment(gradebook.getUid(), assignmentId); + final Assignment assignment = this.gradingService.getAssignment(gradebook.getUid(), assignmentId); // if the assignment has a sort order, return that if (assignment.getSortOrder() != null) { @@ -2242,17 +2225,17 @@ public void updateAssignment(final Assignment assignment) { // need the original name as the service needs that as the key... final Assignment original = this.getAssignment(assignment.getId()); - this.gradebookService.updateAssignment(gradebook.getUid(), original.getId(), assignment); + gradingService.updateAssignment(gradebook.getUid(), original.getId(), assignment); // Update task - String reference = GradebookService.REFERENCE_ROOT + Entity.SEPARATOR + "a" + Entity.SEPARATOR + getCurrentSiteId() + Entity.SEPARATOR + original.getId(); + String reference = GradingConstants.REFERENCE_ROOT + Entity.SEPARATOR + "a" + Entity.SEPARATOR + getCurrentSiteId() + Entity.SEPARATOR + original.getId(); Optional optTask = taskService.getTask(reference); if (optTask.isPresent()) { Task task = optTask.get(); task.setDescription(assignment.getName()); task.setDue((assignment.getDueDate() == null) ? null : assignment.getDueDate().toInstant()); taskService.saveTask(task); - } else if(assignment.isReleased()) { + } else if(assignment.getReleased()) { // Create the task Task task = new Task(); task.setSiteId(getCurrentSiteId()); @@ -2301,7 +2284,7 @@ public boolean updateUngradedItems(final long assignmentId, final String grade, final List studentUuids = (group == null) ? this.getGradeableUsers() : this.getGradeableUsers(group); // get grades (only returns those where there is a grade, or comment; does not return those where there is no grade AND no comment) - final List defs = this.gradebookService.getGradesForStudentsForItem(gradebook.getUid(), assignmentId, studentUuids); + final List defs = this.gradingService.getGradesForStudentsForItem(gradebook.getUid(), assignmentId, studentUuids); // Remove students who already have a grade studentUuids.removeIf(studentUUID -> defs.stream().anyMatch(def -> studentUUID.equals(def.getStudentUid()) && StringUtils.isNotBlank(def.getGrade()))); @@ -2312,8 +2295,8 @@ public boolean updateUngradedItems(final long assignmentId, final String grade, if (defs.stream().noneMatch(def -> studentUUID.equals(def.getStudentUid()))) { GradeDefinition def = new GradeDefinition(); def.setStudentUid(studentUUID); - def.setGradeEntryType(gradebook.getGrade_type()); - def.setGradeReleased(gradebook.isAssignmentsDisplayed() && assignment.isReleased()); + def.setGradeEntryType(gradebook.getGradeType()); + def.setGradeReleased(gradebook.getAssignmentsDisplayed() && assignment.getReleased()); defs.add(def); } } @@ -2331,7 +2314,7 @@ public boolean updateUngradedItems(final long assignmentId, final String grade, // Batch update the GradeDefinitions, and post an event on completion try { - gradebookService.saveGradesAndComments(gradebook.getUid(), assignmentId, defs); + gradingService.saveGradesAndComments(gradebook.getUid(), assignmentId, defs); EventHelper.postUpdateUngradedEvent(gradebook, assignmentId, String.valueOf(grade), getUserRoleOrNone()); return true; } catch (final Exception e) { @@ -2349,7 +2332,7 @@ public boolean updateUngradedItems(final long assignmentId, final String grade, * @return */ public List getGradeLog(final String studentUuid, final long assignmentId) { - final List gradingEvents = this.gradebookService.getGradingEvents(studentUuid, assignmentId); + final List gradingEvents = this.gradingService.getGradingEvents(studentUuid, assignmentId); final List rval = new ArrayList<>(); for (final GradingEvent ge : gradingEvents) { @@ -2397,12 +2380,12 @@ public String getAssignmentGradeComment(final String siteId, final long assignme final Gradebook gradebook = getGradebook(siteId); try { - final CommentDefinition def = this.gradebookService.getAssignmentScoreComment(gradebook.getUid(), + final CommentDefinition def = this.gradingService.getAssignmentScoreComment(gradebook.getUid(), assignmentId, studentUuid); if (def != null) { return def.getCommentText(); } - } catch (GradebookNotFoundException | AssessmentNotFoundException e) { + } catch (AssessmentNotFoundException e) { log.error("An error occurred retrieving the comment. {}: {}", e.getClass(), e.getMessage()); } return null; @@ -2416,13 +2399,13 @@ public String getAssignmentExcuse(final String siteId, final long assignmentId, final Gradebook gradebook = getGradebook(siteId); try{ - final boolean excuse = this.gradebookService.getIsAssignmentExcused(gradebook.getUid(), assignmentId, studentUuid); + final boolean excuse = this.gradingService.getIsAssignmentExcused(gradebook.getUid(), assignmentId, studentUuid); if(excuse){ return "1"; }else{ return "0"; } - } catch (GradebookNotFoundException | AssessmentNotFoundException e) { + } catch (AssessmentNotFoundException e) { log.error("An error occurred retrieving the excuse. " + e.getClass() + ": " + e.getMessage()); } return null; @@ -2445,12 +2428,12 @@ public boolean updateAssignmentGradeComment(final long assignmentId, final Strin try { // could do a check here to ensure we aren't overwriting someone // else's comment that has been updated in the interim... - this.gradebookService.setAssignmentScoreComment(gradebook.getUid(), assignmentId, studentUuid, comment); + this.gradingService.setAssignmentScoreComment(gradebook.getUid(), assignmentId, studentUuid, comment); EventHelper.postUpdateCommentEvent(getGradebook(), assignmentId, studentUuid, comment, getUserRoleOrNone()); return true; - } catch (GradebookNotFoundException | AssessmentNotFoundException | IllegalArgumentException e) { + } catch (AssessmentNotFoundException | IllegalArgumentException e) { log.error("An error occurred saving the comment. {}: {}", e.getClass(), e.getMessage()); } @@ -2534,7 +2517,7 @@ public Map getGradesForStudent(final String studentUuid) { // if student, only proceed if grades are released for the site // if instructor or TA, skip this check // permission checks are still applied at the assignment level in the - // GradebookService + // GradingService GbRole role; try { role = this.getUserRole(siteId); @@ -2544,14 +2527,14 @@ public Map getGradesForStudent(final String studentUuid) { } if (role == GbRole.STUDENT) { - final boolean released = gradebook.isAssignmentsDisplayed(); + final boolean released = gradebook.getAssignmentsDisplayed(); if (!released) { return rval; } } for (final Assignment assignment : assignments) { - final GradeDefinition def = this.gradebookService.getGradeDefinitionForStudentForItem(gradebook.getUid(), + final GradeDefinition def = this.gradingService.getGradeDefinitionForStudentForItem(gradebook.getUid(), assignment.getId(), studentUuid); rval.put(assignment.getId(), new GbGradeInfo(def)); } @@ -2560,7 +2543,7 @@ public Map getGradesForStudent(final String studentUuid) { } public GradeDefinition getGradeForStudentForItem(String studentId, Long assignmentId) { - return this.gradebookService.getGradeDefinitionForStudentForItem(getCurrentSiteId(), assignmentId, studentId); + return this.gradingService.getGradeDefinitionForStudentForItem(getCurrentSiteId(), assignmentId, studentId); } /** @@ -2575,7 +2558,7 @@ public Optional getCategoryScoreForStudent(final Long categor final Gradebook gradebook = getGradebook(); - final Optional result = gradebookService.calculateCategoryScore(gradebook.getId(), studentUuid, categoryId, isInstructor, gradebook.getCategory_type(), null); + final Optional result = gradingService.calculateCategoryScore(gradebook.getId(), studentUuid, categoryId, isInstructor, gradebook.getCategoryType(), null); log.debug("Category score for category: {}, student: {}:{}", categoryId, studentUuid, result.map(r -> r.score).orElse(null)); return result; @@ -2602,7 +2585,7 @@ public GradebookInformation getGradebookSettings(final String siteId) { SecurityAdvisor advisor = null; try { advisor = addSecurityAdvisor(); - final GradebookInformation settings = this.gradebookService.getGradebookInformation(gradebook.getUid()); + final GradebookInformation settings = this.gradingService.getGradebookInformation(gradebook.getUid()); Collections.sort(settings.getCategories(), CategoryDefinition.orderComparator); return settings; } finally { @@ -2621,7 +2604,7 @@ public void updateGradebookSettings(final GradebookInformation settings) { final String siteId = getCurrentSiteId(); final Gradebook gradebook = getGradebook(siteId); - this.gradebookService.updateGradebookSettings(gradebook.getUid(), settings); + this.gradingService.updateGradebookSettings(gradebook.getUid(), settings); EventHelper.postUpdateSettingsEvent(gradebook); } @@ -2634,11 +2617,11 @@ public void updateGradebookSettings(final GradebookInformation settings) { public void removeAssignment(final Long assignmentId) { // Delete task - String reference = GradebookService.REFERENCE_ROOT + Entity.SEPARATOR + "a" + Entity.SEPARATOR + getCurrentSiteId() + Entity.SEPARATOR + assignmentId; + String reference = GradingConstants.REFERENCE_ROOT + Entity.SEPARATOR + "a" + Entity.SEPARATOR + getCurrentSiteId() + Entity.SEPARATOR + assignmentId; taskService.removeTaskByReference(reference); rubricsService.deleteRubricAssociationsByItemIdPrefix(assignmentId.toString(), RubricsConstants.RBCS_TOOL_GRADEBOOKNG); - this.gradebookService.removeAssignment(assignmentId); + this.gradingService.removeAssignment(assignmentId); EventHelper.postDeleteAssignmentEvent(getGradebook(), assignmentId, getUserRoleOrNone()); } @@ -2678,13 +2661,13 @@ public List getPermissionsForUser(final String userUuid) { final String siteId = getCurrentSiteId(); final Gradebook gradebook = getGradebook(siteId); - List permissions = this.gradebookPermissionService + List permissions = this.gradingPermissionService .getPermissionsForUser(gradebook.getUid(), userUuid); //if db permissions are null, check realms permissions. if (permissions == null || permissions.isEmpty()) { //This method should return empty arraylist if they have no realms perms - permissions = this.gradebookPermissionService.getRealmsPermissionsForUser(userUuid, siteId, Role.TA); + permissions = this.gradingPermissionService.getRealmsPermissionsForUser(userUuid, siteId, Role.TA); } return permissions; } @@ -2697,13 +2680,13 @@ private List getPermissionsForUser(final String userUuid, final Gradebook gradebook = getGradebook(siteId); - List permissions = this.gradebookPermissionService + List permissions = this.gradingPermissionService .getPermissionsForUser(gradebook.getUid(), userUuid); //if db permissions are null, check realms permissions. if (permissions == null || permissions.isEmpty()) { //This method should return empty arraylist if they have no realms perms - permissions = this.gradebookPermissionService.getRealmsPermissionsForUser(userUuid, siteId, Role.TA); + permissions = this.gradingPermissionService.getRealmsPermissionsForUser(userUuid, siteId, Role.TA); } return permissions; } @@ -2718,7 +2701,7 @@ public void updatePermissionsForUser(final String userUuid, final List site = getCurrentSite(); return user != null && site.isPresent() && getCandidateDetailProvider().isInstitutionalNumericIdEnabled(site.get()) - && this.gradebookService.currentUserHasViewStudentNumbersPerm(getGradebook().getUid()); + && this.gradingService.currentUserHasViewStudentNumbersPerm(getGradebook().getUid()); } public String getStudentNumber(final User u, final Site site) @@ -2912,22 +2895,20 @@ public boolean categoriesAreEnabled() { final String siteId = getCurrentSiteId(); final Gradebook gradebook = getGradebook(siteId); - return GbCategoryType.ONLY_CATEGORY.getValue() == gradebook.getCategory_type() - || GbCategoryType.WEIGHTED_CATEGORY.getValue() == gradebook.getCategory_type(); + return GradingCategoryType.ONLY_CATEGORY == gradebook.getCategoryType() + || GradingCategoryType.WEIGHTED_CATEGORY == gradebook.getCategoryType(); } /** * Get the currently configured gradebook category type * - * @return GbCategoryType wrapper around the int value + * @return GradingCategoryType wrapper around the int value */ - public GbCategoryType getGradebookCategoryType() { + public GradingCategoryType getGradebookCategoryType() { final String siteId = getCurrentSiteId(); final Gradebook gradebook = getGradebook(siteId); - final int configuredType = gradebook.getCategory_type(); - - return GbCategoryType.valueOf(configuredType); + return gradebook.getCategoryType(); } /** @@ -2943,7 +2924,7 @@ public boolean updateCourseGrade(final String studentUuid, final String grade, f final Gradebook gradebook = getGradebook(siteId); try { - this.gradebookService.updateCourseGradeForStudent(gradebook.getUid(), studentUuid, grade, gradeScale); + gradingService.updateCourseGradeForStudent(gradebook.getUid(), studentUuid, grade, gradeScale); EventHelper.postOverrideCourseGradeEvent(gradebook, studentUuid, grade, grade != null); return true; } catch (final Exception e) { @@ -3044,7 +3025,7 @@ public boolean isUserAbleToEditAssessments(){ */ public boolean isValidNumericGrade(String grade) { - return gradebookService.isValidNumericGrade(grade); + return gradingService.isValidNumericGrade(grade); } /** diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/LetterGradeComparator.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/LetterGradeComparator.java index fc85cd55543e..3e5d547e234b 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/LetterGradeComparator.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/LetterGradeComparator.java @@ -21,7 +21,7 @@ import org.apache.commons.lang3.StringUtils; /** - * Comparator to ensure correct ordering of letter grades, catering for + and - in the grade Copied from GradebookService and made + * Comparator to ensure correct ordering of letter grades, catering for + and - in the grade Copied from GradingService and made * Serializable as we use it in a TreeMap. Also has the fix from SAK-30094. If this changes, be sure to update the other. */ public class LetterGradeComparator implements Comparator, Serializable { diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbCourseGrade.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbCourseGrade.java deleted file mode 100644 index 8823638b6422..000000000000 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbCourseGrade.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (c) 2003-2016 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.gradebookng.business.model; - -import java.io.Serializable; - -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.sakaiproject.service.gradebook.shared.CourseGrade; - -import lombok.Getter; -import lombok.Setter; - -/** - * Wraps a {@link CourseGrade} and provides a display string formatted according to the settings from various places in the UI - * - * @author Steve Swinsburg (steve.swinsburg@gmail.com) - * - */ -public class GbCourseGrade implements Serializable { - - private static final long serialVersionUID = 1L; - - @Getter - private final CourseGrade courseGrade; - - @Getter - @Setter - private String displayString; - - /** - * Constructor. Takes a {@link CourseGrade}. Display string is set afterwards. - * - * @param courseGrade CourseGrade object - */ - public GbCourseGrade(final CourseGrade courseGrade) { - this.courseGrade = courseGrade; - } - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this); - } - -} diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbGradeComparisonItem.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbGradeComparisonItem.java index 4590da9ea1d4..a83f6805fac1 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbGradeComparisonItem.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbGradeComparisonItem.java @@ -11,7 +11,7 @@ import lombok.Setter; import org.sakaiproject.gradebookng.business.model.GbGradeInfo; import org.sakaiproject.gradebookng.business.model.GbStudentGradeInfo; -import org.sakaiproject.service.gradebook.shared.GradeDefinition; +import org.sakaiproject.grading.api.GradeDefinition; import java.io.Serializable; import java.util.Optional; diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbGradeInfo.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbGradeInfo.java index 7b200631b3ee..3275a25c6855 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbGradeInfo.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbGradeInfo.java @@ -19,7 +19,7 @@ import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; -import org.sakaiproject.service.gradebook.shared.GradeDefinition; +import org.sakaiproject.grading.api.GradeDefinition; import lombok.Getter; import lombok.Setter; diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbGradeLog.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbGradeLog.java index 05086dcfd7ea..f71f11260635 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbGradeLog.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbGradeLog.java @@ -19,8 +19,8 @@ import java.util.Date; import org.apache.commons.lang3.builder.ReflectionToStringBuilder; -import org.sakaiproject.service.gradebook.shared.GradingEventStatus; -import org.sakaiproject.tool.gradebook.GradingEvent; +import org.sakaiproject.grading.api.GradingEventStatus; +import org.sakaiproject.grading.api.model.GradingEvent; import lombok.Getter; diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbStudentGradeInfo.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbStudentGradeInfo.java index 851bd3919b59..2db66b1d56c5 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbStudentGradeInfo.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/model/GbStudentGradeInfo.java @@ -24,6 +24,7 @@ import lombok.Getter; import lombok.Setter; +import org.sakaiproject.grading.api.CourseGradeTransferBean; import org.sakaiproject.user.api.User; /** @@ -44,7 +45,7 @@ public class GbStudentGradeInfo implements Serializable { private String studentEid; private String studentNumber; @Setter - private GbCourseGrade courseGrade; + private CourseGradeTransferBean courseGrade; private Map grades; private Map categoryAverages; private List sections; diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/CourseGradeFormatter.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/CourseGradeFormatter.java index 15f55bfc005f..992ced305e6d 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/CourseGradeFormatter.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/CourseGradeFormatter.java @@ -21,10 +21,10 @@ import org.apache.commons.lang3.StringUtils; import org.apache.wicket.model.StringResourceModel; -import org.sakaiproject.gradebookng.business.GbCategoryType; +import org.sakaiproject.grading.api.GradingCategoryType; import org.sakaiproject.gradebookng.business.GbRole; -import org.sakaiproject.service.gradebook.shared.CourseGrade; -import org.sakaiproject.tool.gradebook.Gradebook; +import org.sakaiproject.grading.api.CourseGradeTransferBean; +import org.sakaiproject.grading.api.model.Gradebook; /** * Helper class to handle the formatting of the course grade display string @@ -73,7 +73,7 @@ public CourseGradeFormatter(final Gradebook gradebook, final GbRole currentUserR * * @return the formatted display string */ - public String format(final CourseGrade courseGrade) { + public String format(final CourseGradeTransferBean courseGrade) { String rval = null; @@ -92,7 +92,7 @@ public String format(final CourseGrade courseGrade) { } // student, check if course grade released, and permission check } else { - if (this.gradebook.isCourseGradeDisplayed()) { + if (this.gradebook.getCourseGradeDisplayed()) { if (!this.isCourseGradeVisible) { rval = MessageHelper.getString("label.coursegrade.nopermission"); } else { @@ -117,7 +117,7 @@ public String format(final CourseGrade courseGrade) { * * @return formatted string ready for display */ - private String build(final CourseGrade courseGrade) { + private String build(final CourseGradeTransferBean courseGrade) { final List parts = new ArrayList<>(); // letter grade @@ -129,7 +129,7 @@ private String build(final CourseGrade courseGrade) { } if (StringUtils.isNotBlank(letterGrade) - && (this.gradebook.isCourseLetterGradeDisplayed() || shouldDisplayFullCourseGrade())) { + && (this.gradebook.getCourseLetterGradeDisplayed() || shouldDisplayFullCourseGrade())) { parts.add(letterGrade); } @@ -138,7 +138,7 @@ private String build(final CourseGrade courseGrade) { final String calculatedGrade = FormatHelper.formatStringAsPercentage(courseGrade.getCalculatedGrade()); if (StringUtils.isNotBlank(calculatedGrade) - && (this.gradebook.isCourseAverageDisplayed() || shouldDisplayFullCourseGrade())) { + && (this.gradebook.getCourseAverageDisplayed() || shouldDisplayFullCourseGrade())) { if (parts.isEmpty()) { parts.add(new StringResourceModel("coursegrade.display.percentage-first", null, new Object[] { calculatedGrade }).getString()); @@ -152,8 +152,8 @@ private String build(final CourseGrade courseGrade) { if (this.showPoints) { // don't display points for weighted category type - final GbCategoryType categoryType = GbCategoryType.valueOf(this.gradebook.getCategory_type()); - if (categoryType != GbCategoryType.WEIGHTED_CATEGORY) { + final GradingCategoryType categoryType = this.gradebook.getCategoryType(); + if (categoryType != GradingCategoryType.WEIGHTED_CATEGORY) { Double pointsEarned = courseGrade.getPointsEarned(); Double totalPointsPossible = courseGrade.getTotalPointsPossible(); @@ -166,7 +166,7 @@ private String build(final CourseGrade courseGrade) { // if instructor, show the points if requested // otherwise check the settings - if (shouldDisplayFullCourseGrade() || this.gradebook.isCoursePointsDisplayed()) { + if (shouldDisplayFullCourseGrade() || this.gradebook.getCoursePointsDisplayed()) { if (pointsEarned != null && totalPointsPossible != null) { final String pointsEarnedDisplayString = FormatHelper.formatGradeForDisplay(pointsEarned); final String totalPointsPossibleDisplayString = FormatHelper.formatGradeForDisplay(totalPointsPossible); diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/EventHelper.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/EventHelper.java index b5d42ede6e78..48ca8f821166 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/EventHelper.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/EventHelper.java @@ -21,8 +21,8 @@ import org.sakaiproject.gradebookng.business.GbRole; import org.sakaiproject.gradebookng.business.GradeSaveResponse; -import org.sakaiproject.tool.gradebook.Gradebook; -import org.sakaiproject.service.gradebook.shared.Assignment; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.Assignment; import org.sakaiproject.event.cover.EventTrackingService; import org.sakaiproject.event.api.Event; @@ -38,8 +38,8 @@ public static void postAddAssignmentEvent(Gradebook gradebook, long assignmentId assignment.getName(), String.valueOf(assignment.getPoints()), String.valueOf(assignment.getDueDate() == null ? -1 : assignment.getDueDate().getTime()), - String.valueOf(assignment.isReleased()), - String.valueOf(assignment.isCounted()), + String.valueOf(assignment.getReleased()), + String.valueOf(assignment.getCounted()), assignment.getCategoryId() == null ? "uncategorized" : String.valueOf(assignment.getCategoryId()), currentRole.toString().toLowerCase() }; @@ -55,8 +55,8 @@ public static void postUpdateAssignmentEvent(Gradebook gradebook, Assignment ass assignment.getName(), String.valueOf(assignment.getPoints()), String.valueOf(assignment.getDueDate() == null ? -1 : assignment.getDueDate().getTime()), - String.valueOf(assignment.isReleased()), - String.valueOf(assignment.isCounted()), + String.valueOf(assignment.getReleased()), + String.valueOf(assignment.getCounted()), assignment.getCategoryId() == null ? "uncategorized" : String.valueOf(assignment.getCategoryId()), currentRole.toString().toLowerCase() }; diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/FormatHelper.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/FormatHelper.java index e115b6a976bb..d42e82dbce0c 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/FormatHelper.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/FormatHelper.java @@ -40,7 +40,7 @@ import org.sakaiproject.util.ResourceLoader; import lombok.extern.slf4j.Slf4j; -import org.sakaiproject.service.gradebook.shared.CategoryDefinition; +import org.sakaiproject.grading.api.CategoryDefinition; @Slf4j public class FormatHelper { diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/ImportGradesHelper.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/ImportGradesHelper.java index d7d1101e419f..780231f46f0b 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/ImportGradesHelper.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/ImportGradesHelper.java @@ -66,7 +66,7 @@ import org.sakaiproject.gradebookng.tool.model.AssignmentStudentGradeInfo; import org.sakaiproject.gradebookng.tool.model.ImportWizardModel; import org.sakaiproject.gradebookng.tool.pages.ImportExportPage; -import org.sakaiproject.service.gradebook.shared.Assignment; +import org.sakaiproject.grading.api.Assignment; import org.sakaiproject.util.ResourceLoader; import com.opencsv.CSVParser; diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/framework/GradebookNgContextObserver.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/framework/GradebookNgContextObserver.java deleted file mode 100644 index 99f7b88b0036..000000000000 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/framework/GradebookNgContextObserver.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) 2003-2016 The Apereo Foundation - * - * Licensed under the Educational Community License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://opensource.org/licenses/ecl2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sakaiproject.gradebookng.framework; - -import lombok.extern.slf4j.Slf4j; -import org.sakaiproject.entity.api.ContextObserver; -import org.sakaiproject.service.gradebook.shared.GradebookNotFoundException; - -/** - * Sakai context observer that responds to changes in the site (ie tools added/deleted etc) so that we can respond and ensure everything is - * set. - * - * Note that we must also be an EntityProducer so we extend GradebookNgEntityProducer which handles that. - */ -@Slf4j -public class GradebookNgContextObserver extends GradebookNgEntityProducer implements ContextObserver { - - @Override - public void contextCreated(final String context, final boolean toolPlacement) { - // A new site has been created with this tool. Ensure we have a gradebook - if (toolPlacement && !this.gradebookFrameworkService.isGradebookDefined(context)) { - log.debug("Gradebook NG added to site " + context + ". Bootstrapping a gradebook."); - this.gradebookFrameworkService.addGradebook(context, context); - } - - } - - @Override - public void contextUpdated(final String context, final boolean toolPlacement) { - if (toolPlacement) { - // Site has been edited and this tool has been added - log.debug("Gradebook NG added to site " + context + ". Bootstrapping a gradebook."); - if (!this.gradebookFrameworkService.isGradebookDefined(context)) { - this.gradebookFrameworkService.addGradebook(context, context); - } - } else { - // Site has been edited and this tool has been removed - // Do nothing, this may have been an error and we don't want to lose the gradebook data - log.debug("Gradebook NG removed from site " + context + " but any data will remain until site deletion."); - } - } - - @Override - public void contextDeleted(final String context, final boolean toolPlacement) { - // Site has been deleted - if (this.gradebookFrameworkService.isGradebookDefined(context)) { - log.debug("Site " + context + " has been deleted. Removing associated gradebook data."); - try { - this.gradebookFrameworkService.deleteGradebook(context); - } catch (final GradebookNotFoundException e) { - log.debug("Couldnt find gradebook. Nothing to delete.", e); - } - } - } - -} diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/framework/GradebookNgEntityProducer.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/framework/GradebookNgEntityProducer.java index 536f74cab3b8..89789dd0db01 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/framework/GradebookNgEntityProducer.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/framework/GradebookNgEntityProducer.java @@ -28,12 +28,11 @@ import org.sakaiproject.entity.api.HttpAccess; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.api.ResourceProperties; -import org.sakaiproject.service.gradebook.shared.Assignment; -import org.sakaiproject.service.gradebook.shared.CategoryDefinition; -import org.sakaiproject.service.gradebook.shared.GradebookFrameworkService; -import org.sakaiproject.service.gradebook.shared.GradebookInformation; -import org.sakaiproject.service.gradebook.shared.GradebookService; -import org.sakaiproject.tool.gradebook.Gradebook; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.GradingService; +import org.sakaiproject.grading.api.Assignment; +import org.sakaiproject.grading.api.CategoryDefinition; +import org.sakaiproject.grading.api.GradebookInformation; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -50,17 +49,11 @@ public class GradebookNgEntityProducer implements EntityProducer, EntityTransfer protected final static String LABEL = "GradebookNG"; protected final static String referenceRoot = "/gradebookng"; - /** - * These are shared with the GradebookNgContextObserver - */ @Setter protected EntityManager entityManager; @Setter - protected GradebookService gradebookService; - - @Setter - protected GradebookFrameworkService gradebookFrameworkService; + protected GradingService gradingService; /** * Register this class as an EntityProducer. @@ -135,13 +128,13 @@ public String[] myToolIds() { @Override public Map transferCopyEntities(String fromContext, String toContext, List ids, List options) { - final Gradebook gradebook = (Gradebook) this.gradebookService.getGradebook(fromContext); + final Gradebook gradebook = (Gradebook) this.gradingService.getGradebook(fromContext); - final GradebookInformation gradebookInformation = this.gradebookService.getGradebookInformation(gradebook.getUid()); + final GradebookInformation gradebookInformation = this.gradingService.getGradebookInformation(gradebook.getUid()); - final List assignments = this.gradebookService.getAssignments(fromContext); + final List assignments = this.gradingService.getAssignments(fromContext); - return this.gradebookService.transferGradebook(gradebookInformation, assignments, toContext, fromContext); + return this.gradingService.transferGradebook(gradebookInformation, assignments, toContext, fromContext); } @Override @@ -149,15 +142,15 @@ public Map transferCopyEntities(String fromContext, String toCon if (cleanup == true) { - final Gradebook gradebook = (Gradebook) this.gradebookService.getGradebook(toContext); + final Gradebook gradebook = (Gradebook) this.gradingService.getGradebook(toContext); // remove assignments in 'to' site - final List assignments = this.gradebookService.getAssignments(gradebook.getUid()); - assignments.forEach(a -> this.gradebookService.removeAssignment(a.getId())); + final List assignments = this.gradingService.getAssignments(gradebook.getUid()); + assignments.forEach(a -> this.gradingService.removeAssignment(a.getId())); // remove categories in 'to' site - final List categories = this.gradebookService.getCategoryDefinitions(gradebook.getUid()); - categories.forEach(c -> this.gradebookService.removeCategory(c.getId())); + final List categories = this.gradingService.getCategoryDefinitions(gradebook.getUid()); + categories.forEach(c -> this.gradingService.removeCategory(c.getId())); } // now migrate diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/rest/GradebookNgEntityProvider.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/rest/GradebookNgEntityProvider.java index c5874962c319..03f0d0b83e7e 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/rest/GradebookNgEntityProvider.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/rest/GradebookNgEntityProvider.java @@ -50,9 +50,9 @@ import org.sakaiproject.gradebookng.business.GradebookNgBusinessService; import org.sakaiproject.gradebookng.business.exception.GbAccessDeniedException; import org.sakaiproject.gradebookng.business.model.GbGradeCell; -import org.sakaiproject.service.gradebook.shared.GradeDefinition; -import org.sakaiproject.service.gradebook.shared.GradebookService; -import org.sakaiproject.tool.gradebook.facades.Authz; +import org.sakaiproject.grading.api.GradingAuthz; +import org.sakaiproject.grading.api.GradingService; +import org.sakaiproject.grading.api.GradeDefinition; import org.sakaiproject.user.api.User; import org.sakaiproject.user.api.UserDirectoryService; import org.sakaiproject.user.api.UserNotDefinedException; @@ -103,7 +103,7 @@ public class GradebookNgEntityProvider extends AbstractEntityProvider implements private GradebookNgBusinessService businessService; @Setter - private GradebookService gradebookService; + private GradingService gradingService; @Setter private UserDirectoryService userDirectoryService; @@ -261,14 +261,14 @@ private Set getRecipients(Map params) { AuthzGroup authzGroup = authzGroupService.getAuthzGroup(groupRef); recipients = authzGroup.getUsers(); // Remove the instructors - recipients.removeAll(authzGroup.getUsersIsAllowed(Authz.PERMISSION_GRADE_ALL)); - recipients.removeAll(authzGroup.getUsersIsAllowed(Authz.PERMISSION_GRADE_SECTION)); + recipients.removeAll(authzGroup.getUsersIsAllowed(GradingAuthz.PERMISSION_GRADE_ALL)); + recipients.removeAll(authzGroup.getUsersIsAllowed(GradingAuthz.PERMISSION_GRADE_SECTION)); } catch (GroupNotDefinedException gnde) { throw new IllegalArgumentException("No group defined for " + groupRef); } List grades - = gradebookService.getGradesForStudentsForItem(siteId, assignmentId, new ArrayList(recipients)); + = gradingService.getGradesForStudentsForItem(siteId, assignmentId, new ArrayList(recipients)); if (MESSAGE_GRADED.equals(action)) { // We want to message graded students. Filter by min and max score, if needed. diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/ExcuseGradeAction.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/ExcuseGradeAction.java index 6c4bfaf2ead9..51e6c8ea1ba7 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/ExcuseGradeAction.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/ExcuseGradeAction.java @@ -29,9 +29,9 @@ import org.sakaiproject.gradebookng.business.util.FormatHelper; import org.sakaiproject.gradebookng.tool.model.GradebookUiSettings; import org.sakaiproject.gradebookng.tool.pages.GradebookPage; -import org.sakaiproject.service.gradebook.shared.CategoryScoreData; -import org.sakaiproject.service.gradebook.shared.CourseGrade; -import org.sakaiproject.tool.gradebook.Gradebook; +import org.sakaiproject.grading.api.CategoryScoreData; +import org.sakaiproject.grading.api.CourseGradeTransferBean; +import org.sakaiproject.grading.api.model.Gradebook; import java.io.Serializable; import java.util.Collections; @@ -114,7 +114,7 @@ public ActionResponse handleEvent(final JsonNode params, final AjaxRequestTarget String.format("GbGradeTable.updateExcuse('%s', '%s', '%s');", assignmentId, studentUuid, excuse)); - final CourseGrade studentCourseGrade = businessService.getCourseGrade(studentUuid); + final CourseGradeTransferBean studentCourseGrade = businessService.getCourseGrade(studentUuid); boolean isOverride = false; String grade = getGrade(studentCourseGrade, page); @@ -141,7 +141,7 @@ public ActionResponse handleEvent(final JsonNode params, final AjaxRequestTarget droppedItems); } - private String getGrade(CourseGrade studentCourseGrade, GradebookPage page) { + private String getGrade(CourseGradeTransferBean studentCourseGrade, GradebookPage page) { final GradebookUiSettings uiSettings = page.getUiSettings(); final Gradebook gradebook = businessService.getGradebook(); diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/GradeUpdateAction.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/GradeUpdateAction.java index f6b4f9453fa4..c3e87c9787f3 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/GradeUpdateAction.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/GradeUpdateAction.java @@ -35,11 +35,11 @@ import org.sakaiproject.gradebookng.business.util.FormatHelper; import org.sakaiproject.gradebookng.tool.pages.GradebookPage; import org.sakaiproject.gradebookng.business.GradebookNgBusinessService; -import org.sakaiproject.gradebookng.business.model.GbCourseGrade; import org.sakaiproject.gradebookng.tool.model.GbGradebookData; -import org.sakaiproject.service.gradebook.shared.CategoryScoreData; -import org.sakaiproject.service.gradebook.shared.CourseGrade; -import org.sakaiproject.tool.gradebook.Gradebook; +import org.sakaiproject.grading.api.CategoryScoreData; +import org.sakaiproject.grading.api.CourseGradeTransferBean; +import org.sakaiproject.grading.api.model.Gradebook; +import org.sakaiproject.grading.api.model.CourseGrade; import org.sakaiproject.util.NumberUtil; import org.sakaiproject.util.api.FormattedText; @@ -174,7 +174,7 @@ public ActionResponse handleEvent(final JsonNode params, final AjaxRequestTarget return new SaveGradeErrorResponse(result); } - final CourseGrade studentCourseGrade = businessService.getCourseGrade(studentUuid); + final CourseGradeTransferBean studentCourseGrade = businessService.getCourseGrade(studentUuid); final Gradebook gradebook = businessService.getGradebook(); final CourseGradeFormatter courseGradeFormatter = new CourseGradeFormatter( gradebook, @@ -183,10 +183,10 @@ public ActionResponse handleEvent(final JsonNode params, final AjaxRequestTarget page.getUiSettings().getShowPoints(), true, true); - final GbCourseGrade gbcg = new GbCourseGrade(studentCourseGrade); - gbcg.setDisplayString(courseGradeFormatter.format(studentCourseGrade)); - final String[] courseGradeData = GbGradebookData.getCourseGradeData(gbcg, gradebook.getSelectedGradeMapping().getGradeMap()); + studentCourseGrade.setDisplayString(courseGradeFormatter.format(studentCourseGrade)); + + final String[] courseGradeData = GbGradebookData.getCourseGradeData(studentCourseGrade, gradebook.getSelectedGradeMapping().getGradeMap()); Optional catData = categoryId == null ? Optional.empty() : businessService.getCategoryScoreForStudent(Long.valueOf(categoryId), studentUuid, true); diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/MoveAssignmentAction.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/MoveAssignmentAction.java index 8310d1208da7..3cfd634d598b 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/MoveAssignmentAction.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/actions/MoveAssignmentAction.java @@ -18,7 +18,7 @@ import com.fasterxml.jackson.databind.JsonNode; import org.apache.wicket.spring.injection.annot.SpringBean; import org.sakaiproject.gradebookng.business.GradebookNgBusinessService; -import org.sakaiproject.service.gradebook.shared.Assignment; +import org.sakaiproject.grading.api.Assignment; import java.io.Serializable; import java.util.List; diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/chart/AssignmentGradeChart.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/chart/AssignmentGradeChart.java index ce7086d4d4e3..f59af1cce4a0 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/chart/AssignmentGradeChart.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/chart/AssignmentGradeChart.java @@ -26,8 +26,8 @@ import org.sakaiproject.gradebookng.business.model.GbStudentGradeInfo; import org.sakaiproject.gradebookng.business.util.MessageHelper; import org.sakaiproject.gradebookng.tool.model.GbChartData; -import org.sakaiproject.service.gradebook.shared.Assignment; -import org.sakaiproject.service.gradebook.shared.GradingType; +import org.sakaiproject.grading.api.Assignment; +import org.sakaiproject.grading.api.GradeType; /** * Panel that renders the individual assignment grade charts @@ -58,7 +58,7 @@ protected GbChartData getData() { // so students can get grade stats addAdvisor(); - final GradingType gradingType = GradingType.valueOf(this.businessService.getGradebook().getGrade_type()); + final GradeType gradingType = this.businessService.getGradebook().getGradeType(); final Assignment assignment = this.businessService.getAssignment(this.assignmentId); final List gradeInfo = this.businessService.buildGradeMatrix(Arrays.asList(assignment)); @@ -90,7 +90,7 @@ protected GbChartData getData() { } for (final Double grade : allGrades) { - if (isExtraCredit(grade, assignment, gradingType)) { + if (getExtraCredit(grade, assignment, gradingType)) { data.add(getString("label.statistics.chart.extracredit")); continue; } @@ -134,9 +134,9 @@ private String buildRangeLabel(final int start, final int end) { * @param gradingType * @return */ - private boolean isExtraCredit(final Double grade, final Assignment assignment, final GradingType gradingType) { - return (GradingType.PERCENTAGE.equals(gradingType) && grade > 100) - || (GradingType.POINTS.equals(gradingType) && grade > assignment.getPoints()); + private boolean getExtraCredit(final Double grade, final Assignment assignment, final GradeType gradingType) { + return (GradeType.PERCENTAGE.equals(gradingType) && grade > 100) + || (GradeType.POINTS.equals(gradingType) && grade > assignment.getPoints()); } private String determineKeyForGrade(final double percentage, final int range) { @@ -154,12 +154,12 @@ private String determineKeyForGrade(final double percentage, final int range) { } } - private double getPercentage(final Double grade, final Assignment assignment, final GradingType gradingType) { - if (GradingType.PERCENTAGE.equals(gradingType)) { + private double getPercentage(final Double grade, final Assignment assignment, final GradeType gradingType) { + if (GradeType.PERCENTAGE == gradingType) { return grade; } else { return grade / assignment.getPoints() * 100; } } -} \ No newline at end of file +} diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/chart/CourseGradeChart.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/chart/CourseGradeChart.java index cfb101c5f8c7..e8aac3ec7d75 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/chart/CourseGradeChart.java +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/chart/CourseGradeChart.java @@ -22,9 +22,9 @@ import org.apache.wicket.ajax.AjaxRequestTarget; import org.sakaiproject.gradebookng.business.util.MessageHelper; import org.sakaiproject.gradebookng.tool.model.GbChartData; -import org.sakaiproject.service.gradebook.shared.CourseGrade; -import org.sakaiproject.service.gradebook.shared.GradeMappingDefinition; -import org.sakaiproject.service.gradebook.shared.GradebookInformation; +import org.sakaiproject.grading.api.CourseGradeTransferBean; +import org.sakaiproject.grading.api.GradeMappingDefinition; +import org.sakaiproject.grading.api.GradebookInformation; /** * Panel that renders the course grade chart for a site. @@ -35,9 +35,9 @@ public class CourseGradeChart extends BaseChart { private final String siteId; - private CourseGrade studentGrade; + private CourseGradeTransferBean studentGrade; - public CourseGradeChart(final String id, final String siteId, CourseGrade studentGrade) { + public CourseGradeChart(final String id, final String siteId, CourseGradeTransferBean studentGrade) { super(id); this.siteId = siteId; this.studentGrade = studentGrade; @@ -90,7 +90,7 @@ private GbChartData getData(final Map gradingSchema) { final Map schema = GradeMappingDefinition.sortGradeMapping(gradingSchema); // get the course grades and re-map. Also sorts the data so it is ready for the consumer to use - final Map courseGrades = this.businessService.getCourseGrades(this.siteId, schema); + final Map courseGrades = this.businessService.getCourseGrades(this.siteId, schema); final GbChartData data = reMap(courseGrades, gradingSchema.keySet()); return data; @@ -103,7 +103,7 @@ private GbChartData getData(final Map gradingSchema) { * @param gradingSchema the grading schema that has the order * @return */ - private GbChartData reMap(final Map courseGrades, final Set order) { + private GbChartData reMap(final Map courseGrades, final Set order) { final GbChartData data = new GbChartData(); courseGrades.forEach((k, v) -> { data.add(v.getDisplayGrade()); diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/component/GbGradeTable.html b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/component/GbGradeTable.html index 7f174009bf65..bc4125512144 100644 --- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/component/GbGradeTable.html +++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/component/GbGradeTable.html @@ -161,7 +161,7 @@