{"action":"created","comment":{"url":"https://api.github.com/repos/sakaicontrib/evaluation/pulls/comments/64956698","id":64956698,"diff_hunk":"@@ -79,223 +95,627 @@ public void setMessageLocator(MessageLocator locator) {\n         this.messageLocator = locator;\n     }\n \n+    private UserDirectoryService userDirectoryService;\n+    public void setUserDirectoryService( UserDirectoryService userDirectoryService )\n+    {\n+        this.userDirectoryService = userDirectoryService;\n+    }\n+\n     CellStyle dateCellStyle;\n     CreationHelper creationHelper;\n \n-    /*\n-     * (non-Javadoc)\n-     * \n-     * @see\n-     * org.sakaiproject.evaluation.tool.reporting.ReportExporter#buildReport(org.sakaiproject.evaluation\n-     * .model.EvalEvaluation, java.lang.String[], java.io.OutputStream)\n-     */\n-    public void buildReport(EvalEvaluation evaluation, String[] groupIds, OutputStream outputStream) {\n-        buildReport(evaluation, groupIds, null, outputStream);\n-    }\n-\t\n-    /*\n-     * (non-Javadoc)\n+    /**\n+     * Build the .xls report in the new (section based) format.\n      * \n-     * @see\n-     * org.sakaiproject.evaluation.tool.reporting.ReportExporter#buildReport(org.sakaiproject.evaluation\n-     * .model.EvalEvaluation, java.lang.String[], java.lang.String, java.io.OutputStream)\n+     * @param evaluation\n+     * @param groupIDs\n+     * @param outputStream\n      */\n-    public void buildReport(EvalEvaluation evaluation, String[] groupIds, String evaluateeId, OutputStream outputStream) {\n-    \t\n-        /*\n-         * Logic for creating this view 1) make tidl 2) get DTIs for this eval from tidl 3) use DTIs\n-         * to make the headers 4) get responseIds from tidl 5) loop over response ids 6) loop over\n-         * DTIs 7) check answersmap for an answer, if there put in cell, if missing, insert blank 8)\n-         * done\n-         */\n-\n-    \t\n-    \t//Make sure responseAggregator is using this messageLocator\n-        responseAggregator.setMessageLocator(messageLocator);\n-\n-         Boolean instructorViewAllResults = (boolean) evaluation.getInstructorViewAllResults();\n-         String currentUserId = commonLogic.getCurrentUserId();\n-         String evalOwner = evaluation.getOwner();\n-\n-\tboolean isCurrentUserAdmin = commonLogic.isUserAdmin(currentUserId);\n-\n-        // 1 Make TIDL\n-    \tTemplateItemDataList tidl = getEvalTIDL(evaluation, groupIds);\n-    \t// 2: get DTIs for this eval from tidl\n-        List<DataTemplateItem> dtiList = tidl.getFlatListOfDataTemplateItems(true);\n- \n-        Workbook wb = new XSSFWorkbook();\n+    private void buildReportSectionAware( EvalEvaluation evaluation, String[] groupIDs, OutputStream outputStream )\n+    {\n+        // Get permission to view, current user and eval owner\n+        Boolean instructorViewAllResults = evaluation.getInstructorViewAllResults();\n+        String currentUserId = commonLogic.getCurrentUserId();\n+        String evalOwner = evaluation.getOwner();\n+\n+        // Allow columns greater than 255 (EVALSYS-775)\n+        TemplateItemDataList tidl = getEvalTIDL( evaluation, groupIDs );\n+        List<DataTemplateItem> dtiList = tidl.getFlatListOfDataTemplateItems( true );\n+        Workbook wb;\n+        if( dtiList.size() < 256 )\n+        {\n+            wb = new HSSFWorkbook();\n+        }\n+        else\n+        {\n+            wb = new XSSFWorkbook();\n+        }\n         creationHelper = wb.getCreationHelper();\n-        \n-        Sheet sheet = wb.createSheet(messageLocator.getMessage(\"reporting.xls.sheetname\"));\n \n-        // Title Style\n+        // Title style\n+        Sheet courseSheet = wb.createSheet( messageLocator.getMessage( \"viewreport.xls.courseSheet.name\" ) );\n+        Sheet instructorSheet = wb.createSheet( messageLocator.getMessage( \"viewreport.xls.instructorSheet.name\" ) );\n         Font font = wb.createFont();\n-        font.setFontHeightInPoints((short) 12);\n-        font.setBoldweight(Font.BOLDWEIGHT_BOLD);\n+        font.setFontHeightInPoints( (short) 12 );\n+        font.setBoldweight( Font.BOLDWEIGHT_BOLD );\n         CellStyle mainTitleStyle = wb.createCellStyle();\n-        mainTitleStyle.setFont(font);\n+        mainTitleStyle.setFont( font );\n \n         // Bold header style\n         font = wb.createFont();\n-        font.setFontHeightInPoints((short) 10);\n-        font.setBoldweight(Font.BOLDWEIGHT_BOLD);\n+        font.setFontHeightInPoints( (short) 10 );\n+        font.setBoldweight( Font.BOLDWEIGHT_BOLD );\n         CellStyle boldHeaderStyle = wb.createCellStyle();\n-        boldHeaderStyle.setFont(font);\n+        boldHeaderStyle.setFont( font );\n \n         // Italic meta header style\n         font = wb.createFont();\n-        font.setFontHeightInPoints((short) 10);\n-        font.setItalic(true);\n+        font.setFontHeightInPoints( (short) 10 );\n+        font.setItalic( true );\n         CellStyle italicMiniHeaderStyle = wb.createCellStyle();\n-        italicMiniHeaderStyle.setFont(font);\n+        italicMiniHeaderStyle.setFont( font );\n \n         // Date meta Style\n         dateCellStyle = wb.createCellStyle();\n-        // TODO FIXME HELPME To properly\n-        // String dateCellFormat = ((SimpleDateFormat)DateFormat.getDateInstance(DateFormat.MEDIUM,\n-        // localeGetter.get())).toLocalizedPattern();\n-        // http://poi.apache.org/apidocs/org/apache/poi/hssf/usermodel/HSSFDataFormat.html\n-        dateCellStyle.setDataFormat((short) 0x16);\n+        dateCellStyle.setDataFormat( (short) 0x16 );\n \n         // Evaluation Title\n-        Row row1 = sheet.createRow(0);\n-        Cell cellA1 = row1.createCell((short) 0);\n-        setPlainStringCell(cellA1, evaluation.getTitle());\n-        cellA1.setCellStyle(mainTitleStyle);\n-\n-        // calculate the response rate\n-        // int responsesCount = deliveryService.countResponses(evaluation.getId(), null, true);\n-        int responsesCount = evaluationService.countResponses(null, new Long[] {evaluation.getId()}, groupIds, null);\n-        int enrollmentsCount = evaluationService.countParticipantsForEval(evaluation.getId(), groupIds);\n-\n-        Row row2 = sheet.createRow(1);\n-        Cell cellA2 = row2.createCell((short) 0);\n-        cellA2.setCellStyle(boldHeaderStyle);\n-        setPlainStringCell(cellA2, EvalUtils.makeResponseRateStringFromCounts(responsesCount,\n-                enrollmentsCount));\n-\n-        // dates\n-        setPlainStringCell(row1.createCell((short) 2), messageLocator\n-                .getMessage(\"evalsettings.start.date.header\"));\n-        setDateCell(row2.createCell((short) 2), evaluation.getStartDate());\n-        if (evaluation.getDueDate() != null) {\n-            setPlainStringCell(row1.createCell((short) 3), messageLocator\n-                    .getMessage(\"evalsettings.due.date.header\"));\n-            setDateCell(row2.createCell((short) 3), evaluation.getDueDate());\n+        int rowCounter = 0;\n+        Row courseSheetRow1 = courseSheet.createRow( rowCounter );\n+        Row instructorSheetRow1 = instructorSheet.createRow( rowCounter );\n+        Cell courseSheetCellA1 = courseSheetRow1.createCell( (short) 0 );\n+        Cell instructorSheetCellA1 = instructorSheetRow1.createCell( (short) 0 );\n+        setPlainStringCell( courseSheetCellA1, evaluation.getTitle() + \" - \" + messageLocator.getMessage( \"viewreport.xls.courseSheet.name\" ) );\n+        setPlainStringCell( instructorSheetCellA1, evaluation.getTitle() + \" - \" + messageLocator.getMessage( \"viewreport.xls.instructorSheet.name\" ) );\n+        courseSheetCellA1.setCellStyle( mainTitleStyle );\n+        instructorSheetCellA1.setCellStyle( mainTitleStyle );\n+\n+        // Calculate the response rate\n+        rowCounter++;\n+        int responsesCount = evaluationService.countResponses( null, new Long[] { evaluation.getId() }, groupIDs, null );\n+        int enrollmentsCount = evaluationService.countParticipantsForEval( evaluation.getId(), groupIDs );\n+        Row courseSheetRow2 = courseSheet.createRow( rowCounter );\n+        Row instructorSheetRow2 = instructorSheet.createRow( rowCounter );\n+        Cell courseSheetCellA2 = courseSheetRow2.createCell( (short) 0 );\n+        Cell instructorSheetCellA2 = instructorSheetRow2.createCell( (short) 0 );\n+        courseSheetCellA2.setCellStyle( boldHeaderStyle );\n+        instructorSheetCellA2.setCellStyle( boldHeaderStyle );\n+        setPlainStringCell( courseSheetCellA2, EvalUtils.makeResponseRateStringFromCounts( responsesCount, enrollmentsCount ) );\n+        setPlainStringCell( instructorSheetCellA2, EvalUtils.makeResponseRateStringFromCounts( responsesCount, enrollmentsCount ) );\n+\n+        // Dates\n+        setPlainStringCell( courseSheetRow1.createCell( (short) 2 ), messageLocator.getMessage( \"evalsettings.start.date.header\" ) );\n+        setPlainStringCell( instructorSheetRow1.createCell( (short)  2 ), messageLocator.getMessage( \"evalsettings.start.date.header\" ) );\n+        setDateCell( courseSheetRow2.createCell( (short) 2 ), evaluation.getStartDate() );\n+        setDateCell( instructorSheetRow2.createCell( (short)  2 ), evaluation.getStartDate() );\n+        if( evaluation.getDueDate() != null )\n+        {\n+            setPlainStringCell( courseSheetRow1.createCell( (short) 3 ), messageLocator.getMessage( \"evalsettings.due.date.header\" ) );\n+            setPlainStringCell( instructorSheetRow1.createCell( (short) 3), messageLocator.getMessage( \"evalsettings.due.date.header\" ) );\n+            setDateCell( courseSheetRow2.createCell( (short) 3 ), evaluation.getDueDate() );\n+            setDateCell( instructorSheetRow2.createCell( (short) 3), evaluation.getDueDate() );\n+        }\n+\n+        // List of groups\n+        if( groupIDs.length > 0 )\n+        {\n+            rowCounter++;\n+            Row courseSheetRow3 = courseSheet.createRow( rowCounter );\n+            Row instructorSheetRow3 = instructorSheet.createRow( rowCounter );\n+            Cell courseSheetCellA3 = courseSheetRow3.createCell( (short) 0 );\n+            Cell instructorSheetCellA3 = instructorSheetRow3.createCell( (short) 0 );\n+\n+            // Get the section/site titles\n+            setPlainStringCell( courseSheetCellA3, messageLocator.getMessage( \"reporting.xls.participants\",\n+                    new Object[] { responseAggregator.getCommaSeparatedGroupNames( groupIDs ) } ) );\n+            setPlainStringCell( instructorSheetCellA3, messageLocator.getMessage( \"reporting.xls.participants\",\n+                    new Object[] { responseAggregator.getCommaSeparatedGroupNames( groupIDs ) } ) );\n         }\n \n-        // add in list of groups\n-        if (groupIds.length > 0) {\n-            Row row3 = sheet.createRow(2);\n-            Cell cellA3 = row3.createCell((short) 0);\n-            setPlainStringCell(cellA3, messageLocator.getMessage(\"reporting.xls.participants\",\n-                    new Object[] { responseAggregator.getCommaSeparatedGroupNames(groupIds) }));\n+        // Column headers (static)\n+        rowCounter += 2;\n+        short courseSheetHeaderCount = 1;\n+        short instructorSheetHeaderCount = 1;\n+        Row courseSheetHeaderRow = courseSheet.createRow( rowCounter );\n+        Row instructorSheetHeaderRow = instructorSheet.createRow( rowCounter );\n+        Cell courseSheetSectionHeaderCell = courseSheetHeaderRow.createCell( courseSheetHeaderCount++ );\n+        Cell instructorSheetSectionHeaderCell = instructorSheetHeaderRow.createCell( instructorSheetHeaderCount++ );\n+        if( evaluation.getSectionAwareness() )\n+        {\n+            courseSheetSectionHeaderCell.setCellValue( messageLocator.getMessage( \"viewreport.section.header\" ) );\n+            instructorSheetSectionHeaderCell.setCellValue( messageLocator.getMessage( \"viewreport.section.header\" ) );\n+        }\n+        else\n+        {\n+            courseSheetSectionHeaderCell.setCellValue( messageLocator.getMessage( \"viewreport.site.header\" ) );\n+            instructorSheetSectionHeaderCell.setCellValue( messageLocator.getMessage( \"viewreport.site.header\" ) );\n         }\n+        courseSheetSectionHeaderCell.setCellStyle( boldHeaderStyle );\n+        instructorSheetSectionHeaderCell.setCellStyle( boldHeaderStyle );\n+        Cell courseSheetResponseIdHeaderCell = courseSheetHeaderRow.createCell( courseSheetHeaderCount++ );\n+        Cell instructorSheetResponseIdHeaderCell = instructorSheetHeaderRow.createCell( instructorSheetHeaderCount++ );\n+        courseSheetResponseIdHeaderCell.setCellValue( messageLocator.getMessage( \"viewreport.responseID.header\" ) );\n+        instructorSheetResponseIdHeaderCell.setCellValue( messageLocator.getMessage( \"viewreport.responseID.header\" ) );\n+        courseSheetResponseIdHeaderCell.setCellStyle( boldHeaderStyle );\n+        instructorSheetResponseIdHeaderCell.setCellStyle( boldHeaderStyle );\n+        Cell instructorSheetInstructorIdHeaderCell = instructorSheetHeaderRow.createCell( instructorSheetHeaderCount++ );\n+        instructorSheetInstructorIdHeaderCell.setCellValue( messageLocator.getMessage( \"viewreport.instructorID.header\" ) );\n+        instructorSheetInstructorIdHeaderCell.setCellStyle( boldHeaderStyle );\n+        Cell instructorSheetFirstNameHeaderCell = instructorSheetHeaderRow.createCell( instructorSheetHeaderCount++ );\n+        instructorSheetFirstNameHeaderCell.setCellValue( messageLocator.getMessage( \"viewreport.firstName.header\" ) );\n+        instructorSheetFirstNameHeaderCell.setCellStyle( boldHeaderStyle );\n+        Cell instructorSheetLastNameHeaderCell = instructorSheetHeaderRow.createCell( instructorSheetHeaderCount++ );\n+        instructorSheetLastNameHeaderCell.setCellValue( messageLocator.getMessage( \"viewreport.lastName.header\" ) );\n+        instructorSheetLastNameHeaderCell.setCellStyle( boldHeaderStyle );\n+\n+        // Generate dynamic question headers\n+        List<String> instructorRelatedQuestions = new ArrayList<String>();\n+        for( DataTemplateItem dti : dtiList )\n+        {\n+            if( !instructorViewAllResults                                                       // If the eval is so configured,\n+                  && !commonLogic.isUserAdmin( currentUserId )                                  // and currentUser is not an admin\n+                  && !currentUserId.equals( evalOwner )                                         // and currentUser is not the eval creator\n+                  && !EvalConstants.ITEM_CATEGORY_COURSE.equals( dti.associateType )            // and the associate type is not 'course'\n+                  && !currentUserId.equals( commonLogic.getEvalUserById( dti.associateId ).userId ) )\n+            {\n+                continue;                                                                       // skip items that aren't for the current user\n+            }\n \n-        // 3 use DTIs to make the headers\n-        Row questionCatRow = sheet.createRow(QUESTION_CAT_ROW);\n-        Row questionTypeRow = sheet.createRow(QUESTION_TYPE_ROW);\n-        Row questionTextRow = sheet.createRow(QUESTION_TEXT_ROW);\n-        short headerCount = 1;\n-        for (DataTemplateItem dti : dtiList) {\n-            \n-            if (!instructorViewAllResults // If the eval is so configured,\n-              && !isCurrentUserAdmin // and currentUser is not an admin\n-              && !currentUserId.equals(evalOwner) // and currentUser is not the eval creator\n-              && !EvalConstants.ITEM_CATEGORY_COURSE.equals(dti.associateType) \n-              && !currentUserId.equals(commonLogic.getEvalUserById(dti.associateId).userId) ) {\n-                // skip items that aren't for the current user\n+            // If there's already a header for a specific instructor question, don't list it twice\n+            String questionText = commonLogic.makePlainTextFromHTML( dti.templateItem.getItem().getItemText() );\n+            if( instructorRelatedQuestions.contains( questionText ) )\n+            {\n                 continue;\n             }\n-            \n-            Cell cell = questionTypeRow.createCell(headerCount);\n-\n-            setPlainStringCell(cell, responseAggregator.getHeaderLabelForItemType(dti\n-                    .getTemplateItemType()));\n-            cell.setCellStyle(italicMiniHeaderStyle);\n-\n-            Cell questionText = questionTextRow.createCell(headerCount);\n-            setPlainStringCell(questionText, commonLogic.makePlainTextFromHTML(dti.templateItem\n-                    .getItem().getItemText()));\n-\n-            Cell questionCat = questionCatRow.createCell(headerCount);\n-            if (EvalConstants.ITEM_CATEGORY_INSTRUCTOR.equals(dti.associateType)) {\n-                EvalUser user = commonLogic.getEvalUserById( dti.associateId );\n-                String instructorMsg = messageLocator.getMessage(\"reporting.spreadsheet.instructor\", \n-                        new Object[] {user.displayName});\n-                setPlainStringCell(questionCat, instructorMsg );\n-            } else if (EvalConstants.ITEM_CATEGORY_ASSISTANT.equals(dti.associateType)) {\n-                EvalUser user = commonLogic.getEvalUserById( dti.associateId );\n-                String assistantMsg = messageLocator.getMessage(\"reporting.spreadsheet.ta\", \n-                        new Object[] {user.displayName});\n-                setPlainStringCell(questionCat, assistantMsg );\n-            } else if (EvalConstants.ITEM_CATEGORY_COURSE.equals(dti.associateType)) {\n-                setPlainStringCell(questionCat, messageLocator\n-                        .getMessage(\"reporting.spreadsheet.course\"));\n-            } else {\n-                setPlainStringCell(questionCat, messageLocator.getMessage(\"unknown.caps\"));\n+\n+            // Add the header to the appropriate worksheet\n+            Cell questionTextHeaderCell;\n+            if( EvalConstants.ITEM_CATEGORY_ASSISTANT.equals( dti.associateType ) || EvalConstants.ITEM_CATEGORY_INSTRUCTOR.equals( dti.associateType ) )\n+            {\n+                instructorRelatedQuestions.add( questionText );\n+                questionTextHeaderCell = instructorSheetHeaderRow.createCell( instructorSheetHeaderCount++ );\n+                questionTextHeaderCell.setCellStyle( boldHeaderStyle );\n+                if( dti.usesComments() )\n+                {\n+                    setPlainStringCell( instructorSheetHeaderRow.createCell( instructorSheetHeaderCount++ ),\n+                            messageLocator.getMessage( \"viewreport.comments.header\" ) ).setCellStyle( italicMiniHeaderStyle );\n+                }\n             }\n+            else\n+            {\n+                questionTextHeaderCell = courseSheetHeaderRow.createCell( courseSheetHeaderCount++ );\n+                questionTextHeaderCell.setCellStyle( boldHeaderStyle );\n+                if( dti.usesComments() )\n+                {\n+                    setPlainStringCell( courseSheetHeaderRow.createCell( courseSheetHeaderCount++ ),\n+                            messageLocator.getMessage( \"viewreport.comments.header\" ) ).setCellStyle( italicMiniHeaderStyle );\n+                }\n+            }\n+            setPlainStringCell( questionTextHeaderCell, questionText );\n+        }\n \n-            headerCount++;\n+        // Parse out the instructor and course related responeses into separate structures\n+        List<Long> responseIDs = tidl.getResponseIdsForAnswers();\n+        List<List<String>> courseRelatedResponses = new ArrayList<List<String>>();\n+        Map<Long, Map<User, List<EvalAnswer>>> answerMap = new HashMap<Long, Map<User, List<EvalAnswer>>>();\n+        for( Long responseID : responseIDs )\n+        {\n+            // Dump the (course related) data into a list of strings representing a spreadsheet row (so it can be sorted)\n+            List<String> row = new ArrayList<String>();\n+            row.add( SECTION_OR_SITE_COLUMN_NUM, responseAggregator.getCommaSeparatedGroupNames(\n+                    new String[] { tidl.getAnswersByResponseId( responseID ).get( 0 ).getResponse().getEvalGroupId() } ) );\n+            row.add( RESPONSE_ID_COLUMN_NUM, responseID.toString() );\n+\n+            // Add the response ID to the answer map\n+            answerMap.put( responseID, new HashMap<User, List<EvalAnswer>>() );\n+\n+            // Loop through the data template items...\n+            int questionCounter = 0;\n+            for( DataTemplateItem dti : dtiList )\n+            {\n+                if( !instructorViewAllResults                                                       // If the eval is so configured,\n+                      && !commonLogic.isUserAdmin( currentUserId )                                  // and currentUser is not an admin\n+                      && !currentUserId.equals( evalOwner )                                         // and currentUser is not the eval creator\n+                      && !EvalConstants.ITEM_CATEGORY_COURSE.equals( dti.associateType )            // and the associate type is not 'course'\n+                      && !currentUserId.equals( commonLogic.getEvalUserById( dti.associateId ).userId ) )\n+                {\n+                    continue;                                                                       // skip items that aren't for the current user\n+                }\n+\n+                // If it's an instructor related item... \n+                EvalAnswer answer = dti.getAnswer( responseID );\n+                if( EvalConstants.ITEM_CATEGORY_ASSISTANT.equals( dti.associateType ) || EvalConstants.ITEM_CATEGORY_INSTRUCTOR.equals( dti.associateType ) )\n+                {\n+                    // If the answer is NOT null (it would be null for an instructor from a different section than the evaluator)\n+                    if( answer != null )\n+                    {\n+                        // Get the instructor\n+                        User instructor = null;\n+                        try { instructor = userDirectoryService.getUser( answer.getAssociatedId() ); }\n+                        catch( UserNotDefinedException ex ) { continue; }\n+\n+                        // If the answer map has a list of answers for this response and this instructor, add the answer to the list\n+                        if( answerMap.get( responseID ).containsKey( instructor ) )\n+                        {\n+                            answerMap.get( responseID ).get( instructor ).add( answer );","path":"sakai-evaluation-tool/src/java/org/sakaiproject/evaluation/tool/reporting/XLSReportExporter.java","position":null,"original_position":418,"commit_id":"105fe8743a93ca310e608c53b2e6d01b3c6f3a9f","original_commit_id":"6fe520478d4d5dc438c2e9b45da097591993b068","user":{"login":"bbailla2","id":10620229,"avatar_url":"https://avatars.githubusercontent.com/u/10620229?v=3","gravatar_id":"","url":"https://api.github.com/users/bbailla2","html_url":"https://github.com/bbailla2","followers_url":"https://api.github.com/users/bbailla2/followers","following_url":"https://api.github.com/users/bbailla2/following{/other_user}","gists_url":"https://api.github.com/users/bbailla2/gists{/gist_id}","starred_url":"https://api.github.com/users/bbailla2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bbailla2/subscriptions","organizations_url":"https://api.github.com/users/bbailla2/orgs","repos_url":"https://api.github.com/users/bbailla2/repos","events_url":"https://api.github.com/users/bbailla2/events{/privacy}","received_events_url":"https://api.github.com/users/bbailla2/received_events","type":"User","site_admin":false},"body":"Should be safe, see line 310","created_at":"2016-05-27T19:36:02Z","updated_at":"2016-05-27T19:36:02Z","html_url":"https://github.com/sakaicontrib/evaluation/pull/38#discussion_r64956698","pull_request_url":"https://api.github.com/repos/sakaicontrib/evaluation/pulls/38","_links":{"self":{"href":"https://api.github.com/repos/sakaicontrib/evaluation/pulls/comments/64956698"},"html":{"href":"https://github.com/sakaicontrib/evaluation/pull/38#discussion_r64956698"},"pull_request":{"href":"https://api.github.com/repos/sakaicontrib/evaluation/pulls/38"}}},"pull_request":{"url":"https://api.github.com/repos/sakaicontrib/evaluation/pulls/38","id":71603627,"html_url":"https://github.com/sakaicontrib/evaluation/pull/38","diff_url":"https://github.com/sakaicontrib/evaluation/pull/38.diff","patch_url":"https://github.com/sakaicontrib/evaluation/pull/38.patch","issue_url":"https://api.github.com/repos/sakaicontrib/evaluation/issues/38","number":38,"state":"open","locked":false,"title":"EVALSYS-1472 Support for roster/section based delivery","user":{"login":"bjones86","id":10403943,"avatar_url":"https://avatars.githubusercontent.com/u/10403943?v=3","gravatar_id":"","url":"https://api.github.com/users/bjones86","html_url":"https://github.com/bjones86","followers_url":"https://api.github.com/users/bjones86/followers","following_url":"https://api.github.com/users/bjones86/following{/other_user}","gists_url":"https://api.github.com/users/bjones86/gists{/gist_id}","starred_url":"https://api.github.com/users/bjones86/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bjones86/subscriptions","organizations_url":"https://api.github.com/users/bjones86/orgs","repos_url":"https://api.github.com/users/bjones86/repos","events_url":"https://api.github.com/users/bjones86/events{/privacy}","received_events_url":"https://api.github.com/users/bjones86/received_events","type":"User","site_admin":false},"body":"https://jira.sakaiproject.org/browse/EVALSYS-1472\r\n\r\nI'm currently waiting on an Oracle instance to test against (should be getting it soon), but I figured I should submit this early so that anyone interested in reviewing can do so at their earliest convenience. Screen shots of the various new and modified features are included in the JIRA ticket.\r\n\r\nI would recommend reviewing only the first commit (cefd388), as it contains all functional changes. The second commit is purely code clean-up; statics and finals, unused imports, unnecessary boxing/unboxing, Java 7/8 syntax, etc.\r\n\r\nIf anyone is of the opinion that the second commit is too much, unwieldy, etc., I can simply drop the commit.","created_at":"2016-05-26T18:39:57Z","updated_at":"2016-05-27T19:36:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":"20fa46620dca3f0f85bda09f66409eccb7689b9c","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/sakaicontrib/evaluation/pulls/38/commits","review_comments_url":"https://api.github.com/repos/sakaicontrib/evaluation/pulls/38/comments","review_comment_url":"https://api.github.com/repos/sakaicontrib/evaluation/pulls/comments{/number}","comments_url":"https://api.github.com/repos/sakaicontrib/evaluation/issues/38/comments","statuses_url":"https://api.github.com/repos/sakaicontrib/evaluation/statuses/105fe8743a93ca310e608c53b2e6d01b3c6f3a9f","head":{"label":"bjones86:EVALSYS-1472","ref":"EVALSYS-1472","sha":"105fe8743a93ca310e608c53b2e6d01b3c6f3a9f","user":{"login":"bjones86","id":10403943,"avatar_url":"https://avatars.githubusercontent.com/u/10403943?v=3","gravatar_id":"","url":"https://api.github.com/users/bjones86","html_url":"https://github.com/bjones86","followers_url":"https://api.github.com/users/bjones86/followers","following_url":"https://api.github.com/users/bjones86/following{/other_user}","gists_url":"https://api.github.com/users/bjones86/gists{/gist_id}","starred_url":"https://api.github.com/users/bjones86/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bjones86/subscriptions","organizations_url":"https://api.github.com/users/bjones86/orgs","repos_url":"https://api.github.com/users/bjones86/repos","events_url":"https://api.github.com/users/bjones86/events{/privacy}","received_events_url":"https://api.github.com/users/bjones86/received_events","type":"User","site_admin":false},"repo":{"id":51532659,"name":"evaluation","full_name":"bjones86/evaluation","owner":{"login":"bjones86","id":10403943,"avatar_url":"https://avatars.githubusercontent.com/u/10403943?v=3","gravatar_id":"","url":"https://api.github.com/users/bjones86","html_url":"https://github.com/bjones86","followers_url":"https://api.github.com/users/bjones86/followers","following_url":"https://api.github.com/users/bjones86/following{/other_user}","gists_url":"https://api.github.com/users/bjones86/gists{/gist_id}","starred_url":"https://api.github.com/users/bjones86/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bjones86/subscriptions","organizations_url":"https://api.github.com/users/bjones86/orgs","repos_url":"https://api.github.com/users/bjones86/repos","events_url":"https://api.github.com/users/bjones86/events{/privacy}","received_events_url":"https://api.github.com/users/bjones86/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bjones86/evaluation","description":"The Evaluation System is designed specifically for conducting formative and summative (end of term) evaluations of your course sites. However, it can also be used for more general surveys. An evaluation consists of a series of scaled (i.e. rating) questions, multiple choice with single answer questions, multiple choice with multiple answer questions, and/or free text questions, which are delivered to site members over a fixed time period (typically a few days).","fork":true,"url":"https://api.github.com/repos/bjones86/evaluation","forks_url":"https://api.github.com/repos/bjones86/evaluation/forks","keys_url":"https://api.github.com/repos/bjones86/evaluation/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bjones86/evaluation/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bjones86/evaluation/teams","hooks_url":"https://api.github.com/repos/bjones86/evaluation/hooks","issue_events_url":"https://api.github.com/repos/bjones86/evaluation/issues/events{/number}","events_url":"https://api.github.com/repos/bjones86/evaluation/events","assignees_url":"https://api.github.com/repos/bjones86/evaluation/assignees{/user}","branches_url":"https://api.github.com/repos/bjones86/evaluation/branches{/branch}","tags_url":"https://api.github.com/repos/bjones86/evaluation/tags","blobs_url":"https://api.github.com/repos/bjones86/evaluation/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bjones86/evaluation/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bjones86/evaluation/git/refs{/sha}","trees_url":"https://api.github.com/repos/bjones86/evaluation/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bjones86/evaluation/statuses/{sha}","languages_url":"https://api.github.com/repos/bjones86/evaluation/languages","stargazers_url":"https://api.github.com/repos/bjones86/evaluation/stargazers","contributors_url":"https://api.github.com/repos/bjones86/evaluation/contributors","subscribers_url":"https://api.github.com/repos/bjones86/evaluation/subscribers","subscription_url":"https://api.github.com/repos/bjones86/evaluation/subscription","commits_url":"https://api.github.com/repos/bjones86/evaluation/commits{/sha}","git_commits_url":"https://api.github.com/repos/bjones86/evaluation/git/commits{/sha}","comments_url":"https://api.github.com/repos/bjones86/evaluation/comments{/number}","issue_comment_url":"https://api.github.com/repos/bjones86/evaluation/issues/comments{/number}","contents_url":"https://api.github.com/repos/bjones86/evaluation/contents/{+path}","compare_url":"https://api.github.com/repos/bjones86/evaluation/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bjones86/evaluation/merges","archive_url":"https://api.github.com/repos/bjones86/evaluation/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bjones86/evaluation/downloads","issues_url":"https://api.github.com/repos/bjones86/evaluation/issues{/number}","pulls_url":"https://api.github.com/repos/bjones86/evaluation/pulls{/number}","milestones_url":"https://api.github.com/repos/bjones86/evaluation/milestones{/number}","notifications_url":"https://api.github.com/repos/bjones86/evaluation/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bjones86/evaluation/labels{/name}","releases_url":"https://api.github.com/repos/bjones86/evaluation/releases{/id}","deployments_url":"https://api.github.com/repos/bjones86/evaluation/deployments","created_at":"2016-02-11T17:33:40Z","updated_at":"2016-04-11T14:41:14Z","pushed_at":"2016-05-27T19:03:46Z","git_url":"git://github.com/bjones86/evaluation.git","ssh_url":"git@github.com:bjones86/evaluation.git","clone_url":"https://github.com/bjones86/evaluation.git","svn_url":"https://github.com/bjones86/evaluation","homepage":null,"size":9916,"stargazers_count":1,"watchers_count":1,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"sakaicontrib:master","ref":"master","sha":"d9dbdb0060d588837b4e86b593baf4023f568a30","user":{"login":"sakaicontrib","id":16902267,"avatar_url":"https://avatars.githubusercontent.com/u/16902267?v=3","gravatar_id":"","url":"https://api.github.com/users/sakaicontrib","html_url":"https://github.com/sakaicontrib","followers_url":"https://api.github.com/users/sakaicontrib/followers","following_url":"https://api.github.com/users/sakaicontrib/following{/other_user}","gists_url":"https://api.github.com/users/sakaicontrib/gists{/gist_id}","starred_url":"https://api.github.com/users/sakaicontrib/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sakaicontrib/subscriptions","organizations_url":"https://api.github.com/users/sakaicontrib/orgs","repos_url":"https://api.github.com/users/sakaicontrib/repos","events_url":"https://api.github.com/users/sakaicontrib/events{/privacy}","received_events_url":"https://api.github.com/users/sakaicontrib/received_events","type":"Organization","site_admin":false},"repo":{"id":30932453,"name":"evaluation","full_name":"sakaicontrib/evaluation","owner":{"login":"sakaicontrib","id":16902267,"avatar_url":"https://avatars.githubusercontent.com/u/16902267?v=3","gravatar_id":"","url":"https://api.github.com/users/sakaicontrib","html_url":"https://github.com/sakaicontrib","followers_url":"https://api.github.com/users/sakaicontrib/followers","following_url":"https://api.github.com/users/sakaicontrib/following{/other_user}","gists_url":"https://api.github.com/users/sakaicontrib/gists{/gist_id}","starred_url":"https://api.github.com/users/sakaicontrib/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sakaicontrib/subscriptions","organizations_url":"https://api.github.com/users/sakaicontrib/orgs","repos_url":"https://api.github.com/users/sakaicontrib/repos","events_url":"https://api.github.com/users/sakaicontrib/events{/privacy}","received_events_url":"https://api.github.com/users/sakaicontrib/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/sakaicontrib/evaluation","description":"The Evaluation System is designed specifically for conducting formative and summative (end of term) evaluations of your course sites. However, it can also be used for more general surveys. An evaluation consists of a series of scaled (i.e. rating) questions, multiple choice with single answer questions, multiple choice with multiple answer questions, and/or free text questions, which are delivered to site members over a fixed time period (typically a few days).","fork":false,"url":"https://api.github.com/repos/sakaicontrib/evaluation","forks_url":"https://api.github.com/repos/sakaicontrib/evaluation/forks","keys_url":"https://api.github.com/repos/sakaicontrib/evaluation/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sakaicontrib/evaluation/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sakaicontrib/evaluation/teams","hooks_url":"https://api.github.com/repos/sakaicontrib/evaluation/hooks","issue_events_url":"https://api.github.com/repos/sakaicontrib/evaluation/issues/events{/number}","events_url":"https://api.github.com/repos/sakaicontrib/evaluation/events","assignees_url":"https://api.github.com/repos/sakaicontrib/evaluation/assignees{/user}","branches_url":"https://api.github.com/repos/sakaicontrib/evaluation/branches{/branch}","tags_url":"https://api.github.com/repos/sakaicontrib/evaluation/tags","blobs_url":"https://api.github.com/repos/sakaicontrib/evaluation/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sakaicontrib/evaluation/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sakaicontrib/evaluation/git/refs{/sha}","trees_url":"https://api.github.com/repos/sakaicontrib/evaluation/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sakaicontrib/evaluation/statuses/{sha}","languages_url":"https://api.github.com/repos/sakaicontrib/evaluation/languages","stargazers_url":"https://api.github.com/repos/sakaicontrib/evaluation/stargazers","contributors_url":"https://api.github.com/repos/sakaicontrib/evaluation/contributors","subscribers_url":"https://api.github.com/repos/sakaicontrib/evaluation/subscribers","subscription_url":"https://api.github.com/repos/sakaicontrib/evaluation/subscription","commits_url":"https://api.github.com/repos/sakaicontrib/evaluation/commits{/sha}","git_commits_url":"https://api.github.com/repos/sakaicontrib/evaluation/git/commits{/sha}","comments_url":"https://api.github.com/repos/sakaicontrib/evaluation/comments{/number}","issue_comment_url":"https://api.github.com/repos/sakaicontrib/evaluation/issues/comments{/number}","contents_url":"https://api.github.com/repos/sakaicontrib/evaluation/contents/{+path}","compare_url":"https://api.github.com/repos/sakaicontrib/evaluation/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sakaicontrib/evaluation/merges","archive_url":"https://api.github.com/repos/sakaicontrib/evaluation/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sakaicontrib/evaluation/downloads","issues_url":"https://api.github.com/repos/sakaicontrib/evaluation/issues{/number}","pulls_url":"https://api.github.com/repos/sakaicontrib/evaluation/pulls{/number}","milestones_url":"https://api.github.com/repos/sakaicontrib/evaluation/milestones{/number}","notifications_url":"https://api.github.com/repos/sakaicontrib/evaluation/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sakaicontrib/evaluation/labels{/name}","releases_url":"https://api.github.com/repos/sakaicontrib/evaluation/releases{/id}","deployments_url":"https://api.github.com/repos/sakaicontrib/evaluation/deployments","created_at":"2015-02-17T19:04:48Z","updated_at":"2016-02-11T17:24:04Z","pushed_at":"2016-05-27T19:03:47Z","git_url":"git://github.com/sakaicontrib/evaluation.git","ssh_url":"git@github.com:sakaicontrib/evaluation.git","clone_url":"https://github.com/sakaicontrib/evaluation.git","svn_url":"https://github.com/sakaicontrib/evaluation","homepage":null,"size":11614,"stargazers_count":3,"watchers_count":3,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":15,"mirror_url":null,"open_issues_count":2,"forks":15,"open_issues":2,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/sakaicontrib/evaluation/pulls/38"},"html":{"href":"https://github.com/sakaicontrib/evaluation/pull/38"},"issue":{"href":"https://api.github.com/repos/sakaicontrib/evaluation/issues/38"},"comments":{"href":"https://api.github.com/repos/sakaicontrib/evaluation/issues/38/comments"},"review_comments":{"href":"https://api.github.com/repos/sakaicontrib/evaluation/pulls/38/comments"},"review_comment":{"href":"https://api.github.com/repos/sakaicontrib/evaluation/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/sakaicontrib/evaluation/pulls/38/commits"},"statuses":{"href":"https://api.github.com/repos/sakaicontrib/evaluation/statuses/105fe8743a93ca310e608c53b2e6d01b3c6f3a9f"}}}}
{"actor":{"display_login":"bbailla2"}}