/**
* Copyright © 2002 Instituto Superior Técnico
*
* This file is part of FenixEdu Academic.
*
* FenixEdu Academic is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FenixEdu Academic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FenixEdu Academic. If not, see <http://www.gnu.org/licenses/>.
*/
package org.fenixedu.academic.ui.struts.action.student;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.fenixedu.academic.domain.Attends;
import org.fenixedu.academic.domain.CurricularCourse;
import org.fenixedu.academic.domain.Enrolment;
import org.fenixedu.academic.domain.ExecutionCourse;
import org.fenixedu.academic.domain.ExecutionSemester;
import org.fenixedu.academic.domain.GradeScale;
import org.fenixedu.academic.domain.Mark;
import org.fenixedu.academic.domain.WrittenEvaluation;
import org.fenixedu.academic.domain.student.Registration;
import org.fenixedu.academic.domain.student.Student;
import org.fenixedu.academic.domain.time.calendarStructure.AcademicInterval;
import org.fenixedu.academic.ui.struts.action.base.FenixDispatchAction;
import org.fenixedu.academic.ui.struts.action.student.StudentApplication.StudentViewApp;
import org.fenixedu.academic.util.Bundle;
import org.fenixedu.bennu.core.i18n.BundleUtil;
import org.fenixedu.bennu.struts.annotations.Forward;
import org.fenixedu.bennu.struts.annotations.Forwards;
import org.fenixedu.bennu.struts.annotations.Mapping;
import org.fenixedu.bennu.struts.portal.EntryPoint;
import org.fenixedu.bennu.struts.portal.StrutsFunctionality;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
@StrutsFunctionality(app = StudentViewApp.class, descriptionKey = "link.student.statistics", path = "statistics",
titleKey = "link.title.statistics")
@Mapping(module = "student", path = "/showStudentStatistics", scope = "request", parameter = "method")
@Forwards({ @Forward(name = "showStudentStatisticsHome", path = "/student/statistics/home.jsp"),
@Forward(name = "showExecutionCourseStatistics", path = "/student/statistics/executionCourse.jsp") })
public class ShowStudentStatisticsDispatchAction extends FenixDispatchAction {
@EntryPoint
public ActionForward showStudentStatisticsHome(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception {
final Student student = getLoggedPerson(request).getStudent();
if (student != null) {
Collection<Enrolment> approvedEnrolments = new HashSet<Enrolment>();
for (Registration registration : getValidRegistrations(student)) {
approvedEnrolments.addAll(registration.getApprovedEnrolments());
}
request.setAttribute("progress", computeStudentProgress(student));
request.setAttribute("curricularCoursesOvertime", computeCurricularCoursesOvertimeStatistics(approvedEnrolments));
}
return mapping.findForward("showStudentStatisticsHome");
}
public ActionForward showExecutionCourseStatistics(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception {
final Student student = getLoggedPerson(request).getStudent();
ExecutionCourse executionCourse = getDomainObject(request, "executionCourseId");
if (student != null && executionCourse != null) {
request.setAttribute("executionCourse", executionCourse);
request.setAttribute("executionCourseStatistics", computeStudentExecutionCourseStatistics(student, executionCourse));
request.setAttribute("curricularCourseOvertimeStatistics", computeCurricularCourseOvertimeStatistics(student
.getAttends(executionCourse).getEnrolment().getCurricularCourse()));
}
return mapping.findForward("showExecutionCourseStatistics");
}
private List<Registration> getValidRegistrations(Student student) {
List<Registration> result = new ArrayList<Registration>();
for (Registration registration : student.getRegistrationsSet()) {
if (!registration.isCanceled()) {
result.add(registration);
}
}
return result;
}
private JsonObject computeCurricularCoursesOvertimeStatistics(Collection<Enrolment> approvedEnrolments) {
JsonObject curricularCoursesOvertime = new JsonObject();
int lowestStartYear = Integer.MAX_VALUE;
int highestStartYear = Integer.MIN_VALUE;
JsonArray curricularCoursesJsonArray = new JsonArray();
for (Enrolment enrolment : approvedEnrolments) {
CurricularCourse curricularCourse = enrolment.getCurricularCourse();
JsonObject curricularCourseJsonObject = new JsonObject();
curricularCourseJsonObject.addProperty("name", curricularCourse.getNameI18N().getContent());
JsonArray entriesArray = new JsonArray();
Map<Integer, CurricularCourseYearStatistics> entries = new HashMap<Integer, CurricularCourseYearStatistics>();
for (ExecutionCourse executionCourse : curricularCourse.getAssociatedExecutionCoursesSet()) {
AcademicInterval academicInterval = executionCourse.getAcademicInterval();
int startYear = academicInterval.getStart().getYear();
CurricularCourseYearStatistics curricularCourseYearStatistics =
computeExecutionCourseJsonArray(executionCourse, entries.get(startYear));
if (curricularCourseYearStatistics == null) {
continue;
}
entries.put(startYear, curricularCourseYearStatistics);
if (lowestStartYear > startYear) {
lowestStartYear = startYear;
}
if (highestStartYear < startYear) {
highestStartYear = startYear;
}
}
for (int year : entries.keySet()) {
JsonArray jsonArray = new JsonArray();
jsonArray.add(new JsonPrimitive(year));
jsonArray.add(new JsonPrimitive(entries.get(year).getApproved()));
jsonArray.add(new JsonPrimitive(entries.get(year).getFlunked()));
jsonArray.add(new JsonPrimitive(entries.get(year).getNotEvaluated()));
entriesArray.add(jsonArray);
}
curricularCourseJsonObject.add("years", entriesArray);
curricularCoursesJsonArray.add(curricularCourseJsonObject);
}
curricularCoursesOvertime.addProperty("start-year", lowestStartYear);
curricularCoursesOvertime.addProperty("end-year", highestStartYear);
curricularCoursesOvertime.add("entries", curricularCoursesJsonArray);
return curricularCoursesOvertime;
}
public static class CurricularCourseYearStatistics {
private int approved;
private int flunked;
private int notEvaluated;
public CurricularCourseYearStatistics() {
this(0, 0, 0);
}
public CurricularCourseYearStatistics(int approved, int flunked, int notEvaluated) {
this.approved = approved;
this.flunked = flunked;
this.notEvaluated = notEvaluated;
}
public void inc(int approved, int flunked, int notEvaluated) {
this.approved += approved;
this.flunked += flunked;
this.notEvaluated += notEvaluated;
}
public int getApproved() {
return approved;
}
public int getFlunked() {
return flunked;
}
public int getNotEvaluated() {
return notEvaluated;
}
}
private CurricularCourseYearStatistics computeExecutionCourseJsonArray(ExecutionCourse executionCourse,
CurricularCourseYearStatistics curricularCourseYearStatistics) {
int approvedStudents = 0;
int flunkedStudents = 0;
int notEvaluatedStudents = 0;
for (Attends attends : executionCourse.getAttendsSet()) {
if (attends.getEnrolment() != null) {
Enrolment enrolment = attends.getEnrolment();
if (enrolment.isApproved()) {
approvedStudents++;
} else if (enrolment.isFlunked()) {
flunkedStudents++;
} else {
notEvaluatedStudents++;
}
}
}
if (approvedStudents + flunkedStudents + notEvaluatedStudents == 0) {
return null;
} else {
if (curricularCourseYearStatistics == null) {
return new CurricularCourseYearStatistics(approvedStudents, flunkedStudents, notEvaluatedStudents);
} else {
curricularCourseYearStatistics.inc(approvedStudents, flunkedStudents, notEvaluatedStudents);
return curricularCourseYearStatistics;
}
}
}
private JsonObject computeStudentInfo(Student student) {
JsonObject studentInfoJsonObject = new JsonObject();
studentInfoJsonObject.addProperty("id", student.getNumber());
studentInfoJsonObject.addProperty("name", student.getName());
return studentInfoJsonObject;
}
private JsonElement computeStudentExecutionCourseStatistics(Student student, ExecutionCourse executionCourse) {
JsonObject executionCourseJsonObject = new JsonObject();
executionCourseJsonObject.addProperty("acronym", executionCourse.getSigla());
executionCourseJsonObject.addProperty("name", executionCourse.getNameI18N().getContent());
executionCourseJsonObject.add("student", computeStudentInfo(student));
JsonArray evaluationsArray = computeExecutionCourseEvaluations(student, executionCourse);
evaluationsArray.add(computeFinalGrades(executionCourse));
executionCourseJsonObject.add("evaluations", evaluationsArray);
return executionCourseJsonObject;
}
private JsonArray computeExecutionCourseEvaluations(Student student, ExecutionCourse executionCourse) {
JsonArray evaluationsArray = new JsonArray();
for (WrittenEvaluation writtenEvaluation : executionCourse.getAssociatedWrittenEvaluations()) {
JsonObject writtenEvaluationJsonObject = computeWrittenEvaluation(writtenEvaluation);
if (writtenEvaluationJsonObject != null) {
evaluationsArray.add(writtenEvaluationJsonObject);
}
}
return evaluationsArray;
}
private JsonObject computeWrittenEvaluation(WrittenEvaluation writtenEvaluation) {
if (writtenEvaluation.getMarksSet().size() > 0) {
JsonObject writtenEvaluationJsonObject = new JsonObject();
writtenEvaluationJsonObject.addProperty("name", writtenEvaluation.getPresentationName());
writtenEvaluationJsonObject.addProperty("grade-scale", writtenEvaluation.getGradeScale().name());
populateMinAndMaxGrade(writtenEvaluation.getGradeScale(), writtenEvaluationJsonObject);
writtenEvaluationJsonObject.add("grades", computeWrittenEvaluationGrades(writtenEvaluation));
return writtenEvaluationJsonObject;
} else {
return null;
}
}
private void populateMinAndMaxGrade(GradeScale scale, JsonObject evaluationJsonObject) {
String minGrade = "0";
String minRequiredGrade = "10";
String maxGrade = "20";
switch (scale) {
case TYPE5:
minGrade = "0";
minRequiredGrade = "3";
maxGrade = "5";
break;
case TYPEAP:
minGrade = "RE";
minRequiredGrade = "AP";
maxGrade = "AP";
break;
default:
break;
}
evaluationJsonObject.addProperty("minRequiredGrade", minRequiredGrade);
evaluationJsonObject.addProperty("minGrade", minGrade);
evaluationJsonObject.addProperty("maxGrade", maxGrade);
}
private JsonArray computeWrittenEvaluationGrades(WrittenEvaluation writtenEvaluation) {
JsonArray gradesArray = new JsonArray();
for (Mark mark : writtenEvaluation.getMarksSet()) {
gradesArray.add(computeMark(mark));
}
return gradesArray;
}
private JsonElement computeMark(Mark mark) {
Student student = mark.getAttend().getRegistration().getStudent();
JsonObject markJsonObject = new JsonObject();
markJsonObject.addProperty("id", student.getNumber());
markJsonObject.addProperty("name", student.getName());
markJsonObject.addProperty("grade", mark.getMark());
return markJsonObject;
}
private JsonElement computeCurricularCourseOvertimeStatistics(CurricularCourse curricularCourse) {
JsonObject jsonObject = new JsonObject();
JsonArray entries = new JsonArray();
for (ExecutionCourse executionCourse : curricularCourse.getAssociatedExecutionCoursesSet()) {
if (executionCourse.getExecutionPeriod().isBefore(ExecutionSemester.readActualExecutionSemester())
&& executionCourse.getEnrolmentCount() > 0) {
JsonElement executionCourseStatistics = computeExecutionCourseStatistics(executionCourse);
if (executionCourseStatistics != null) {
entries.add(executionCourseStatistics);
}
}
}
jsonObject.add("entries", entries);
JsonArray domainJsonArray = new JsonArray();
domainJsonArray.add(new JsonPrimitive("positive-grades"));
domainJsonArray.add(new JsonPrimitive("negative-grades"));
domainJsonArray.add(new JsonPrimitive("not-evaluated-grades"));
jsonObject.add("domain", domainJsonArray);
return jsonObject;
}
private JsonElement computeExecutionCourseStatistics(ExecutionCourse executionCourse) {
JsonObject executionCourseJsonObject = new JsonObject();
executionCourseJsonObject.addProperty("text", getShortExecutionSemesterName(executionCourse.getExecutionPeriod()));
executionCourseJsonObject.addProperty("description", executionCourse.getNameI18N().getContent());
int positiveGrades = 0;
int negativeGrades = 0;
int notEvaluatedGrades = 0;
for (Enrolment enrolment : executionCourse.getActiveEnrollments()) {
if (enrolment.isNotEvaluated()) {
notEvaluatedGrades++;
} else if (enrolment.isFlunked()) {
negativeGrades++;
} else {
positiveGrades++;
}
}
if (positiveGrades + negativeGrades + notEvaluatedGrades == 0) {
return null;
}
executionCourseJsonObject.addProperty("positive-grades", positiveGrades);
executionCourseJsonObject.addProperty("negative-grades", negativeGrades);
executionCourseJsonObject.addProperty("not-evaluated-grades", notEvaluatedGrades);
return executionCourseJsonObject;
}
//REFACTOR: THIS IS HORRIBLE
private String getShortExecutionSemesterName(ExecutionSemester executionSemester) {
return executionSemester.getExecutionYear().getName().substring(2, 4) + "/"
+ executionSemester.getExecutionYear().getName().substring(7, 9) + " (S" + executionSemester.getSemester() + ")";
}
private JsonObject computeFinalGrades(ExecutionCourse executionCourse) {
List<Enrolment> enrolments = executionCourse.getActiveEnrollments();
if (enrolments.isEmpty()) {
return null;
}
JsonObject enrolmentsJsonObject = new JsonObject();
enrolmentsJsonObject.addProperty("name", BundleUtil.getString(Bundle.STUDENT, "label.student.statistics.marksheet"));
GradeScale gradeScale = enrolments.get(0).getGradeScale();
enrolmentsJsonObject.addProperty("grade-scale", gradeScale.name());
populateMinAndMaxGrade(gradeScale, enrolmentsJsonObject);
JsonArray gradesArray = new JsonArray();
for (Enrolment enrolment : enrolments) {
JsonObject enrolmentJsonObject = new JsonObject();
enrolmentJsonObject.addProperty("id", enrolment.getRegistration().getStudent().getNumber());
enrolmentJsonObject.addProperty("name", enrolment.getRegistration().getStudent().getName());
enrolmentJsonObject.addProperty("grade",
enrolment.isNotEvaluated() ? "NE" : enrolment.isFlunked() ? "RE" : enrolment.getGradeValue());
gradesArray.add(enrolmentJsonObject);
}
enrolmentsJsonObject.add("grades", gradesArray);
return enrolmentsJsonObject;
}
private JsonElement computeStudentProgress(Student student) {
JsonArray jsonArray = new JsonArray();
for (Registration registration : student.getRegistrationsSet()) {
if (registration.isActive() || registration.isConcluded()) {
jsonArray.add(computeRegistrationProgress(registration));
}
}
return jsonArray;
}
private JsonElement computeRegistrationProgress(Registration registration) {
JsonObject registrationProgressJsonObject = new JsonObject();
registrationProgressJsonObject.addProperty("year", registration.getStartDate().getYear());
registrationProgressJsonObject.addProperty("degree", registration.getDegreeCurricularPlanName());
registrationProgressJsonObject.addProperty("required-credits", registration.getDegree().getEctsCredits());
registrationProgressJsonObject.addProperty("credits", registration.getEctsCredits());
if (registration.isConcluded()) {
registrationProgressJsonObject.addProperty("status", "concluded");
} else if (registration.isActive()) {
registrationProgressJsonObject.addProperty("status", "active");
}
return registrationProgressJsonObject;
}
}