1 /***************************************************************************
2                           studentsstatisticform.cpp  -  description
3                              -------------------
4     begin                : March 25, 2006
5     copyright            : (C) 2006 by Lalescu Liviu
6     email                : Please see https://lalescu.ro/liviu/ for details about contacting Liviu Lalescu (in particular, you can find here the e-mail address)
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software: you can redistribute it and/or modify  *
12  *   it under the terms of the GNU Affero General Public License as        *
13  *   published by the Free Software Foundation, either version 3 of the    *
14  *   License, or (at your option) any later version.                       *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "studentsstatisticsform.h"
19 
20 #include "timetable_defs.h"
21 #include "timetable.h"
22 
23 #include "fet.h"
24 
25 #include "messageboxes.h"
26 
27 #include "longtextmessagebox.h"
28 
29 #include <Qt>
30 
31 #include <QString>
32 #include <QStringList>
33 
34 #include <QHash>
35 
36 #include <QProgressDialog>
37 
38 #include <QMessageBox>
39 #include <QApplication>
40 
41 #include <QHeaderView>
42 #include <QTableWidget>
43 
44 extern QApplication* pqapplication;
45 
StudentsStatisticsForm(QWidget * parent)46 StudentsStatisticsForm::StudentsStatisticsForm(QWidget* parent): QDialog(parent)
47 {
48 	setupUi(this);
49 
50 	closeButton->setDefault(true);
51 
52 	connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
53 
54 	tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
55 	tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
56 
57 	centerWidgetOnScreen(this);
58 	restoreFETDialogGeometry(this);
59 
60 	connect(showYearsCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxesModified()));
61 	connect(showGroupsCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxesModified()));
62 	connect(showSubgroupsCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxesModified()));
63 
64 	connect(showCompleteStructureCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxesModified()));
65 
66 	//2014-12-18
67 	QSet<StudentsYear*> allYears;
68 	QSet<StudentsGroup*> allGroups;
69 	QSet<StudentsSubgroup*> allSubgroups;
70 
71 	QHash<QString, StudentsYear*> yearsHash;
72 	QHash<QString, StudentsGroup*> groupsHash;
73 	QHash<QString, StudentsSubgroup*> subgroupsHash;
74 
75 	QHash<StudentsYear*, QSet<Activity*>> activitiesForYear;
76 	QHash<StudentsGroup*, QSet<Activity*>> activitiesForGroup;
77 	QHash<StudentsSubgroup*, QSet<Activity*>> activitiesForSubgroup;
78 
79 	for(StudentsYear* year : qAsConst(gt.rules.yearsList)){
80 		yearsHash.insert(year->name, year);
81 
82 		allYears.insert(year);
83 
84 		for(StudentsGroup* group : qAsConst(year->groupsList)){
85 			groupsHash.insert(group->name, group);
86 
87 			allGroups.insert(group);
88 
89 			for(StudentsSubgroup* subgroup : qAsConst(group->subgroupsList)){
90 				subgroupsHash.insert(subgroup->name, subgroup);
91 
92 				allSubgroups.insert(subgroup);
93 			}
94 		}
95 	}
96 
97 	QString warnings=QString("");
98 	for(Activity* act : qAsConst(gt.rules.activitiesList))
99 		if(act->active){
100 			for(const QString& sts : qAsConst(act->studentsNames)){
101 				if(yearsHash.contains(sts)){
102 					StudentsYear* year=yearsHash.value(sts);
103 
104 					QSet<Activity*> acts=activitiesForYear.value(year, QSet<Activity*>());
105 					acts.insert(act);
106 					activitiesForYear.insert(year, acts);
107 				}
108 				else if(groupsHash.contains(sts)){
109 					StudentsGroup* group=groupsHash.value(sts);
110 
111 					QSet<Activity*> acts=activitiesForGroup.value(group, QSet<Activity*>());
112 					acts.insert(act);
113 					activitiesForGroup.insert(group, acts);
114 				}
115 				else if(subgroupsHash.contains(sts)){
116 					StudentsSubgroup* subgroup=subgroupsHash.value(sts);
117 
118 					QSet<Activity*> acts=activitiesForSubgroup.value(subgroup, QSet<Activity*>());
119 					acts.insert(act);
120 					activitiesForSubgroup.insert(subgroup, acts);
121 				}
122 				else
123 					warnings+=tr("Students set %1 from activity with id %2 is inexistent in the students list. Please correct this.").arg(sts).arg(act->id)+QString("\n");
124 			}
125 		}
126 	if(!warnings.isEmpty())
127 		FetMessage::warning(this, tr("FET warning"), warnings);
128 
129 	//phase 1a
130 	for(StudentsYear* year : qAsConst(gt.rules.yearsList)){
131 		QSet<Activity*> actsYear=activitiesForYear.value(year, QSet<Activity*>());
132 		for(StudentsGroup* group : qAsConst(year->groupsList)){
133 			QSet<Activity*> actsGroup=activitiesForGroup.value(group, QSet<Activity*>());
134 			actsGroup.unite(actsYear);
135 			activitiesForGroup.insert(group, actsGroup);
136 		}
137 	}
138 	//phase 1b
139 	for(StudentsYear* year : qAsConst(gt.rules.yearsList)){
140 		for(StudentsGroup* group : qAsConst(year->groupsList)){
141 			QSet<Activity*> actsGroup=activitiesForGroup.value(group, QSet<Activity*>());
142 			for(StudentsSubgroup* subgroup : qAsConst(group->subgroupsList)){
143 				QSet<Activity*> actsSubgroup=activitiesForSubgroup.value(subgroup, QSet<Activity*>());
144 				actsSubgroup.unite(actsGroup);
145 				activitiesForSubgroup.insert(subgroup, actsSubgroup);
146 			}
147 		}
148 	}
149 	//phase 2a
150 	for(StudentsYear* year : qAsConst(gt.rules.yearsList)){
151 		for(StudentsGroup* group : qAsConst(year->groupsList)){
152 			QSet<Activity*> actsGroup=activitiesForGroup.value(group, QSet<Activity*>());
153 			for(StudentsSubgroup* subgroup : qAsConst(group->subgroupsList)){
154 				QSet<Activity*> actsSubgroup=activitiesForSubgroup.value(subgroup, QSet<Activity*>());
155 				actsGroup.unite(actsSubgroup);
156 			}
157 			activitiesForGroup.insert(group, actsGroup);
158 		}
159 	}
160 	//phase 2b
161 	for(StudentsYear* year : qAsConst(gt.rules.yearsList)){
162 		QSet<Activity*> actsYear=activitiesForYear.value(year, QSet<Activity*>());
163 		for(StudentsGroup* group : qAsConst(year->groupsList)){
164 			QSet<Activity*> actsGroup=activitiesForGroup.value(group, QSet<Activity*>());
165 			actsYear.unite(actsGroup);
166 		}
167 		activitiesForYear.insert(year, actsYear);
168 	}
169 
170 	allActivities.clear();
171 	allHours.clear();
172 
173 	for(StudentsYear* year : qAsConst(allYears)){
174 		QSet<Activity*> acts=activitiesForYear.value(year, QSet<Activity*>());
175 		int n=0, d=0;
176 		for(Activity* act : qAsConst(acts)){
177 			n++;
178 			d+=act->duration;
179 		}
180 		assert(!allActivities.contains(year->name));
181 		assert(!allHours.contains(year->name));
182 		allActivities.insert(year->name, n);
183 		allHours.insert(year->name, d);
184 	}
185 
186 	for(StudentsGroup* group : qAsConst(allGroups)){
187 		QSet<Activity*> acts=activitiesForGroup.value(group, QSet<Activity*>());
188 		int n=0, d=0;
189 		for(Activity* act : qAsConst(acts)){
190 			n++;
191 			d+=act->duration;
192 		}
193 		assert(!allActivities.contains(group->name));
194 		assert(!allHours.contains(group->name));
195 		allActivities.insert(group->name, n);
196 		allHours.insert(group->name, d);
197 	}
198 
199 	for(StudentsSubgroup* subgroup : qAsConst(allSubgroups)){
200 		QSet<Activity*> acts=activitiesForSubgroup.value(subgroup, QSet<Activity*>());
201 		int n=0, d=0;
202 		for(Activity* act : qAsConst(acts)){
203 			n++;
204 			d+=act->duration;
205 		}
206 		assert(!allActivities.contains(subgroup->name));
207 		assert(!allHours.contains(subgroup->name));
208 		allActivities.insert(subgroup->name, n);
209 		allHours.insert(subgroup->name, d);
210 	}
211 
212 	checkBoxesModified();
213 }
214 
~StudentsStatisticsForm()215 StudentsStatisticsForm::~StudentsStatisticsForm()
216 {
217 	saveFETDialogGeometry(this);
218 }
219 
checkBoxesModified()220 void StudentsStatisticsForm::checkBoxesModified()
221 {
222 	bool complete=showCompleteStructureCheckBox->isChecked();
223 
224 	QSet<QString> setOfStudents;
225 
226 	setOfStudents.clear();
227 	int nStudentsSets=0;
228 	for(StudentsYear* year : qAsConst(gt.rules.yearsList)){
229 		bool sy=true;
230 		if(!complete){
231 			if(setOfStudents.contains(year->name))
232 				sy=false;
233 			else
234 				setOfStudents.insert(year->name);
235 		}
236 		if(showYearsCheckBox->isChecked() && sy)
237 			nStudentsSets++;
238 		for(StudentsGroup* group : qAsConst(year->groupsList)){
239 			bool sg=true;
240 			if(!complete){
241 				if(setOfStudents.contains(group->name))
242 					sg=false;
243 				else
244 					setOfStudents.insert(group->name);
245 			}
246 			if(showGroupsCheckBox->isChecked() && sg)
247 				nStudentsSets++;
248 			for(StudentsSubgroup* subgroup : qAsConst(group->subgroupsList)){
249 				bool ss=true;
250 				if(!complete){
251 					if(setOfStudents.contains(subgroup->name))
252 						ss=false;
253 					else
254 						setOfStudents.insert(subgroup->name);
255 				}
256 				if(showSubgroupsCheckBox->isChecked() && ss)
257 					nStudentsSets++;
258 			}
259 		}
260 	}
261 
262 	tableWidget->clear();
263 	tableWidget->setColumnCount(3);
264 	tableWidget->setRowCount(nStudentsSets);
265 
266 	QStringList columns;
267 	columns<<tr("Students set");
268 	columns<<tr("No. of activities");
269 	columns<<tr("Duration");
270 
271 	tableWidget->setHorizontalHeaderLabels(columns);
272 
273 	setOfStudents.clear();
274 
275 	int currentStudentsSet=-1;
276 	for(StudentsYear* year : qAsConst(gt.rules.yearsList)){
277 		bool sy=true;
278 		if(!complete){
279 			if(setOfStudents.contains(year->name))
280 				sy=false;
281 			else
282 				setOfStudents.insert(year->name);
283 		}
284 
285 		if(showYearsCheckBox->isChecked() && sy){
286 			currentStudentsSet++;
287 			insertStudentsSet(year, currentStudentsSet);
288 		}
289 
290 		for(StudentsGroup* group : qAsConst(year->groupsList)){
291 			bool sg=true;
292 			if(!complete){
293 				if(setOfStudents.contains(group->name))
294 					sg=false;
295 				else
296 					setOfStudents.insert(group->name);
297 			}
298 
299 			if(showGroupsCheckBox->isChecked() && sg){
300 				currentStudentsSet++;
301 				insertStudentsSet(group, currentStudentsSet);
302 			}
303 
304 			for(StudentsSubgroup* subgroup : qAsConst(group->subgroupsList)){
305 				bool ss=true;
306 				if(!complete){
307 					if(setOfStudents.contains(subgroup->name))
308 						ss=false;
309 					else
310 						setOfStudents.insert(subgroup->name);
311 				}
312 
313 				if(showSubgroupsCheckBox->isChecked() && ss){
314 					currentStudentsSet++;
315 					insertStudentsSet(subgroup, currentStudentsSet);
316 				}
317 			}
318 		}
319 	}
320 
321 	tableWidget->resizeColumnsToContents();
322 	tableWidget->resizeRowsToContents();
323 }
324 
insertStudentsSet(StudentsSet * studentsSet,int row)325 void StudentsStatisticsForm::insertStudentsSet(StudentsSet* studentsSet, int row)
326 {
327 	QTableWidgetItem* newItem=new QTableWidgetItem(studentsSet->name);
328 	newItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
329 	tableWidget->setItem(row, 0, newItem);
330 
331 	int nSubActivities=0;
332 	int nHours=0;
333 
334 	if(allHours.contains(studentsSet->name))
335 		nHours=allHours.value(studentsSet->name);
336 	else
337 		assert(0);
338 
339 	if(allActivities.contains(studentsSet->name))
340 		nSubActivities=allActivities.value(studentsSet->name);
341 	else
342 		assert(0);
343 
344 	newItem=new QTableWidgetItem(CustomFETString::number(nSubActivities));
345 	newItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
346 	tableWidget->setItem(row, 1, newItem);
347 
348 	newItem=new QTableWidgetItem(CustomFETString::number(nHours));
349 	newItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
350 	tableWidget->setItem(row, 2, newItem);
351 }
352 
on_helpPushButton_clicked()353 void StudentsStatisticsForm::on_helpPushButton_clicked()
354 {
355 	QString s;
356 
357 	s+=tr("The check boxes '%1', '%2' and '%3': they permit you to show/hide information related to years, groups or subgroups")
358 	 .arg(tr("Show years"))
359 	 .arg(tr("Show groups"))
360 	 .arg(tr("Show subgroups"));
361 
362 	s+="\n\n";
363 
364 	s+=tr("The check box '%1': it has effect only if you have overlapping groups/years, and means that FET will show the complete tree structure"
365 	 ", even if that means that some subgroups/groups will appear twice or more in the table, with the same information."
366 	 " For instance, if you have year Y1, groups G1 and G2, subgroups S1, S2, S3, with structure: Y1 (G1 (S1, S2), G2 (S1, S3)),"
367 	 " S1 will appear twice in the table with the same information attached").arg(tr("Show duplicates"));
368 
369 	LongTextMessageBox::largeInformation(this, tr("FET help"), s);
370 }
371