|
@@ -0,0 +1,1605 @@
|
|
|
+package com.qmth.teachcloud.report.business.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
+import com.qmth.teachcloud.common.contant.SystemConstant;
|
|
|
+import com.qmth.teachcloud.common.entity.SysOrg;
|
|
|
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
|
|
|
+import com.qmth.teachcloud.common.service.BasicCourseService;
|
|
|
+import com.qmth.teachcloud.common.service.SysOrgService;
|
|
|
+import com.qmth.teachcloud.common.service.SysUserService;
|
|
|
+import com.qmth.teachcloud.report.business.bean.dto.query.BasicAnswerDto;
|
|
|
+import com.qmth.teachcloud.report.business.bean.dto.query.ValidAnswerDetailDto;
|
|
|
+import com.qmth.teachcloud.report.business.entity.*;
|
|
|
+import com.qmth.teachcloud.report.business.enums.LevelRuleEnum;
|
|
|
+import com.qmth.teachcloud.report.business.enums.QuantileEnum;
|
|
|
+import com.qmth.teachcloud.report.business.enums.ValidityEnum;
|
|
|
+import com.qmth.teachcloud.report.business.service.*;
|
|
|
+import com.qmth.teachcloud.report.business.utils.AnalyzeScopeUtil;
|
|
|
+import com.qmth.teachcloud.report.business.utils.ConversionUtils;
|
|
|
+import com.qmth.teachcloud.report.business.utils.MathUtil;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.lang.reflect.Field;
|
|
|
+import java.lang.reflect.InvocationTargetException;
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @Description: 学生看板数据计算
|
|
|
+ * @Author: CaoZixuan
|
|
|
+ * @Date: 2021-06-06
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class AnalyzeForStudentServiceImpl implements AnalyzeForStudentService {
|
|
|
+ @Resource
|
|
|
+ private TAExamCourseRecordService taExamCourseRecordService;
|
|
|
+ @Resource
|
|
|
+ private TBExamCourseService tbExamCourseService;
|
|
|
+ @Resource
|
|
|
+ private TBCommonLevelConfigService tbCommonLevelConfigService;
|
|
|
+ @Resource
|
|
|
+ private TBCommonRankLevelConfigService tbCommonRankLevelConfigService;
|
|
|
+ @Resource
|
|
|
+ private BasicCourseService basicCourseService;
|
|
|
+ @Resource
|
|
|
+ private TAExamCourseCollegeInspectService taExamCourseCollegeInspectService;
|
|
|
+ @Resource
|
|
|
+ private TBPaperService tbPaperService;
|
|
|
+ @Resource
|
|
|
+ private TBExamStudentService tbExamStudentService;
|
|
|
+ @Resource
|
|
|
+ private SysOrgService sysOrgService;
|
|
|
+ @Resource
|
|
|
+ private TBExamService tbExamService;
|
|
|
+ @Resource
|
|
|
+ private TAExamCourseService taExamCourseService;
|
|
|
+ @Resource
|
|
|
+ private TAExamCourseClazzService taExamCourseClazzService;
|
|
|
+ @Resource
|
|
|
+ private TBDimensionService tbDimensionService;
|
|
|
+ @Resource
|
|
|
+ private TAExamCourseDioService taExamCourseDioService;
|
|
|
+ @Resource
|
|
|
+ private TAExamCourseRecordDioService taExamCourseRecordDioService;
|
|
|
+ @Resource
|
|
|
+ private TAExamCourseRecordModService taExamCourseRecordModService;
|
|
|
+ @Resource
|
|
|
+ private TBPaperStructService tbPaperStructService;
|
|
|
+ @Resource
|
|
|
+ private TBAnswerService tbAnswerService;
|
|
|
+ @Resource
|
|
|
+ private TBModuleConfigService tbModuleConfigService;
|
|
|
+ @Resource
|
|
|
+ private TBModuleProficiencyService tbModuleProficiencyService;
|
|
|
+ @Resource
|
|
|
+ private TAExamCourseCollegeInspectDioService taExamCourseCollegeInspectDioService;
|
|
|
+ @Resource
|
|
|
+ private TAPaperStructService taPaperStructService;
|
|
|
+ @Resource
|
|
|
+ private TAExamTotalService taExamTotalService;
|
|
|
+ @Resource
|
|
|
+ private TAExamCourseCollegeTeacherService taExamCourseCollegeTeacherService;
|
|
|
+ @Resource
|
|
|
+ private TAExamCourseTeacherService taExamCourseTeacherService;
|
|
|
+ @Resource
|
|
|
+ private SysUserService sysUserService;
|
|
|
+
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @Override
|
|
|
+ public String buildAnalyzeExamCourse(Long examId, String courseCode) {
|
|
|
+ List<String> effectiveCourseCodeList = tbExamCourseService.findEffectiveByExamId(examId,courseCode);
|
|
|
+ // 考试下考生作答数据源
|
|
|
+ List<TAExamCourseRecord> dataSource = taExamCourseRecordService.list(new QueryWrapper<TAExamCourseRecord>().lambda()
|
|
|
+ .eq(TAExamCourseRecord::getExamId,examId));
|
|
|
+ List<TAExamCourse> taExamCourseList = new ArrayList<>();
|
|
|
+ // 有效的课程
|
|
|
+ for (String effectiveCourseCode : effectiveCourseCodeList) {
|
|
|
+ // 原数据删除
|
|
|
+ taExamCourseService.remove(new QueryWrapper<TAExamCourse>().lambda()
|
|
|
+ .eq(TAExamCourse::getExamId,examId)
|
|
|
+ .eq(TAExamCourse::getCourseCode,effectiveCourseCode));
|
|
|
+
|
|
|
+ // TODO: 2021/6/7 查询试卷信息AB卷的准确方式(目前AB卷规则视为一致)
|
|
|
+ List<TBPaper> tbPaperList = tbPaperService.list(new QueryWrapper<TBPaper>().lambda()
|
|
|
+ .eq(TBPaper::getExamId,examId).eq(TBPaper::getCourseCode,effectiveCourseCode));
|
|
|
+ if (tbPaperList.size() < 1){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("未找到试卷基础信息");
|
|
|
+ }
|
|
|
+ TBPaper tbPaper = tbPaperList.get(0);
|
|
|
+ // 通过分
|
|
|
+ BigDecimal passScore = tbPaper.getPassScore();
|
|
|
+ // 满分
|
|
|
+ BigDecimal totalScore = tbPaper.getTotalScore();
|
|
|
+ // 赋分系数
|
|
|
+ BigDecimal coefficient = tbPaper.getCoefficient();
|
|
|
+
|
|
|
+ /*
|
|
|
+ 该课程总体学生成绩分析
|
|
|
+ */
|
|
|
+ // 该课程考生数据
|
|
|
+ List<TAExamCourseRecord> totalDatasource = dataSource.stream()
|
|
|
+ .filter(e -> effectiveCourseCode.equals(e.getCourseCode())).collect(Collectors.toList());
|
|
|
+ int totalCount = totalDatasource.size();
|
|
|
+ long realityCount = totalDatasource.stream().filter(e -> !e.getAbsent()).count();
|
|
|
+ long absentCount = totalDatasource.stream().filter(TAExamCourseRecord::getAbsent).count();
|
|
|
+ if (totalCount != realityCount + absentCount){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("考试人数数据异常");
|
|
|
+ }
|
|
|
+ // 赋分数据集合
|
|
|
+ // -------------------赋分------------------------
|
|
|
+ List<Double> totalScoreList = totalDatasource.stream().filter(e -> !e.getAbsent()).map(e -> e.getAssignedScore().doubleValue()).collect(Collectors.toList());
|
|
|
+ DoubleSummaryStatistics totalStatistics = totalScoreList.stream().collect(Collectors.summarizingDouble(e -> e));
|
|
|
+ double avgScore = totalStatistics.getAverage();
|
|
|
+ long passCount = totalScoreList.stream().filter(e -> e >= passScore.doubleValue()).count();
|
|
|
+ BigDecimal passRate = BigDecimal.valueOf(passCount).divide(BigDecimal.valueOf(realityCount),4,BigDecimal.ROUND_HALF_DOWN);
|
|
|
+
|
|
|
+ // -------------------卷面分------------------------
|
|
|
+ List<Double> paperTotalScoreList = totalDatasource.stream().filter(e -> !e.getAbsent()).map(e -> e.getTotalScore().doubleValue()).collect(Collectors.toList());
|
|
|
+ DoubleSummaryStatistics paperTotalStatistics = paperTotalScoreList.stream().collect(Collectors.summarizingDouble(e -> e));
|
|
|
+ double paperAvgScore = paperTotalStatistics.getAverage();
|
|
|
+ long paperPassCount = paperTotalScoreList.stream().filter(e -> e >= passScore.doubleValue()).count();
|
|
|
+ BigDecimal paperPassRate = BigDecimal.valueOf(paperPassCount).divide(BigDecimal.valueOf(realityCount),4,BigDecimal.ROUND_HALF_DOWN);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /*
|
|
|
+ 该课程往届生成绩分析
|
|
|
+ */
|
|
|
+ // 该课程往届数据
|
|
|
+ List<TAExamCourseRecord> pastDatasource = totalDatasource.stream()
|
|
|
+ .filter(e -> !e.getStudentCurrent()).collect(Collectors.toList());
|
|
|
+ int pastTotalCount = pastDatasource.size();
|
|
|
+ long pastRealityCount = pastDatasource.stream().filter(e -> !e.getAbsent()).count();
|
|
|
+ long pastAbsentCount = pastDatasource.stream().filter(TAExamCourseRecord::getAbsent).count();
|
|
|
+ if (pastTotalCount != pastRealityCount + pastAbsentCount){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("考试往届生人数数据异常");
|
|
|
+ }
|
|
|
+ BigDecimal pastRealityRate = BigDecimal.valueOf(pastRealityCount).divide(BigDecimal.valueOf(realityCount),4,BigDecimal.ROUND_HALF_DOWN);
|
|
|
+
|
|
|
+ /*
|
|
|
+ 该课程应届生成绩分析
|
|
|
+ */
|
|
|
+ // 该课程下应届数据
|
|
|
+ List<TAExamCourseRecord> currentDatasource = totalDatasource.stream()
|
|
|
+ .filter(TAExamCourseRecord::getStudentCurrent).collect(Collectors.toList());
|
|
|
+ // 人数统计
|
|
|
+ int currentTotalCount = currentDatasource.size();
|
|
|
+ long currentRealityCount = currentDatasource.stream().filter(e -> !e.getAbsent()).count();
|
|
|
+ long currentAbsentCount = currentDatasource.stream().filter(TAExamCourseRecord::getAbsent).count();
|
|
|
+ if (currentTotalCount != currentRealityCount + currentAbsentCount){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("考试应届生人数数据异常");
|
|
|
+ }
|
|
|
+ BigDecimal currentRealityRate = BigDecimal.valueOf(currentRealityCount).divide(BigDecimal.valueOf(realityCount),4,BigDecimal.ROUND_HALF_DOWN);
|
|
|
+
|
|
|
+ // 通过率统计
|
|
|
+ List<Double> currentScoreList = currentDatasource.stream().filter(e -> !e.getAbsent()).map(e -> e.getAssignedScore().doubleValue()).collect(Collectors.toList());
|
|
|
+ DoubleSummaryStatistics currentStatistics = currentScoreList.stream().collect(Collectors.summarizingDouble(e -> e));
|
|
|
+ long currentPassCount = currentScoreList.stream().filter(e -> e >= passScore.doubleValue()).count();
|
|
|
+ BigDecimal currentPassRate = BigDecimal.valueOf(currentPassCount).divide(BigDecimal.valueOf(currentRealityCount),4,BigDecimal.ROUND_HALF_DOWN);
|
|
|
+
|
|
|
+ // 描述统计
|
|
|
+ double currentMinScore = currentStatistics.getMin();
|
|
|
+ double currentMaxScore = currentStatistics.getMax();
|
|
|
+ double currentAvgScore = currentStatistics.getAverage();
|
|
|
+
|
|
|
+ // 计算众数
|
|
|
+ String currentMode = MathUtil.calculateMode(currentScoreList);
|
|
|
+ // 计算上四分位数
|
|
|
+ double currentUpperQuartile = MathUtil.calculateQuantile(currentScoreList, QuantileEnum.UPPER_QUARTILE.getValue());
|
|
|
+ // 计算中位数
|
|
|
+ double currentMedian = MathUtil.calculateQuantile(currentScoreList, QuantileEnum.MEDIAN.getValue());
|
|
|
+ // 计算下四分位数
|
|
|
+ double currentLowerQuartile = MathUtil.calculateQuantile(currentScoreList, QuantileEnum.LOWER_QUARTILE.getValue());
|
|
|
+
|
|
|
+ // 极端情况下分位数值处理
|
|
|
+ if (currentUpperQuartile == 0) {
|
|
|
+ currentUpperQuartile = currentMedian;
|
|
|
+ }
|
|
|
+ if (currentLowerQuartile == 0) {
|
|
|
+ currentLowerQuartile = currentMedian;
|
|
|
+ }
|
|
|
+ // 计算标准差
|
|
|
+ double currentStandardDeviation = MathUtil.calculateStandardDeviation(currentScoreList);
|
|
|
+
|
|
|
+ // TODO: 2021/6/7 难度系数使用标准分计算
|
|
|
+ BigDecimal scoreRate;
|
|
|
+ double standardAvgScore = currentDatasource.stream().collect(Collectors.summarizingDouble(e -> e.getTotalScore().doubleValue())).getAverage();
|
|
|
+ if (standardAvgScore != 0 && totalScore.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ scoreRate = BigDecimal.valueOf(standardAvgScore).divide(totalScore,4,BigDecimal.ROUND_HALF_DOWN);
|
|
|
+ } else {
|
|
|
+ scoreRate = BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+ // 难度系数保留1位数
|
|
|
+ scoreRate = scoreRate.setScale(1,BigDecimal.ROUND_HALF_DOWN); // 难度系数保留1位有效数字
|
|
|
+ String difficulty = this.handleLevel(examId,effectiveCourseCode,"难度等级", scoreRate.doubleValue());
|
|
|
+ //--------------------------应届-卷面成绩-----------------------
|
|
|
+ List<Double> paperCurrentScoreList = currentDatasource.stream().filter(e -> !e.getAbsent()).map(e -> e.getTotalScore().doubleValue()).collect(Collectors.toList());
|
|
|
+ DoubleSummaryStatistics paperCurrentStatistics = paperCurrentScoreList.stream().collect(Collectors.summarizingDouble(e -> e));
|
|
|
+ double paperCurrentAvgScore = paperCurrentStatistics.getAverage();
|
|
|
+ long paperCurrentPassCount = paperCurrentScoreList.stream().filter(e -> e >= passScore.doubleValue()).count();
|
|
|
+ BigDecimal paperCurrentPassRate = BigDecimal.valueOf(paperCurrentPassCount).divide(BigDecimal.valueOf(currentRealityCount),4,BigDecimal.ROUND_HALF_DOWN);
|
|
|
+
|
|
|
+ /*
|
|
|
+ 学院信息
|
|
|
+ */
|
|
|
+ // TODO: 2021/6/7 考察学院数量算进去往届和缺考的吗
|
|
|
+ List<TBExamStudent> tbExamStudentList = tbExamStudentService.list(new QueryWrapper<TBExamStudent>().lambda()
|
|
|
+ .eq(TBExamStudent::getExamId,examId).eq(TBExamStudent::getCourseCode,effectiveCourseCode));
|
|
|
+
|
|
|
+ List<Long> teachCollegeIdList = tbExamStudentList.stream().map(TBExamStudent::getTeachCollegeId).distinct().collect(Collectors.toList());
|
|
|
+ List<Long> inspectCollegeIdList = tbExamStudentList.stream().map(TBExamStudent::getInspectCollegeId).distinct().collect(Collectors.toList());
|
|
|
+ if (teachCollegeIdList.size() != 1){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("该课程有多个开课学院异常");
|
|
|
+ }
|
|
|
+ Long teachCollegeId = teachCollegeIdList.get(0);
|
|
|
+ SysOrg tbSchoolCollege = sysOrgService.getById(teachCollegeId);
|
|
|
+ String teachCollegeName;
|
|
|
+ if (Objects.isNull(tbSchoolCollege)){
|
|
|
+ teachCollegeName = SystemConstant.DEFAULT_SIGN;
|
|
|
+ }else {
|
|
|
+ teachCollegeName= tbSchoolCollege.getName();
|
|
|
+ }
|
|
|
+ int inspectCollegeCount = inspectCollegeIdList.size();
|
|
|
+
|
|
|
+ /*
|
|
|
+ 组装
|
|
|
+ */
|
|
|
+ TBExam tbExam = tbExamService.getById(examId);
|
|
|
+ TAExamCourse taExamCourse = new TAExamCourse();
|
|
|
+ taExamCourse.setId(SystemConstant.getDbUuid());
|
|
|
+ taExamCourse.setExamId(examId);
|
|
|
+ taExamCourse.setExamName(tbExam.getExamName());
|
|
|
+ taExamCourse.setCourseCode(effectiveCourseCode);
|
|
|
+ taExamCourse.setCourseName(basicCourseService.findByCourseCode(effectiveCourseCode).getName());
|
|
|
+ taExamCourse.setSchoolId(tbExam.getSchoolId());
|
|
|
+ taExamCourse.setTeachCollegeId(teachCollegeId);
|
|
|
+ taExamCourse.setTeachCollegeName(teachCollegeName);
|
|
|
+ taExamCourse.setInspectCollegeCount(inspectCollegeCount);
|
|
|
+ taExamCourse.setAvgScore(BigDecimal.valueOf(avgScore));
|
|
|
+ taExamCourse.setPassCount((int) passCount);
|
|
|
+ taExamCourse.setPassRate(passRate);
|
|
|
+ taExamCourse.setCurrentPassCount((int) currentPassCount);
|
|
|
+ taExamCourse.setCurrentPassRate(currentPassRate);
|
|
|
+ taExamCourse.setTotalScore(totalScore);
|
|
|
+ taExamCourse.setRealityCount((int) realityCount);
|
|
|
+ taExamCourse.setAbsentCount((int) absentCount);
|
|
|
+ taExamCourse.setTotalCount(totalCount);
|
|
|
+ taExamCourse.setCurrentMinScore(BigDecimal.valueOf(currentMinScore));
|
|
|
+ taExamCourse.setCurrentMaxScore(BigDecimal.valueOf(currentMaxScore));
|
|
|
+ taExamCourse.setCurrentAvgScore(BigDecimal.valueOf(currentAvgScore));
|
|
|
+ taExamCourse.setCurrentScoreRate(scoreRate);
|
|
|
+ taExamCourse.setCurrentRealityCount((int) currentRealityCount);
|
|
|
+ taExamCourse.setCurrentAbsentCount((int) currentAbsentCount);
|
|
|
+ taExamCourse.setCurrentTotalCount(currentTotalCount);
|
|
|
+ taExamCourse.setCurrentRealityRate(currentRealityRate);
|
|
|
+ taExamCourse.setPastRealityCount((int) pastRealityCount);
|
|
|
+ taExamCourse.setPastAbsentCount((int) pastAbsentCount);
|
|
|
+ taExamCourse.setPastTotalCount(pastTotalCount);
|
|
|
+ taExamCourse.setPastRealityRate(pastRealityRate);
|
|
|
+ taExamCourse.setCurrentUpperQuartile(BigDecimal.valueOf(currentUpperQuartile));
|
|
|
+ taExamCourse.setCurrentMedian(BigDecimal.valueOf(currentMedian));
|
|
|
+ taExamCourse.setCurrentLowerQuartile(BigDecimal.valueOf(currentLowerQuartile));
|
|
|
+ taExamCourse.setCurrentMode(currentMode);
|
|
|
+ taExamCourse.setCurrentStandardDeviation(BigDecimal.valueOf(currentStandardDeviation));
|
|
|
+ taExamCourse.setDifficulty(difficulty);
|
|
|
+ taExamCourse.setPaperAvgScore(BigDecimal.valueOf(paperAvgScore));
|
|
|
+ taExamCourse.setPaperPassRate(paperPassRate);
|
|
|
+ taExamCourse.setPaperCurrentAvgScore(BigDecimal.valueOf(paperCurrentAvgScore));
|
|
|
+ taExamCourse.setPaperCurrentPassRate(paperCurrentPassRate);
|
|
|
+ taExamCourse.setCoefficient(coefficient);
|
|
|
+ taExamCourseList.add(taExamCourse);
|
|
|
+ }
|
|
|
+ taExamCourseService.saveBatch(taExamCourseList);
|
|
|
+ return " 't_a_exam_course' 表构建完毕 " ;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @Override
|
|
|
+ public String buildAnalyzeExamCourseRecord(Long examId,String courseCode) {
|
|
|
+ // 获取本次考试所有考试记录数据源
|
|
|
+ List<TAExamCourseRecord> taExamCourseRecordDatasource = taExamCourseRecordService.list(new QueryWrapper<TAExamCourseRecord>().lambda()
|
|
|
+ .eq(TAExamCourseRecord::getExamId,examId)
|
|
|
+ .eq(TAExamCourseRecord::getAbsent,false)
|
|
|
+ .eq(TAExamCourseRecord::getStudentCurrent,true));
|
|
|
+
|
|
|
+ List<String> effectiveCourseCodeList = tbExamCourseService.findEffectiveByExamId(examId,courseCode);
|
|
|
+
|
|
|
+ for (String effectiveCourseCode : effectiveCourseCodeList) {
|
|
|
+ List<TAExamCourse> taExamCourseList = taExamCourseService.list(new QueryWrapper<TAExamCourse>().lambda()
|
|
|
+ .eq(TAExamCourse::getExamId,examId)
|
|
|
+ .eq(TAExamCourse::getCourseCode,effectiveCourseCode));
|
|
|
+ if (taExamCourseList.size() != 1){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("考试课程分析数据获取失败");
|
|
|
+ }
|
|
|
+ TAExamCourse taExamCourse = taExamCourseList.get(0);
|
|
|
+ BigDecimal avgScore = taExamCourse.getAvgScore();
|
|
|
+ BigDecimal standardDeviation = taExamCourse.getCurrentStandardDeviation();
|
|
|
+
|
|
|
+ List<TAExamCourseRecord> dataSource = taExamCourseRecordDatasource.stream().filter(e -> effectiveCourseCode.equals(e.getCourseCode())).collect(Collectors.toList());
|
|
|
+ for (TAExamCourseRecord taExamCourseRecord : dataSource) {
|
|
|
+ BigDecimal totalScore = taExamCourseRecord.getTotalScore(); // 标准分
|
|
|
+ long lowCount = dataSource.stream().filter(e -> e.getTotalScore().compareTo(totalScore) < 0).count(); // 低于我的人数
|
|
|
+ long sameCount = dataSource.stream().filter(e -> e.getTotalScore().compareTo(totalScore) == 0).count(); // 和我相同的人数
|
|
|
+ int totalCount = dataSource.size(); //总人数
|
|
|
+ // 标准回归系数
|
|
|
+ BigDecimal standardizedCoefficients = (taExamCourseRecord.getAssignedScore().subtract(avgScore)).divide(standardDeviation,4,BigDecimal.ROUND_HALF_DOWN);
|
|
|
+
|
|
|
+ int percentGrade = this.handlePercentGrade(BigDecimal.valueOf(lowCount),BigDecimal.valueOf(sameCount),BigDecimal.valueOf(totalCount));
|
|
|
+ taExamCourseRecord.setPercentGrade(percentGrade);
|
|
|
+
|
|
|
+ String scoreLevel = this.handleLevel(examId,effectiveCourseCode,"百分等级",percentGrade);
|
|
|
+ taExamCourseRecord.setScoreLevel(scoreLevel);
|
|
|
+ taExamCourseRecord.setStandardizedCoefficients(standardizedCoefficients);
|
|
|
+ this.buildColData(taExamCourseRecord,dataSource); // 总成绩在学院的排名、排名等级档位、超过学院百分比
|
|
|
+ }
|
|
|
+ taExamCourseRecordService.saveOrUpdateBatch(dataSource);
|
|
|
+ }
|
|
|
+ return " 't_a_exam_course_record' 表更新完成";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @Override
|
|
|
+ public String buildAnalyzeExamCourseCollegeInspect(Long examId, String courseCode) {
|
|
|
+ List<TAExamCourseRecord> recordDatasource = taExamCourseRecordService.list(new QueryWrapper<TAExamCourseRecord>().lambda()
|
|
|
+ .eq(TAExamCourseRecord::getExamId,examId));
|
|
|
+ // 可分析有效课程信息
|
|
|
+ List<String> effectiveCourseCodeList = tbExamCourseService.findEffectiveByExamId(examId,courseCode);
|
|
|
+ List<TAExamCourseCollegeInspect> inspectCollegeList = new ArrayList<>();
|
|
|
+
|
|
|
+ for (String effectiveCourseCode : effectiveCourseCodeList) {
|
|
|
+ // 删除原有数据
|
|
|
+ taExamCourseCollegeInspectService.remove(new QueryWrapper<TAExamCourseCollegeInspect>().lambda()
|
|
|
+ .eq(TAExamCourseCollegeInspect::getExamId,examId).eq(TAExamCourseCollegeInspect::getCourseCode,effectiveCourseCode));
|
|
|
+
|
|
|
+ // 本课程下所有应届考生考生记录
|
|
|
+ List<TAExamCourseRecord> recordCourseCurrentDatasource = recordDatasource.stream()
|
|
|
+ .filter(e -> effectiveCourseCode.equals(e.getCourseCode()) && e.getStudentCurrent()).collect(Collectors.toList());
|
|
|
+
|
|
|
+ List<Long> inspectCollegeIdList = recordCourseCurrentDatasource.stream().filter(e -> !e.getAbsent()).map(TAExamCourseRecord::getInspectCollegeId).distinct().collect(Collectors.toList());
|
|
|
+ for (Long collegeId : inspectCollegeIdList) {
|
|
|
+ List<TAExamCourseRecord> collegeEffectiveRecordDatasource = recordCourseCurrentDatasource.stream()
|
|
|
+ .filter(e -> collegeId.equals(e.getInspectCollegeId())).collect(Collectors.toList());
|
|
|
+ // 课程-学院 总应届人数
|
|
|
+ int totalCount = collegeEffectiveRecordDatasource.size();
|
|
|
+ // 有效分析数据源
|
|
|
+ List<TAExamCourseRecord> effectiveList = collegeEffectiveRecordDatasource.stream().filter(e -> !e.getAbsent()).collect(Collectors.toList());
|
|
|
+ // 课程-学院 总应届实考人数
|
|
|
+ int realityCount = effectiveList.size();
|
|
|
+ // 课程-学院 总缺考应届人数
|
|
|
+ int absentCount = totalCount - realityCount;
|
|
|
+
|
|
|
+ List<Double> assignedScoreList = effectiveList.stream().map(e -> e.getAssignedScore().doubleValue()).collect(Collectors.toList());
|
|
|
+ DoubleSummaryStatistics doubleSummaryStatistics = assignedScoreList.stream().collect(Collectors.summarizingDouble(e -> e));
|
|
|
+ double minScore = doubleSummaryStatistics.getMin();
|
|
|
+ double maxScore = doubleSummaryStatistics.getMax();
|
|
|
+ double avgScore = doubleSummaryStatistics.getAverage();
|
|
|
+ String mode = MathUtil.calculateMode(assignedScoreList);
|
|
|
+ double upperQuartile = MathUtil.calculateQuantile(assignedScoreList, QuantileEnum.UPPER_QUARTILE.getValue());
|
|
|
+ double median = MathUtil.calculateQuantile(assignedScoreList, QuantileEnum.MEDIAN.getValue());
|
|
|
+ double lowerQuartile = MathUtil.calculateQuantile(assignedScoreList, QuantileEnum.LOWER_QUARTILE.getValue());
|
|
|
+
|
|
|
+ // TODO: 2021/6/7 缺考的学院信息处理
|
|
|
+ if (effectiveList.size() == 0){
|
|
|
+ minScore = 0;
|
|
|
+ maxScore = 0;
|
|
|
+ avgScore = 0;
|
|
|
+ mode = "";
|
|
|
+ upperQuartile = 0;
|
|
|
+ median = 0;
|
|
|
+ lowerQuartile = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 极端情况下分位数值处理
|
|
|
+ if (upperQuartile == 0) {
|
|
|
+ upperQuartile = median;
|
|
|
+ }
|
|
|
+ if (lowerQuartile == 0) {
|
|
|
+ lowerQuartile = median;
|
|
|
+ }
|
|
|
+ double standardDeviation = MathUtil.calculateStandardDeviation(assignedScoreList);
|
|
|
+ // 相对位置
|
|
|
+ double relativePosition = effectiveList.stream().collect(Collectors.summarizingDouble(e -> e.getStandardizedCoefficients().doubleValue())).getAverage();
|
|
|
+
|
|
|
+ TAExamCourseCollegeInspect taExamCourseCollegeInspect = new TAExamCourseCollegeInspect();
|
|
|
+ taExamCourseCollegeInspect.setId(SystemConstant.getDbUuid());
|
|
|
+ taExamCourseCollegeInspect.setExamId(examId);
|
|
|
+ taExamCourseCollegeInspect.setSchoolId(tbExamService.getById(examId).getSchoolId());
|
|
|
+ taExamCourseCollegeInspect.setCourseCode(effectiveCourseCode);
|
|
|
+ taExamCourseCollegeInspect.setCourseName(basicCourseService.findByCourseCode(effectiveCourseCode).getName());
|
|
|
+ taExamCourseCollegeInspect.setCollegeId(collegeId);
|
|
|
+ taExamCourseCollegeInspect.setMaxScore(BigDecimal.valueOf(maxScore));
|
|
|
+ taExamCourseCollegeInspect.setMinScore(BigDecimal.valueOf(minScore));
|
|
|
+ taExamCourseCollegeInspect.setAvgScore(BigDecimal.valueOf(avgScore));
|
|
|
+ taExamCourseCollegeInspect.setRealityCount(realityCount);
|
|
|
+ taExamCourseCollegeInspect.setAbsentCount(absentCount);
|
|
|
+ taExamCourseCollegeInspect.setTotalCount(totalCount);
|
|
|
+ taExamCourseCollegeInspect.setUpperQuartile(BigDecimal.valueOf(upperQuartile));
|
|
|
+ taExamCourseCollegeInspect.setMedian(BigDecimal.valueOf(median));
|
|
|
+ taExamCourseCollegeInspect.setLowerQuartile(BigDecimal.valueOf(lowerQuartile));
|
|
|
+ taExamCourseCollegeInspect.setMode(mode);
|
|
|
+ taExamCourseCollegeInspect.setStandardDeviation(BigDecimal.valueOf(standardDeviation));
|
|
|
+ taExamCourseCollegeInspect.setRelativePosition(BigDecimal.valueOf(relativePosition));
|
|
|
+ inspectCollegeList.add(taExamCourseCollegeInspect);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ taExamCourseCollegeInspectService.saveBatch(inspectCollegeList);
|
|
|
+ return "t_a_exam_course_college_inspect";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @Override
|
|
|
+ public String buildAnalyzeExamCourseClazz(Long examId, String courseCode) {
|
|
|
+ List<TAExamCourseRecord> recordDatasource = taExamCourseRecordService.list(new QueryWrapper<TAExamCourseRecord>().lambda()
|
|
|
+ .eq(TAExamCourseRecord::getExamId,examId));
|
|
|
+ // 可分析有效课程信息
|
|
|
+ List<String> effectiveCourseCodeList = tbExamCourseService.findEffectiveByExamId(examId,courseCode);
|
|
|
+
|
|
|
+ List<TAExamCourseClazz> taExamCourseClazzList = new ArrayList<>();
|
|
|
+ for (String effectiveCourseCode : effectiveCourseCodeList) {
|
|
|
+ // 删除原有数据
|
|
|
+ taExamCourseClazzService.remove(new QueryWrapper<TAExamCourseClazz>().lambda()
|
|
|
+ .eq(TAExamCourseClazz::getExamId,examId)
|
|
|
+ .eq(TAExamCourseClazz::getCourseCode,effectiveCourseCode));
|
|
|
+
|
|
|
+ List<TAExamCourseRecord> recordCourseCurrentDatasource = recordDatasource.stream()
|
|
|
+ .filter(e -> effectiveCourseCode.equals(e.getCourseCode()) && e.getStudentCurrent()).collect(Collectors.toList());
|
|
|
+ List<Long> clazzIdList = recordCourseCurrentDatasource.stream().filter(e -> !e.getAbsent()).map(TAExamCourseRecord::getClazzId).distinct().collect(Collectors.toList());
|
|
|
+ for (Long clazzId : clazzIdList) {
|
|
|
+ List<TAExamCourseRecord> clazzEffectiveRecordDatasource = recordCourseCurrentDatasource.stream()
|
|
|
+ .filter(e -> clazzId.equals(e.getClazzId())).collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 课程-班级 总应届人数
|
|
|
+ int totalCount = clazzEffectiveRecordDatasource.size();
|
|
|
+ // 有效分析数据源
|
|
|
+ List<TAExamCourseRecord> effectiveList = clazzEffectiveRecordDatasource.stream().filter(e -> !e.getAbsent()).collect(Collectors.toList());
|
|
|
+ // 课程-班级 总应届实考人数
|
|
|
+ int realityCount = effectiveList.size();
|
|
|
+ // 课程-班级 总缺考应届人数
|
|
|
+ int absentCount = totalCount - realityCount;
|
|
|
+ List<Double> assignedScoreList = effectiveList.stream().map(e -> e.getAssignedScore().doubleValue()).collect(Collectors.toList());
|
|
|
+
|
|
|
+ DoubleSummaryStatistics doubleSummaryStatistics = assignedScoreList.stream().collect(Collectors.summarizingDouble(e -> e));
|
|
|
+ double minScore = doubleSummaryStatistics.getMin();
|
|
|
+ double maxScore = doubleSummaryStatistics.getMax();
|
|
|
+ double avgScore = doubleSummaryStatistics.getAverage();
|
|
|
+
|
|
|
+ List<TBExamStudent> tbExamStudentList = tbExamStudentService.list(new QueryWrapper<TBExamStudent>().lambda()
|
|
|
+ .eq(TBExamStudent::getExamId,examId)
|
|
|
+ .eq(TBExamStudent::getCourseCode,effectiveCourseCode)
|
|
|
+ .eq(TBExamStudent::getClazzId,clazzId));
|
|
|
+ List<Long> teachCollegeIdList = tbExamStudentList.stream().map(TBExamStudent::getTeachCollegeId).distinct().collect(Collectors.toList());
|
|
|
+ String collegeName = "";
|
|
|
+ for (Long collegeId : teachCollegeIdList) {
|
|
|
+ SysOrg tbSchoolCollege = sysOrgService.getById(collegeId);
|
|
|
+ if (Objects.isNull(tbSchoolCollege)){
|
|
|
+ collegeName = collegeName + SystemConstant.DEFAULT_SIGN + ",";
|
|
|
+ } else {
|
|
|
+ collegeName = collegeName + tbSchoolCollege.getName() + ",";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ collegeName = collegeName.substring(0,collegeName.length() - 1);
|
|
|
+
|
|
|
+ TAExamCourseClazz taExamCourseClazz = new TAExamCourseClazz();
|
|
|
+ taExamCourseClazz.setId(SystemConstant.getDbUuid());
|
|
|
+ taExamCourseClazz.setExamId(examId);
|
|
|
+ taExamCourseClazz.setSchoolId(tbExamService.getById(examId).getSchoolId());
|
|
|
+ taExamCourseClazz.setCourseCode(effectiveCourseCode);
|
|
|
+ taExamCourseClazz.setCourseName(basicCourseService.findByCourseCode(effectiveCourseCode).getName());
|
|
|
+ taExamCourseClazz.setClazzId(clazzId);
|
|
|
+ taExamCourseClazz.setMaxScore(BigDecimal.valueOf(maxScore));
|
|
|
+ taExamCourseClazz.setMinScore(BigDecimal.valueOf(minScore));
|
|
|
+ taExamCourseClazz.setAvgScore(BigDecimal.valueOf(avgScore));
|
|
|
+ taExamCourseClazz.setRealityCount(realityCount);
|
|
|
+ taExamCourseClazz.setAbsentCount(absentCount);
|
|
|
+ taExamCourseClazz.setTotalCount(totalCount);
|
|
|
+ taExamCourseClazz.setCollegeName(collegeName);
|
|
|
+ taExamCourseClazzList.add(taExamCourseClazz);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ taExamCourseClazzService.saveBatch(taExamCourseClazzList);
|
|
|
+ return " 't_a_exam_course_clazz'表构建成功 ";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @Override
|
|
|
+ public String AnalyzePointScoreRate(Long examId, String courseCode) throws Exception {
|
|
|
+ // 可分析有效课程信息
|
|
|
+ List<String> effectiveCourseCodeList = tbExamCourseService.findEffectiveByExamId(examId,courseCode);
|
|
|
+
|
|
|
+ List<TAExamCourseRecord> dataSource = taExamCourseRecordService.list(new QueryWrapper<TAExamCourseRecord>().lambda()
|
|
|
+ .eq(TAExamCourseRecord::getExamId,examId));
|
|
|
+
|
|
|
+ // 循环进入每次考试课程下
|
|
|
+ for (String effectiveCourseCode : effectiveCourseCodeList) {
|
|
|
+ // 有效考生作答数据源
|
|
|
+ List<TAExamCourseRecord> effectiveDatasource = dataSource.stream()
|
|
|
+ .filter(e -> effectiveCourseCode.equals(e.getCourseCode()) && !e.getAbsent() && e.getStudentCurrent()).collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 考察点数据源
|
|
|
+ List<TBDimension> dimensionDatasource = tbDimensionService.list(new QueryWrapper<TBDimension>().lambda()
|
|
|
+ .eq(TBDimension::getExamId,examId)
|
|
|
+ .eq(TBDimension::getCourseCode,courseCode));
|
|
|
+
|
|
|
+ if (dimensionDatasource.size() < 1){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 删除原数据
|
|
|
+ taExamCourseDioService.remove(new QueryWrapper<TAExamCourseDio>().lambda()
|
|
|
+ .eq(TAExamCourseDio::getExamId,examId)
|
|
|
+ .eq(TAExamCourseDio::getCourseCode,effectiveCourseCode));
|
|
|
+
|
|
|
+ taExamCourseRecordDioService.remove(new QueryWrapper<TAExamCourseRecordDio>().lambda()
|
|
|
+ .eq(TAExamCourseRecordDio::getExamId,examId)
|
|
|
+ .eq(TAExamCourseRecordDio::getCourseCode,effectiveCourseCode));
|
|
|
+
|
|
|
+ taExamCourseRecordModService.remove(new QueryWrapper<TAExamCourseRecordMod>().lambda()
|
|
|
+ .eq(TAExamCourseRecordMod::getExamId,examId)
|
|
|
+ .eq(TAExamCourseRecordMod::getCourseCode,courseCode));
|
|
|
+
|
|
|
+ List<TBPaper> tbPaperList = tbPaperService.list(new QueryWrapper<TBPaper>().lambda()
|
|
|
+ .eq(TBPaper::getExamId,examId)
|
|
|
+ .eq(TBPaper::getCourseCode,courseCode));
|
|
|
+
|
|
|
+ if (tbPaperList.size() == 0){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("试卷基础数据查找失败");
|
|
|
+ }
|
|
|
+ for (TBPaper tbPaper : tbPaperList) {
|
|
|
+ String paperType = tbPaper.getPaperType();
|
|
|
+ Long paperId = tbPaper.getId();
|
|
|
+ // 试卷结构数据源
|
|
|
+ List<TBPaperStruct> tbPaperStructList = tbPaperStructService.list(new QueryWrapper<TBPaperStruct>().lambda()
|
|
|
+ .eq(TBPaperStruct::getPaperId,paperId));
|
|
|
+ if (tbPaperStructList.size() == 0){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("试卷结构数据查找失败");
|
|
|
+ }
|
|
|
+ // 该试卷下所有考生作答数据
|
|
|
+ List<BasicAnswerDto> answerDtoList = tbAnswerService.findByPaperId(paperId);
|
|
|
+ if (answerDtoList.size() == 0){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("试卷id为[" + paperId + "]的考生作答详细记录不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5.计算每个考察点所对应的题目集合
|
|
|
+ Map<String, List<TBPaperStruct>> pointToPaper = this.handlePointToPaper(dimensionDatasource, tbPaperStructList, examId,courseCode);
|
|
|
+
|
|
|
+ // 计算每个模块对应的题目集合
|
|
|
+ Map<String, List<TBPaperStruct>> moduleToPaper = this.handleModuleToPaper(dimensionDatasource, tbPaperStructList, examId, courseCode);
|
|
|
+
|
|
|
+ // 6.计算每个考察点在此次考试所占分数
|
|
|
+ Map<String, Object> everyPointTotalScore = this.handleEveryPointTotalScore(pointToPaper);
|
|
|
+
|
|
|
+ // 计算每个模块在此次考试中所占分数
|
|
|
+ Map<String, Object> everyModuleTotalScore = this.handleEveryPointTotalScore(moduleToPaper);
|
|
|
+
|
|
|
+ // TODO: 2021/6/7 数据组装
|
|
|
+ // 7.计算每个考生各个考察点和考察模块得分率并更新't_a_exam_course_record_dio'表和't_a_exam_course_record_mod'表
|
|
|
+ List<TAExamCourseRecordDio> taExamCourseRecordDioList = new ArrayList<>();
|
|
|
+ List<TAExamCourseRecordMod> taExamCourseRecordModList = new ArrayList<>();
|
|
|
+ for (TAExamCourseRecord taExamCourseRecord : effectiveDatasource) {
|
|
|
+ Long inspectCollegeId = taExamCourseRecord.getInspectCollegeId();
|
|
|
+ String inspectCollegeName = sysOrgService.getById(inspectCollegeId).getName();
|
|
|
+ List<BasicAnswerDto> oneStudentAnswerDetailDataSource = answerDtoList.stream()
|
|
|
+ .filter(e -> taExamCourseRecord.getExamRecordId().equals(e.getExamRecordId()))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ if (oneStudentAnswerDetailDataSource.size() == 0){
|
|
|
+ continue; // 当AB卷时,examPaperTikDataSource还是整体数据,所以会匹配不到,匹配不到的说明不是本套试卷,因此匹配不到的不能处理。
|
|
|
+ }
|
|
|
+ // 考察点得分率
|
|
|
+ for (String s : pointToPaper.keySet()) { // 遍历考察知识点所对应的题目集合键值对
|
|
|
+ List<TBPaperStruct> paperStructList = pointToPaper.get(s);
|
|
|
+
|
|
|
+ final String split = SystemConstant.HYPHEN;
|
|
|
+ Set<String> questionIndex = paperStructList
|
|
|
+ .stream()
|
|
|
+ .map(e -> e.getNumberType() + split + e.getBigQuestionNumber() + split + e.getSmallQuestionNumber())
|
|
|
+ .collect(Collectors.toSet()); // 该考察点考察到的所有试题在试卷结构(paper)中'题号类型-大题号-小题号'组成的索引集合
|
|
|
+
|
|
|
+
|
|
|
+ // 获取学生在该知识点的总得分
|
|
|
+ double studentScore = oneStudentAnswerDetailDataSource.stream()
|
|
|
+ .filter(e -> questionIndex.contains(e.getNumberType() + split + e.getMainNumber() + split + e.getSubNumber()))
|
|
|
+ .collect(Collectors.summarizingDouble(e -> e.getScore().doubleValue()))
|
|
|
+ .getSum(); // 查找该考察点所考察的所有试题在试卷结构(paper)中的索引('题号类型-大题号-小题号')包含该学生每题答题记录的索引的答题记录信息,计算描述统计
|
|
|
+
|
|
|
+ // 获取该知识点在试卷中的总得分
|
|
|
+ double totalScore = (double) everyPointTotalScore.get(s);
|
|
|
+
|
|
|
+ // 获取该知识点该学生的得分率
|
|
|
+ double rate;
|
|
|
+ if (totalScore != 0 && studentScore != 0) {
|
|
|
+ rate = studentScore / totalScore;
|
|
|
+ } else {
|
|
|
+ rate = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ //组装taExamCourseRecordDio数据
|
|
|
+ TAExamCourseRecordDio taExamCourseRecordDio = new TAExamCourseRecordDio();
|
|
|
+ taExamCourseRecordDio.setId(SystemConstant.getDbUuid());
|
|
|
+ taExamCourseRecordDio.setSchoolId(tbExamService.getById(examId).getSchoolId());
|
|
|
+ taExamCourseRecordDio.setExamRecordId(taExamCourseRecord.getExamRecordId());
|
|
|
+ taExamCourseRecordDio.setPaperId(taExamCourseRecord.getPaperId());
|
|
|
+ taExamCourseRecordDio.setExamId(taExamCourseRecord.getExamId());
|
|
|
+ taExamCourseRecordDio.setCourseCode(effectiveCourseCode);
|
|
|
+ taExamCourseRecordDio.setCourseName(basicCourseService.findByCourseCode(effectiveCourseCode).getName());
|
|
|
+ String[] dimCodeArr = s.split("-");
|
|
|
+ if (dimCodeArr.length != 2) {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("获得的考察点标识不符合标准,标准为 type-dimensionCode,结果为: " + s);
|
|
|
+ }
|
|
|
+ String dimensionType = dimCodeArr[0];
|
|
|
+ String dimensionCode = dimCodeArr[1];
|
|
|
+ taExamCourseRecordDio.setDimensionType(dimensionType);
|
|
|
+ taExamCourseRecordDio.setDimensionCode(dimensionCode);
|
|
|
+ taExamCourseRecordDio.setStudentScore(BigDecimal.valueOf(studentScore));
|
|
|
+ taExamCourseRecordDio.setScoreRate(BigDecimal.valueOf(rate));
|
|
|
+ taExamCourseRecordDio.setProficiency(this.handleModuleProficiency(examId,effectiveCourseCode, dimensionType, rate));
|
|
|
+ taExamCourseRecordDio.setInspectCollegeId(inspectCollegeId);
|
|
|
+ taExamCourseRecordDio.setInspectCollegeName(inspectCollegeName);
|
|
|
+ taExamCourseRecordDioList.add(taExamCourseRecordDio);
|
|
|
+ }
|
|
|
+ // 考察模块得分率
|
|
|
+ for (String s : moduleToPaper.keySet()) { // 遍历考察模块对应的题目集合键值对
|
|
|
+ List<TBPaperStruct> paperStructList = moduleToPaper.get(s);
|
|
|
+
|
|
|
+ final String split = SystemConstant.HYPHEN;
|
|
|
+
|
|
|
+ Set<String> questionIndex = paperStructList
|
|
|
+ .stream()
|
|
|
+ .map(e -> e.getNumberType() + split + e.getBigQuestionNumber() + split + e.getSmallQuestionNumber())
|
|
|
+ .collect(Collectors.toSet()); // 该考察点考察到的所有试题在试卷结构(paper)中'题号类型-大题号-小题号'组成的索引集合
|
|
|
+
|
|
|
+ // 获取学生在该知识点的总得分
|
|
|
+ double studentScore = oneStudentAnswerDetailDataSource.stream()
|
|
|
+ .filter(e -> questionIndex.contains(e.getNumberType() + split + e.getMainNumber() + split + e.getSubNumber()))
|
|
|
+ .collect(Collectors.summarizingDouble(e -> e.getScore().doubleValue()))
|
|
|
+ .getSum(); // 查找该考察点所考察的所有试题在试卷结构(paper)中的索引('题号类型-大题号-小题号')包含该学生每题答题记录的索引的答题记录信息,计算描述统计
|
|
|
+
|
|
|
+
|
|
|
+ // 获取该知识点在试卷中的总得分
|
|
|
+ double totalScore = (double) everyModuleTotalScore.get(s);
|
|
|
+
|
|
|
+ // 获取该知识点该学生的得分率
|
|
|
+ double rate;
|
|
|
+ if (totalScore != 0 && studentScore != 0) {
|
|
|
+ rate = studentScore / totalScore;
|
|
|
+ } else {
|
|
|
+ rate = 0;
|
|
|
+ }
|
|
|
+ TAExamCourseRecordMod taExamCourseRecordMod = new TAExamCourseRecordMod();
|
|
|
+ taExamCourseRecordMod.setId(SystemConstant.getDbUuid());
|
|
|
+ taExamCourseRecordMod.setExamRecordId(taExamCourseRecord.getExamRecordId());
|
|
|
+ taExamCourseRecordMod.setPaperId(taExamCourseRecord.getPaperId());
|
|
|
+ taExamCourseRecordMod.setExamId(taExamCourseRecord.getExamId());
|
|
|
+ taExamCourseRecordMod.setSchoolId(tbExamService.getById(examId).getSchoolId());
|
|
|
+ taExamCourseRecordMod.setCourseCode(effectiveCourseCode);
|
|
|
+ taExamCourseRecordMod.setCourseName(basicCourseService.findByCourseCode(effectiveCourseCode).getName());
|
|
|
+ taExamCourseRecordMod.setModuleType(s);
|
|
|
+ taExamCourseRecordMod.setTotalScore(BigDecimal.valueOf(studentScore));
|
|
|
+ taExamCourseRecordMod.setScoreRate(BigDecimal.valueOf(rate));
|
|
|
+ taExamCourseRecordMod.setFullScore(BigDecimal.valueOf(totalScore));
|
|
|
+ taExamCourseRecordMod.setProficiency(this.handleModuleProficiency(examId,effectiveCourseCode,s,rate));
|
|
|
+ taExamCourseRecordMod.setInspectCollegeId(inspectCollegeId);
|
|
|
+ taExamCourseRecordMod.setInspectCollegeName(inspectCollegeName);
|
|
|
+ taExamCourseRecordModList.add(taExamCourseRecordMod);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ taExamCourseRecordDioService.saveBatch(taExamCourseRecordDioList);
|
|
|
+ taExamCourseRecordModService.saveBatch(taExamCourseRecordModList);
|
|
|
+
|
|
|
+ // 8.计算此次考试各个考察点得分率并插入't_a_exam_course_dio'表
|
|
|
+ List<TAExamCourseDio> taExamCourseDioList = new ArrayList<>();
|
|
|
+ for (String s : everyPointTotalScore.keySet()) {
|
|
|
+ String[] dimCodeArr = s.split("-");
|
|
|
+ if (dimCodeArr.length != 2) {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("获得的考察点标识不符合标准,标准为 type-dimensionCode,结果为: " + s);
|
|
|
+ }
|
|
|
+ String dimensionType = dimCodeArr[0];
|
|
|
+ String dimensionCode = dimCodeArr[1];
|
|
|
+ double totalScore = (double) everyPointTotalScore.get(s);
|
|
|
+ double avgScore = taExamCourseRecordDioList.stream()
|
|
|
+ .filter(e -> dimensionType.equals(e.getDimensionType()) && dimensionCode.equals(e.getDimensionCode()))
|
|
|
+ .collect(Collectors.summarizingDouble(e -> e.getStudentScore().doubleValue()))
|
|
|
+ .getAverage();
|
|
|
+ double rate;
|
|
|
+ if (totalScore != 0 && avgScore != 0) {
|
|
|
+ rate = avgScore / totalScore;
|
|
|
+ } else {
|
|
|
+ rate = 0;
|
|
|
+ }
|
|
|
+ double totalCount = pointToPaper.get(s).size();
|
|
|
+
|
|
|
+ //组装't_a_exam_course_dio'数据
|
|
|
+ TAExamCourseDio taExamCourseDio = new TAExamCourseDio();
|
|
|
+ taExamCourseDio.setId(SystemConstant.getDbUuid());
|
|
|
+ taExamCourseDio.setExamId(examId);
|
|
|
+ taExamCourseDio.setSchoolId(tbExamService.getById(examId).getSchoolId());
|
|
|
+ taExamCourseDio.setCourseCode(effectiveCourseCode);
|
|
|
+ taExamCourseDio.setCourseName(basicCourseService.findByCourseCode(courseCode).getName());
|
|
|
+ taExamCourseDio.setPaperId(paperId);
|
|
|
+ taExamCourseDio.setDimensionType(dimensionType);
|
|
|
+ taExamCourseDio.setDimensionCode(dimensionCode);
|
|
|
+ taExamCourseDio.setScoreRate(BigDecimal.valueOf(rate));
|
|
|
+ taExamCourseDio.setTotalScore(BigDecimal.valueOf(totalScore));
|
|
|
+ taExamCourseDio.setTotalCount(BigDecimal.valueOf(totalCount));
|
|
|
+
|
|
|
+ taExamCourseDioList.add(taExamCourseDio);
|
|
|
+ }
|
|
|
+ taExamCourseDioService.saveBatch(taExamCourseDioList);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<TAExamCourseRecordMod> examCourseRecordModList = taExamCourseRecordModService.list(new QueryWrapper<TAExamCourseRecordMod>().lambda()
|
|
|
+ .eq(TAExamCourseRecordMod::getExamId,examId)
|
|
|
+ .eq(TAExamCourseRecordMod::getCourseCode,effectiveCourseCode));
|
|
|
+
|
|
|
+ // 计算该考生在该模块得分率在该学院排名
|
|
|
+ List<TBModuleConfig> tbModuleConfigList = tbModuleConfigService.findDistinctModuleInfoByExamIdAndCourseCode(examId,effectiveCourseCode);
|
|
|
+ for (TAExamCourseRecordMod taExamCourseRecordMod : examCourseRecordModList) {
|
|
|
+ String moduleType = taExamCourseRecordMod.getModuleType();
|
|
|
+ Long myCollegeId = taExamCourseRecordMod.getInspectCollegeId();
|
|
|
+ List<TAExamCourseRecordMod> sameColModuleList = examCourseRecordModList.stream().filter(e -> e.getInspectCollegeId().equals(myCollegeId)).collect(Collectors.toList()); // 同学院下学生该模块数据
|
|
|
+
|
|
|
+ // 计算模块得分率在学院的排名和排名等级
|
|
|
+ double size = sameColModuleList.size();
|
|
|
+
|
|
|
+
|
|
|
+ BigDecimal myScoreRate = taExamCourseRecordMod.getScoreRate();
|
|
|
+ double myNumber = sameColModuleList.stream().filter(e -> e.getScoreRate().compareTo(myScoreRate) > 0 ).count() + 1; //我在学院的排名
|
|
|
+ taExamCourseRecordMod.setColRank((int) myNumber);
|
|
|
+
|
|
|
+
|
|
|
+ // 计算模块得分率在学院的百分等级
|
|
|
+ double lowCount = sameColModuleList.stream().filter(e -> e.getScoreRate().compareTo(myScoreRate) < 0).count(); // 在学院该模块得分低于我的人数
|
|
|
+ double sameCount = sameColModuleList.stream().filter(e -> e.getScoreRate().compareTo(myScoreRate) == 0).count(); // 在学院该模块得分等于我的人数
|
|
|
+ double totalCount = sameColModuleList.size(); // 该学院总人数
|
|
|
+ int percentGrade = this.handlePercentGrade(BigDecimal.valueOf(lowCount),BigDecimal.valueOf(sameCount), BigDecimal.valueOf(totalCount)); // 该学生该模块成绩在该学院的百分等级
|
|
|
+ taExamCourseRecordMod.setPercentGrade(percentGrade);
|
|
|
+
|
|
|
+ // 计算模块等级
|
|
|
+ List<TBModuleConfig> formulaList = tbModuleConfigList.stream().filter(e -> e.getModuleType().equals(moduleType)).collect(Collectors.toList());
|
|
|
+ if (formulaList.size() != 1) {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("数据异常");
|
|
|
+ }
|
|
|
+ String formula = formulaList.get(0).getFormula();
|
|
|
+ LevelRuleEnum[] levelRuleEnumArr = LevelRuleEnum.values();
|
|
|
+ for (LevelRuleEnum levelRuleEnum : levelRuleEnumArr) {
|
|
|
+ if (formula.equals(levelRuleEnum.getValue())) {
|
|
|
+ Field field = taExamCourseRecordMod.getClass().getDeclaredField(levelRuleEnum.getAttribute()); //对应属性
|
|
|
+ Method getMethod = taExamCourseRecordMod.getClass().getDeclaredMethod("get" + ConversionUtils.initCap(field.getName())); // 找到该属性的方法
|
|
|
+
|
|
|
+ double value = Double.parseDouble(String.valueOf(getMethod.invoke(taExamCourseRecordMod)));
|
|
|
+ if (!formula.equals(LevelRuleEnum.COLLEGE_RANK.getValue())) { // 当计算规则不为学院排名时,为了方便计算,将minNumber设置为1 使其无效
|
|
|
+ size = -1;
|
|
|
+ }
|
|
|
+ taExamCourseRecordMod.setLevel(this.handleModuleRankLevel(examId,courseCode,moduleType, value, size));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ taExamCourseRecordModService.updateBatchById(examCourseRecordModList);
|
|
|
+
|
|
|
+ }
|
|
|
+ return "维度表构建完毕";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @Override
|
|
|
+ public String buildAnalyzeExamCourseCollegeInspectDio(Long examId, String courseCode) {
|
|
|
+ // 可分析有效课程信息
|
|
|
+ List<String> effectiveCourseCodeList = tbExamCourseService.findEffectiveByExamId(examId,courseCode);
|
|
|
+ for (String effectiveCourseCode : effectiveCourseCodeList) {
|
|
|
+ // 删除原有数据
|
|
|
+ taExamCourseCollegeInspectDioService.remove(new QueryWrapper<TAExamCourseCollegeInspectDio>().lambda()
|
|
|
+ .eq(TAExamCourseCollegeInspectDio::getExamId,examId)
|
|
|
+ .eq(TAExamCourseCollegeInspectDio::getCourseCode,effectiveCourseCode));
|
|
|
+
|
|
|
+ taExamCourseCollegeInspectDioService.insertByTAExamCourseRecordDio(examId,courseCode);
|
|
|
+ }
|
|
|
+ return "'t_a_exam_course_college_inspect_dio'表构建完成 ";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @Override
|
|
|
+ public String buildAnalyzePaperStruct(Long examId, String courseCode) {
|
|
|
+ // 可分析有效课程信息
|
|
|
+ List<String> effectiveCourseCodeList = tbExamCourseService.findEffectiveByExamId(examId,courseCode);
|
|
|
+ for (String effectiveCourseCode : effectiveCourseCodeList) {
|
|
|
+ List<TBPaper> tbPaperList = tbPaperService.list(new QueryWrapper<TBPaper>().lambda()
|
|
|
+ .eq(TBPaper::getExamId,examId).eq(TBPaper::getCourseCode,effectiveCourseCode));
|
|
|
+ if (tbPaperList.size() == 0){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("基础试卷信息异常");
|
|
|
+ }
|
|
|
+ // 删除原有数据
|
|
|
+ taPaperStructService.remove(new QueryWrapper<TAPaperStruct>().lambda()
|
|
|
+ .eq(TAPaperStruct::getExamId,examId)
|
|
|
+ .eq(TAPaperStruct::getCourseCode,effectiveCourseCode));
|
|
|
+ List<TAPaperStruct> taPaperStructList = new ArrayList<>();
|
|
|
+ // 该课程有效试卷结构数据
|
|
|
+ List<ValidAnswerDetailDto> answerDetailDtoList = tbAnswerService.findValid(examId,courseCode);
|
|
|
+ for (TBPaper tbPaper : tbPaperList) {
|
|
|
+ Long paperId = tbPaper.getId();
|
|
|
+ String paperType = tbPaper.getPaperType();
|
|
|
+ List<TBPaperStruct> tbPaperStructList = tbPaperStructService.list(new QueryWrapper<TBPaperStruct>().lambda()
|
|
|
+ .eq(TBPaperStruct::getPaperId,paperId));
|
|
|
+ if (tbPaperStructList.size() == 0){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("试卷结构数据异常");
|
|
|
+ }
|
|
|
+ for (TBPaperStruct paperStruct : tbPaperStructList) {
|
|
|
+ String numberType = paperStruct.getNumberType();
|
|
|
+ String mainNumber = paperStruct.getBigQuestionNumber();
|
|
|
+ String subNumber = paperStruct.getSmallQuestionNumber();
|
|
|
+
|
|
|
+ List<ValidAnswerDetailDto> oneQuestionAnswerDetailList = answerDetailDtoList
|
|
|
+ .stream().filter(e -> paperType.equals(e.getPaperType()) &&
|
|
|
+ numberType.equals(e.getNumberType()) &&
|
|
|
+ mainNumber.equals(e.getMainNumber()) &&
|
|
|
+ subNumber.equals(e.getSubNumber())).collect(Collectors.toList()); //某道小题的所有参考学生作答信息(并且按照该科目的参考学生百分等级从高到低排序了)
|
|
|
+
|
|
|
+ DoubleSummaryStatistics descriptiveStatistics = oneQuestionAnswerDetailList.stream()
|
|
|
+ .collect(Collectors.summarizingDouble(e -> e.getScore().doubleValue()));
|
|
|
+ BigDecimal fullScore = paperStruct.getFullScore();
|
|
|
+ double scoreAvg = descriptiveStatistics.getAverage();
|
|
|
+ BigDecimal scoreRate = BigDecimal.valueOf(scoreAvg).divide(fullScore,4,BigDecimal.ROUND_HALF_DOWN);
|
|
|
+ scoreRate = scoreRate.setScale(1,BigDecimal.ROUND_HALF_DOWN);
|
|
|
+ String difficult = this.analyzeDifficult(examId,courseCode, scoreRate.doubleValue());
|
|
|
+
|
|
|
+ double validity = this.calculateValidity(oneQuestionAnswerDetailList, fullScore.doubleValue());
|
|
|
+
|
|
|
+ TAPaperStruct taPaperStruct = new TAPaperStruct();
|
|
|
+ taPaperStruct.setId(SystemConstant.getDbUuid());
|
|
|
+ taPaperStruct.setPaperStructId(paperStruct.getId());
|
|
|
+ taPaperStruct.setExamId(examId);
|
|
|
+ taPaperStruct.setSchoolId(tbExamService.getById(examId).getSchoolId());
|
|
|
+ taPaperStruct.setCourseCode(courseCode);
|
|
|
+ taPaperStruct.setCourseName(basicCourseService.findByCourseCode(courseCode).getName());
|
|
|
+ taPaperStruct.setScoreRate(scoreRate);
|
|
|
+ taPaperStruct.setDifficult(difficult);
|
|
|
+ taPaperStruct.setValidity(BigDecimal.valueOf(validity));
|
|
|
+ taPaperStructList.add(taPaperStruct);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ taPaperStructService.saveBatch(taPaperStructList);
|
|
|
+ }
|
|
|
+ // TODO: 2021/6/8 部分效度有问题
|
|
|
+ return " 't_a_paper_struct'表构建成功 ";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @Override
|
|
|
+ public String buildAnalyzeExamTotal(Long examId) {
|
|
|
+ // 删除原数据
|
|
|
+ taExamTotalService.remove(new QueryWrapper<TAExamTotal>().lambda()
|
|
|
+ .eq(TAExamTotal::getExamId,examId));
|
|
|
+
|
|
|
+ // 数据源
|
|
|
+ List<TAExamCourseRecord> taExamCourseRecordList = taExamCourseRecordService.list(new QueryWrapper<TAExamCourseRecord>().lambda()
|
|
|
+ .eq(TAExamCourseRecord::getExamId,examId));
|
|
|
+
|
|
|
+ //考察学院
|
|
|
+ // TODO: 2021/6/8 是否计算只有缺考的学院 是否计算只有往届的学院
|
|
|
+ List<Long> inspectCollegeInfo = taExamCourseRecordList.stream()
|
|
|
+ .map(TAExamCourseRecord::getInspectCollegeId).distinct().collect(Collectors.toList());
|
|
|
+ // 考察学院数量
|
|
|
+ int collegeCount = inspectCollegeInfo.size();
|
|
|
+ String inspectCollegeNames = "";
|
|
|
+ if (collegeCount > 0){
|
|
|
+ for (Long inspectCollegeId : inspectCollegeInfo) {
|
|
|
+ SysOrg college = sysOrgService.getById(inspectCollegeId);
|
|
|
+ inspectCollegeNames = inspectCollegeNames + college.getName() + "、";
|
|
|
+ }
|
|
|
+ // 考察学院名称
|
|
|
+ inspectCollegeNames = inspectCollegeNames.substring(0,inspectCollegeNames.length() - 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 课程
|
|
|
+ List<String> courseInfo = taExamCourseRecordList.stream()
|
|
|
+ .map(TAExamCourseRecord::getCourseCode).distinct().collect(Collectors.toList());
|
|
|
+ int courseCount = courseInfo.size();
|
|
|
+
|
|
|
+ // 总人数
|
|
|
+ List<Long> studentInfo = taExamCourseRecordList.stream()
|
|
|
+ .map(TAExamCourseRecord::getStudentId).distinct().collect(Collectors.toList());
|
|
|
+ int totalCount = studentInfo.size();
|
|
|
+
|
|
|
+ // ------只要有一门考试了就不算缺考
|
|
|
+ List<Long> studentRealityInfo = taExamCourseRecordList.stream()
|
|
|
+ .filter(e -> !e.getAbsent())
|
|
|
+ .map(TAExamCourseRecord::getStudentId).distinct().collect(Collectors.toList());
|
|
|
+ int realityCount = studentRealityInfo.size();
|
|
|
+ int absentCount = totalCount - realityCount;
|
|
|
+
|
|
|
+ // 课次
|
|
|
+ int totalCourseTimes = taExamCourseRecordList.size();
|
|
|
+ int realityCourseTimes = (int) taExamCourseRecordList.stream().filter(e -> !e.getAbsent()).count();
|
|
|
+ int absentCourseTimes = (int) taExamCourseRecordList.stream().filter(TAExamCourseRecord::getAbsent).count();
|
|
|
+
|
|
|
+ // 考试时间
|
|
|
+ TBExam tbExam = tbExamService.getById(examId);
|
|
|
+ String examTime = tbExam.getExamTime();
|
|
|
+
|
|
|
+ TAExamTotal taExamTotal = new TAExamTotal();
|
|
|
+ taExamTotal.setId(SystemConstant.getDbUuid());
|
|
|
+ taExamTotal.setExamId(examId);
|
|
|
+ taExamTotal.setSchoolId(tbExamService.getById(examId).getSchoolId());
|
|
|
+ taExamTotal.setCollegeCount(collegeCount);
|
|
|
+ taExamTotal.setCollegeNames(inspectCollegeNames);
|
|
|
+ taExamTotal.setCourseCount(courseCount);
|
|
|
+ taExamTotal.setPublicCourseCount(0);
|
|
|
+ taExamTotal.setMajorCourseCount(courseCount);
|
|
|
+ taExamTotal.setTotalCount(totalCount);
|
|
|
+ taExamTotal.setRealityCount(realityCount);
|
|
|
+ taExamTotal.setAbsentCount(absentCount);
|
|
|
+ taExamTotal.setTotalCourseTimes(totalCourseTimes);
|
|
|
+ taExamTotal.setRealityCourseTimes(realityCourseTimes);
|
|
|
+ taExamTotal.setAbsentCourseTimes(absentCourseTimes);
|
|
|
+ taExamTotal.setExamTime(examTime);
|
|
|
+
|
|
|
+ taExamTotalService.save(taExamTotal);
|
|
|
+ return " 't_a_exam_total'表构建完毕 ";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @Override
|
|
|
+ public String buildAnalyzeExamCourseCollegeTeacher(Long examId, String courseCode) {
|
|
|
+ // 可分析有效课程信息
|
|
|
+ List<String> effectiveCourseCodeList = tbExamCourseService.findEffectiveByExamId(examId,courseCode);
|
|
|
+ for (String effectiveCourseCode : effectiveCourseCodeList) {
|
|
|
+ // 删除该课程原有分析
|
|
|
+ taExamCourseCollegeTeacherService.remove(new QueryWrapper<TAExamCourseCollegeTeacher>().lambda()
|
|
|
+ .eq(TAExamCourseCollegeTeacher::getExamId,examId)
|
|
|
+ .eq(TAExamCourseCollegeTeacher::getCourseCode,courseCode));
|
|
|
+
|
|
|
+ List<TAExamCourseCollegeTeacher> taExamCourseCollegeTeacherList = new ArrayList<>();
|
|
|
+ // 整体该课程下数据源
|
|
|
+ List<TAExamCourseRecord> dataSource = taExamCourseRecordService.list(new QueryWrapper<TAExamCourseRecord>().lambda()
|
|
|
+ .eq(TAExamCourseRecord::getExamId,examId)
|
|
|
+ .eq(TAExamCourseRecord::getCourseCode,courseCode));
|
|
|
+
|
|
|
+ // 考察学院
|
|
|
+ Set<Long> inspectCollegeIdSet = dataSource.stream().map(TAExamCourseRecord::getInspectCollegeId).collect(Collectors.toSet());
|
|
|
+
|
|
|
+
|
|
|
+ for (Long inspectCollegeId : inspectCollegeIdSet) {
|
|
|
+ // 算教师排名用
|
|
|
+ List<TAExamCourseCollegeTeacher> rankTempList = new ArrayList<>();
|
|
|
+ // 该考察学院维度下授课教师
|
|
|
+ Set<Long> teacherIdSet = dataSource.stream()
|
|
|
+ .filter(e -> inspectCollegeId.equals(e.getInspectCollegeId()))
|
|
|
+ .map(TAExamCourseRecord::getTeacherId)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ // 教师人数
|
|
|
+ int teacherCount = teacherIdSet.size();
|
|
|
+ for (Long teacherId : teacherIdSet) {
|
|
|
+ // 该教师数据
|
|
|
+ List<TAExamCourseRecord> teacherRecordList = dataSource.stream()
|
|
|
+ .filter(e -> inspectCollegeId.equals(e.getInspectCollegeId()) && teacherId.equals(e.getTeacherId()) && e.getStudentCurrent())
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 学生人数
|
|
|
+ int totalCount = teacherRecordList.size();
|
|
|
+ List<TAExamCourseRecord> teacherEffectiveList = teacherRecordList.stream().filter(e -> !e.getAbsent()).collect(Collectors.toList());
|
|
|
+ int realityCount = teacherEffectiveList.size();
|
|
|
+ if (realityCount == 0){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ int absentCount = totalCount - realityCount;
|
|
|
+
|
|
|
+ // 成绩统计
|
|
|
+ DoubleSummaryStatistics describeStatistic = teacherEffectiveList.stream()
|
|
|
+ .collect(Collectors.summarizingDouble(e -> e.getAssignedScore().doubleValue())); // 学生赋分的描述统计
|
|
|
+ double minScoreAssign = describeStatistic.getMin();
|
|
|
+ double maxScoreAssign = describeStatistic.getMax();
|
|
|
+ double avgScoreAssign = describeStatistic.getAverage();
|
|
|
+ double avgScore = teacherEffectiveList.stream()
|
|
|
+ .collect(Collectors.summarizingDouble(e -> e.getTotalScore().doubleValue())).getAverage();
|
|
|
+ List<Double> assignedScore = teacherEffectiveList.stream().map(e -> e.getAssignedScore().doubleValue()).collect(Collectors.toList());
|
|
|
+ // 计算上四分位数
|
|
|
+ double upperQuartile = MathUtil.calculateQuantile(assignedScore, QuantileEnum.UPPER_QUARTILE.getValue());
|
|
|
+
|
|
|
+ // 计算中位数
|
|
|
+ double median = MathUtil.calculateQuantile(assignedScore, QuantileEnum.MEDIAN.getValue());
|
|
|
+
|
|
|
+ // 计算下四分位数
|
|
|
+ double lowerQuartile = MathUtil.calculateQuantile(assignedScore, QuantileEnum.LOWER_QUARTILE.getValue());
|
|
|
+
|
|
|
+ // 极端情况下分位数值处理
|
|
|
+ if (upperQuartile == 0) {
|
|
|
+ upperQuartile = median;
|
|
|
+ }
|
|
|
+ if (lowerQuartile == 0) {
|
|
|
+ lowerQuartile = median;
|
|
|
+ }
|
|
|
+
|
|
|
+ String mode = MathUtil.calculateMode(assignedScore); // 众数
|
|
|
+ double standardDeviation = MathUtil.calculateStandardDeviation(assignedScore); // 标准差
|
|
|
+
|
|
|
+ double relativePosition = teacherEffectiveList.stream()
|
|
|
+ .collect(Collectors.summarizingDouble(e -> e.getStandardizedCoefficients().doubleValue())).getAverage(); // 相对位置
|
|
|
+
|
|
|
+ // 开课学院数据
|
|
|
+ List<Long> teachCollegeIdList = teacherEffectiveList.stream().map(TAExamCourseRecord::getTeachCollegeId).distinct().collect(Collectors.toList());
|
|
|
+ if (teachCollegeIdList.size() > 1){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("有多个开课学院");
|
|
|
+ }
|
|
|
+ Long teachCollegeId = teachCollegeIdList.get(0);
|
|
|
+ String teachCollegeName = SystemConstant.DEFAULT_SIGN;
|
|
|
+ if (teachCollegeId > 0){
|
|
|
+ teachCollegeName = sysOrgService.getById(teachCollegeId).getName();
|
|
|
+ }
|
|
|
+
|
|
|
+ TAExamCourseCollegeTeacher taExamCourseCollegeTeacher = new TAExamCourseCollegeTeacher();
|
|
|
+ taExamCourseCollegeTeacher.setId(SystemConstant.getDbUuid());
|
|
|
+ taExamCourseCollegeTeacher.setExamId(examId);
|
|
|
+ taExamCourseCollegeTeacher.setSchoolId(tbExamService.getById(examId).getSchoolId());
|
|
|
+ taExamCourseCollegeTeacher.setCourseCode(effectiveCourseCode);
|
|
|
+ taExamCourseCollegeTeacher.setCourseName(basicCourseService.findByCourseCode(effectiveCourseCode).getName());
|
|
|
+ taExamCourseCollegeTeacher.setTeacherId(teacherId);
|
|
|
+ taExamCourseCollegeTeacher.setTeacherName(sysUserService.getById(teacherId).getRealName());
|
|
|
+ taExamCourseCollegeTeacher.setMinScoreAssign(BigDecimal.valueOf(minScoreAssign));
|
|
|
+ taExamCourseCollegeTeacher.setMaxScoreAssign(BigDecimal.valueOf(maxScoreAssign));
|
|
|
+ taExamCourseCollegeTeacher.setAvgScoreAssign(BigDecimal.valueOf(avgScoreAssign));
|
|
|
+ taExamCourseCollegeTeacher.setAvgScore(BigDecimal.valueOf(avgScore));
|
|
|
+ taExamCourseCollegeTeacher.setUpperQuartile(BigDecimal.valueOf(upperQuartile));
|
|
|
+ taExamCourseCollegeTeacher.setMedian(BigDecimal.valueOf(median));
|
|
|
+ taExamCourseCollegeTeacher.setLowerQuartile(BigDecimal.valueOf(lowerQuartile));
|
|
|
+ taExamCourseCollegeTeacher.setMode(mode);
|
|
|
+ taExamCourseCollegeTeacher.setStandardDeviation(BigDecimal.valueOf(standardDeviation));
|
|
|
+ taExamCourseCollegeTeacher.setRelativePosition(BigDecimal.valueOf(relativePosition));
|
|
|
+ taExamCourseCollegeTeacher.setRealityCount(realityCount);
|
|
|
+ taExamCourseCollegeTeacher.setTotalCount(totalCount);
|
|
|
+ taExamCourseCollegeTeacher.setAbsentCount(absentCount);
|
|
|
+ taExamCourseCollegeTeacher.setInspectCollegeId(inspectCollegeId);
|
|
|
+ taExamCourseCollegeTeacher.setInspectCollegeName(sysOrgService.getById(inspectCollegeId).getName());
|
|
|
+ taExamCourseCollegeTeacher.setTeacherCount(teacherCount);
|
|
|
+ taExamCourseCollegeTeacher.setTeachCollegeId(teachCollegeId);
|
|
|
+ taExamCourseCollegeTeacher.setTeachCollegeName(teachCollegeName);
|
|
|
+ rankTempList.add(taExamCourseCollegeTeacher);
|
|
|
+ }
|
|
|
+ // 计算教师排名
|
|
|
+ for (TAExamCourseCollegeTeacher taExamCourseCollegeTeacher : rankTempList) {
|
|
|
+ BigDecimal thisAvgScoreAssign = taExamCourseCollegeTeacher.getAvgScoreAssign();
|
|
|
+ int rank = (int) (rankTempList.stream().filter(e -> e.getAvgScoreAssign().compareTo(thisAvgScoreAssign) > 0).count() + 1);
|
|
|
+ taExamCourseCollegeTeacher.setTeacherRank(rank);
|
|
|
+ taExamCourseCollegeTeacherList.add(taExamCourseCollegeTeacher);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ taExamCourseCollegeTeacherService.saveBatch(taExamCourseCollegeTeacherList);
|
|
|
+
|
|
|
+ }
|
|
|
+ return " 't_a_exam_course_college_teacher' 表构建我那成";
|
|
|
+ }
|
|
|
+
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @Override
|
|
|
+ public String buildAnalyzeExamCourseTeacher(Long examId, String courseCode) {
|
|
|
+ // 可分析有效课程信息
|
|
|
+ List<String> effectiveCourseCodeList = tbExamCourseService.findEffectiveByExamId(examId,courseCode);
|
|
|
+ for (String effectiveCourseCode : effectiveCourseCodeList) {
|
|
|
+ taExamCourseTeacherService.remove(new QueryWrapper<TAExamCourseTeacher>().lambda()
|
|
|
+ .eq(TAExamCourseTeacher::getExamId,examId)
|
|
|
+ .eq(TAExamCourseTeacher::getCourseCode,courseCode));
|
|
|
+
|
|
|
+ List<TAExamCourseTeacher> taExamCourseTeacherList = new ArrayList<>();
|
|
|
+ List<TAExamCourseRecord> dataSource = taExamCourseRecordService.list(new QueryWrapper<TAExamCourseRecord>().lambda()
|
|
|
+ .eq(TAExamCourseRecord::getExamId,examId).eq(TAExamCourseRecord::getCourseCode,effectiveCourseCode));
|
|
|
+ List<TAExamCourseRecord> currentSource = dataSource.stream().filter(TAExamCourseRecord::getStudentCurrent).collect(Collectors.toList());
|
|
|
+
|
|
|
+ Set<Long> teacherIdList = currentSource.stream().map(TAExamCourseRecord::getTeacherId).collect(Collectors.toSet());
|
|
|
+ for (Long teacherId : teacherIdList) {
|
|
|
+ List<TAExamCourseRecord> teacherRecordList = currentSource.stream()
|
|
|
+ .filter(e -> teacherId.equals(e.getTeacherId())).collect(Collectors.toList());
|
|
|
+ int totalCount = teacherRecordList.size();
|
|
|
+ List<TAExamCourseRecord> effectiveList = teacherRecordList.stream().filter(e -> !e.getAbsent()).collect(Collectors.toList());
|
|
|
+ if (effectiveList.size() == 0){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ int realityCount = effectiveList.size();
|
|
|
+ int absentCount = totalCount - realityCount;
|
|
|
+
|
|
|
+ DoubleSummaryStatistics describeStatistic = effectiveList.stream()
|
|
|
+ .collect(Collectors.summarizingDouble(e -> e.getAssignedScore().doubleValue())); // 学生赋分的描述统计
|
|
|
+ double minScore = describeStatistic.getMin();
|
|
|
+ double maxScore = describeStatistic.getMax();
|
|
|
+ double avgScore = describeStatistic.getAverage();
|
|
|
+
|
|
|
+ List<Double> assignedScore = effectiveList.stream().map(e -> e.getAssignedScore().doubleValue()).collect(Collectors.toList());
|
|
|
+ // 计算上四分位数
|
|
|
+ double upperQuartile = MathUtil.calculateQuantile(assignedScore, QuantileEnum.UPPER_QUARTILE.getValue());
|
|
|
+
|
|
|
+ // 计算中位数
|
|
|
+ double median = MathUtil.calculateQuantile(assignedScore, QuantileEnum.MEDIAN.getValue());
|
|
|
+
|
|
|
+ // 计算下四分位数
|
|
|
+ double lowerQuartile = MathUtil.calculateQuantile(assignedScore, QuantileEnum.LOWER_QUARTILE.getValue());
|
|
|
+
|
|
|
+ // 极端情况下分位数值处理
|
|
|
+ if (upperQuartile == 0) {
|
|
|
+ upperQuartile = median;
|
|
|
+ }
|
|
|
+ if (lowerQuartile == 0) {
|
|
|
+ lowerQuartile = median;
|
|
|
+ }
|
|
|
+ String mode = MathUtil.calculateMode(assignedScore); // 众数
|
|
|
+ double standardDeviation = MathUtil.calculateStandardDeviation(assignedScore); // 标准差
|
|
|
+
|
|
|
+ // 相对位置
|
|
|
+ double relativePosition = effectiveList.stream()
|
|
|
+ .collect(Collectors.summarizingDouble(e -> e.getStandardizedCoefficients().doubleValue())).getAverage();
|
|
|
+
|
|
|
+
|
|
|
+ TAExamCourseTeacher taExamCourseTeacher = new TAExamCourseTeacher();
|
|
|
+ taExamCourseTeacher.setId(SystemConstant.getDbUuid());
|
|
|
+ taExamCourseTeacher.setExamId(examId);
|
|
|
+ taExamCourseTeacher.setSchoolId(tbExamService.getById(examId).getSchoolId());
|
|
|
+ taExamCourseTeacher.setCourseCode(effectiveCourseCode);
|
|
|
+ taExamCourseTeacher.setCourseName(basicCourseService.findByCourseCode(effectiveCourseCode).getName());
|
|
|
+ taExamCourseTeacher.setTeacherId(teacherId);
|
|
|
+ taExamCourseTeacher.setTeacherName(sysUserService.getById(teacherId).getRealName());
|
|
|
+ taExamCourseTeacher.setMinScore(BigDecimal.valueOf(minScore));
|
|
|
+ taExamCourseTeacher.setMaxScore(BigDecimal.valueOf(maxScore));
|
|
|
+ taExamCourseTeacher.setAvgScore(BigDecimal.valueOf(avgScore));
|
|
|
+ taExamCourseTeacher.setUpperQuartile(BigDecimal.valueOf(upperQuartile));
|
|
|
+ taExamCourseTeacher.setMedian(BigDecimal.valueOf(median));
|
|
|
+ taExamCourseTeacher.setLowerQuartile(BigDecimal.valueOf(lowerQuartile));
|
|
|
+ taExamCourseTeacher.setMode(mode);
|
|
|
+ taExamCourseTeacher.setStandardDeviation(BigDecimal.valueOf(standardDeviation));
|
|
|
+ taExamCourseTeacher.setRelativePosition(BigDecimal.valueOf(relativePosition));
|
|
|
+ taExamCourseTeacher.setTotalCount(totalCount);
|
|
|
+ taExamCourseTeacher.setRealityCount(realityCount);
|
|
|
+ taExamCourseTeacher.setAbsentCount(absentCount);
|
|
|
+ taExamCourseTeacherList.add(taExamCourseTeacher);
|
|
|
+ }
|
|
|
+ taExamCourseTeacherService.saveBatch(taExamCourseTeacherList);
|
|
|
+ }
|
|
|
+ return " 't_a_exam_course_teacher'表构建完毕 ";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算百分位等级
|
|
|
+ *
|
|
|
+ * @param lowCount 低于我的成绩的人数
|
|
|
+ * @param sameCount 和我成绩相同的人数
|
|
|
+ * @param totalCount 总人数
|
|
|
+ * @return 百分位等级
|
|
|
+ */
|
|
|
+ private int handlePercentGrade(BigDecimal lowCount, BigDecimal sameCount, BigDecimal totalCount) {
|
|
|
+ // ((lowCount + sameCount * 0.5) / totalCount) * 100
|
|
|
+ BigDecimal percentGrade = (lowCount.add(sameCount.multiply(new BigDecimal("0.5"))))
|
|
|
+ .divide(totalCount,4,BigDecimal.ROUND_HALF_DOWN).multiply(new BigDecimal("100"));
|
|
|
+ int result = percentGrade.setScale(0,BigDecimal.ROUND_HALF_DOWN).intValue();
|
|
|
+ if (result == 100) {
|
|
|
+ result = 99;
|
|
|
+ }
|
|
|
+ if (result == 0) {
|
|
|
+ result = 1;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理计算百分等级和难度等级档位
|
|
|
+ *
|
|
|
+ * @param type 档位类型(百分等级、难度系数)
|
|
|
+ * @param value 值
|
|
|
+ * @return 档位
|
|
|
+ */
|
|
|
+ private String handleLevel(Long examId,String courseCode, String type, double value) {
|
|
|
+ List<TBCommonLevelConfig> levelList = tbCommonLevelConfigService.list(new QueryWrapper<TBCommonLevelConfig>().lambda()
|
|
|
+ .eq(TBCommonLevelConfig::getExamId,examId)
|
|
|
+ .eq(TBCommonLevelConfig::getCourseCode,courseCode)
|
|
|
+ .eq(TBCommonLevelConfig::getLevelType,type));
|
|
|
+ String level = "";
|
|
|
+ if ("百分等级".equals(type) || "难度等级".equals(type)) {
|
|
|
+ for (TBCommonLevelConfig levelTemp : levelList) {
|
|
|
+ Map<String, Object> scopeMap = AnalyzeScopeUtil.analyzeScope(levelTemp.getScope());
|
|
|
+ if ("(".equals(scopeMap.get("minSign")) && ")".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) < value && value < Double.parseDouble(String.valueOf(scopeMap.get("maxValue")))) {
|
|
|
+ level = levelTemp.getInterpret();
|
|
|
+ }
|
|
|
+ } else if ("(".equals(scopeMap.get("minSign")) && "]".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) < value && value <= Double.parseDouble(String.valueOf(scopeMap.get("maxValue")))) {
|
|
|
+ level = levelTemp.getInterpret();
|
|
|
+ }
|
|
|
+ } else if ("[".equals(scopeMap.get("minSign")) && ")".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) <= value && value < Double.parseDouble(String.valueOf(scopeMap.get("maxValue")))) {
|
|
|
+ level = levelTemp.getInterpret();
|
|
|
+ }
|
|
|
+ } else if ("[".equals(scopeMap.get("minSign")) && "]".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) <= value && value <= Double.parseDouble(String.valueOf(scopeMap.get("maxValue")))) {
|
|
|
+ level = levelTemp.getInterpret();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (level == null || level.length() == 0) {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("范围解析异常");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("没有进行计算的等级类型 : " + type);
|
|
|
+ }
|
|
|
+ return level;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建和学院比较的有关信息(超过学院百分比、排名、排名等级)
|
|
|
+ * @param taExamCourseRecord 该学生数据
|
|
|
+ * @param taExamCourseRecordList 该课程下学生数据
|
|
|
+ */
|
|
|
+ private void buildColData(TAExamCourseRecord taExamCourseRecord, List<TAExamCourseRecord> taExamCourseRecordList) {
|
|
|
+
|
|
|
+ //查询出该学生所在学院的全体学生在该科目的考试情况
|
|
|
+ List<TAExamCourseRecord> examRecordSameCol = taExamCourseRecordList.stream()
|
|
|
+ .filter(e -> taExamCourseRecord.getInspectCollegeId().equals(e.getInspectCollegeId())).collect(Collectors.toList()); // 同考察学院考生数据
|
|
|
+
|
|
|
+ //计算超过学院百分比
|
|
|
+ double lowCount = examRecordSameCol.stream().filter(e -> e.getAssignedScore().compareTo(taExamCourseRecord.getAssignedScore()) < 0).count();
|
|
|
+ BigDecimal overCollegeRate;
|
|
|
+ if (examRecordSameCol.size() > 1) {
|
|
|
+ overCollegeRate = BigDecimal.valueOf(lowCount).divide(BigDecimal.valueOf(examRecordSameCol.size() - 1),4,BigDecimal.ROUND_HALF_DOWN);
|
|
|
+ } else {
|
|
|
+ overCollegeRate = BigDecimal.ONE;
|
|
|
+ }
|
|
|
+ taExamCourseRecord.setOverCollegeRate(overCollegeRate);
|
|
|
+
|
|
|
+ //计算在学院的排名
|
|
|
+ double overCount = examRecordSameCol.stream().filter(e -> e.getAssignedScore().compareTo(taExamCourseRecord.getAssignedScore()) > 0).count();
|
|
|
+ double myNumber = overCount + 1;
|
|
|
+ taExamCourseRecord.setColRank((int) myNumber);
|
|
|
+
|
|
|
+ //计算在学院排名的档次
|
|
|
+ double size = examRecordSameCol.size();
|
|
|
+ List<TBCommonRankLevelConfig> tbCommonRankLevelConfigList = tbCommonRankLevelConfigService.list(new QueryWrapper<TBCommonRankLevelConfig>().lambda()
|
|
|
+ .eq(TBCommonRankLevelConfig::getExamId,taExamCourseRecord.getExamId())
|
|
|
+ .eq(TBCommonRankLevelConfig::getCourseCode,taExamCourseRecord.getCourseCode()));
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if (tbCommonRankLevelConfigList.size() < 1) {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("试卷基础信息不匹配");
|
|
|
+ }
|
|
|
+ String myLevel = "";
|
|
|
+ if (size < tbCommonRankLevelConfigList.size()) {
|
|
|
+ myLevel = String.valueOf(tbCommonRankLevelConfigList.get((int) (myNumber - 1)).getLevel());
|
|
|
+ } else {
|
|
|
+ for (TBCommonRankLevelConfig temp : tbCommonRankLevelConfigList) {
|
|
|
+
|
|
|
+ Map<String, Object> scopeMap = AnalyzeScopeUtil.analyzeScope(temp.getScope());
|
|
|
+ if ("(".equals(scopeMap.get("minSign")) && ")".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) * size < myNumber && myNumber < Double.parseDouble(String.valueOf(scopeMap.get("maxValue"))) * size) {
|
|
|
+ myLevel = temp.getLevel();
|
|
|
+ }
|
|
|
+ } else if ("(".equals(scopeMap.get("minSign")) && "]".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) * size < myNumber && myNumber <= Double.parseDouble(String.valueOf(scopeMap.get("maxValue"))) * size) {
|
|
|
+ myLevel = temp.getLevel();
|
|
|
+ }
|
|
|
+ } else if ("[".equals(scopeMap.get("minSign")) && ")".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) * size <= myNumber && myNumber < Double.parseDouble(String.valueOf(scopeMap.get("maxValue"))) * size) {
|
|
|
+ myLevel = temp.getLevel();
|
|
|
+ }
|
|
|
+ } else if ("[".equals(scopeMap.get("minSign")) && "]".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) * size <= myNumber && myNumber <= Double.parseDouble(String.valueOf(scopeMap.get("maxValue"))) * size) {
|
|
|
+ myLevel = temp.getLevel();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (myLevel == null || myLevel.length() == 0) {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("范围解析异常");
|
|
|
+ }
|
|
|
+ taExamCourseRecord.setRankLevel(myLevel);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算每个考察点所对应的该考试所有题目信息集合(如果要仅记录本次考试匹配到的考察点,那么要判断只有当paperList.size() > 0的结果才put进Map键值对)
|
|
|
+ * 通过模块枚举类进行匹配,可以在枚举类横向扩展维度但要和paper表结构对应
|
|
|
+ *
|
|
|
+ * @param dimensionDatasource 知识点数据源
|
|
|
+ * @param tbPaperStructList 试卷结构数据源
|
|
|
+ * @param examId 考试id
|
|
|
+ * @param courseCode 课程编号
|
|
|
+ * @return 键值对 (类型-考察点 -> 考察此考察点的题目信息集合) 例如 :”知识模块-A“,List<>
|
|
|
+ */
|
|
|
+ private Map<String, List<TBPaperStruct>> handlePointToPaper(List<TBDimension> dimensionDatasource, List<TBPaperStruct> tbPaperStructList, Long examId, String courseCode) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
|
|
+ Map<String, List<TBPaperStruct>> pointToPaper = new HashMap<>();
|
|
|
+
|
|
|
+ // 模块配置信息
|
|
|
+ List<TBModuleConfig> tbModuleConfigList = tbModuleConfigService.findDistinctModuleInfoByExamIdAndCourseCode(examId, courseCode);
|
|
|
+ if (tbModuleConfigList.size() < 1){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("基础模块信息异常");
|
|
|
+ }
|
|
|
+
|
|
|
+ for (TBModuleConfig value : tbModuleConfigList) {
|
|
|
+
|
|
|
+ //每个考察点所对应的题目集合
|
|
|
+ List<TBDimension> pointList = dimensionDatasource
|
|
|
+ .stream().filter(e -> value.getModuleType().equals(e.getDimensionType())).collect(Collectors.toList()); //对应某个模块的考察点集合
|
|
|
+
|
|
|
+ Set<String> keysOne = pointList.stream().map(TBDimension::getCodePrimary).collect(Collectors.toSet()); //考察点一级维度不重复的code集合
|
|
|
+ Set<String> keysTwo = pointList.stream().map(TBDimension::getCodeSecond).collect(Collectors.toSet()); //考察点二级维度不重复的code集合
|
|
|
+
|
|
|
+ for (String tmpOne : keysOne) {
|
|
|
+ List<TBPaperStruct> paperStructList = new ArrayList<>();
|
|
|
+ for (TBPaperStruct paperStruct : tbPaperStructList) {
|
|
|
+ Field field = paperStruct.getClass().getDeclaredField(value.getAttribute()); //获取该模块对应试卷结构中的属性
|
|
|
+ Method getMethod = paperStruct.getClass().getDeclaredMethod("get" + ConversionUtils.initCap(field.getName())); //获取该属性的get方法
|
|
|
+ String dimensions = (String) getMethod.invoke(paperStruct); //调用该属性的get方法获取改题目的考察点
|
|
|
+ if (dimensions != null && dimensions.length() > 0) {
|
|
|
+ if (dimensions.contains(tmpOne)) {
|
|
|
+ paperStructList.add(paperStruct); //如果该题目包含考察点,则记录该题目
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (paperStructList.size() > 0) {
|
|
|
+ // 只记录试卷结构中出现的考察点包含的题目
|
|
|
+ pointToPaper.put(value.getModuleType() + "-" + tmpOne, paperStructList);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (String tmpTwo : keysTwo) {
|
|
|
+ List<TBPaperStruct> paperStructList = new ArrayList<>();
|
|
|
+ for (TBPaperStruct paperStruct : tbPaperStructList) {
|
|
|
+ Field field = paperStruct.getClass().getDeclaredField(value.getAttribute());
|
|
|
+ Method getMethod = paperStruct.getClass().getDeclaredMethod("get" + ConversionUtils.initCap(field.getName()));
|
|
|
+ String dimensions = (String) getMethod.invoke(paperStruct);
|
|
|
+ if (dimensions != null && dimensions.length() > 0) {
|
|
|
+ if (Arrays.asList(dimensions.split(",")).contains(tmpTwo)) {
|
|
|
+ paperStructList.add(paperStruct);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (paperStructList.size() > 0) {
|
|
|
+ // 只记录试卷结构中出现的考察点包含的题目
|
|
|
+ pointToPaper.put(value.getModuleType() + "-" + tmpTwo, paperStructList);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return pointToPaper;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算每个考察模块对应的题目集合
|
|
|
+ *
|
|
|
+ * @param dimensionDatasource 知识点数据源
|
|
|
+ * @param tbPaperStructList 试卷结构数据源
|
|
|
+ * @param examId 考试id
|
|
|
+ * @param courseCode 课程编号
|
|
|
+ * @return 键值对 (考察模块 -> 考察此考模块的题目信息集合) 例如 :”知识模块“,List<>
|
|
|
+ * @throws Exception 反射异常
|
|
|
+ */
|
|
|
+ private Map<String, List<TBPaperStruct>> handleModuleToPaper(List<TBDimension> dimensionDatasource, List<TBPaperStruct> tbPaperStructList, Long examId, String courseCode) throws Exception {
|
|
|
+ Map<String, List<TBPaperStruct>> result = new HashMap<>();
|
|
|
+
|
|
|
+ // 模块配置信息
|
|
|
+ List<TBModuleConfig> tbModuleConfigList = tbModuleConfigService.findDistinctModuleInfoByExamIdAndCourseCode(examId, courseCode);
|
|
|
+ if (tbModuleConfigList.size() < 1){
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("基础模块信息异常");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<String> realityTypes = dimensionDatasource.stream().map(TBDimension::getDimensionType).collect(Collectors.toList()); // 以知识点表中的模块类型为准
|
|
|
+// int tmp = 0;
|
|
|
+ for (TBModuleConfig module : tbModuleConfigList) {
|
|
|
+ String moduleType = module.getModuleType(); // 模块名称
|
|
|
+ if (realityTypes.contains(moduleType)) {
|
|
|
+// tmp++;
|
|
|
+ String attribute = module.getAttribute(); // 对应paper表列名称
|
|
|
+ List<TBPaperStruct> paperStructList = new ArrayList<>();
|
|
|
+
|
|
|
+ for (TBPaperStruct paperStruct : tbPaperStructList) {
|
|
|
+ Field field = paperStruct.getClass().getDeclaredField(attribute);
|
|
|
+ Method getMethod = paperStruct.getClass().getDeclaredMethod("get" + ConversionUtils.initCap(field.getName()));
|
|
|
+ String value = (String) getMethod.invoke(paperStruct);
|
|
|
+ if (value != null && value.length() != 0) {
|
|
|
+ paperStructList.add(paperStruct);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result.put(moduleType, paperStructList);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算每个模块或考察点在该考试中的分数
|
|
|
+ * (其实就是计算试题集合总分)
|
|
|
+ *
|
|
|
+ * @param questionMap 考察点或模块对应的题目信息集合
|
|
|
+ * @return 键值对 (考察点 -> 该考察点在此次考试所占分数) 例如 :”知识模块-A“,46.0000、(模块 -> 该考察点在此次考试所占分数) 例如 :”能力模块“,75.0000
|
|
|
+ */
|
|
|
+ private Map<String, Object> handleEveryPointTotalScore(Map<String, List<TBPaperStruct>> questionMap) {
|
|
|
+ Map<String, Object> result = new HashMap<>();
|
|
|
+ for (String s : questionMap.keySet()) {
|
|
|
+ List<TBPaperStruct> paperList = questionMap.get(s);
|
|
|
+ double totalScore = paperList.stream().collect(Collectors.summarizingDouble(e -> e.getFullScore().doubleValue())).getSum();
|
|
|
+ result.put(s, totalScore);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据考察点得分率计算该考察点的熟练度
|
|
|
+ *
|
|
|
+ * @param examId 考试id
|
|
|
+ * @param courseCode 课程编号
|
|
|
+ * @param dimensionType 考察点类型
|
|
|
+ * @param value 得分率
|
|
|
+ * @return 熟练度等级
|
|
|
+ */
|
|
|
+ private String handleModuleProficiency(Long examId, String courseCode, String dimensionType, double value) {
|
|
|
+ List<TBModuleProficiency> tbModuleProficiencyList = tbModuleProficiencyService.list(new QueryWrapper<TBModuleProficiency>().lambda()
|
|
|
+ .eq(TBModuleProficiency::getExamId,examId)
|
|
|
+ .eq(TBModuleProficiency::getCourseCode,courseCode)
|
|
|
+ .eq(TBModuleProficiency::getModuleType,dimensionType));
|
|
|
+
|
|
|
+ if (tbModuleProficiencyList.size() < 1) {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("模块二级维度熟练度配置信息不存在");
|
|
|
+ }
|
|
|
+ String level = "";
|
|
|
+ for (TBModuleProficiency tbModuleProficiency : tbModuleProficiencyList) {
|
|
|
+ if (tbModuleProficiency.getScope() == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Map<String, Object> scopeMap = AnalyzeScopeUtil.analyzeScope(tbModuleProficiency.getScope());
|
|
|
+ if ("(".equals(scopeMap.get("minSign")) && ")".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) < value && value < Double.parseDouble(String.valueOf(scopeMap.get("maxValue")))) {
|
|
|
+ level = tbModuleProficiency.getLevel();
|
|
|
+ }
|
|
|
+ } else if ("(".equals(scopeMap.get("minSign")) && "]".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) < value && value <= Double.parseDouble(String.valueOf(scopeMap.get("maxValue")))) {
|
|
|
+ level = tbModuleProficiency.getLevel();
|
|
|
+ }
|
|
|
+ } else if ("[".equals(scopeMap.get("minSign")) && ")".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) <= value && value < Double.parseDouble(String.valueOf(scopeMap.get("maxValue")))) {
|
|
|
+ level = tbModuleProficiency.getLevel();
|
|
|
+ }
|
|
|
+ } else if ("[".equals(scopeMap.get("minSign")) && "]".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) <= value && value <= Double.parseDouble(String.valueOf(scopeMap.get("maxValue")))) {
|
|
|
+ level = tbModuleProficiency.getLevel();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return level;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理学生在该模块成绩在该学院的排名等级
|
|
|
+ *
|
|
|
+ * @param examId 考试id
|
|
|
+ * @param courseCode 考试编号
|
|
|
+ * @param module 模块类型
|
|
|
+ * @param value 值(排名、得分率、百分等级)
|
|
|
+ * @param size 该学院最低分排名(当其不为排名时,赋值-1)
|
|
|
+ * @return 排名等级编号
|
|
|
+ */
|
|
|
+ private String handleModuleRankLevel(Long examId, String courseCode, String module, double value, double size) {
|
|
|
+ List<TBModuleConfig> tbModuleConfigList = tbModuleConfigService.list(new QueryWrapper<TBModuleConfig>().lambda()
|
|
|
+ .eq(TBModuleConfig::getExamId,examId)
|
|
|
+ .eq(TBModuleConfig::getCourseCode,courseCode)
|
|
|
+ .eq(TBModuleConfig::getModuleType,module));
|
|
|
+
|
|
|
+ String result = null;
|
|
|
+ if (size < tbModuleConfigList.size() && size > 0) {
|
|
|
+ tbModuleConfigList = tbModuleConfigList.stream()
|
|
|
+ .sorted((o1, o2) -> o1.getLevelCode().compareToIgnoreCase(o2.getLevelCode()))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ result = tbModuleConfigList.get((int) (size - 1)).getLevelCode();
|
|
|
+ } else {
|
|
|
+ if (size < 0) {
|
|
|
+ size = -size;
|
|
|
+ }
|
|
|
+ for (TBModuleConfig tbModuleConfig : tbModuleConfigList) {
|
|
|
+ Map<String, Object> scopeMap = AnalyzeScopeUtil.analyzeScope(tbModuleConfig.getScope());
|
|
|
+ if ("(".equals(scopeMap.get("minSign")) && ")".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) * size < value && value < Double.parseDouble(String.valueOf(scopeMap.get("maxValue"))) * size) {
|
|
|
+ result = tbModuleConfig.getLevelCode();
|
|
|
+ }
|
|
|
+ } else if ("(".equals(scopeMap.get("minSign")) && "]".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) * size < value && value <= Double.parseDouble(String.valueOf(scopeMap.get("maxValue"))) * size) {
|
|
|
+ result = tbModuleConfig.getLevelCode();
|
|
|
+ }
|
|
|
+ } else if ("[".equals(scopeMap.get("minSign")) && ")".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) * size <= value && value < Double.parseDouble(String.valueOf(scopeMap.get("maxValue"))) * size) {
|
|
|
+ result = tbModuleConfig.getLevelCode();
|
|
|
+ }
|
|
|
+ } else if ("[".equals(scopeMap.get("minSign")) && "]".equals(scopeMap.get("maxSign"))) {
|
|
|
+ if (Double.parseDouble(String.valueOf(scopeMap.get("minValue"))) * size <= value && value <= Double.parseDouble(String.valueOf(scopeMap.get("maxValue"))) * size) {
|
|
|
+ result = tbModuleConfig.getLevelCode();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (result == null || result.length() == 0) {
|
|
|
+ throw ExceptionResultEnum.ERROR.exception("范围解析异常");
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据得分率计算每道题目的难度
|
|
|
+ *
|
|
|
+ * @param examId 考试id
|
|
|
+ * @param courseCode 课程编号
|
|
|
+ * @param scoreRate 题目得分率
|
|
|
+ * @return 难度
|
|
|
+ */
|
|
|
+ private String analyzeDifficult(Long examId, String courseCode, double scoreRate) {
|
|
|
+ List<TBCommonLevelConfig> levelConfigList = tbCommonLevelConfigService.list(new QueryWrapper<TBCommonLevelConfig>().lambda()
|
|
|
+ .eq(TBCommonLevelConfig::getExamId,examId)
|
|
|
+ .eq(TBCommonLevelConfig::getCourseCode,courseCode)
|
|
|
+ .eq(TBCommonLevelConfig::getLevelType,"难度等级"));
|
|
|
+ String difficult = "";
|
|
|
+ for (TBCommonLevelConfig levelConfig : levelConfigList) {
|
|
|
+ Map<String, Object> scopeMap = AnalyzeScopeUtil.analyzeScope(levelConfig.getScope());
|
|
|
+ String minSign = String.valueOf(scopeMap.get("minSign"));
|
|
|
+ String maxSign = String.valueOf(scopeMap.get("maxSign"));
|
|
|
+ double minValue = Double.parseDouble(String.valueOf(scopeMap.get("minValue")));
|
|
|
+ double maxValue = Double.parseDouble(String.valueOf(scopeMap.get("maxValue")));
|
|
|
+ if ("(".equals(minSign) && ")".equals(maxSign)) {
|
|
|
+ if (minValue < scoreRate && scoreRate < maxValue) {
|
|
|
+ difficult = levelConfig.getInterpret();
|
|
|
+ }
|
|
|
+ } else if ("(".equals(minSign) && "]".equals(maxSign)) {
|
|
|
+ if (minValue < scoreRate && scoreRate <= maxValue) {
|
|
|
+ difficult = levelConfig.getInterpret();
|
|
|
+ }
|
|
|
+ } else if ("[".equals(minSign) && ")".equals(maxSign)) {
|
|
|
+ if (minValue <= scoreRate && scoreRate < maxValue) {
|
|
|
+ difficult = levelConfig.getInterpret();
|
|
|
+ }
|
|
|
+ } else if ("[".equals(minSign) && "]".equals(maxSign)) {
|
|
|
+ if (minValue <= scoreRate && scoreRate <= maxValue) {
|
|
|
+ difficult = levelConfig.getInterpret();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (difficult == null || difficult.length() < 1) {
|
|
|
+ difficult = "容易";
|
|
|
+ }
|
|
|
+ return difficult;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算某个小题的题目鉴别度
|
|
|
+ *
|
|
|
+ * @param oneQuestionAnswerDetailList 某个小题的所有参考学生作答情况列表(且按照参考学生百分等级降序排列)
|
|
|
+ * @param fullScore 该小题总分
|
|
|
+ * @return 鉴别度
|
|
|
+ */
|
|
|
+ private double calculateValidity(List<ValidAnswerDetailDto> oneQuestionAnswerDetailList, double fullScore) {
|
|
|
+ List<ValidAnswerDetailDto> sortList = oneQuestionAnswerDetailList.stream().sorted(((o1, o2) -> {
|
|
|
+ double compare1 = o1.getPercentGrade();
|
|
|
+ double compare2 = o2.getPercentGrade();
|
|
|
+ return Double.compare(compare2, compare1);
|
|
|
+ })).collect(Collectors.toList());
|
|
|
+
|
|
|
+ int rank = 0;
|
|
|
+ int number = 0;
|
|
|
+ double value = 10000;
|
|
|
+ for (ValidAnswerDetailDto temp : sortList) {
|
|
|
+ double compareValue = temp.getPercentGrade();
|
|
|
+ number++;
|
|
|
+ if (compareValue < value) {
|
|
|
+ rank = number;
|
|
|
+ value = compareValue;
|
|
|
+ }
|
|
|
+ temp.setRank(rank);
|
|
|
+ }
|
|
|
+ double endRank = sortList.get(sortList.size() - 1).getRank();
|
|
|
+ double topRank = ValidityEnum.VALIDITY_FOR_RANK.getTopLimitPercent() * endRank;
|
|
|
+ double lowRank = ValidityEnum.VALIDITY_FOR_RANK.getLowLimitPercent() * endRank;
|
|
|
+
|
|
|
+ // 计算题目效度
|
|
|
+ double topScopeAvgScoreRate = sortList.stream()
|
|
|
+ .filter(e -> topRank >= e.getRank())
|
|
|
+ .collect(Collectors.summarizingDouble(e -> e.getScore().doubleValue()))
|
|
|
+ .getAverage() / fullScore; // 优秀部分统计有效区间平均得分率
|
|
|
+
|
|
|
+ double lowScopeAvgScoreRate = sortList.stream()
|
|
|
+ .filter(e -> lowRank <= e.getRank())
|
|
|
+ .collect(Collectors.summarizingDouble(e -> e.getScore().doubleValue()))
|
|
|
+ .getAverage() / fullScore; // 较差部分统计有效区间平均得分率
|
|
|
+
|
|
|
+ return topScopeAvgScoreRate - lowScopeAvgScoreRate;
|
|
|
+ }
|
|
|
+}
|