TeachingJunior.php 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234
  1. <?php
  2. class TeachingJunior
  3. {
  4. private $subjectId;
  5. private $schoolId = 0;
  6. private $examId = 0;
  7. private $classId = 0;
  8. private $examGroupId = 0;
  9. private $error = array();
  10. private $controllerObj = null;//controller对象
  11. private $examObj = null;
  12. private $sprObj = null;
  13. private $sptRsObj = null;
  14. //TODO 全学科题库 题型方法对应栏目id
  15. const FULLTEXT_COLUMN_ID = 163; //方法解读富文本
  16. const CLASSIC_COLUMN_ID = 224; //典型例题
  17. const SPECIAL_COLUMN_ID = 225; //方法专练
  18. const DIFFICULT_COLUMN_ID = 227; //难度
  19. //题型方法难度
  20. static $difficult = array(
  21. 719 => '简单',
  22. 720 => '中等',
  23. 721 => '难'
  24. );
  25. public function __construct(&$controllerObj)
  26. {
  27. $this->controllerObj = $controllerObj;
  28. }
  29. /**
  30. * 生成PDF返回PDF地址
  31. * @param $schoolId
  32. * @param $examId
  33. * @param int $force
  34. * @param int $viewHtml
  35. * @param int $isWb
  36. * @return array
  37. */
  38. public function getTeachingPdf($schoolId,$examId,$force = 0,$viewHtml = 0, $isWb = 0)
  39. {
  40. if (!$schoolId) {
  41. $this->errorMsg(0, '学校ID不正确!');
  42. }
  43. $this->schoolId = $schoolId;
  44. if(!$examId || !is_numeric($examId)){
  45. $this->errorMsg(0, '考试ID参数不正确!');
  46. }
  47. if (!$this->error) {
  48. $force = $viewHtml ? 1 : 0;
  49. if (is_cli()) {
  50. $force = 1;
  51. }
  52. $this->examId = $examId;
  53. $this->examObj = new Exam();
  54. $examInfo = $this->examObj->getTeachingInfo($examId);
  55. if(!$examInfo){
  56. $this->errorMsg(0, '没有考试信息!');
  57. }
  58. }
  59. if (!$this->error) {
  60. if($examInfo['subject_id']){
  61. $this->subjectId = $examInfo['subject_id'];
  62. $this->examGroupId = $examInfo['exam_group_id'];
  63. $this->classId = $examInfo['class_id'];
  64. }
  65. // $pdfurl = $examInfo['academicr_pdf_path'];
  66. $res = array();//生成pdf命令结果
  67. // if(strpos($examInfo['academicr_pdf_path'],'http') !== false){
  68. // if(!file_get_contents($examInfo['academicr_pdf_path'])){
  69. // $force = 1;
  70. // }
  71. // }else{
  72. // $force = 1;
  73. // }
  74. //
  75. // if ($examInfo['academicr_pdf_path'] && !$force) {
  76. // //文件已生成不再生成
  77. // $result = array(
  78. // 'status' => 0,
  79. // 'error' => '',
  80. // 'is_create' => 1,
  81. // 'pdf_url' => $pdfurl,
  82. // 'pdf_path' => '',
  83. // );
  84. //
  85. // return $result;
  86. // }
  87. }
  88. if (!$this->error) {
  89. $paperObj = new SPaper();
  90. $ptrObj = new SPaperTopicRelation();
  91. $this->sprObj = new SStudentPaperRelation();
  92. $this->sptRsObj = new SStudentPaperTopicRs();
  93. /**************************** 一、班级情况分析 ***********************************/
  94. //当前考试需要的数据
  95. $actionRs = $this->curExamNeedData($this->sprObj ,$paperObj);
  96. if ($actionRs) {
  97. list($paperId, $paperScore, $examStuScore) = $actionRs;
  98. }
  99. //上次考试需要的数据
  100. if (!$this->error) {
  101. $actionRs = $this->preExamNeedData($examInfo,$this->sprObj,$paperObj);
  102. if ($actionRs) {
  103. list($prePaperScore, $preExamStuScore) = $actionRs;
  104. }
  105. }
  106. //班级排名得分率人数等数据
  107. if (!$this->error) {
  108. $classAnalyseArr = $this->getClassAnalyse($paperScore,$examStuScore,$prePaperScore, $preExamStuScore);
  109. }
  110. //大幅进步,大幅退步,前5名和后5名($needStudentArr:得分大于0的学生)
  111. if (!$this->error) {
  112. list($largeOrderArr,$needStudentArr) = $this->getLargeOrder($examStuScore,$preExamStuScore);
  113. }
  114. unset($preExamStuScore);
  115. /**************************** 二、考试情况分析 ***********************************/
  116. if (!$this->error) {
  117. $paperTopicInfo = $ptrObj->getPaperInfo($paperId);
  118. if(!$paperTopicInfo){
  119. $this->errorMsg(0, '没有试卷题数据!');
  120. }else{
  121. $topicScoreArr = array();
  122. foreach ($paperTopicInfo as $item) {
  123. $topicScoreArr[$item['topic_id']] = $item['score'];
  124. }
  125. $tplDataArr = json_decode($examInfo['tpl_data'], true);
  126. $topicNoArr = $this->getTopicNoArr($paperTopicInfo, $tplDataArr);
  127. unset($paperTopicInfo);
  128. }
  129. }
  130. // 学生得分
  131. if (!$this->error) {
  132. $stuRsArr = $this->sptRsObj->getStudentRsOnePaper($paperId);
  133. if(!$stuRsArr){
  134. $this->errorMsg(0, '没有学生做题数据!');
  135. }
  136. }
  137. if (!$this->error) {
  138. $paperAnalyseArr = $this->paperAnalyse($topicNoArr,$stuRsArr,$needStudentArr,$topicScoreArr);
  139. }
  140. /**************************** 三、知识点掌握情况 ***********************************/
  141. //TODO 题型方法考察情况
  142. if (!$this->error) {
  143. $topicInfoArr = $this->getTopicInfoArr($topicNoArr);
  144. }
  145. // if (!$this->error) {
  146. // $kpsAnalyseArr = $this->kpsAnalyse($topicInfoArr,$topicScoreArr,$topicNoArr,$stuRsArr,$needStudentArr);
  147. // }
  148. /**************************** 四、试卷讲评 ***********************************/
  149. if (!$this->error) {
  150. $paperCommentsArr = $this->paperComments($needStudentArr,$stuRsArr,$topicScoreArr,$topicInfoArr,$topicNoArr);
  151. }
  152. if(!$this->error){
  153. list($methodAnalyseArr,$methodTopics) = $this->methodAnalyse($topicInfoArr,$paperCommentsArr);
  154. }
  155. /**************************** 五、共性题 ***********************************/
  156. if(!$this->error){
  157. list($commonMethods,$commonTopicIds) = $this->commonTopic($methodAnalyseArr);
  158. $commonTopics = $commonTopicIds + $methodTopics;
  159. $commonTopics = $this->getTopicInfoArr($commonTopics);
  160. }
  161. /**************************** 生产HTML和PDF ***********************************/
  162. if (!$this->error) {
  163. //学生名称
  164. $stuInfoObj = new SStudentInfo();
  165. $stuNameArr = $stuInfoObj->getStudentNames(array_keys($needStudentArr));
  166. $classObj = new SClass();
  167. $className = $classObj->getClassName($this->classId);
  168. $data = array();
  169. $data['className'] = $className;
  170. $data['examInfo'] = $examInfo;
  171. $data['stuNameArr'] = $stuNameArr;
  172. $data['classAnalyseArr'] = $classAnalyseArr;
  173. $data['largeOrderArr'] = $largeOrderArr;
  174. $data['paperAnalyseArr'] = $paperAnalyseArr;
  175. $data['paperCommentsArr'] = $paperCommentsArr;
  176. $data['topicInfoArr'] = $topicInfoArr;
  177. $data['stuNameArr'] = $stuNameArr;
  178. $data['methodAnalyseArr'] = $methodAnalyseArr;
  179. $data['commonTopics'] = $commonTopics;
  180. $data['commonMethods'] = $commonMethods;
  181. $data['zsyas2Url'] = $this->getDomain();
  182. $data['isWb'] = $isWb;
  183. $html = $this->controllerObj->renderPartial("/junior/teach", $data, true);
  184. // if (is_cli()) {
  185. // $html = $this->controllerObj->viewRender("teach_physics", $data, true);
  186. // }else{
  187. // if ($viewHtml) {
  188. // $this->controllerObj->renderPartial("teaching/teach_physics",$data);exit();
  189. // }else{
  190. // $html = $this->controllerObj->renderPartial("teaching/teach_physics", $data, true);
  191. // }
  192. // }
  193. unset($data);
  194. $actionRs = $this->htmlToPdf($res,$html,$className,$examInfo['exam_name']);
  195. if ($actionRs) {
  196. list($pdfurl, $pdfpath) = $actionRs;
  197. }
  198. }
  199. }
  200. if(!(isset($pdfurl) && $pdfurl && fileGetContents($pdfurl))){
  201. $this->error[] = '文件尚未生成!!,错题信息:'.(isset($res)?json_encode($res):'');
  202. }
  203. $result = array(
  204. 'status' => 1,
  205. 'error' => '',
  206. 'is_create' => 0,
  207. 'pdf_url' => '',
  208. 'pdf_path' => '',
  209. );
  210. if (!$this->error) {
  211. $result['status'] = 0;
  212. $result['is_create'] = 1;
  213. $result['pdf_url'] = $pdfurl;
  214. $result['pdf_path'] = $pdfpath;
  215. }else{
  216. $result['error'] = implode(',',$this->error);
  217. }
  218. return $result;
  219. }
  220. /**
  221. * 获取题号
  222. * @param $info 试卷题数据
  223. * @param $tpl_data_arr exam tpl_data数组
  224. */
  225. private function getTopicNoArr(&$info,$tplDataArr)
  226. {
  227. $topicNoArr = array();
  228. foreach ($info as $item) {
  229. $topicNoArr[$item['topic_id']] = $item['order'];
  230. }
  231. asort($topicNoArr);
  232. $topicIdsArr = array_keys($topicNoArr);
  233. //选做题题号
  234. if (isset($tplDataArr['new_items']) && $tplDataArr['new_items']) {
  235. foreach (array_values($tplDataArr['new_items']) as $key => $item) {
  236. if (isset($topicIdsArr[$key])) {
  237. $topic_id = $topicIdsArr[$key];
  238. $no = preg_replace('/[A-Za-z]+/','' ,$item['alias']);
  239. $topicNoArr[$topic_id] = $no;
  240. }
  241. }
  242. }
  243. return $topicNoArr;
  244. }
  245. /**
  246. * 判断PDF是否存在
  247. * @param $pdf_path
  248. * @return bool
  249. */
  250. private function check_pdf($pdf_path) {
  251. if (substr($pdf_path, strlen($pdf_path) - 4, 4) == ".pdf") {
  252. if (file_exists($pdf_path)) {
  253. return true;
  254. }
  255. }
  256. return false;
  257. }
  258. /**
  259. * 当前考试需要的数据
  260. * @param $examObj
  261. * @param $examInfo
  262. * @param $sprObj
  263. * @param $paperObj
  264. */
  265. private function curExamNeedData(&$sprObj,&$paperObj)
  266. {
  267. $error = array();
  268. $examId = $this->examId;
  269. //试卷关联学生
  270. $sprInfoArr = $sprObj->getStudentExamInfo(array($examId));
  271. if(!$sprInfoArr){
  272. $error[] = '考试没有学生数据!';
  273. }
  274. if (!$error) {
  275. $examStuScore = array();//考试关联学生得分
  276. $paperId = 0;
  277. foreach($sprInfoArr as $key => $value){
  278. $studentId = $value['student_id'];
  279. $paperId = $value['paper_id'];
  280. $examStuScore[$studentId] = $value['scoring'];
  281. unset($sprInfoArr[$key]);
  282. }
  283. unset($sprInfoArr);
  284. //试卷总分
  285. $paperScore = $paperObj->getPaperScore(array($paperId));
  286. if(!$paperScore){
  287. $error[] = '试卷总分不正确!';
  288. }
  289. if (!$examStuScore) {
  290. $error[] = '没有当前班级数据!';
  291. }
  292. }
  293. if ($error) {
  294. $this->errorMsg(0, implode(',', $error));
  295. return false;
  296. }else{
  297. return array($paperId,$paperScore, $examStuScore);
  298. }
  299. }
  300. /**
  301. * 上次考试需要的数据
  302. * @param $examInfo
  303. * @param $examObj
  304. * @param $sprObj
  305. * @param $paperObj
  306. * @return array|boolean
  307. */
  308. private function preExamNeedData($examInfo,$sprObj,$paperObj)
  309. {
  310. $error = array();
  311. $examId = $this->examId;
  312. //获取当前考试的上一次考试数据
  313. $cepObj = new SClassExamPrinter();
  314. $preInfo = $cepObj->getPreExamId($examId,$examInfo['subject_id'],$examInfo['class_id']);
  315. $preExamId = isset($preInfo['exam_id'])?$preInfo['exam_id']:'';
  316. $prePaperScore = 0;
  317. $examRelPaper = array();
  318. $preExamStuScore = array();//考试关联学生得分
  319. if($preExamId){
  320. $sprInfoArr = $sprObj->getStudentExamInfo(array($preExamId));
  321. if(!$sprInfoArr){
  322. $error[] = '考试没有关联学生数据!';
  323. }
  324. if (!$error) {
  325. foreach($sprInfoArr as $key => $value){
  326. $studentId = $value['student_id'];
  327. $examRelPaper[$value['exam_id']] = $value['paper_id'];
  328. $preExamStuScore[$studentId] = $value['scoring'];
  329. unset($sprInfoArr[$key]);
  330. }
  331. unset($sprInfoArr);
  332. if($examRelPaper){
  333. $prePaperScore = $paperObj->getPaperScore(array_values($examRelPaper));
  334. }
  335. if(!$prePaperScore){
  336. $error[] = '试卷总分不正确!';
  337. }
  338. }
  339. }
  340. if ($error) {
  341. $this->errorMsg(0, implode(',', $error));
  342. return false;
  343. }else{
  344. return array($prePaperScore, $preExamStuScore);
  345. }
  346. }
  347. /**
  348. * 班级排名得分率人数等数据
  349. * @param $paperScore
  350. * @param $examStuScore
  351. * @return array
  352. */
  353. private function getClassAnalyse($paperScore,&$examStuScore,$prePaperScore, &$preExamStuScore)
  354. {
  355. $error = array();
  356. $classAnalyArr = array(
  357. 'exam_stu_num' => 0,//参加考试人数
  358. 'miss_stu_num' => 0,//缺考考人数
  359. 'avg_score_rate' => 0,//学生平均得分率
  360. 'pre_avg_rate_diff' => 0,//较上次考试平均得分率上升或下降
  361. 'avg_scoring' => 0,//学生平均分
  362. 'max_scoring' => 0,//最高分
  363. 'min_scoring' => 0,//最低分
  364. 'full_scoring' => 0,//满分
  365. 'pass_rate' => 0,//及格率
  366. 'pass_rate_gt_90' => 0,//其中得分率90%以上的有x人
  367. );
  368. $passStuNum = 0;//及格人数
  369. foreach ($examStuScore as $stuId => $stuScoring) {
  370. if ($stuScoring > 0) {
  371. $classAnalyArr['exam_stu_num']++;
  372. }else{
  373. $classAnalyArr['miss_stu_num']++;
  374. continue;
  375. }
  376. if ($stuScoring > $classAnalyArr['max_scoring']) {
  377. $classAnalyArr['max_scoring'] = $stuScoring;
  378. }
  379. if ($classAnalyArr['min_scoring'] == 0) {
  380. $classAnalyArr['min_scoring'] = $stuScoring;
  381. }
  382. if ($stuScoring < $classAnalyArr['min_scoring']) {
  383. $classAnalyArr['min_scoring'] = $stuScoring;
  384. }
  385. $scoring_rate = ($stuScoring / $paperScore) * 100;
  386. if ($scoring_rate >= 60) {
  387. $passStuNum++;
  388. }
  389. if ($scoring_rate > 90) {
  390. $classAnalyArr['pass_rate_gt_90']++;
  391. }
  392. }
  393. if (!$classAnalyArr['exam_stu_num']) {
  394. $error[] = '班级没有得分大于0的学生!';
  395. }
  396. if (!$error) {
  397. $classAnalyArr['pass_rate'] = $this->numberFormat($passStuNum / $classAnalyArr['exam_stu_num'], 4) * 100;
  398. $classAnalyArr['full_scoring'] = $paperScore;
  399. $classAnalyArr['avg_score_rate'] = $this->numberFormat(array_sum($examStuScore) / ($classAnalyArr['exam_stu_num'] * $paperScore), 4) * 100;
  400. $classAnalyArr['avg_scoring'] = $this->numberFormat(array_sum($examStuScore) / $classAnalyArr['exam_stu_num'], 1);
  401. //上一次班级平均得分率
  402. if ($preExamStuScore) {
  403. $preExamStuNum = 0;//上次参加考试人数
  404. foreach ($preExamStuScore as $stuId => $stuScoring) {
  405. if ($stuScoring > 0) {
  406. $preExamStuNum++;
  407. }
  408. }
  409. //上次平均得分率
  410. if ($preExamStuNum) {
  411. $preAvgScoreRate = $this->numberFormat(array_sum($preExamStuScore) / ($preExamStuNum * $prePaperScore), 4) * 100;
  412. }else{
  413. $preAvgScoreRate = 0;
  414. }
  415. $classAnalyArr['pre_avg_rate_diff'] = $classAnalyArr['avg_score_rate'] - $preAvgScoreRate;
  416. }
  417. }
  418. if ($error) {
  419. $this->errorMsg(0, implode(',', $error));
  420. return false;
  421. }else{
  422. return $classAnalyArr;
  423. }
  424. }
  425. /**
  426. * 考试分析,大幅进步,大幅退步,前5名和后5名
  427. * @param $examStuScore
  428. * @param $preExamStuScore
  429. * @return array
  430. */
  431. private function getLargeOrder($examStuScore,$preExamStuScore){
  432. $largeOrderArr = array(
  433. 'font_five' => array(),//班级前五名
  434. 'back_five' => array(),//班级后五名
  435. 'stu_diff_order' => array(),//名次提升或下降
  436. 'stu_order' => array(),//学生名次
  437. 'increase_stu' => array(),//大幅进步五名
  438. 'reduce_stu' => array(),//大幅退步五名
  439. );
  440. //得分数组(得分>0)
  441. $scoringArr = array();
  442. $needStudentArr = array();//得分大于0的学生
  443. foreach ($examStuScore as $stuId => $stuScoring) {
  444. if ($stuScoring > 0) {
  445. $scoringArr[] = $stuScoring;
  446. $needStudentArr[$stuId] = $stuId;
  447. }
  448. }
  449. //学生排名
  450. $stuOrderArr = array();
  451. foreach($examStuScore as $stuId => $stuScoring){
  452. if($stuScoring > 0){
  453. $stuOrderArr[$stuId] = $this->getOrder($stuScoring,$scoringArr);
  454. }
  455. }
  456. if($stuOrderArr){
  457. asort($stuOrderArr);
  458. }
  459. $diffNum = ceil(count($stuOrderArr)*0.2);
  460. //班级前五名
  461. //前5名和后5名
  462. $font5 = $back5 = array();
  463. $i = 1;
  464. foreach($stuOrderArr as $stuId => $order){
  465. if($i <= 5){
  466. $font5[$stuId] = $order;
  467. }else{
  468. break;
  469. }
  470. $i++;
  471. }
  472. $largeOrderArr['font_five'] = $font5;
  473. $i = 1;
  474. //删除前5排名
  475. $_stuOrderArr = $stuOrderArr;
  476. foreach($_stuOrderArr as $k => $order){
  477. if(isset($font5[$k])){
  478. unset($_stuOrderArr[$k]);
  479. }
  480. }
  481. arsort($_stuOrderArr);
  482. foreach($_stuOrderArr as $stuId => $order){
  483. if($i <= 5){
  484. $back5[$stuId] = $order;
  485. }else{
  486. break;
  487. }
  488. $i++;
  489. }
  490. $largeOrderArr['back_five'] = $back5;
  491. /////大幅进步,退步
  492. //上次得分数组(>0)
  493. $preScoringArr = array();
  494. foreach ($preExamStuScore as $stuId => $stuScoring) {
  495. if ($stuScoring > 0) {
  496. $preScoringArr[] = $stuScoring;
  497. }
  498. }
  499. //上次考试学生排名
  500. $preStuOrderArr = array();
  501. foreach($preExamStuScore as $stuId => $stuScoring){
  502. if(!$stuScoring) continue;
  503. $preStuOrderArr[$stuId] = $this->getOrder($stuScoring,$preScoringArr);
  504. }
  505. if($preStuOrderArr){
  506. asort($preStuOrderArr);
  507. }
  508. if($preStuOrderArr){
  509. $interOrderArr = array_intersect_key($stuOrderArr,$preStuOrderArr);
  510. if(!$interOrderArr){
  511. $interOrderArr = $stuOrderArr;
  512. }
  513. }else{
  514. $interOrderArr = array();
  515. }
  516. $increaseArr = $reduceArr = array();
  517. $stuDiffOrder = array();
  518. if($interOrderArr){
  519. foreach($stuOrderArr as $stuId => $order){
  520. if(isset($interOrderArr[$stuId]) && isset($preStuOrderArr[$stuId])){
  521. $stuDiffOrder[$stuId] = -($order - $preStuOrderArr[$stuId]);
  522. }else{
  523. $stuDiffOrder[$stuId] = 0;
  524. }
  525. }
  526. arsort($stuDiffOrder);
  527. foreach($stuDiffOrder as $stuId => $num){
  528. if($num >= $diffNum){
  529. $increaseArr[] = $stuId;
  530. }
  531. }
  532. $increaseArr = array_slice($increaseArr,0,5,true);
  533. asort($stuDiffOrder);
  534. foreach($stuDiffOrder as $stuId => $num){
  535. if($num <= -$diffNum){
  536. $reduceArr[] = $stuId;
  537. }
  538. }
  539. $reduceArr = array_slice($reduceArr,0,5,true);
  540. }else{
  541. $increaseArr = array_slice($stuOrderArr,0,5,true);
  542. $reduceArr = array();
  543. }
  544. $largeOrderArr['stu_diff_order'] = $stuDiffOrder;
  545. $largeOrderArr['stu_order'] = $stuOrderArr;
  546. $largeOrderArr['increase_stu'] = $increaseArr;
  547. $largeOrderArr['reduce_stu'] = $reduceArr;
  548. return array($largeOrderArr,$needStudentArr);
  549. }
  550. /**
  551. * 获取排名
  552. * @param $ele
  553. * @param $arr
  554. * @return false|int|string
  555. */
  556. private function getOrder($ele,$arr){
  557. $orderRs = 0;
  558. if($arr){
  559. arsort($arr);
  560. $arr = array_values($arr);
  561. $index = array_search($ele,$arr);
  562. $orderRs = $index + 1;
  563. }
  564. return $orderRs;
  565. }
  566. /**
  567. * 试卷情况分析
  568. * @param $topicNoArr
  569. * @param $stuRsArr
  570. * @param $needStudentArr
  571. * @param $topicScoreArr
  572. * @return array
  573. */
  574. private function paperAnalyse($topicNoArr,&$stuRsArr,$needStudentArr,$topicScoreArr)
  575. {
  576. $paperAnalyseArr = array();
  577. $topicWrongNumArr = array();//题答错人数
  578. foreach ($needStudentArr as $stuId) {
  579. if(!(isset($stuRsArr[$stuId])) && $stuRsArr[$stuId]) continue;
  580. foreach ($stuRsArr[$stuId] as $topicId => $rs) {
  581. if (!isset($topicWrongNumArr[$topicId])) {
  582. $topicWrongNumArr[$topicId] = 0;
  583. }
  584. if ($rs['scoring'] < $topicScoreArr[$topicId]) {
  585. $topicWrongNumArr[$topicId]++;
  586. }
  587. }
  588. }
  589. $stuTotalNum = count($needStudentArr);//学生总人数
  590. foreach ($topicNoArr as $topicId => $no) {
  591. $wrongNum = isset($topicWrongNumArr[$topicId]) ? $topicWrongNumArr[$topicId] : 0;
  592. $paperAnalyseArr[] = array(
  593. 'no' => $no,
  594. 'wrong_num' => $wrongNum,
  595. 'wrong_rate' => $this->numberFormat($wrongNum / $stuTotalNum, 4) * 100,
  596. );
  597. }
  598. return $paperAnalyseArr;
  599. }
  600. /**
  601. * 获取题内容
  602. * @param $topicNoArr
  603. * @return array|false
  604. */
  605. private function getTopicInfoArr($topicNoArr)
  606. {
  607. $error = array();
  608. $topicIds = array_keys($topicNoArr);
  609. $topicDetails = $this->apiBrainPost('/topic/batchAll', array('topicIds' => $topicIds,'subjectId' => $this->subjectId,'params'=>array('isFormat'=>1,'isThird'=>1)),25,true);
  610. if (!$error) {
  611. $diff_id_arr = array();
  612. foreach ($topicNoArr as $topicId => $v) {
  613. if (!isset($topicDetails[$topicId])) {
  614. $diff_id_arr[] = $topicId;
  615. }
  616. }
  617. if (!empty($diff_id_arr)) {
  618. $error[] = "没有试题内容!(" . implode(',', $diff_id_arr) . ")";
  619. }
  620. }
  621. if ($error) {
  622. $this->errorMsg(0, implode(',', $error));
  623. return false;
  624. }else{
  625. return $topicDetails;
  626. }
  627. }
  628. /**
  629. * 题型方法掌握情况
  630. * @param $topicInfoArr
  631. * @param $paperCommentsArr
  632. * @return array
  633. */
  634. public function methodAnalyse(&$topicInfoArr,$paperCommentsArr)
  635. {
  636. $topicArr = $methodInfos=$topics=array();
  637. foreach ($paperCommentsArr as $item){
  638. $topicArr[$item['topic_id']] = $item;
  639. }
  640. //题型方法对应题
  641. $methodsTopics = $this->methodTopics($topicInfoArr);
  642. //判断题型方法是否为空
  643. if(!$methodsTopics){
  644. return array($methodInfos,$topics);
  645. }
  646. //题型方法信息
  647. list($methodInfos,$topics) = $this->methodInfos(array_keys($methodsTopics));
  648. //题型方法得分率
  649. foreach ($methodsTopics as $methodId=>$methodsTopic){
  650. $own = 0;
  651. $full = 0;
  652. foreach ($methodsTopic as $methodTopicId){
  653. $own += $topicArr[$methodTopicId]['class_scoring_rate'];
  654. $full += 100;
  655. if(isset($topicArr[$methodTopicId])) {
  656. $methodInfos[$methodId]['topicRs'][$methodTopicId] = $topicArr[$methodTopicId];
  657. }
  658. }
  659. $methodInfos[$methodId]['rate'] = $full ? number_format($own/$full,2)*100 : 0;
  660. }
  661. return array($methodInfos,$topics);
  662. }
  663. /**
  664. * 试卷讲评
  665. * @param $needStudentArr
  666. * @param $stuRsArr
  667. * @param $topicScoreArr
  668. * @param $topicInfoArr
  669. * @param $topicNoArr
  670. * @return array
  671. */
  672. private function paperComments(&$needStudentArr,&$stuRsArr,$topicScoreArr,&$topicInfoArr,$topicNoArr)
  673. {
  674. $paperCommentsArr = array();
  675. // $_paperCommentsArr = array(
  676. // 'topic_no' => 0,//试题序号
  677. // 'class_scoring_rate' => 0,//班级得分率
  678. // 'grade_scoring_rate' => 0,//年级得分率
  679. // 'wrong_stu_arr' => 0,//答错学生
  680. // 'right_stu_num' => 0,//答对学生数
  681. // 'push_topic_id' => 0,//推送题ID
  682. // );
  683. //试题对应的所有学生得分
  684. $topicScoringArr = array();//试题得分
  685. $topicTotalScoreArr = array();//试题总分
  686. $wrongStuArr = array();//答错学生
  687. $rightStuNumArr = array();//答对学生数
  688. foreach ($stuRsArr as $stuId => $stuTopicRs) {
  689. foreach ($stuTopicRs as $topicId => $stuRs) {
  690. if(!isset($needStudentArr[$stuId])) continue;
  691. if (!isset($topicScoringArr[$topicId])) {
  692. $topicScoringArr[$topicId] = 0;
  693. }
  694. $topicScoringArr[$topicId] += $stuRs['scoring'];
  695. if (!isset($topicTotalScoreArr[$topicId])) {
  696. $topicTotalScoreArr[$topicId] = 0;
  697. }
  698. $topicTotalScoreArr[$topicId] += isset($topicScoreArr[$topicId]) ? $topicScoreArr[$topicId] : 0;
  699. //答错学生
  700. if ($stuRs['scoring'] == $topicScoreArr[$topicId]) {
  701. if (!isset($rightStuNumArr[$topicId])) {
  702. $rightStuNumArr[$topicId] = 0;
  703. }
  704. $rightStuNumArr[$topicId]++;
  705. }else{
  706. if (!isset($wrongStuArr[$topicId])) {
  707. $wrongStuArr[$topicId] = array();
  708. }
  709. $wrongStuArr[$topicId][$stuId] = $stuId;
  710. }
  711. }
  712. }
  713. //班级得分率
  714. $classScoringRateArr = array();
  715. foreach ($topicScoringArr as $topicId => $scoring) {
  716. $classScoringRateArr[$topicId] = $this->numberFormat($scoring / $topicTotalScoreArr[$topicId], 2) * 100;
  717. }
  718. //年级得分率
  719. $gradeScoringRateArr = $this->getGradeScoringRate($topicScoreArr);
  720. foreach ($topicNoArr as $topicId => $topicNo) {
  721. $_wrongStuArr = isset($wrongStuArr[$topicId]) ? $wrongStuArr[$topicId] : array();
  722. $paperCommentsArr[] = array(
  723. 'topic_id' => $topicId,
  724. 'topic_no' => $topicNo,
  725. 'class_scoring_rate' => isset($classScoringRateArr[$topicId])?$classScoringRateArr[$topicId]:0,
  726. 'grade_scoring_rate' => isset($gradeScoringRateArr[$topicId])?$gradeScoringRateArr[$topicId]:0,
  727. 'wrong_stu_arr' => array_keys($_wrongStuArr),
  728. 'wrong_stu_num' => count($_wrongStuArr),
  729. 'right_stu_num' => isset($rightStuNumArr[$topicId])?$rightStuNumArr[$topicId]:0,
  730. );
  731. }
  732. return $paperCommentsArr;
  733. }
  734. /**
  735. * 年级得分率
  736. * @param $topicScoreArr 试题分数
  737. * @param $topicScoringArr 试题得分(初始值是班级得分)
  738. * @param $topicTotalScoreArr 试题总分分(初始值是班级总分)
  739. * @return array
  740. */
  741. private function getGradeScoringRate($topicScoreArr)
  742. {
  743. $gradeScoringRateArr = array();
  744. $examIds = $this->examObj->getExamIds($this->examGroupId);
  745. $needStuArr = array();//得分大于0的学生
  746. $paperIds = array();
  747. if($examIds){
  748. //试卷关联学生
  749. $sprInfoArr = $this->sprObj->getStudentExamInfo($examIds);
  750. if ($sprInfoArr) {
  751. foreach ($sprInfoArr as $sprInfo) {
  752. if ($sprInfo['scoring'] > 0) {
  753. $needStuArr[$sprInfo['student_id']] = $sprInfo['student_id'];
  754. $paperIds[$sprInfo['paper_id']] = $sprInfo['paper_id'];
  755. }
  756. }
  757. }
  758. unset($sprInfoArr);
  759. }
  760. $topicScoringArr = array();
  761. $topicTotalScoreArr = array();
  762. if ($paperIds) {
  763. foreach ($paperIds as $paperId) {
  764. $_stuRsArr = $this->sptRsObj->getStudentRsOnePaper($paperId);
  765. if(!$_stuRsArr){
  766. continue;
  767. }
  768. foreach ($_stuRsArr as $stuId => $stuTopicRs) {
  769. foreach ($stuTopicRs as $topicId => $stuRs) {
  770. if(!isset($needStuArr[$stuId])) continue;
  771. if (!isset($topicScoringArr[$topicId])) {
  772. $topicScoringArr[$topicId] = 0;
  773. }
  774. $topicScoringArr[$topicId] += $stuRs['scoring'];
  775. if (!isset($topicTotalScoreArr[$topicId])) {
  776. $topicTotalScoreArr[$topicId] = 0;
  777. }
  778. $topicTotalScoreArr[$topicId] += isset($topicScoreArr[$topicId]) ? $topicScoreArr[$topicId] : 0;
  779. }
  780. unset($stuTopicRs);
  781. }
  782. unset($_stuRsArr);
  783. }
  784. }
  785. //年级得分率
  786. foreach ($topicScoringArr as $topicId => $scoring) {
  787. $gradeScoringRateArr[$topicId] = $this->numberFormat($scoring / $topicTotalScoreArr[$topicId], 2) * 100;
  788. }
  789. return $gradeScoringRateArr;
  790. }
  791. /**
  792. * hmtl生产PDF
  793. * @param $res
  794. * @param $html
  795. * @param $className
  796. * @param $examName
  797. * @return boolean|array
  798. */
  799. private function htmlToPdf($res,&$html,$className,$examName)
  800. {
  801. $error = array();
  802. $examId = $this->examId;
  803. $htmlpath = str_replace("protected", "", Yii::app()->basePath) . '/upload/tmpDir/academicr/'; //存放生成的HTML路径
  804. $pdfpath = str_replace("protected", "", Yii::app()->basePath) . '/upload/tmpDir/academicr/' . $this->schoolId . "/"; //存放生成的PDF路径
  805. $pdfurl = '/upload/tmpDir/academicr/' . $this->schoolId . "/";
  806. if (!is_dir($htmlpath)) {
  807. if (!mkdir($htmlpath, 0777, true)) {
  808. $error[] = 'Create directory fail: ' . $htmlpath;
  809. }
  810. }
  811. if (!is_dir($pdfpath)) {
  812. if (!mkdir($pdfpath, 0777, true)) {
  813. $error[] = 'Create directory fail: ' . $pdfpath;
  814. }
  815. }
  816. if (!$error) {
  817. $locale='en_US.UTF-8'; // 或 $locale='zh_CN.UTF-8';
  818. setlocale(LC_ALL,$locale);
  819. putenv('LC_ALL='.$locale);
  820. $htmlpath .= $examId . ".html";
  821. $f = fopen($htmlpath, "w");
  822. fwrite($f, $html);
  823. fclose($f);
  824. $htmlurl = '/upload/tmpDir/academicr/' . $examId . ".html"; //访问HTML的路径
  825. $server = Yii::app()->params['phantomjs_server'];
  826. $js = 'html2pdf.js';
  827. $htmlurl = $this->getDomain() . $htmlurl;
  828. $pdffname = $this->classId . "-" . $examId . ".pdf";
  829. $pdfpath = $pdfpath . $pdffname;
  830. $pdfurl = $pdfurl . $pdffname;
  831. $schoolInfo = $this->controllerObj->schoolInfo;
  832. $schoolName = isset($schoolInfo['school_name'])?$schoolInfo['school_name']:'';
  833. $commond = $server . " "
  834. . Yii::app()->basePath . '/../js/'.$js . " "
  835. . " {$htmlurl}"
  836. . " {$pdfpath}"
  837. . " A4 '{$schoolName}{$className}|||时间:".date('Y-m-d')."'";
  838. exec($commond, $res, $code);
  839. }
  840. if(isset($res) && isset($res[0])){
  841. $isBool = false;
  842. foreach($res as $msg){
  843. if (strpos($msg, 'succeed') !== false){
  844. $isBool = true;
  845. }
  846. }
  847. if ($isBool) {//命令返回成功
  848. if (file_exists($pdfpath)) {
  849. // $pdfurl=iconv("GBK", "UTF-8",$pdfurl);
  850. $repdf = $examId . ".pdf";
  851. $rename = 'zsyas2/academicr/'.$this->schoolId.'/'. date('Y') . '/' . date('m') . '/' . date('d').'/'. uniqid() . '.' . $repdf;
  852. $ucloud = new HuaweiCloud();
  853. $uploadInfo = $ucloud->putFile($rename, $pdfpath);
  854. if ($uploadInfo['status'] == 0) {
  855. $pdfurl = $this->getDomain() . $pdfurl;
  856. $this->getSchoolDbObj()->createCommand()->update("exam", array("academicr_pdf_path" => $pdfurl, "academicr_pdf_time" => time(), "is_academicr_pdf" => 1), "exam_id = '{$examId}'");
  857. }else{
  858. $pdfurl = $uploadInfo['url'];
  859. $this->getSchoolDbObj()->createCommand()->update("exam", array("academicr_pdf_path" => $pdfurl, "academicr_pdf_time" => time(), "is_academicr_pdf" => 1), "exam_id = '{$examId}'");
  860. //批量生产不生产PDF
  861. if (!is_cli()) {
  862. @unlink($pdfpath);
  863. }
  864. }
  865. @unlink($htmlpath);
  866. } else {
  867. $this->getSchoolDbObj()->createCommand()->update("exam", array("academicr_pdf_path" => "", "academicr_pdf_time" => 0, "is_academicr_pdf" => 0), "exam_id = '{$examId}'");
  868. @unlink($htmlpath);
  869. $error[] = 'PDF文件未找到! ';
  870. }
  871. } else {
  872. @unlink($htmlpath);
  873. $error[] = 'PDF创建失败!,错误信息:(' . json_encode($res) . ') ';
  874. }
  875. }else{
  876. Curl::post(Yii::app()->params['handle_log_api'], array(
  877. "exam_group_id" => (string)$this->examGroupId,
  878. "operate_project" => 'zsyas2',
  879. "school_id" => $this->schoolId,
  880. "title" => '下载教师讲案',
  881. "operate_account" => is_cli()?'':Yii::app()->session['coachInfo']['coach_name'],
  882. "operate_method" => $this->controllerObj->action,
  883. "operate_url" => $this->controllerObj->getRoute(),
  884. "operate_sql" => '',
  885. "operate_param" =>json_encode(array('post'=>array("examId"=>$examId,"pdf"=>array("commond"=>$commond,"res"=>$res,"code"=>$code),"htmlurl"=>''))),
  886. ));
  887. $error[] = 'PDF创建失败! ';
  888. }
  889. if ($error) {
  890. $this->errorMsg(0, implode(',', $error));
  891. return false;
  892. }else{
  893. return array($pdfurl,$pdfpath);
  894. }
  895. }
  896. /**
  897. * 调题的接口
  898. * @param $path
  899. * @param $arr
  900. * @param int $type
  901. * @return bool|mixed
  902. */
  903. public function apiBrainPost($path, $arr, $timeout=25,$is_array = false)
  904. {
  905. $ch = @curl_init();
  906. $result = FALSE;
  907. if ($ch)
  908. {
  909. $data = json_encode($arr);
  910. $url = Yii::app()->params['api'][0]['prefix'] . $path;
  911. $username = Yii::app()->params['api'][0]['username'];
  912. $password = Yii::app()->params['api'][0]['password'];
  913. curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
  914. curl_setopt($ch, CURLOPT_USERPWD, $username . ':' . $password);
  915. // 不输出头部
  916. curl_setopt($ch, CURLOPT_HEADER, 0);
  917. // curl_exec 获取到的内容不直接输出, 而是返回
  918. curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
  919. curl_setopt($ch, CURLOPT_TIMEOUT,$timeout);
  920. // 请求重启路由器的地址 传参 进行重启
  921. curl_setopt($ch, CURLOPT_URL, $url);
  922. curl_setopt($ch, CURLOPT_USERAGENT, 'Api Client/1.0.0 (chengfei@liancaitech.com)');
  923. curl_setopt($ch, CURLOPT_POST, 1);
  924. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  925. curl_setopt($ch, CURLOPT_HTTPHEADER, array(
  926. 'Content-Type: application/json',
  927. 'Content-Length: '. strlen($data),
  928. ));
  929. if( !curl_errno($ch))
  930. {
  931. $result = json_decode(curl_exec($ch),$is_array);
  932. }
  933. // 释放资源
  934. curl_close($ch);
  935. }
  936. return $result;
  937. }
  938. public function getDomain()
  939. {
  940. if (is_cli()) {
  941. return Yii::app()->params['zsyas2_url'];
  942. }else{
  943. return Yii::app()->request->hostInfo;
  944. }
  945. }
  946. /**
  947. * 错误信息
  948. * @param $status
  949. * @param $msg
  950. */
  951. private function errorMsg($status,$msg)
  952. {
  953. $this->error[] = $msg;
  954. // echo json_encode(array("status" => $status, "error" => $msg));
  955. // exit;
  956. }
  957. /**
  958. * 学校数据库连接对象
  959. * @return mixed
  960. */
  961. protected function getSchoolDbObj()
  962. {
  963. if (is_cli()) {
  964. return $this->controllerObj->getSchConnObj();
  965. }else{
  966. return $this->controllerObj->sConn;
  967. }
  968. }
  969. /**
  970. * 数组格式化
  971. * @param $num 小数
  972. * @param $bat 小数位数
  973. * @return float|int
  974. */
  975. private function numberFormat($num,$bat)
  976. {
  977. $pow = pow(10, $bat);
  978. $num = $num * $pow;
  979. $num = floor($num) / $pow;
  980. return $num;
  981. }
  982. /**
  983. * 获取题型方法对应题id集
  984. * @param $topicInfos
  985. * @return array
  986. */
  987. private function methodTopics($topicInfos){
  988. $res = array();
  989. foreach ($topicInfos as $key=>$topicInfo){
  990. if(isset($topicInfo['affiliate'])){
  991. foreach ($topicInfo['affiliate'] as $item){
  992. if($item['field_key'] == 'tag_41_175' && !empty($item['field_value'])){
  993. $methodIds = explode('|', $item['field_value']);
  994. foreach ($methodIds as $methodId){
  995. $res[$methodId][] = $key;
  996. }
  997. break;
  998. }
  999. }
  1000. }
  1001. }
  1002. if(empty($res)){
  1003. $error[] = '未获取到题型方法数据';
  1004. }
  1005. return $res;
  1006. }
  1007. /**
  1008. * 获取题型方法信息
  1009. * @param $methodIds
  1010. * @return array|boolean
  1011. */
  1012. private function methodInfos($methodIds){
  1013. $rs = $this->apiBrainPost('/all_content/detail', array('catalogIds' => $methodIds),25,true);
  1014. if(isset($rs['status']) && $rs['status']==1){
  1015. $methodDetails = $rs['data'];
  1016. }else{
  1017. $error[] = '题型方法数据为空! ';
  1018. return false;
  1019. }
  1020. $methods = array();
  1021. $topicIds = array();
  1022. $difficult = static::$difficult;
  1023. foreach ($methodDetails as $detail){
  1024. if(isset($detail['column_id'])){
  1025. switch ($detail['column_id']){
  1026. case self::FULLTEXT_COLUMN_ID:
  1027. $methods[$detail['catalog_id']]['name'] = $detail['catalog_name'];
  1028. $methods[$detail['catalog_id']]['content'] = $detail['content'];
  1029. break;
  1030. case self::CLASSIC_COLUMN_ID:
  1031. $methods[$detail['catalog_id']]['name'] = $detail['catalog_name'];
  1032. $methods[$detail['catalog_id']]['classic_topic_ids'][] = number_format($detail['topic_id'], 0, '', '');
  1033. $topicIds[number_format($detail['topic_id'], 0, '', '')] = number_format($detail['topic_id'], 0, '', '');
  1034. break;
  1035. case self::DIFFICULT_COLUMN_ID:
  1036. $methods[$detail['catalog_id']]['name'] = $detail['catalog_name'];
  1037. $methods[$detail['catalog_id']]['difficult'] = isset($difficult[$detail['column_detail_id']]) ? $difficult[$detail['column_detail_id']] : '简单';
  1038. break;
  1039. default:
  1040. $methods[$detail['catalog_id']]['name'] = $detail['catalog_name'];
  1041. break;
  1042. }
  1043. }
  1044. }
  1045. return array($methods,$topicIds);
  1046. }
  1047. /**
  1048. * 获取共性题数据
  1049. * @return array
  1050. */
  1051. private function commonTopic()
  1052. {
  1053. $model = new SJuniorMathCommonTopic();
  1054. $rs = $model->getCommonTopic($this->examId,$this->classId);
  1055. if(empty($rs)){
  1056. $error[] = '未生成共性题! ';
  1057. }
  1058. $common = array();
  1059. $topicIds = array();
  1060. if($rs) {
  1061. foreach ($rs as $item) {
  1062. $topicIds[$item['topic_id']] = $item['topic_id'];
  1063. $common[(string)$item['tymhd_id']][] = $item['topic_id'];
  1064. }
  1065. }
  1066. return array($common,$topicIds);
  1067. }
  1068. }