2020-04-20 01:26:46 +02:00
|
|
|
|
// swad_exam_event.c: exam events (each ocurrence of an exam)
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
SWAD (Shared Workspace At a Distance),
|
|
|
|
|
is a web platform developed at the University of Granada (Spain),
|
|
|
|
|
and used to support university teaching.
|
|
|
|
|
|
|
|
|
|
This file is part of SWAD core.
|
|
|
|
|
Copyright (C) 1999-2020 Antonio Ca<EFBFBD>as Vargas
|
|
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU Affero General Public License as
|
|
|
|
|
published by the Free Software Foundation, either version 3 of the
|
|
|
|
|
License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program 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 Affero General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************************* Headers ***********************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#define _GNU_SOURCE // For asprintf
|
|
|
|
|
#include <linux/limits.h> // For PATH_MAX
|
|
|
|
|
#include <stddef.h> // For NULL
|
|
|
|
|
#include <stdio.h> // For asprintf
|
|
|
|
|
#include <stdlib.h> // For calloc
|
|
|
|
|
#include <string.h> // For string functions
|
|
|
|
|
|
|
|
|
|
#include "swad_database.h"
|
|
|
|
|
#include "swad_date.h"
|
|
|
|
|
#include "swad_exam.h"
|
|
|
|
|
#include "swad_exam_event.h"
|
|
|
|
|
#include "swad_exam_result.h"
|
|
|
|
|
#include "swad_form.h"
|
|
|
|
|
#include "swad_global.h"
|
|
|
|
|
#include "swad_HTML.h"
|
|
|
|
|
#include "swad_role.h"
|
|
|
|
|
#include "swad_setting.h"
|
|
|
|
|
#include "swad_test.h"
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** External global variables from others modules ****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
extern struct Globals Gbl;
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************************** Private constants *****************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#define ExaEvt_ICON_CLOSE "fas fa-times"
|
|
|
|
|
#define ExaEvt_ICON_PLAY "fas fa-play"
|
|
|
|
|
#define ExaEvt_ICON_PAUSE "fas fa-pause"
|
|
|
|
|
#define ExaEvt_ICON_PREVIOUS "fas fa-step-backward"
|
|
|
|
|
#define ExaEvt_ICON_NEXT "fas fa-step-forward"
|
|
|
|
|
#define ExaEvt_ICON_RESULTS "fas fa-chart-bar"
|
|
|
|
|
|
|
|
|
|
#define ExaEvt_COUNTDOWN_SECONDS_LARGE 60
|
|
|
|
|
#define ExaEvt_COUNTDOWN_SECONDS_MEDIUM 30
|
|
|
|
|
#define ExaEvt_COUNTDOWN_SECONDS_SMALL 10
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************************* Private types *******************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
ExaEvt_CHANGE_STATUS_BY_STUDENT,
|
|
|
|
|
ExaEvt_REFRESH_STATUS_BY_SERVER,
|
|
|
|
|
} ExaEvt_Update_t;
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************************** Private constants *****************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
const char *ExaEvt_ShowingStringsDB[ExaEvt_NUM_SHOWING] =
|
|
|
|
|
{
|
|
|
|
|
[ExaEvt_START ] = "start",
|
|
|
|
|
[ExaEvt_STEM ] = "stem",
|
|
|
|
|
[ExaEvt_ANSWERS] = "answers",
|
|
|
|
|
[ExaEvt_RESULTS] = "results",
|
|
|
|
|
[ExaEvt_END ] = "end",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define ExaEvt_MAX_COLS 4
|
|
|
|
|
#define ExaEvt_NUM_COLS_DEFAULT 1
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************************** Private variables *****************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
long ExaEvt_EvtCodBeingPlayed; // Used to refresh exam via AJAX
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************************** Private prototypes ****************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_SetEvtCodBeingPlayed (long EvtCod);
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutIconsInListOfEvents (void *Exams);
|
|
|
|
|
static void ExaEvt_PutIconToCreateNewEvent (struct Exa_Exams *Exams);
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_ListOneOrMoreEvents (struct Exa_Exams *Exams,
|
|
|
|
|
const struct Exa_Exam *Exam,
|
|
|
|
|
unsigned NumEvents,
|
|
|
|
|
MYSQL_RES *mysql_res);
|
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsHeading (bool ICanEditEvents);
|
|
|
|
|
static bool ExaEvt_CheckIfICanEditEvents (void);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static bool ExaEvt_CheckIfICanEditThisEvent (const struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsIcons (struct Exa_Exams *Exams,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
const struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsAuthor (const struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsTimes (const struct ExaEvt_Event *Event,unsigned UniqueId);
|
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsTitleGrps (const struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_GetAndWriteNamesOfGrpsAssociatedToEvent (const struct ExaEvt_Event *Event);
|
2020-04-23 02:28:28 +02:00
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsNumParticipants (const struct ExaEvt_Event *Event);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsStatus (struct ExaEvt_Event *Event,unsigned NumQsts);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsResult (struct Exa_Exams *Exams,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
const struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsResultStd (struct Exa_Exams *Exams,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
const struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsResultTch (struct Exa_Exams *Exams,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
const struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
static void ExaEvt_GetEventDataFromRow (MYSQL_RES *mysql_res,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
static ExaEvt_Showing_t ExaEvt_GetShowingFromStr (const char *Str);
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_RemoveEventFromAllTables (long EvtCod);
|
|
|
|
|
static void ExaEvt_RemoveEventFromTable (long EvtCod,const char *TableName);
|
|
|
|
|
static void ExaEvt_RemoveEventsInExamFromTable (long ExaCod,const char *TableName);
|
|
|
|
|
static void ExaEvt_RemoveEventInCourseFromTable (long CrsCod,const char *TableName);
|
|
|
|
|
static void ExaEvt_RemoveUsrEvtResultsInCrs (long UsrCod,long CrsCod,const char *TableName);
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutParamsPlay (void *EvtCod);
|
|
|
|
|
static void ExaEvt_PutParamEvtCod (long EvtCod);
|
|
|
|
|
|
2020-04-22 03:48:38 +02:00
|
|
|
|
static void ExaEvt_PutFormNewEvent (const struct Exa_Exam *Exam);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
static void ExaEvt_ShowLstGrpsToCreateEvent (void);
|
|
|
|
|
|
|
|
|
|
static long ExaEvt_CreateEvent (long ExaCod,char Title[Exa_MAX_BYTES_TITLE + 1]);
|
|
|
|
|
static void ExaEvt_CreateIndexes (long ExaCod,long EvtCod);
|
|
|
|
|
static void ExaEvt_ReorderAnswer (long EvtCod,unsigned QstInd,
|
|
|
|
|
const struct Tst_Question *Question);
|
|
|
|
|
static void ExaEvt_CreateGrps (long EvtCod);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_UpdateEventStatusInDB (const struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_UpdateElapsedTimeInQuestion (const struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_GetElapsedTimeInQuestion (const struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
struct Time *Time);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_GetElapsedTimeInMatch (const struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
struct Time *Time);
|
|
|
|
|
static void ExaEvt_GetElapsedTime (unsigned NumRows,MYSQL_RES *mysql_res,
|
|
|
|
|
struct Time *Time);
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_SetMatchStatusToPrev (struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_SetMatchStatusToPrevQst (struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_SetMatchStatusToStart (struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_SetMatchStatusToNext (struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_SetMatchStatusToNextQst (struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_SetMatchStatusToEnd (struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowMatchStatusForTch (struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_ShowMatchStatusForStd (struct ExaEvt_Event *Event,ExaEvt_Update_t Update);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowLeftColumnTch (struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_ShowRefreshablePartTch (struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_WriteElapsedTimeInEvt (struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_WriteElapsedTimeInQst (struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_WriteNumRespondersQst (struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_PutFormCountdown (struct ExaEvt_Event *Event,long Seconds,const char *Color);
|
|
|
|
|
static void ExaEvt_PutCountdownAndHourglassIcon (struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_PutFormsCountdown (struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowRightColumnTch (const struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_ShowLeftColumnStd (const struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
const struct ExaEvt_UsrAnswer *UsrAnswer);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowRightColumnStd (struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
const struct ExaEvt_UsrAnswer *UsrAnswer,
|
|
|
|
|
ExaEvt_Update_t Update);
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowNumQstInEvt (const struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_PutMatchControlButtons (const struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_ShowFormColumns (const struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
static void ExaEvt_PutParamNumCols (unsigned NumCols);
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowEventTitleTch (const struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_ShowEventTitleStd (const struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_PutCheckboxResult (const struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_PutIfAnswered (const struct ExaEvt_Event *Event,bool Answered);
|
|
|
|
|
static void ExaEvt_PutIconToRemoveMyAnswer (const struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_ShowQuestionAndAnswersTch (const struct ExaEvt_Event *Event);
|
|
|
|
|
static void ExaEvt_WriteAnswersEventResult (const struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
const struct Tst_Question *Question,
|
|
|
|
|
const char *Class,bool ShowResult);
|
2020-04-22 22:40:07 +02:00
|
|
|
|
static void ExaEvt_ShowQuestionAndAnswersStd (const struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
const struct ExaEvt_UsrAnswer *UsrAnswer,
|
|
|
|
|
ExaEvt_Update_t Update);
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowEventScore (const struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
static void ExaEvt_DrawEmptyScoreRow (unsigned NumRow,double MinScore,double MaxScore);
|
|
|
|
|
static void ExaEvt_DrawScoreRow (double Score,double MinScore,double MaxScore,
|
|
|
|
|
unsigned NumRow,unsigned NumUsrs,unsigned MaxUsrs);
|
|
|
|
|
static const char *ExaEvt_GetClassBorder (unsigned NumRow);
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutParamNumOpt (unsigned NumOpt);
|
|
|
|
|
static unsigned ExaEvt_GetParamNumOpt (void);
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutBigButton (Act_Action_t NextAction,const char *Id,
|
|
|
|
|
long EvtCod,const char *Icon,const char *Txt);
|
|
|
|
|
static void ExaEvt_PutBigButtonHidden (const char *Icon);
|
|
|
|
|
static void ExaEvt_PutBigButtonClose (void);
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_ShowWaitImage (const char *Txt);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
static void ExaEvt_RemoveOldParticipants (void);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
static void ExaEvt_UpdateEventAsBeingPlayed (long EvtCod);
|
|
|
|
|
static void ExaEvt_SetEventAsNotBeingPlayed (long EvtCod);
|
|
|
|
|
static bool ExaEvt_GetIfEventIsBeingPlayed (long EvtCod);
|
2020-04-23 02:28:28 +02:00
|
|
|
|
static void ExaEvt_GetNumParticipants (struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_RemoveMyAnswerToEventQuestion (const struct ExaEvt_Event *Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ComputeScore (struct TstRes_Result *Result);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
static unsigned ExaEvt_GetNumUsrsWhoHaveAnswerEvt (long EvtCod);
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********** Set/Get exam event code of the exam event being played ***********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_SetEvtCodBeingPlayed (long EvtCod)
|
|
|
|
|
{
|
|
|
|
|
ExaEvt_EvtCodBeingPlayed = EvtCod;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long ExaEvt_GetEvtCodBeingPlayed (void)
|
|
|
|
|
{
|
|
|
|
|
return ExaEvt_EvtCodBeingPlayed;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************************** Reset exam event *****************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_ResetEvent (struct ExaEvt_Event *Event)
|
|
|
|
|
{
|
|
|
|
|
/***** Initialize to empty match *****/
|
|
|
|
|
Event->EvtCod = -1L;
|
|
|
|
|
Event->ExaCod = -1L;
|
|
|
|
|
Event->UsrCod = -1L;
|
|
|
|
|
Event->TimeUTC[Dat_START_TIME] = (time_t) 0;
|
|
|
|
|
Event->TimeUTC[Dat_END_TIME ] = (time_t) 0;
|
|
|
|
|
Event->Title[0] = '\0';
|
|
|
|
|
Event->Status.QstInd = 0;
|
|
|
|
|
Event->Status.QstCod = -1L;
|
|
|
|
|
Event->Status.QstStartTimeUTC = (time_t) 0;
|
|
|
|
|
Event->Status.Showing = ExaEvt_SHOWING_DEFAULT;
|
|
|
|
|
Event->Status.Countdown = 0;
|
|
|
|
|
Event->Status.NumCols = 1;
|
|
|
|
|
Event->Status.ShowQstResults = false;
|
|
|
|
|
Event->Status.ShowUsrResults = false;
|
|
|
|
|
Event->Status.Happening = false;
|
|
|
|
|
Event->Status.NumParticipants = 0;
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************************* List the events of an exam ************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_ListEvents (struct Exa_Exams *Exams,
|
|
|
|
|
struct Exa_Exam *Exam,
|
|
|
|
|
bool PutFormNewEvent)
|
|
|
|
|
{
|
2020-04-22 22:40:07 +02:00
|
|
|
|
extern const char *Hlp_ASSESSMENT_Exams_events;
|
|
|
|
|
extern const char *Txt_Events;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
char *SubQuery;
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
unsigned NumEvents;
|
|
|
|
|
|
|
|
|
|
/***** Get data of events from database *****/
|
|
|
|
|
/* Fill subquery for exam */
|
|
|
|
|
if (Gbl.Crs.Grps.WhichGrps == Grp_MY_GROUPS)
|
|
|
|
|
{
|
|
|
|
|
if (asprintf (&SubQuery," AND"
|
|
|
|
|
"(EvtCod NOT IN"
|
|
|
|
|
" (SELECT EvtCod FROM exa_groups)"
|
|
|
|
|
" OR"
|
|
|
|
|
" EvtCod IN"
|
|
|
|
|
" (SELECT exa_groups.EvtCod"
|
|
|
|
|
" FROM exa_groups,crs_grp_usr"
|
|
|
|
|
" WHERE crs_grp_usr.UsrCod=%ld"
|
|
|
|
|
" AND exa_groups.GrpCod=crs_grp_usr.GrpCod))",
|
|
|
|
|
Gbl.Usrs.Me.UsrDat.UsrCod) < 0)
|
|
|
|
|
Lay_NotEnoughMemoryExit ();
|
|
|
|
|
}
|
|
|
|
|
else // Gbl.Crs.Grps.WhichGrps == Grp_ALL_GROUPS
|
|
|
|
|
if (asprintf (&SubQuery,"%s","") < 0)
|
|
|
|
|
Lay_NotEnoughMemoryExit ();
|
|
|
|
|
|
|
|
|
|
/* Make query */
|
|
|
|
|
NumEvents = (unsigned) DB_QuerySELECT (&mysql_res,"can not get events",
|
|
|
|
|
"SELECT EvtCod," // row[ 0]
|
|
|
|
|
"ExaCod," // row[ 1]
|
|
|
|
|
"UsrCod," // row[ 2]
|
|
|
|
|
"UNIX_TIMESTAMP(StartTime)," // row[ 3]
|
|
|
|
|
"UNIX_TIMESTAMP(EndTime)," // row[ 4]
|
|
|
|
|
"Title," // row[ 5]
|
|
|
|
|
"QstInd," // row[ 6]
|
|
|
|
|
"QstCod," // row[ 7]
|
|
|
|
|
"Showing," // row[ 8]
|
|
|
|
|
"Countdown," // row[ 9]
|
|
|
|
|
"NumCols," // row[10]
|
|
|
|
|
"ShowQstResults," // row[11]
|
|
|
|
|
"ShowUsrResults" // row[12]
|
|
|
|
|
" FROM exa_events"
|
|
|
|
|
" WHERE ExaCod=%ld%s"
|
|
|
|
|
" ORDER BY EvtCod",
|
|
|
|
|
Exam->ExaCod,
|
|
|
|
|
SubQuery);
|
|
|
|
|
|
|
|
|
|
/* Free allocated memory for subquery */
|
|
|
|
|
free (SubQuery);
|
|
|
|
|
|
|
|
|
|
/***** Begin box *****/
|
|
|
|
|
Exams->ExaCod = Exam->ExaCod;
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Box_BoxBegin ("100%",Txt_Events,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_PutIconsInListOfEvents,Exams,
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Hlp_ASSESSMENT_Exams_events,Box_NOT_CLOSABLE);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Select whether show only my groups or all groups *****/
|
|
|
|
|
if (Gbl.Crs.Grps.NumGrps)
|
|
|
|
|
{
|
|
|
|
|
Set_StartSettingsHead ();
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Grp_ShowFormToSelWhichGrps (ActSeeExa,
|
|
|
|
|
Exa_PutParams,Exams);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Set_EndSettingsHead ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NumEvents)
|
|
|
|
|
/***** Show the table with the events *****/
|
|
|
|
|
ExaEvt_ListOneOrMoreEvents (Exams,Exam,NumEvents,mysql_res);
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
|
|
|
|
|
/***** Put button to play a new exam event in this exam *****/
|
|
|
|
|
switch (Gbl.Usrs.Me.Role.Logged)
|
|
|
|
|
{
|
|
|
|
|
case Rol_NET:
|
|
|
|
|
case Rol_TCH:
|
|
|
|
|
case Rol_SYS_ADM:
|
|
|
|
|
if (PutFormNewEvent)
|
2020-04-22 03:48:38 +02:00
|
|
|
|
ExaEvt_PutFormNewEvent (Exam); // Form to fill in data and start playing a new exam event
|
2020-04-20 01:26:46 +02:00
|
|
|
|
else
|
2020-04-22 03:48:38 +02:00
|
|
|
|
Exa_PutButtonNewEvent (Exams,Exam->ExaCod); // Button to create a new exam event
|
2020-04-20 01:26:46 +02:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** End box *****/
|
|
|
|
|
Box_BoxEnd ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Get exam event data using its code *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
void ExaEvt_GetDataOfEventByCod (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
unsigned long NumRows;
|
|
|
|
|
Dat_StartEndTime_t StartEndTime;
|
|
|
|
|
|
2020-04-26 01:54:27 +02:00
|
|
|
|
/***** Get exam data event from database *****/
|
2020-04-20 01:26:46 +02:00
|
|
|
|
NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get events",
|
|
|
|
|
"SELECT EvtCod," // row[ 0]
|
|
|
|
|
"ExaCod," // row[ 1]
|
|
|
|
|
"UsrCod," // row[ 2]
|
|
|
|
|
"UNIX_TIMESTAMP(StartTime)," // row[ 3]
|
|
|
|
|
"UNIX_TIMESTAMP(EndTime)," // row[ 4]
|
|
|
|
|
"Title," // row[ 5]
|
|
|
|
|
"QstInd," // row[ 6]
|
|
|
|
|
"QstCod," // row[ 7]
|
|
|
|
|
"Showing," // row[ 8]
|
|
|
|
|
"Countdown," // row[ 9]
|
|
|
|
|
"NumCols," // row[10]
|
|
|
|
|
"ShowQstResults," // row[11]
|
|
|
|
|
"ShowUsrResults" // row[12]
|
|
|
|
|
" FROM exa_events"
|
|
|
|
|
" WHERE EvtCod=%ld"
|
|
|
|
|
" AND ExaCod IN" // Extra check
|
|
|
|
|
" (SELECT ExaCod FROM exa_exams"
|
|
|
|
|
" WHERE CrsCod='%ld')",
|
|
|
|
|
Event->EvtCod,
|
|
|
|
|
Gbl.Hierarchy.Crs.CrsCod);
|
|
|
|
|
if (NumRows) // Event found...
|
|
|
|
|
/***** Get exam event data from row *****/
|
|
|
|
|
ExaEvt_GetEventDataFromRow (mysql_res,Event);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Initialize to empty exam event */
|
|
|
|
|
Event->EvtCod = -1L;
|
|
|
|
|
Event->ExaCod = -1L;
|
|
|
|
|
Event->UsrCod = -1L;
|
|
|
|
|
for (StartEndTime = (Dat_StartEndTime_t) 0;
|
|
|
|
|
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
|
|
|
|
|
StartEndTime++)
|
|
|
|
|
Event->TimeUTC[StartEndTime] = (time_t) 0;
|
|
|
|
|
Event->Title[0] = '\0';
|
|
|
|
|
Event->Status.QstInd = 0;
|
|
|
|
|
Event->Status.QstCod = -1L;
|
|
|
|
|
Event->Status.QstStartTimeUTC = (time_t) 0;
|
|
|
|
|
Event->Status.Showing = ExaEvt_START;
|
|
|
|
|
Event->Status.Countdown = -1L;
|
2020-04-23 02:28:28 +02:00
|
|
|
|
Event->Status.Happening = false;
|
|
|
|
|
Event->Status.NumParticipants = 0;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event->Status.NumCols = ExaEvt_NUM_COLS_DEFAULT;
|
|
|
|
|
Event->Status.ShowQstResults = false;
|
|
|
|
|
Event->Status.ShowUsrResults = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************** Put icons in list of events of an exam *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutIconsInListOfEvents (void *Exams)
|
|
|
|
|
{
|
|
|
|
|
bool ICanEditEvents;
|
|
|
|
|
|
|
|
|
|
if (Exams)
|
|
|
|
|
{
|
|
|
|
|
/***** Put icon to create a new exam event in current exam *****/
|
|
|
|
|
ICanEditEvents = ExaEvt_CheckIfICanEditEvents ();
|
|
|
|
|
if (ICanEditEvents)
|
|
|
|
|
ExaEvt_PutIconToCreateNewEvent ((struct Exa_Exams *) Exams);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************* Put icon to create a new exam event *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutIconToCreateNewEvent (struct Exa_Exams *Exams)
|
|
|
|
|
{
|
2020-04-22 22:40:07 +02:00
|
|
|
|
extern const char *Txt_New_event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Put form to create a new exam event *****/
|
2020-04-22 03:15:04 +02:00
|
|
|
|
Ico_PutContextualIconToAdd (ActReqNewExaEvt,ExaEvt_NEW_EVENT_SECTION_ID,
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Exa_PutParams,Exams,
|
|
|
|
|
Txt_New_event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** List exam events for edition ************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_ListOneOrMoreEvents (struct Exa_Exams *Exams,
|
|
|
|
|
const struct Exa_Exam *Exam,
|
|
|
|
|
unsigned NumEvents,
|
|
|
|
|
MYSQL_RES *mysql_res)
|
|
|
|
|
{
|
|
|
|
|
unsigned NumEvent;
|
|
|
|
|
unsigned UniqueId;
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
bool ICanEditEvents = ExaEvt_CheckIfICanEditEvents ();
|
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/***** Write the heading *****/
|
|
|
|
|
HTM_TABLE_BeginWidePadding (2);
|
|
|
|
|
ExaEvt_ListOneOrMoreEventsHeading (ICanEditEvents);
|
|
|
|
|
|
|
|
|
|
/***** Write rows *****/
|
|
|
|
|
for (NumEvent = 0, UniqueId = 1;
|
|
|
|
|
NumEvent < NumEvents;
|
|
|
|
|
NumEvent++, UniqueId++)
|
|
|
|
|
{
|
|
|
|
|
Gbl.RowEvenOdd = NumEvent % 2;
|
|
|
|
|
|
|
|
|
|
/***** Get exam event data from row *****/
|
|
|
|
|
ExaEvt_GetEventDataFromRow (mysql_res,&Event);
|
|
|
|
|
|
|
|
|
|
if (ExaEvt_CheckIfICanPlayThisEventBasedOnGrps (&Event))
|
|
|
|
|
{
|
|
|
|
|
/***** Write row for this exam event ****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
/* Icons */
|
|
|
|
|
if (ICanEditEvents)
|
|
|
|
|
ExaEvt_ListOneOrMoreEventsIcons (Exams,&Event);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/* Event participant */
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ListOneOrMoreEventsAuthor (&Event);
|
|
|
|
|
|
|
|
|
|
/* Start/end date/time */
|
|
|
|
|
ExaEvt_ListOneOrMoreEventsTimes (&Event,UniqueId);
|
|
|
|
|
|
|
|
|
|
/* Title and groups */
|
|
|
|
|
ExaEvt_ListOneOrMoreEventsTitleGrps (&Event);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/* Number of participants who have answered any question in the exam event */
|
|
|
|
|
ExaEvt_ListOneOrMoreEventsNumParticipants (&Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/* Event status */
|
|
|
|
|
ExaEvt_ListOneOrMoreEventsStatus (&Event,Exam->NumQsts);
|
|
|
|
|
|
|
|
|
|
/* Event result visible? */
|
|
|
|
|
ExaEvt_ListOneOrMoreEventsResult (Exams,&Event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** End table *****/
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** Put a column for exam event start and end times **************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsHeading (bool ICanEditEvents)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
|
|
|
|
|
extern const char *Txt_START_END_TIME[Dat_NUM_START_END_TIME];
|
2020-04-23 02:28:28 +02:00
|
|
|
|
extern const char *Txt_Event;
|
|
|
|
|
extern const char *Txt_ROLES_PLURAL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
|
2020-04-20 01:26:46 +02:00
|
|
|
|
extern const char *Txt_Status;
|
|
|
|
|
extern const char *Txt_Results;
|
|
|
|
|
|
|
|
|
|
/***** Start row *****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
/***** Column for icons *****/
|
|
|
|
|
if (ICanEditEvents)
|
|
|
|
|
HTM_TH_Empty (1);
|
|
|
|
|
|
|
|
|
|
/***** The rest of columns *****/
|
|
|
|
|
HTM_TH (1,1,"LT",Txt_ROLES_SINGUL_Abc[Rol_TCH][Usr_SEX_UNKNOWN]);
|
2020-04-22 22:40:07 +02:00
|
|
|
|
HTM_TH (1,1,"LT",Txt_START_END_TIME[Exa_ORDER_BY_START_DATE]);
|
|
|
|
|
HTM_TH (1,1,"LT",Txt_START_END_TIME[Exa_ORDER_BY_END_DATE ]);
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_TH (1,1,"LT",Txt_Event);
|
|
|
|
|
HTM_TH (1,1,"RT",Txt_ROLES_PLURAL_Abc[Rol_STD][Usr_SEX_UNKNOWN]);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
HTM_TH (1,1,"CT",Txt_Status);
|
|
|
|
|
HTM_TH (1,1,"CT",Txt_Results);
|
|
|
|
|
|
|
|
|
|
/***** End row *****/
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Check if I can edit events **************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static bool ExaEvt_CheckIfICanEditEvents (void)
|
|
|
|
|
{
|
|
|
|
|
switch (Gbl.Usrs.Me.Role.Logged)
|
|
|
|
|
{
|
|
|
|
|
case Rol_NET:
|
|
|
|
|
case Rol_TCH:
|
|
|
|
|
case Rol_SYS_ADM:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** Check if I can edit (remove/resume) an exam event ************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static bool ExaEvt_CheckIfICanEditThisEvent (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
switch (Gbl.Usrs.Me.Role.Logged)
|
|
|
|
|
{
|
|
|
|
|
case Rol_NET:
|
|
|
|
|
return (Event->UsrCod == Gbl.Usrs.Me.UsrDat.UsrCod); // Only if I am the creator
|
|
|
|
|
case Rol_TCH:
|
|
|
|
|
case Rol_SYS_ADM:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************************* Put a column for icons ****************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsIcons (struct Exa_Exams *Exams,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
HTM_TD_Begin ("class=\"BT%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
|
|
|
|
|
/***** Put icon to remove the exam event *****/
|
|
|
|
|
if (ExaEvt_CheckIfICanEditThisEvent (Event))
|
|
|
|
|
{
|
|
|
|
|
Exams->ExaCod = Event->ExaCod;
|
|
|
|
|
Exams->EvtCod = Event->EvtCod;
|
|
|
|
|
Frm_StartForm (ActReqRemExaEvt);
|
|
|
|
|
ExaEvt_PutParamsEdit (Exams);
|
|
|
|
|
Ico_PutIconRemove ();
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
Ico_PutIconRemovalNotAllowed ();
|
|
|
|
|
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********** Put a column for teacher who created the exam event *************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsAuthor (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Event author (teacher) *****/
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
Usr_WriteAuthor1Line (Event->UsrCod,false);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Put a column for exam event start and end times *************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsTimes (const struct ExaEvt_Event *Event,unsigned UniqueId)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
Dat_StartEndTime_t StartEndTime;
|
|
|
|
|
char *Id;
|
|
|
|
|
|
|
|
|
|
for (StartEndTime = (Dat_StartEndTime_t) 0;
|
|
|
|
|
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
|
|
|
|
|
StartEndTime++)
|
|
|
|
|
{
|
|
|
|
|
if (asprintf (&Id,"exa_time_%u_%u",(unsigned) StartEndTime,UniqueId) < 0)
|
|
|
|
|
Lay_NotEnoughMemoryExit ();
|
|
|
|
|
HTM_TD_Begin ("id=\"%s\" class=\"%s LT COLOR%u\"",
|
|
|
|
|
Id,
|
|
|
|
|
Event->Status.Showing == ExaEvt_END ? "DATE_RED" :
|
|
|
|
|
"DATE_GREEN",
|
|
|
|
|
Gbl.RowEvenOdd);
|
|
|
|
|
Dat_WriteLocalDateHMSFromUTC (Id,Event->TimeUTC[StartEndTime],
|
|
|
|
|
Gbl.Prefs.DateFormat,Dat_SEPARATOR_BREAK,
|
|
|
|
|
true,true,true,0x7);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
free (Id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Put a column for exam event title and grous *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsTitleGrps (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Play;
|
|
|
|
|
extern const char *Txt_Resume;
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"LT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
|
|
|
|
|
/***** Event title *****/
|
|
|
|
|
Frm_StartForm (Gbl.Usrs.Me.Role.Logged == Rol_STD ? ActJoiExaEvt :
|
|
|
|
|
ActResExaEvt);
|
|
|
|
|
ExaEvt_PutParamEvtCod (Event->EvtCod);
|
|
|
|
|
HTM_BUTTON_SUBMIT_Begin (Gbl.Usrs.Me.Role.Logged == Rol_STD ? Txt_Play :
|
|
|
|
|
Txt_Resume,
|
|
|
|
|
"BT_LINK LT ASG_TITLE",NULL);
|
|
|
|
|
HTM_Txt (Event->Title);
|
|
|
|
|
HTM_BUTTON_End ();
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
|
|
|
|
|
/***** Groups whose students can answer this exam event *****/
|
|
|
|
|
if (Gbl.Crs.Grps.NumGrps)
|
|
|
|
|
ExaEvt_GetAndWriteNamesOfGrpsAssociatedToEvent (Event);
|
|
|
|
|
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********** Get and write the names of the groups of an exam event ***********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_GetAndWriteNamesOfGrpsAssociatedToEvent (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Group;
|
|
|
|
|
extern const char *Txt_Groups;
|
|
|
|
|
extern const char *Txt_and;
|
|
|
|
|
extern const char *Txt_The_whole_course;
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
unsigned long NumRow;
|
|
|
|
|
unsigned long NumRows;
|
|
|
|
|
|
|
|
|
|
/***** Get groups associated to an exam event from database *****/
|
|
|
|
|
NumRows = DB_QuerySELECT (&mysql_res,"can not get groups of an exam event",
|
|
|
|
|
"SELECT crs_grp_types.GrpTypName,crs_grp.GrpName"
|
|
|
|
|
" FROM exa_groups,crs_grp,crs_grp_types"
|
|
|
|
|
" WHERE exa_groups.EvtCod=%ld"
|
|
|
|
|
" AND exa_groups.GrpCod=crs_grp.GrpCod"
|
|
|
|
|
" AND crs_grp.GrpTypCod=crs_grp_types.GrpTypCod"
|
|
|
|
|
" ORDER BY crs_grp_types.GrpTypName,crs_grp.GrpName",
|
|
|
|
|
Event->EvtCod);
|
|
|
|
|
|
|
|
|
|
/***** Write heading *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"ASG_GRP\"");
|
|
|
|
|
HTM_TxtColonNBSP (NumRows == 1 ? Txt_Group :
|
|
|
|
|
Txt_Groups);
|
|
|
|
|
|
|
|
|
|
/***** Write groups *****/
|
|
|
|
|
if (NumRows) // Groups found...
|
|
|
|
|
{
|
|
|
|
|
/* Get and write the group types and names */
|
|
|
|
|
for (NumRow = 0;
|
|
|
|
|
NumRow < NumRows;
|
|
|
|
|
NumRow++)
|
|
|
|
|
{
|
|
|
|
|
/* Get next group */
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
|
|
|
|
|
/* Write group type name and group name */
|
|
|
|
|
HTM_TxtF ("%s %s",row[0],row[1]);
|
|
|
|
|
|
|
|
|
|
if (NumRows >= 2)
|
|
|
|
|
{
|
|
|
|
|
if (NumRow == NumRows-2)
|
|
|
|
|
HTM_TxtF (" %s ",Txt_and);
|
|
|
|
|
if (NumRows >= 3)
|
|
|
|
|
if (NumRow < NumRows-2)
|
|
|
|
|
HTM_Txt (", ");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
HTM_TxtF ("%s %s",Txt_The_whole_course,Gbl.Hierarchy.Crs.ShrtName);
|
|
|
|
|
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********** Check if an exam event is associated to a given group ************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
bool ExaEvt_CheckIfMatchIsAssociatedToGrp (long EvtCod,long GrpCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Get if an exam event is associated to a group from database *****/
|
|
|
|
|
return (DB_QueryCOUNT ("can not check if an exam event is associated to a group",
|
|
|
|
|
"SELECT COUNT(*) FROM exa_groups"
|
|
|
|
|
" WHERE EvtCod=%ld AND GrpCod=%ld",
|
|
|
|
|
EvtCod,GrpCod) != 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/**************** Put a column for number of participants ********************/
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsNumParticipants (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Number of participants who have answered any question in the exam event ******/
|
2020-04-20 01:26:46 +02:00
|
|
|
|
HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
HTM_Unsigned (ExaEvt_GetNumUsrsWhoHaveAnswerEvt (Event->EvtCod));
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Put a column for exam event status *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsStatus (struct ExaEvt_Event *Event,unsigned NumQsts)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Play;
|
|
|
|
|
extern const char *Txt_Resume;
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"DAT CT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
|
|
|
|
|
if (Event->Status.Showing != ExaEvt_END) // Event not over
|
|
|
|
|
{
|
|
|
|
|
/* Current question index / total of questions */
|
|
|
|
|
HTM_DIV_Begin ("class=\"DAT\"");
|
|
|
|
|
HTM_TxtF ("%u/%u",Event->Status.QstInd,NumQsts);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Icon to join exam event or resume exam event */
|
|
|
|
|
Lay_PutContextualLinkOnlyIcon (Gbl.Usrs.Me.Role.Logged == Rol_STD ? ActJoiExaEvt :
|
|
|
|
|
ActResExaEvt,
|
|
|
|
|
NULL,
|
|
|
|
|
ExaEvt_PutParamsPlay,&Event->EvtCod,
|
|
|
|
|
Event->Status.Showing == ExaEvt_END ? "flag-checkered.svg" :
|
|
|
|
|
"play.svg",
|
|
|
|
|
Gbl.Usrs.Me.Role.Logged == Rol_STD ? Txt_Play :
|
|
|
|
|
Txt_Resume);
|
|
|
|
|
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************* Put a column for visibility of exam event result **************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsResult (struct Exa_Exams *Exams,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
HTM_TD_Begin ("class=\"DAT CT COLOR%u\"",Gbl.RowEvenOdd);
|
|
|
|
|
|
|
|
|
|
switch (Gbl.Usrs.Me.Role.Logged)
|
|
|
|
|
{
|
|
|
|
|
case Rol_STD:
|
|
|
|
|
ExaEvt_ListOneOrMoreEventsResultStd (Exams,Event);
|
|
|
|
|
break;
|
|
|
|
|
case Rol_NET:
|
|
|
|
|
case Rol_TCH:
|
|
|
|
|
case Rol_SYS_ADM:
|
|
|
|
|
ExaEvt_ListOneOrMoreEventsResultTch (Exams,Event);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Rol_WrongRoleExit ();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsResultStd (struct Exa_Exams *Exams,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Results;
|
|
|
|
|
|
|
|
|
|
/***** Is exam event result visible or hidden? *****/
|
|
|
|
|
if (Event->Status.ShowUsrResults)
|
|
|
|
|
{
|
|
|
|
|
/* Result is visible by me */
|
|
|
|
|
Exams->ExaCod = Event->ExaCod;
|
|
|
|
|
Exams->EvtCod = Event->EvtCod;
|
|
|
|
|
Lay_PutContextualLinkOnlyIcon (ActSeeMyExaEvtResEvt,ExaRes_RESULTS_BOX_ID,
|
|
|
|
|
ExaEvt_PutParamsEdit,Exams,
|
|
|
|
|
"trophy.svg",
|
|
|
|
|
Txt_Results);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* Result is forbidden to me */
|
|
|
|
|
Ico_PutIconNotVisible ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_ListOneOrMoreEventsResultTch (struct Exa_Exams *Exams,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Visible_results;
|
|
|
|
|
extern const char *Txt_Hidden_results;
|
|
|
|
|
extern const char *Txt_Results;
|
|
|
|
|
|
|
|
|
|
/***** Can I edit exam event vivibility? *****/
|
|
|
|
|
if (ExaEvt_CheckIfICanEditThisEvent (Event))
|
|
|
|
|
{
|
|
|
|
|
Exams->ExaCod = Event->ExaCod;
|
|
|
|
|
Exams->EvtCod = Event->EvtCod;
|
|
|
|
|
|
|
|
|
|
/* Show exam event results */
|
|
|
|
|
Lay_PutContextualLinkOnlyIcon (ActSeeAllExaEvtResEvt,ExaRes_RESULTS_BOX_ID,
|
|
|
|
|
ExaEvt_PutParamsEdit,Exams,
|
|
|
|
|
"trophy.svg",
|
|
|
|
|
Txt_Results);
|
|
|
|
|
|
|
|
|
|
/* I can edit visibility */
|
|
|
|
|
Lay_PutContextualLinkOnlyIcon (ActChgVisResExaEvtUsr,NULL,
|
|
|
|
|
ExaEvt_PutParamsEdit,Exams,
|
|
|
|
|
Event->Status.ShowUsrResults ? "eye-green.svg" :
|
|
|
|
|
"eye-slash-red.svg",
|
|
|
|
|
Event->Status.ShowUsrResults ? Txt_Visible_results :
|
|
|
|
|
Txt_Hidden_results);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* I can not edit visibility */
|
|
|
|
|
Ico_PutIconOff (Event->Status.ShowUsrResults ? "eye-green.svg" :
|
|
|
|
|
"eye-slash-red.svg",
|
|
|
|
|
Event->Status.ShowUsrResults ? Txt_Visible_results :
|
|
|
|
|
Txt_Hidden_results);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************** Toggle visibility of exam event results ******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_ToggleVisibilResultsEvtUsr (void)
|
|
|
|
|
{
|
|
|
|
|
struct Exa_Exams Exams;
|
|
|
|
|
struct Exa_Exam Exam;
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset exams context *****/
|
2020-04-22 03:48:38 +02:00
|
|
|
|
Exa_ResetExams (&Exams);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset exam and event *****/
|
|
|
|
|
Exa_ResetExam (&Exam);
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/***** Get and check parameters *****/
|
|
|
|
|
ExaEvt_GetAndCheckParameters (&Exams,&Exam,&Event);
|
|
|
|
|
|
|
|
|
|
/***** Check if I have permission to change visibility *****/
|
|
|
|
|
if (!ExaEvt_CheckIfICanEditThisEvent (&Event))
|
|
|
|
|
Lay_NoPermissionExit ();
|
|
|
|
|
|
|
|
|
|
/***** Toggle visibility of exam event results *****/
|
|
|
|
|
Event.Status.ShowUsrResults = !Event.Status.ShowUsrResults;
|
|
|
|
|
DB_QueryUPDATE ("can not toggle visibility of exam event results",
|
|
|
|
|
"UPDATE exa_events"
|
|
|
|
|
" SET ShowUsrResults='%c'"
|
|
|
|
|
" WHERE EvtCod=%ld",
|
|
|
|
|
Event.Status.ShowUsrResults ? 'Y' :
|
|
|
|
|
'N',
|
|
|
|
|
Event.EvtCod);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam *****/
|
2020-04-22 03:48:38 +02:00
|
|
|
|
Exa_ShowOnlyOneExam (&Exams,&Exam,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
false, // Do not list exam questions
|
|
|
|
|
false); // Do not put form to start new exam event
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Get exam data from a database row **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_GetEventDataFromRow (MYSQL_RES *mysql_res,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
Dat_StartEndTime_t StartEndTime;
|
|
|
|
|
long LongNum;
|
|
|
|
|
|
|
|
|
|
/***** Get exam event data *****/
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
/*
|
|
|
|
|
row[ 0] EvtCod
|
|
|
|
|
row[ 1] ExaCod
|
|
|
|
|
row[ 2] UsrCod
|
|
|
|
|
row[ 3] UNIX_TIMESTAMP(StartTime)
|
|
|
|
|
row[ 4] UNIX_TIMESTAMP(EndTime)
|
|
|
|
|
row[ 5] Title
|
|
|
|
|
*/
|
|
|
|
|
/***** Get exam event data *****/
|
|
|
|
|
/* Code of the exam event (row[0]) */
|
|
|
|
|
if ((Event->EvtCod = Str_ConvertStrCodToLongCod (row[0])) <= 0)
|
|
|
|
|
Lay_ShowErrorAndExit ("Wrong code of exam event.");
|
|
|
|
|
|
|
|
|
|
/* Code of the exam (row[1]) */
|
|
|
|
|
if ((Event->ExaCod = Str_ConvertStrCodToLongCod (row[1])) <= 0)
|
|
|
|
|
Lay_ShowErrorAndExit ("Wrong code of exam.");
|
|
|
|
|
|
|
|
|
|
/* Get exam event teacher (row[2]) */
|
|
|
|
|
Event->UsrCod = Str_ConvertStrCodToLongCod (row[2]);
|
|
|
|
|
|
|
|
|
|
/* Get start/end times (row[3], row[4] hold start/end UTC times) */
|
|
|
|
|
for (StartEndTime = (Dat_StartEndTime_t) 0;
|
|
|
|
|
StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1);
|
|
|
|
|
StartEndTime++)
|
|
|
|
|
Event->TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[3 + StartEndTime]);
|
|
|
|
|
|
|
|
|
|
/* Get the title of the exam (row[5]) */
|
|
|
|
|
if (row[5])
|
|
|
|
|
Str_Copy (Event->Title,row[5],
|
|
|
|
|
Exa_MAX_BYTES_TITLE);
|
|
|
|
|
else
|
|
|
|
|
Event->Title[0] = '\0';
|
|
|
|
|
|
|
|
|
|
/***** Get current exam event status *****/
|
|
|
|
|
/*
|
|
|
|
|
row[ 6] QstInd
|
|
|
|
|
row[ 7] QstCod
|
|
|
|
|
row[ 8] Showing
|
|
|
|
|
row[ 9] Countdown
|
|
|
|
|
row[10] NumCols
|
|
|
|
|
row[11] ShowQstResults
|
|
|
|
|
row[12] ShowUsrResults
|
|
|
|
|
*/
|
|
|
|
|
/* Current question index (row[6]) */
|
|
|
|
|
Event->Status.QstInd = Str_ConvertStrToUnsigned (row[6]);
|
|
|
|
|
|
|
|
|
|
/* Current question code (row[7]) */
|
|
|
|
|
Event->Status.QstCod = Str_ConvertStrCodToLongCod (row[7]);
|
|
|
|
|
|
|
|
|
|
/* Get what to show (stem, answers, results) (row(8)) */
|
|
|
|
|
Event->Status.Showing = ExaEvt_GetShowingFromStr (row[8]);
|
|
|
|
|
|
|
|
|
|
/* Get countdown (row[9]) */
|
|
|
|
|
Event->Status.Countdown = Str_ConvertStrCodToLongCod (row[9]);
|
|
|
|
|
|
|
|
|
|
/* Get number of columns (row[10]) */
|
|
|
|
|
LongNum = Str_ConvertStrCodToLongCod (row[10]);
|
|
|
|
|
Event->Status.NumCols = (LongNum <= 1 ) ? 1 :
|
|
|
|
|
((LongNum >= ExaEvt_MAX_COLS) ? ExaEvt_MAX_COLS :
|
|
|
|
|
(unsigned) LongNum);
|
|
|
|
|
|
|
|
|
|
/* Get whether to show question results or not (row(11)) */
|
|
|
|
|
Event->Status.ShowQstResults = (row[11][0] == 'Y');
|
|
|
|
|
|
|
|
|
|
/* Get whether to show user results or not (row(12)) */
|
|
|
|
|
Event->Status.ShowUsrResults = (row[12][0] == 'Y');
|
|
|
|
|
|
|
|
|
|
/***** Get whether the exam event is being played or not *****/
|
|
|
|
|
if (Event->Status.Showing == ExaEvt_END) // Event over
|
2020-04-23 02:28:28 +02:00
|
|
|
|
Event->Status.Happening = false;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
else // Event not over
|
2020-04-23 02:28:28 +02:00
|
|
|
|
Event->Status.Happening = ExaEvt_GetIfEventIsBeingPlayed (Event->EvtCod);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************** Get parameter with what is being shown *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static ExaEvt_Showing_t ExaEvt_GetShowingFromStr (const char *Str)
|
|
|
|
|
{
|
|
|
|
|
ExaEvt_Showing_t Showing;
|
|
|
|
|
|
|
|
|
|
for (Showing = (ExaEvt_Showing_t) 0;
|
|
|
|
|
Showing <= (ExaEvt_Showing_t) (ExaEvt_NUM_SHOWING - 1);
|
|
|
|
|
Showing++)
|
|
|
|
|
if (!strcmp (Str,ExaEvt_ShowingStringsDB[Showing]))
|
|
|
|
|
return Showing;
|
|
|
|
|
|
|
|
|
|
return (ExaEvt_Showing_t) ExaEvt_SHOWING_DEFAULT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********** Request the removal of an exam event (exam instance) ************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_RequestRemoveEvent (void)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Do_you_really_want_to_remove_the_event_X;
|
|
|
|
|
extern const char *Txt_Remove_event;
|
|
|
|
|
struct Exa_Exams Exams;
|
|
|
|
|
struct Exa_Exam Exam;
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset exams context *****/
|
2020-04-22 03:48:38 +02:00
|
|
|
|
Exa_ResetExams (&Exams);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset exam and event *****/
|
|
|
|
|
Exa_ResetExam (&Exam);
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/***** Get and check parameters *****/
|
|
|
|
|
ExaEvt_GetAndCheckParameters (&Exams,&Exam,&Event);
|
|
|
|
|
|
|
|
|
|
/***** Show question and button to remove question *****/
|
|
|
|
|
Exams.ExaCod = Event.ExaCod;
|
|
|
|
|
Exams.EvtCod = Event.EvtCod;
|
|
|
|
|
Ale_ShowAlertAndButton (ActRemExaEvt,NULL,NULL,
|
|
|
|
|
ExaEvt_PutParamsEdit,&Exams,
|
|
|
|
|
Btn_REMOVE_BUTTON,Txt_Remove_event,
|
|
|
|
|
Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_event_X,
|
|
|
|
|
Event.Title);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam *****/
|
2020-04-22 03:48:38 +02:00
|
|
|
|
Exa_ShowOnlyOneExam (&Exams,&Exam,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
false, // Do not list exam questions
|
|
|
|
|
false); // Do not put form to start new exam event
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************* Remove an exam event (exam instance) ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_RemoveEvent (void)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Match_X_removed;
|
|
|
|
|
struct Exa_Exams Exams;
|
|
|
|
|
struct Exa_Exam Exam;
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset exams context *****/
|
2020-04-22 03:48:38 +02:00
|
|
|
|
Exa_ResetExams (&Exams);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset exam and event *****/
|
|
|
|
|
Exa_ResetExam (&Exam);
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/***** Get and check parameters *****/
|
|
|
|
|
ExaEvt_GetAndCheckParameters (&Exams,&Exam,&Event);
|
|
|
|
|
|
|
|
|
|
/***** Check if I can remove this exam event *****/
|
|
|
|
|
if (!ExaEvt_CheckIfICanEditThisEvent (&Event))
|
|
|
|
|
Lay_NoPermissionExit ();
|
|
|
|
|
|
|
|
|
|
/***** Remove the exam event from all database tables *****/
|
|
|
|
|
ExaEvt_RemoveEventFromAllTables (Event.EvtCod);
|
|
|
|
|
|
|
|
|
|
/***** Write message *****/
|
|
|
|
|
Ale_ShowAlert (Ale_SUCCESS,Txt_Match_X_removed,
|
|
|
|
|
Event.Title);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam *****/
|
2020-04-22 03:48:38 +02:00
|
|
|
|
Exa_ShowOnlyOneExam (&Exams,&Exam,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
false, // Do not list exam questions
|
|
|
|
|
false); // Do not put form to start new exam event
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Remove exam event from all tables **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*
|
|
|
|
|
mysql> SELECT table_name FROM information_schema.tables WHERE table_name LIKE 'exa%';
|
|
|
|
|
*/
|
|
|
|
|
static void ExaEvt_RemoveEventFromAllTables (long EvtCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Remove exam event from secondary tables *****/
|
2020-04-22 22:40:07 +02:00
|
|
|
|
ExaEvt_RemoveEventFromTable (EvtCod,"exa_participants");
|
|
|
|
|
ExaEvt_RemoveEventFromTable (EvtCod,"exa_happening");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_RemoveEventFromTable (EvtCod,"exa_results");
|
|
|
|
|
ExaEvt_RemoveEventFromTable (EvtCod,"exa_answers");
|
|
|
|
|
ExaEvt_RemoveEventFromTable (EvtCod,"exa_times");
|
|
|
|
|
ExaEvt_RemoveEventFromTable (EvtCod,"exa_groups");
|
|
|
|
|
ExaEvt_RemoveEventFromTable (EvtCod,"exa_indexes");
|
|
|
|
|
|
|
|
|
|
/***** Remove exam event from main table *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove exam event",
|
|
|
|
|
"DELETE FROM exa_events WHERE EvtCod=%ld",
|
|
|
|
|
EvtCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_RemoveEventFromTable (long EvtCod,const char *TableName)
|
|
|
|
|
{
|
|
|
|
|
/***** Remove exam event from secondary table *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove exam event from table",
|
|
|
|
|
"DELETE FROM %s WHERE EvtCod=%ld",
|
|
|
|
|
TableName,
|
|
|
|
|
EvtCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************** Remove exam event in exam from all tables ****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_RemoveEventsInExamFromAllTables (long ExaCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Remove events from secondary tables *****/
|
2020-04-22 22:40:07 +02:00
|
|
|
|
ExaEvt_RemoveEventsInExamFromTable (ExaCod,"exa_participants");
|
|
|
|
|
ExaEvt_RemoveEventsInExamFromTable (ExaCod,"exa_happening");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_RemoveEventsInExamFromTable (ExaCod,"exa_results");
|
|
|
|
|
ExaEvt_RemoveEventsInExamFromTable (ExaCod,"exa_answers");
|
|
|
|
|
ExaEvt_RemoveEventsInExamFromTable (ExaCod,"exa_times");
|
|
|
|
|
ExaEvt_RemoveEventsInExamFromTable (ExaCod,"exa_groups");
|
|
|
|
|
ExaEvt_RemoveEventsInExamFromTable (ExaCod,"exa_indexes");
|
|
|
|
|
|
|
|
|
|
/***** Remove events from main table *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove events of an exam",
|
|
|
|
|
"DELETE FROM exa_events WHERE ExaCod=%ld",
|
|
|
|
|
ExaCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_RemoveEventsInExamFromTable (long ExaCod,const char *TableName)
|
|
|
|
|
{
|
|
|
|
|
/***** Remove events in exam from secondary table *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove events of an exam from table",
|
|
|
|
|
"DELETE FROM %s"
|
|
|
|
|
" USING exa_events,%s"
|
|
|
|
|
" WHERE exa_events.ExaCod=%ld"
|
|
|
|
|
" AND exa_events.EvtCod=%s.EvtCod",
|
|
|
|
|
TableName,
|
|
|
|
|
TableName,
|
|
|
|
|
ExaCod,
|
|
|
|
|
TableName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Remove exam event in course from all tables ***************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_RemoveEventInCourseFromAllTables (long CrsCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Remove events from secondary tables *****/
|
2020-04-22 22:40:07 +02:00
|
|
|
|
ExaEvt_RemoveEventInCourseFromTable (CrsCod,"exa_participants");
|
|
|
|
|
ExaEvt_RemoveEventInCourseFromTable (CrsCod,"exa_happening");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_RemoveEventInCourseFromTable (CrsCod,"exa_results");
|
|
|
|
|
ExaEvt_RemoveEventInCourseFromTable (CrsCod,"exa_answers");
|
|
|
|
|
ExaEvt_RemoveEventInCourseFromTable (CrsCod,"exa_times");
|
|
|
|
|
ExaEvt_RemoveEventInCourseFromTable (CrsCod,"exa_groups");
|
|
|
|
|
ExaEvt_RemoveEventInCourseFromTable (CrsCod,"exa_indexes");
|
|
|
|
|
|
|
|
|
|
/***** Remove events from main table *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove events of a course",
|
|
|
|
|
"DELETE FROM exa_events"
|
|
|
|
|
" USING exa_exams,exa_events"
|
|
|
|
|
" WHERE exa_exams.CrsCod=%ld"
|
|
|
|
|
" AND exa_exams.ExaCod=exa_events.ExaCod",
|
|
|
|
|
CrsCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_RemoveEventInCourseFromTable (long CrsCod,const char *TableName)
|
|
|
|
|
{
|
|
|
|
|
/***** Remove events in course from secondary table *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove events of a course from table",
|
|
|
|
|
"DELETE FROM %s"
|
|
|
|
|
" USING exa_exams,exa_events,%s"
|
|
|
|
|
" WHERE exa_exams.CrsCod=%ld"
|
|
|
|
|
" AND exa_exams.ExaCod=exa_events.ExaCod"
|
|
|
|
|
" AND exa_events.EvtCod=%s.EvtCod",
|
|
|
|
|
TableName,
|
|
|
|
|
TableName,
|
|
|
|
|
CrsCod,
|
|
|
|
|
TableName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** Remove user from secondary exam event tables *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_RemoveUsrFromEventTablesInCrs (long UsrCod,long CrsCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Remove student from secondary tables *****/
|
2020-04-22 22:40:07 +02:00
|
|
|
|
ExaEvt_RemoveUsrEvtResultsInCrs (UsrCod,CrsCod,"exa_participants");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_RemoveUsrEvtResultsInCrs (UsrCod,CrsCod,"exa_results");
|
|
|
|
|
ExaEvt_RemoveUsrEvtResultsInCrs (UsrCod,CrsCod,"exa_answers");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_RemoveUsrEvtResultsInCrs (long UsrCod,long CrsCod,const char *TableName)
|
|
|
|
|
{
|
|
|
|
|
/***** Remove events in course from secondary table *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove events of a user from table",
|
|
|
|
|
"DELETE FROM %s"
|
|
|
|
|
" USING exa_exams,exa_events,%s"
|
|
|
|
|
" WHERE exa_exams.CrsCod=%ld"
|
|
|
|
|
" AND exa_exams.ExaCod=exa_events.ExaCod"
|
|
|
|
|
" AND exa_events.EvtCod=%s.EvtCod"
|
|
|
|
|
" AND %s.UsrCod=%ld",
|
|
|
|
|
TableName,
|
|
|
|
|
TableName,
|
|
|
|
|
CrsCod,
|
|
|
|
|
TableName,
|
|
|
|
|
TableName,
|
|
|
|
|
UsrCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Params used to edit an exam event **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_PutParamsEdit (void *Exams)
|
|
|
|
|
{
|
|
|
|
|
if (Exams)
|
|
|
|
|
{
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Exa_PutParams (Exams);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_PutParamEvtCod (((struct Exa_Exams *) Exams)->EvtCod);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Params used to edit an exam event *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutParamsPlay (void *EvtCod)
|
|
|
|
|
{
|
|
|
|
|
if (EvtCod)
|
|
|
|
|
{
|
|
|
|
|
if (*((long *) EvtCod) > 0)
|
|
|
|
|
ExaEvt_PutParamEvtCod (*((long *) EvtCod));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Write parameter with code of exam event *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutParamEvtCod (long EvtCod)
|
|
|
|
|
{
|
|
|
|
|
Par_PutHiddenParamLong (NULL,"EvtCod",EvtCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************************** Get and check parameters *************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_GetAndCheckParameters (struct Exa_Exams *Exams,
|
|
|
|
|
struct Exa_Exam *Exam,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Get parameters *****/
|
2020-04-25 01:36:53 +02:00
|
|
|
|
Exa_GetParams (Exams);
|
|
|
|
|
if (Exams->ExaCod <= 0)
|
|
|
|
|
Lay_WrongExamExit ();
|
|
|
|
|
Exam->ExaCod = Exams->ExaCod;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Grp_GetParamWhichGroups ();
|
|
|
|
|
if ((Event->EvtCod = ExaEvt_GetParamEvtCod ()) <= 0)
|
2020-04-25 01:36:53 +02:00
|
|
|
|
Lay_WrongEventExit ();
|
|
|
|
|
|
2020-04-26 01:54:27 +02:00
|
|
|
|
/***** Get exam data and event from database *****/
|
2020-04-25 01:36:53 +02:00
|
|
|
|
Exa_GetDataOfExamByCod (Exam);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_GetDataOfEventByCod (Event);
|
|
|
|
|
|
|
|
|
|
/***** Ensure parameters are correct *****/
|
2020-04-25 01:36:53 +02:00
|
|
|
|
if (Exam->ExaCod != Event->ExaCod ||
|
|
|
|
|
Exam->CrsCod != Gbl.Hierarchy.Crs.CrsCod)
|
|
|
|
|
Lay_WrongExamExit ();
|
2020-04-20 01:26:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Get parameter with code of exam event *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
long ExaEvt_GetParamEvtCod (void)
|
|
|
|
|
{
|
|
|
|
|
/***** Get code of exam event *****/
|
|
|
|
|
return Par_GetParToLong ("EvtCod");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/* Put a big button to play exam event (start a new exam event) as a teacher */
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:48:38 +02:00
|
|
|
|
static void ExaEvt_PutFormNewEvent (const struct Exa_Exam *Exam)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
2020-04-22 22:40:07 +02:00
|
|
|
|
extern const char *Hlp_ASSESSMENT_Exams_events;
|
|
|
|
|
extern const char *Txt_New_event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
extern const char *Txt_Title;
|
|
|
|
|
extern const char *Txt_Play;
|
|
|
|
|
|
|
|
|
|
/***** Start section for a new exam event *****/
|
2020-04-22 03:15:04 +02:00
|
|
|
|
HTM_SECTION_Begin (ExaEvt_NEW_EVENT_SECTION_ID);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Begin form *****/
|
|
|
|
|
Frm_StartForm (ActNewExaEvt);
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Exa_PutParamExamCod (Exam->ExaCod);
|
|
|
|
|
Exa_PutParamQstInd (0); // Start by first question in exam
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Begin box and table *****/
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Box_BoxTableBegin (NULL,Txt_New_event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
NULL,NULL,
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Hlp_ASSESSMENT_Exams_events,Box_NOT_CLOSABLE,2);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Event title *****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
/* Label */
|
|
|
|
|
Frm_LabelColumn ("RT","Title",Txt_Title);
|
|
|
|
|
|
|
|
|
|
/* Data */
|
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
2020-04-27 03:16:55 +02:00
|
|
|
|
HTM_INPUT_TEXT ("Title",Exa_MAX_CHARS_TITLE,Exam->Title,
|
|
|
|
|
HTM_DONT_SUBMIT_ON_CHANGE,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
"id=\"Title\" size=\"45\" required=\"required\"");
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
|
|
|
|
|
/***** Groups *****/
|
|
|
|
|
ExaEvt_ShowLstGrpsToCreateEvent ();
|
|
|
|
|
|
|
|
|
|
/***** End table *****/
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
|
|
|
|
|
/***** Put icon to submit the form *****/
|
|
|
|
|
HTM_INPUT_IMAGE (Cfg_URL_ICON_PUBLIC,"play.svg",
|
|
|
|
|
Txt_Play,"CONTEXT_OPT ICO_HIGHLIGHT ICO64x64");
|
|
|
|
|
|
|
|
|
|
/***** End box *****/
|
|
|
|
|
Box_BoxEnd ();
|
|
|
|
|
|
|
|
|
|
/***** End form *****/
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
|
|
|
|
|
/***** End section for a new exam event *****/
|
|
|
|
|
HTM_SECTION_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** Show list of groups to create a new exam event ***************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_ShowLstGrpsToCreateEvent (void)
|
|
|
|
|
{
|
|
|
|
|
extern const char *The_ClassFormInBox[The_NUM_THEMES];
|
|
|
|
|
extern const char *Txt_Groups;
|
|
|
|
|
extern const char *Txt_The_whole_course;
|
|
|
|
|
unsigned NumGrpTyp;
|
|
|
|
|
|
|
|
|
|
/***** Get list of groups types and groups in this course *****/
|
|
|
|
|
Grp_GetListGrpTypesAndGrpsInThisCrs (Grp_ONLY_GROUP_TYPES_WITH_GROUPS);
|
|
|
|
|
|
|
|
|
|
if (Gbl.Crs.Grps.GrpTypes.Num)
|
|
|
|
|
{
|
|
|
|
|
/***** Begin box and table *****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"%s RT\"",The_ClassFormInBox[Gbl.Prefs.Theme]);
|
|
|
|
|
HTM_TxtF ("%s:",Txt_Groups);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
|
|
|
|
Box_BoxTableBegin ("95%",NULL,
|
|
|
|
|
NULL,NULL,
|
|
|
|
|
NULL,Box_NOT_CLOSABLE,0);
|
|
|
|
|
|
|
|
|
|
/***** First row: checkbox to select the whole course *****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
HTM_TD_Begin ("colspan=\"7\" class=\"DAT LM\"");
|
|
|
|
|
HTM_LABEL_Begin (NULL);
|
|
|
|
|
HTM_INPUT_CHECKBOX ("WholeCrs",HTM_SUBMIT_ON_CHANGE,
|
|
|
|
|
"id=\"WholeCrs\" value=\"Y\" checked=\"checked\""
|
|
|
|
|
" onclick=\"uncheckChildren(this,'GrpCods')\"");
|
|
|
|
|
HTM_TxtF ("%s %s",Txt_The_whole_course,Gbl.Hierarchy.Crs.ShrtName);
|
|
|
|
|
HTM_LABEL_End ();
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
|
|
|
|
|
/***** List the groups for each group type *****/
|
|
|
|
|
for (NumGrpTyp = 0;
|
|
|
|
|
NumGrpTyp < Gbl.Crs.Grps.GrpTypes.Num;
|
|
|
|
|
NumGrpTyp++)
|
|
|
|
|
if (Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp].NumGrps)
|
|
|
|
|
Grp_ListGrpsToEditAsgAttSvyMch (&Gbl.Crs.Grps.GrpTypes.LstGrpTypes[NumGrpTyp],
|
|
|
|
|
-1L, // -1 means "New exam event"
|
|
|
|
|
Grp_MATCH);
|
|
|
|
|
|
|
|
|
|
/***** End table and box *****/
|
|
|
|
|
Box_BoxTableEnd ();
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Free list of groups types and groups in this course *****/
|
|
|
|
|
Grp_FreeListGrpTypesAndGrps ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************* Create a new exam event (by a teacher) ******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_CreateNewEventTch (void)
|
|
|
|
|
{
|
|
|
|
|
long ExaCod;
|
|
|
|
|
char Title[Exa_MAX_BYTES_TITLE + 1];
|
|
|
|
|
|
|
|
|
|
/***** Get form parameters *****/
|
2020-04-22 22:40:07 +02:00
|
|
|
|
/* Get exam code */
|
|
|
|
|
if ((ExaCod = Exa_GetParamExamCod ()) == -1L)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Lay_ShowErrorAndExit ("Code of exam is missing.");
|
|
|
|
|
|
|
|
|
|
/* Get exam event title */
|
|
|
|
|
Par_GetParToText ("Title",Title,Exa_MAX_BYTES_TITLE);
|
|
|
|
|
|
2020-04-22 03:48:38 +02:00
|
|
|
|
/* Get groups for this exams */
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Grp_GetParCodsSeveralGrps ();
|
|
|
|
|
|
|
|
|
|
/***** Create a new exam event *****/
|
|
|
|
|
ExaEvt_SetEvtCodBeingPlayed (ExaEvt_CreateEvent (ExaCod,Title));
|
|
|
|
|
|
|
|
|
|
/***** Free memory for list of selected groups *****/
|
|
|
|
|
Grp_FreeListCodSelectedGrps ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**** Show button to actually start / resume an exam event (by a teacher) ****/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_ResumeEvent (void)
|
|
|
|
|
{
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Remove old participants.
|
2020-04-20 01:26:46 +02:00
|
|
|
|
This function must be called by a teacher
|
|
|
|
|
before getting exam event status. *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
ExaEvt_RemoveOldParticipants ();
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Get data of the exam event from database *****/
|
|
|
|
|
Event.EvtCod = ExaEvt_GetEvtCodBeingPlayed ();
|
|
|
|
|
ExaEvt_GetDataOfEventByCod (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Check if I have permission to resume exam event *****/
|
|
|
|
|
if (!ExaEvt_CheckIfICanEditThisEvent (&Event))
|
|
|
|
|
Lay_NoPermissionExit ();
|
|
|
|
|
|
|
|
|
|
/***** Update exam event status in database *****/
|
|
|
|
|
ExaEvt_UpdateEventStatusInDB (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam event status *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_DIV_Begin ("id=\"exam_event\" class=\"EXA_CONT\"");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ShowMatchStatusForTch (&Event);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************* Create a new exam event in an exam **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static long ExaEvt_CreateEvent (long ExaCod,char Title[Exa_MAX_BYTES_TITLE + 1])
|
|
|
|
|
{
|
|
|
|
|
long EvtCod;
|
|
|
|
|
|
|
|
|
|
/***** Insert this new exam event into database *****/
|
|
|
|
|
EvtCod = DB_QueryINSERTandReturnCode ("can not create exam event",
|
|
|
|
|
"INSERT exa_events "
|
|
|
|
|
"(ExaCod,UsrCod,StartTime,EndTime,Title,"
|
|
|
|
|
"QstInd,QstCod,Showing,Countdown,"
|
|
|
|
|
"NumCols,ShowQstResults,ShowUsrResults)"
|
|
|
|
|
" VALUES "
|
|
|
|
|
"(%ld," // ExaCod
|
|
|
|
|
"%ld," // UsrCod
|
|
|
|
|
"NOW()," // StartTime
|
|
|
|
|
"NOW()," // EndTime
|
|
|
|
|
"'%s'," // Title
|
|
|
|
|
"0," // QstInd: Event has not started, so not the first question yet
|
|
|
|
|
"-1," // QstCod: Non-existent question
|
|
|
|
|
"'%s'," // Showing: What is being shown
|
|
|
|
|
"-1," // Countdown: No countdown
|
|
|
|
|
"%u," // NumCols: Number of columns in answers
|
|
|
|
|
"'N'," // ShowQstResults: Don't show question results initially
|
|
|
|
|
"'N')", // ShowUsrResults: Don't show user results initially
|
|
|
|
|
ExaCod,
|
|
|
|
|
Gbl.Usrs.Me.UsrDat.UsrCod, // Exam creator
|
|
|
|
|
Title,
|
|
|
|
|
ExaEvt_ShowingStringsDB[ExaEvt_SHOWING_DEFAULT],
|
|
|
|
|
ExaEvt_NUM_COLS_DEFAULT);
|
|
|
|
|
|
|
|
|
|
/***** Create indexes for answers *****/
|
|
|
|
|
ExaEvt_CreateIndexes (ExaCod,EvtCod);
|
|
|
|
|
|
|
|
|
|
/***** Create groups associated to the exam event *****/
|
|
|
|
|
if (Gbl.Crs.Grps.LstGrpsSel.NumGrps)
|
|
|
|
|
ExaEvt_CreateGrps (EvtCod);
|
|
|
|
|
|
|
|
|
|
return EvtCod;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Create indexes for an exam event ***********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/* Everytime a new exam event is created,
|
|
|
|
|
the answers of each shufflable question are shuffled.
|
|
|
|
|
The shuffling is stored in a table of indexes
|
|
|
|
|
that will be read when showing an exam event */
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_CreateIndexes (long ExaCod,long EvtCod)
|
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
unsigned NumQsts;
|
|
|
|
|
unsigned NumQst;
|
|
|
|
|
struct Tst_Question Question;
|
|
|
|
|
long LongNum;
|
|
|
|
|
unsigned QstInd;
|
|
|
|
|
|
|
|
|
|
/***** Get questions of the exam *****/
|
|
|
|
|
NumQsts = (unsigned)
|
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get questions of an exam",
|
|
|
|
|
"SELECT exa_questions.QstCod," // row[0]
|
|
|
|
|
"exa_questions.QstInd," // row[1]
|
|
|
|
|
"tst_questions.AnsType," // row[2]
|
|
|
|
|
"tst_questions.Shuffle" // row[3]
|
|
|
|
|
" FROM exa_questions,tst_questions"
|
|
|
|
|
" WHERE exa_questions.ExaCod=%ld"
|
|
|
|
|
" AND exa_questions.QstCod=tst_questions.QstCod"
|
|
|
|
|
" ORDER BY exa_questions.QstInd",
|
|
|
|
|
ExaCod);
|
|
|
|
|
|
|
|
|
|
/***** For each question in exam... *****/
|
|
|
|
|
for (NumQst = 0;
|
|
|
|
|
NumQst < NumQsts;
|
|
|
|
|
NumQst++)
|
|
|
|
|
{
|
|
|
|
|
/***** Create test question *****/
|
|
|
|
|
Tst_QstConstructor (&Question);
|
|
|
|
|
|
|
|
|
|
/***** Get question data *****/
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
/*
|
|
|
|
|
exa_questions.QstCod row[0]
|
|
|
|
|
exa_questions.QstInd row[1]
|
|
|
|
|
tst_questions.AnsType row[2]
|
|
|
|
|
tst_questions.Shuffle row[3]
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Get question code (row[0]) */
|
|
|
|
|
if ((Question.QstCod = Str_ConvertStrCodToLongCod (row[0])) < 0)
|
|
|
|
|
Lay_ShowErrorAndExit ("Wrong code of question.");
|
|
|
|
|
|
|
|
|
|
/* Get question index (row[1]) */
|
|
|
|
|
if ((LongNum = Str_ConvertStrCodToLongCod (row[1])) < 0)
|
|
|
|
|
Lay_ShowErrorAndExit ("Wrong question index.");
|
|
|
|
|
QstInd = (unsigned) LongNum;
|
|
|
|
|
|
|
|
|
|
/* Get answer type (row[2]) */
|
|
|
|
|
Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[2]);
|
|
|
|
|
if (Question.Answer.Type != Tst_ANS_UNIQUE_CHOICE)
|
|
|
|
|
Lay_ShowErrorAndExit ("Wrong answer type.");
|
|
|
|
|
|
|
|
|
|
/* Get shuffle (row[3]) */
|
|
|
|
|
Question.Answer.Shuffle = (row[3][0] == 'Y');
|
|
|
|
|
|
|
|
|
|
/***** Reorder answer *****/
|
|
|
|
|
ExaEvt_ReorderAnswer (EvtCod,QstInd,&Question);
|
|
|
|
|
|
|
|
|
|
/***** Destroy test question *****/
|
|
|
|
|
Tst_QstDestructor (&Question);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************** Reorder answers of an exam event question ******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_ReorderAnswer (long EvtCod,unsigned QstInd,
|
|
|
|
|
const struct Tst_Question *Question)
|
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
unsigned NumAnss;
|
|
|
|
|
unsigned NumAns;
|
|
|
|
|
long LongNum;
|
|
|
|
|
unsigned AnsInd;
|
|
|
|
|
char StrOneAnswer[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
|
2020-04-22 03:15:04 +02:00
|
|
|
|
char StrAnswersOneQst[TstRes_MAX_BYTES_ANSWERS_ONE_QST + 1];
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Initialize list of answers to empty string *****/
|
|
|
|
|
StrAnswersOneQst[0] = '\0';
|
|
|
|
|
|
|
|
|
|
/***** Get questions of the exam *****/
|
|
|
|
|
NumAnss = (unsigned)
|
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get questions of an exam",
|
|
|
|
|
"SELECT AnsInd" // row[0]
|
|
|
|
|
" FROM tst_answers"
|
|
|
|
|
" WHERE QstCod=%ld"
|
|
|
|
|
" ORDER BY %s",
|
|
|
|
|
Question->QstCod,
|
|
|
|
|
Question->Answer.Shuffle ? "RAND()" : // Use RAND() because is really random; RAND(NOW()) repeats order
|
|
|
|
|
"AnsInd");
|
|
|
|
|
|
|
|
|
|
/***** For each answer in question... *****/
|
|
|
|
|
for (NumAns = 0;
|
|
|
|
|
NumAns < NumAnss;
|
|
|
|
|
NumAns++)
|
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
|
|
|
|
|
/* Get answer index (row[0]) */
|
|
|
|
|
if ((LongNum = Str_ConvertStrCodToLongCod (row[0])) < 0)
|
|
|
|
|
Lay_ShowErrorAndExit ("Wrong answer index.");
|
|
|
|
|
AnsInd = (unsigned) LongNum;
|
|
|
|
|
snprintf (StrOneAnswer,sizeof (StrOneAnswer),
|
|
|
|
|
"%u",AnsInd);
|
|
|
|
|
|
|
|
|
|
/* Concatenate answer index to list of answers */
|
|
|
|
|
if (NumAns)
|
|
|
|
|
Str_Concat (StrAnswersOneQst,",",
|
2020-04-22 03:15:04 +02:00
|
|
|
|
TstRes_MAX_BYTES_ANSWERS_ONE_QST);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Str_Concat (StrAnswersOneQst,StrOneAnswer,
|
2020-04-22 03:15:04 +02:00
|
|
|
|
TstRes_MAX_BYTES_ANSWERS_ONE_QST);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
|
|
|
|
|
/***** Create entry for this question in table of exam event indexes *****/
|
|
|
|
|
DB_QueryINSERT ("can not create exam event indexes",
|
|
|
|
|
"INSERT INTO exa_indexes"
|
|
|
|
|
" (EvtCod,QstInd,Indexes)"
|
|
|
|
|
" VALUES"
|
|
|
|
|
" (%ld,%u,'%s')",
|
|
|
|
|
EvtCod,QstInd,StrAnswersOneQst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Get indexes for a question from database ******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_GetIndexes (long EvtCod,unsigned QstInd,
|
|
|
|
|
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION])
|
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
2020-04-22 03:15:04 +02:00
|
|
|
|
char StrIndexesOneQst[TstRes_MAX_BYTES_INDEXES_ONE_QST + 1];
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Get indexes for a question from database *****/
|
|
|
|
|
if (!DB_QuerySELECT (&mysql_res,"can not get data of a question",
|
|
|
|
|
"SELECT Indexes" // row[0]
|
|
|
|
|
" FROM exa_indexes"
|
|
|
|
|
" WHERE EvtCod=%ld AND QstInd=%u",
|
|
|
|
|
EvtCod,QstInd))
|
|
|
|
|
Lay_ShowErrorAndExit ("No indexes found for a question.");
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
|
|
|
|
|
/* Get indexes (row[0]) */
|
|
|
|
|
Str_Copy (StrIndexesOneQst,row[0],
|
2020-04-22 03:15:04 +02:00
|
|
|
|
TstRes_MAX_BYTES_INDEXES_ONE_QST);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
|
|
|
|
|
/***** Get indexes from string *****/
|
|
|
|
|
Par_ReplaceCommaBySeparatorMultiple (StrIndexesOneQst);
|
2020-04-22 03:15:04 +02:00
|
|
|
|
TstRes_GetIndexesFromStr (StrIndexesOneQst,Indexes);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************** Create groups associated to an exam event ******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_CreateGrps (long EvtCod)
|
|
|
|
|
{
|
|
|
|
|
unsigned NumGrpSel;
|
|
|
|
|
|
|
|
|
|
/***** Create groups associated to the exam event *****/
|
|
|
|
|
for (NumGrpSel = 0;
|
|
|
|
|
NumGrpSel < Gbl.Crs.Grps.LstGrpsSel.NumGrps;
|
|
|
|
|
NumGrpSel++)
|
|
|
|
|
/* Create group */
|
|
|
|
|
DB_QueryINSERT ("can not associate a group to an exam event",
|
|
|
|
|
"INSERT INTO exa_groups"
|
|
|
|
|
" (EvtCod,GrpCod)"
|
|
|
|
|
" VALUES"
|
|
|
|
|
" (%ld,%ld)",
|
|
|
|
|
EvtCod,Gbl.Crs.Grps.LstGrpsSel.GrpCods[NumGrpSel]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Remove one group from all events **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_RemoveGroup (long GrpCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Remove group from all the events *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove group"
|
|
|
|
|
" from the associations between events and groups",
|
|
|
|
|
"DELETE FROM exa_groups WHERE GrpCod=%ld",
|
|
|
|
|
GrpCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Remove groups of one type from all events *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_RemoveGroupsOfType (long GrpTypCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Remove group from all the events *****/
|
|
|
|
|
DB_QueryDELETE ("can not remove groups of a type"
|
|
|
|
|
" from the associations between events and groups",
|
|
|
|
|
"DELETE FROM exa_groups"
|
|
|
|
|
" USING crs_grp,exa_groups"
|
|
|
|
|
" WHERE crs_grp.GrpTypCod=%ld"
|
|
|
|
|
" AND crs_grp.GrpCod=exa_groups.GrpCod",
|
|
|
|
|
GrpTypCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** Insert/update an exam exam event being played ****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_UpdateEventStatusInDB (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
char *EvtSubQuery;
|
|
|
|
|
|
|
|
|
|
/***** Update end time only if exam event is currently being played *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (Event->Status.Happening) // Event is being played
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
if (asprintf (&EvtSubQuery,"exa_events.EndTime=NOW(),") < 0)
|
|
|
|
|
Lay_NotEnoughMemoryExit ();
|
|
|
|
|
}
|
|
|
|
|
else // Event is paused, not being played
|
|
|
|
|
{
|
|
|
|
|
if (asprintf (&EvtSubQuery,"%s","") < 0)
|
|
|
|
|
Lay_NotEnoughMemoryExit ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Update exam event status in database *****/
|
|
|
|
|
DB_QueryUPDATE ("can not update exam event being played",
|
|
|
|
|
"UPDATE exa_events,exa_exams"
|
|
|
|
|
" SET %s"
|
|
|
|
|
"exa_events.QstInd=%u,"
|
|
|
|
|
"exa_events.QstCod=%ld,"
|
|
|
|
|
"exa_events.Showing='%s',"
|
|
|
|
|
"exa_events.Countdown=%ld,"
|
|
|
|
|
"exa_events.NumCols=%u,"
|
|
|
|
|
"exa_events.ShowQstResults='%c'"
|
|
|
|
|
" WHERE exa_events.EvtCod=%ld"
|
|
|
|
|
" AND exa_events.ExaCod=exa_exams.ExaCod"
|
|
|
|
|
" AND exa_exams.CrsCod=%ld", // Extra check
|
|
|
|
|
EvtSubQuery,
|
|
|
|
|
Event->Status.QstInd,Event->Status.QstCod,
|
|
|
|
|
ExaEvt_ShowingStringsDB[Event->Status.Showing],
|
|
|
|
|
Event->Status.Countdown,
|
|
|
|
|
Event->Status.NumCols,
|
|
|
|
|
Event->Status.ShowQstResults ? 'Y' : 'N',
|
|
|
|
|
Event->EvtCod,Gbl.Hierarchy.Crs.CrsCod);
|
|
|
|
|
free (EvtSubQuery);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (Event->Status.Happening) // Event is being played
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/* Update exam event as being played */
|
|
|
|
|
ExaEvt_UpdateEventAsBeingPlayed (Event->EvtCod);
|
|
|
|
|
else // Event is paused, not being played
|
|
|
|
|
/* Update exam event as not being played */
|
|
|
|
|
ExaEvt_SetEventAsNotBeingPlayed (Event->EvtCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********** Update elapsed time in current question (by a teacher) ***********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_UpdateElapsedTimeInQuestion (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Update elapsed time in current question in database *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (Event->Status.Happening && // Event is being played
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event->Status.Showing != ExaEvt_START &&
|
|
|
|
|
Event->Status.Showing != ExaEvt_END)
|
|
|
|
|
DB_QueryINSERT ("can not update elapsed time in question",
|
|
|
|
|
"INSERT INTO exa_times (EvtCod,QstInd,ElapsedTime)"
|
|
|
|
|
" VALUES (%ld,%u,SEC_TO_TIME(%u))"
|
|
|
|
|
" ON DUPLICATE KEY"
|
|
|
|
|
" UPDATE ElapsedTime=ADDTIME(ElapsedTime,SEC_TO_TIME(%u))",
|
|
|
|
|
Event->EvtCod,Event->Status.QstInd,
|
|
|
|
|
Cfg_SECONDS_TO_REFRESH_MATCH_TCH,
|
|
|
|
|
Cfg_SECONDS_TO_REFRESH_MATCH_TCH);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************** Get elapsed time in an exam event question *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_GetElapsedTimeInQuestion (const struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
struct Time *Time)
|
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
unsigned NumRows;
|
|
|
|
|
|
|
|
|
|
/***** Query database *****/
|
|
|
|
|
NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get elapsed time",
|
|
|
|
|
"SELECT ElapsedTime"
|
|
|
|
|
" FROM exa_times"
|
|
|
|
|
" WHERE EvtCod=%ld AND QstInd=%u",
|
|
|
|
|
Event->EvtCod,Event->Status.QstInd);
|
|
|
|
|
|
|
|
|
|
/***** Get elapsed time from query result *****/
|
|
|
|
|
ExaEvt_GetElapsedTime (NumRows,mysql_res,Time);
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Get elapsed time in an exam event **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_GetElapsedTimeInMatch (const struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
struct Time *Time)
|
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
unsigned NumRows;
|
|
|
|
|
|
|
|
|
|
/***** Query database *****/
|
|
|
|
|
NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get elapsed time",
|
|
|
|
|
"SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(ElapsedTime)))"
|
|
|
|
|
" FROM exa_times WHERE EvtCod=%ld",
|
|
|
|
|
Event->EvtCod);
|
|
|
|
|
|
|
|
|
|
/***** Get elapsed time from query result *****/
|
|
|
|
|
ExaEvt_GetElapsedTime (NumRows,mysql_res,Time);
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Get elapsed time in an exam event **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_GetElapsedTime (unsigned NumRows,MYSQL_RES *mysql_res,
|
|
|
|
|
struct Time *Time)
|
|
|
|
|
{
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
bool ElapsedTimeGotFromDB = false;
|
|
|
|
|
|
|
|
|
|
/***** Get time from H...H:MM:SS string *****/
|
|
|
|
|
if (NumRows)
|
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
|
|
|
|
|
if (row[0])
|
|
|
|
|
/* Get the elapsed time (row[0]) */
|
|
|
|
|
if (sscanf (row[0],"%u:%02u:%02u",&Time->Hour,&Time->Minute,&Time->Second) == 3)
|
|
|
|
|
ElapsedTimeGotFromDB = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Initialize time to default value (0) *****/
|
|
|
|
|
if (!ElapsedTimeGotFromDB)
|
|
|
|
|
Time->Hour =
|
|
|
|
|
Time->Minute =
|
|
|
|
|
Time->Second = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************* Play/pause exam event (by a teacher) ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_PlayPauseEvent (void)
|
|
|
|
|
{
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Remove old participants.
|
2020-04-20 01:26:46 +02:00
|
|
|
|
This function must be called by a teacher
|
|
|
|
|
before getting exam event status. *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
ExaEvt_RemoveOldParticipants ();
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Get data of the exam event from database *****/
|
|
|
|
|
Event.EvtCod = ExaEvt_GetEvtCodBeingPlayed ();
|
|
|
|
|
ExaEvt_GetDataOfEventByCod (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Update status *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (Event.Status.Happening) // Event is being played ==> pause it
|
|
|
|
|
Event.Status.Happening = false; // Pause exam event
|
2020-04-20 01:26:46 +02:00
|
|
|
|
else // Event is paused, not being played ==> play it
|
|
|
|
|
/* If not over, update status */
|
|
|
|
|
if (Event.Status.Showing != ExaEvt_END) // Event not over
|
2020-04-23 02:28:28 +02:00
|
|
|
|
Event.Status.Happening = true; // Start/resume exam event
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Update exam event status in database *****/
|
|
|
|
|
ExaEvt_UpdateEventStatusInDB (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam event status *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_DIV_Begin ("id=\"exam_event\" class=\"EXA_CONT\"");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ShowMatchStatusForTch (&Event);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**** Change number of columns in answers of an exam event (by a teacher) ****/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_ChangeNumColsEvt (void)
|
|
|
|
|
{
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Remove old participants.
|
2020-04-20 01:26:46 +02:00
|
|
|
|
This function must be called by a teacher
|
|
|
|
|
before getting exam event status. *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
ExaEvt_RemoveOldParticipants ();
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Get data of the exam event from database *****/
|
|
|
|
|
Event.EvtCod = ExaEvt_GetEvtCodBeingPlayed ();
|
|
|
|
|
ExaEvt_GetDataOfEventByCod (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Get number of columns *****/
|
|
|
|
|
Event.Status.NumCols = (unsigned)
|
|
|
|
|
Par_GetParToUnsignedLong ("NumCols",
|
|
|
|
|
1,
|
|
|
|
|
ExaEvt_MAX_COLS,
|
|
|
|
|
ExaEvt_NUM_COLS_DEFAULT);
|
|
|
|
|
|
|
|
|
|
/***** Update exam event status in database *****/
|
|
|
|
|
ExaEvt_UpdateEventStatusInDB (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam event status *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_DIV_Begin ("id=\"exam_event\" class=\"EXA_CONT\"");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ShowMatchStatusForTch (&Event);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****** Toggle the display of results in an exam event (by a teacher) ********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_ToggleVisibilResultsEvtQst (void)
|
|
|
|
|
{
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Remove old participants.
|
2020-04-20 01:26:46 +02:00
|
|
|
|
This function must be called by a teacher
|
|
|
|
|
before getting exam event status. *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
ExaEvt_RemoveOldParticipants ();
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Get data of the exam event from database *****/
|
|
|
|
|
Event.EvtCod = ExaEvt_GetEvtCodBeingPlayed ();
|
|
|
|
|
ExaEvt_GetDataOfEventByCod (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Update status *****/
|
|
|
|
|
Event.Status.ShowQstResults = !Event.Status.ShowQstResults; // Toggle display
|
|
|
|
|
if (Event.Status.Showing == ExaEvt_RESULTS &&
|
|
|
|
|
!Event.Status.ShowQstResults)
|
|
|
|
|
Event.Status.Showing = ExaEvt_ANSWERS; // Hide results
|
|
|
|
|
|
|
|
|
|
/***** Update exam event status in database *****/
|
|
|
|
|
ExaEvt_UpdateEventStatusInDB (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam event status *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_DIV_Begin ("id=\"exam_event\" class=\"EXA_CONT\"");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ShowMatchStatusForTch (&Event);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********** Show previous question in an exam event (by a teacher) ***********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_BackEvent (void)
|
|
|
|
|
{
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Remove old participants.
|
2020-04-20 01:26:46 +02:00
|
|
|
|
This function must be called by a teacher
|
|
|
|
|
before getting exam event status. *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
ExaEvt_RemoveOldParticipants ();
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Get data of the exam event from database *****/
|
|
|
|
|
Event.EvtCod = ExaEvt_GetEvtCodBeingPlayed ();
|
|
|
|
|
ExaEvt_GetDataOfEventByCod (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Update status *****/
|
|
|
|
|
ExaEvt_SetMatchStatusToPrev (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Update exam event status in database *****/
|
|
|
|
|
ExaEvt_UpdateEventStatusInDB (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam event status *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_DIV_Begin ("id=\"exam_event\" class=\"EXA_CONT\"");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ShowMatchStatusForTch (&Event);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************ Show next question in an exam event (by a teacher) *************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_ForwardEvent (void)
|
|
|
|
|
{
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Remove old participants.
|
2020-04-20 01:26:46 +02:00
|
|
|
|
This function must be called by a teacher
|
|
|
|
|
before getting exam event status. *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
ExaEvt_RemoveOldParticipants ();
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Get data of the exam event from database *****/
|
|
|
|
|
Event.EvtCod = ExaEvt_GetEvtCodBeingPlayed ();
|
|
|
|
|
ExaEvt_GetDataOfEventByCod (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Update status *****/
|
|
|
|
|
ExaEvt_SetMatchStatusToNext (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Update exam event status in database *****/
|
|
|
|
|
ExaEvt_UpdateEventStatusInDB (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam event status *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_DIV_Begin ("id=\"exam_event\" class=\"EXA_CONT\"");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ShowMatchStatusForTch (&Event);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************ Set exam event status to previous (backward) status ************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_SetMatchStatusToPrev (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** What to show *****/
|
|
|
|
|
switch (Event->Status.Showing)
|
|
|
|
|
{
|
|
|
|
|
case ExaEvt_START:
|
|
|
|
|
ExaEvt_SetMatchStatusToStart (Event);
|
|
|
|
|
break;
|
|
|
|
|
case ExaEvt_STEM:
|
|
|
|
|
case ExaEvt_END:
|
|
|
|
|
ExaEvt_SetMatchStatusToPrevQst (Event);
|
|
|
|
|
break;
|
|
|
|
|
case ExaEvt_ANSWERS:
|
|
|
|
|
Event->Status.Showing = ExaEvt_STEM;
|
|
|
|
|
break;
|
|
|
|
|
case ExaEvt_RESULTS:
|
|
|
|
|
Event->Status.Showing = ExaEvt_ANSWERS;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
Event->Status.Countdown = -1L; // No countdown
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************** Set exam event status to previous question *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_SetMatchStatusToPrevQst (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Get index of the previous question *****/
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Event->Status.QstInd = Exa_GetPrevQuestionIndexInExam (Event->ExaCod,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event->Status.QstInd);
|
|
|
|
|
if (Event->Status.QstInd) // Start of questions not reached
|
|
|
|
|
{
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Event->Status.QstCod = Exa_GetQstCodFromQstInd (Event->ExaCod,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event->Status.QstInd);
|
|
|
|
|
Event->Status.Showing = Event->Status.ShowQstResults ? ExaEvt_RESULTS :
|
|
|
|
|
ExaEvt_ANSWERS;
|
|
|
|
|
}
|
|
|
|
|
else // Start of questions reached
|
|
|
|
|
ExaEvt_SetMatchStatusToStart (Event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************** Set exam event status to start ***********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_SetMatchStatusToStart (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
Event->Status.QstInd = 0; // Before first question
|
|
|
|
|
Event->Status.QstCod = -1L;
|
2020-04-23 02:28:28 +02:00
|
|
|
|
Event->Status.Happening = false;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event->Status.Showing = ExaEvt_START;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************** Set exam event status to next (forward) status ***************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_SetMatchStatusToNext (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** What to show *****/
|
|
|
|
|
switch (Event->Status.Showing)
|
|
|
|
|
{
|
|
|
|
|
case ExaEvt_START:
|
|
|
|
|
ExaEvt_SetMatchStatusToNextQst (Event);
|
|
|
|
|
break;
|
|
|
|
|
case ExaEvt_STEM:
|
|
|
|
|
Event->Status.Showing = ExaEvt_ANSWERS;
|
|
|
|
|
break;
|
|
|
|
|
case ExaEvt_ANSWERS:
|
|
|
|
|
if (Event->Status.ShowQstResults)
|
|
|
|
|
Event->Status.Showing = ExaEvt_RESULTS;
|
|
|
|
|
else
|
|
|
|
|
ExaEvt_SetMatchStatusToNextQst (Event);
|
|
|
|
|
break;
|
|
|
|
|
case ExaEvt_RESULTS:
|
|
|
|
|
ExaEvt_SetMatchStatusToNextQst (Event);
|
|
|
|
|
break;
|
|
|
|
|
case ExaEvt_END:
|
|
|
|
|
ExaEvt_SetMatchStatusToEnd (Event);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
Event->Status.Countdown = -1L; // No countdown
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************** Set exam event status to next question *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_SetMatchStatusToNextQst (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Get index of the next question *****/
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Event->Status.QstInd = Exa_GetNextQuestionIndexInExam (Event->ExaCod,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event->Status.QstInd);
|
|
|
|
|
|
|
|
|
|
/***** Get question code *****/
|
|
|
|
|
if (Event->Status.QstInd < ExaEvt_AFTER_LAST_QUESTION) // End of questions not reached
|
|
|
|
|
{
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Event->Status.QstCod = Exa_GetQstCodFromQstInd (Event->ExaCod,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event->Status.QstInd);
|
|
|
|
|
Event->Status.Showing = ExaEvt_STEM;
|
|
|
|
|
}
|
|
|
|
|
else // End of questions reached
|
|
|
|
|
ExaEvt_SetMatchStatusToEnd (Event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************** Set exam event status to end *************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_SetMatchStatusToEnd (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
Event->Status.QstInd = ExaEvt_AFTER_LAST_QUESTION; // After last question
|
|
|
|
|
Event->Status.QstCod = -1L;
|
2020-04-23 02:28:28 +02:00
|
|
|
|
Event->Status.Happening = false;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event->Status.Showing = ExaEvt_END;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***** Show current exam event status (number, question, answers, button) ****/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowMatchStatusForTch (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Left column *****/
|
|
|
|
|
ExaEvt_ShowLeftColumnTch (Event);
|
|
|
|
|
|
|
|
|
|
/***** Right column *****/
|
|
|
|
|
ExaEvt_ShowRightColumnTch (Event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************ Show current question being played for a student ***************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowMatchStatusForStd (struct ExaEvt_Event *Event,ExaEvt_Update_t Update)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
bool ICanPlayThisMatchBasedOnGrps;
|
|
|
|
|
struct ExaEvt_UsrAnswer UsrAnswer;
|
|
|
|
|
|
|
|
|
|
/***** Can I play this exam event? *****/
|
|
|
|
|
ICanPlayThisMatchBasedOnGrps = ExaEvt_CheckIfICanPlayThisEventBasedOnGrps (Event);
|
|
|
|
|
if (!ICanPlayThisMatchBasedOnGrps)
|
|
|
|
|
Lay_NoPermissionExit ();
|
|
|
|
|
|
|
|
|
|
/***** Get student's answer to this question
|
|
|
|
|
(<0 ==> no answer) *****/
|
|
|
|
|
ExaEvt_GetQstAnsFromDB (Event->EvtCod,
|
|
|
|
|
Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
|
|
|
Event->Status.QstInd,
|
|
|
|
|
&UsrAnswer);
|
|
|
|
|
|
|
|
|
|
/***** Left column *****/
|
|
|
|
|
ExaEvt_ShowLeftColumnStd (Event,&UsrAnswer);
|
|
|
|
|
|
|
|
|
|
/***** Right column *****/
|
|
|
|
|
ExaEvt_ShowRightColumnStd (Event,&UsrAnswer,Update);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************** Get number of events in an exam **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned ExaEvt_GetNumEventsInExam (long ExaCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Trivial check *****/
|
|
|
|
|
if (ExaCod < 0) // A non-existing exam...
|
|
|
|
|
return 0; // ...has no events
|
|
|
|
|
|
|
|
|
|
/***** Get number of events in an exam from database *****/
|
|
|
|
|
return
|
|
|
|
|
(unsigned) DB_QueryCOUNT ("can not get number of events of an exam",
|
|
|
|
|
"SELECT COUNT(*) FROM exa_events"
|
|
|
|
|
" WHERE ExaCod=%ld",
|
|
|
|
|
ExaCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Get number of unfinished events in an exam ******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned ExaEvt_GetNumUnfinishedEventsInExam (long ExaCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Trivial check *****/
|
|
|
|
|
if (ExaCod < 0) // A non-existing exam...
|
|
|
|
|
return 0; // ...has no events
|
|
|
|
|
|
|
|
|
|
/***** Get number of events in an exam from database *****/
|
|
|
|
|
return
|
|
|
|
|
(unsigned) DB_QueryCOUNT ("can not get number of unfinished events of an exam",
|
|
|
|
|
"SELECT COUNT(*) FROM exa_events"
|
|
|
|
|
" WHERE ExaCod=%ld AND Showing<>'%s'",
|
|
|
|
|
ExaCod,ExaEvt_ShowingStringsDB[ExaEvt_END]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********* Check if I belong to any of the groups of an exam event ***********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
bool ExaEvt_CheckIfICanPlayThisEventBasedOnGrps (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
switch (Gbl.Usrs.Me.Role.Logged)
|
|
|
|
|
{
|
|
|
|
|
case Rol_STD:
|
|
|
|
|
/***** Check if I belong to any of the groups
|
|
|
|
|
associated to the exam event *****/
|
|
|
|
|
return (DB_QueryCOUNT ("can not check if I can play an exam event",
|
|
|
|
|
"SELECT COUNT(*) FROM exa_events"
|
|
|
|
|
" WHERE EvtCod=%ld"
|
|
|
|
|
" AND"
|
|
|
|
|
"(EvtCod NOT IN"
|
|
|
|
|
" (SELECT EvtCod FROM exa_groups)"
|
|
|
|
|
" OR"
|
|
|
|
|
" EvtCod IN"
|
|
|
|
|
" (SELECT exa_groups.EvtCod"
|
|
|
|
|
" FROM exa_groups,crs_grp_usr"
|
|
|
|
|
" WHERE crs_grp_usr.UsrCod=%ld"
|
|
|
|
|
" AND exa_groups.GrpCod=crs_grp_usr.GrpCod))",
|
|
|
|
|
Event->EvtCod,Gbl.Usrs.Me.UsrDat.UsrCod) != 0);
|
|
|
|
|
break;
|
|
|
|
|
case Rol_NET:
|
|
|
|
|
/***** Only if I am the creator *****/
|
|
|
|
|
return (Event->UsrCod == Gbl.Usrs.Me.UsrDat.UsrCod);
|
|
|
|
|
case Rol_TCH:
|
|
|
|
|
case Rol_SYS_ADM:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******** Show left column when playing an exam event (as a teacher) *********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowLeftColumnTch (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Start left container *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_LEFT_TCH\"");
|
|
|
|
|
|
|
|
|
|
/***** Refreshable part *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_DIV_Begin ("id=\"exam_event_left\" class=\"EXA_REFRESHABLE_TEACHER\"");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ShowRefreshablePartTch (Event);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
|
|
|
|
|
/***** Put forms to start countdown *****/
|
|
|
|
|
ExaEvt_PutFormsCountdown (Event);
|
|
|
|
|
|
|
|
|
|
/***** Buttons *****/
|
|
|
|
|
ExaEvt_PutMatchControlButtons (Event);
|
|
|
|
|
|
|
|
|
|
/***** Put forms to choice which projects to show *****/
|
|
|
|
|
Set_StartSettingsHead ();
|
|
|
|
|
ExaEvt_ShowFormColumns (Event);
|
|
|
|
|
Set_EndSettingsHead ();
|
|
|
|
|
|
|
|
|
|
/***** Write button to request viewing results *****/
|
|
|
|
|
ExaEvt_PutCheckboxResult (Event);
|
|
|
|
|
|
|
|
|
|
/***** End left container *****/
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Show left refreshable part for teachers *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowRefreshablePartTch (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Write elapsed time in exam event *****/
|
|
|
|
|
ExaEvt_WriteElapsedTimeInEvt (Event);
|
|
|
|
|
|
|
|
|
|
/***** Write number of question *****/
|
|
|
|
|
ExaEvt_ShowNumQstInEvt (Event);
|
|
|
|
|
|
|
|
|
|
/***** Write elapsed time in question *****/
|
|
|
|
|
ExaEvt_WriteElapsedTimeInQst (Event);
|
|
|
|
|
|
|
|
|
|
/***** Number of users who have responded this question *****/
|
|
|
|
|
ExaEvt_WriteNumRespondersQst (Event);
|
|
|
|
|
|
|
|
|
|
/***** Write hourglass *****/
|
|
|
|
|
ExaEvt_PutCountdownAndHourglassIcon (Event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************** Write elapsed time in current exam event *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_WriteElapsedTimeInEvt (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
struct Time Time;
|
|
|
|
|
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_TOP CT\"");
|
|
|
|
|
|
|
|
|
|
/***** Get elapsed time in exam event *****/
|
|
|
|
|
ExaEvt_GetElapsedTimeInMatch (Event,&Time);
|
|
|
|
|
|
|
|
|
|
/***** Write elapsed time in hh:mm'ss" format *****/
|
|
|
|
|
Dat_WriteHoursMinutesSeconds (&Time);
|
|
|
|
|
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************** Write elapsed time in current question *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_WriteElapsedTimeInQst (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
struct Time Time;
|
|
|
|
|
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_TIME_QST\"");
|
|
|
|
|
|
|
|
|
|
switch (Event->Status.Showing)
|
|
|
|
|
{
|
|
|
|
|
case ExaEvt_START:
|
|
|
|
|
case ExaEvt_END:
|
|
|
|
|
HTM_Hyphen (); // Do not write elapsed time
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ExaEvt_GetElapsedTimeInQuestion (Event,&Time);
|
|
|
|
|
Dat_WriteHoursMinutesSeconds (&Time);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************ Write number of responders to an exam event question ***********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_WriteNumRespondersQst (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_MATCH_respond;
|
|
|
|
|
|
|
|
|
|
/***** Begin block *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_NUM_ANSWERERS\"");
|
|
|
|
|
HTM_Txt (Txt_MATCH_respond);
|
|
|
|
|
HTM_BR ();
|
|
|
|
|
HTM_STRONG_Begin ();
|
|
|
|
|
|
|
|
|
|
/***** Write number of responders *****/
|
|
|
|
|
switch (Event->Status.Showing)
|
|
|
|
|
{
|
|
|
|
|
case ExaEvt_START:
|
|
|
|
|
case ExaEvt_END:
|
|
|
|
|
HTM_Hyphen (); // Do not write number of responders
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
HTM_Unsigned (ExaEvt_GetNumUsrsWhoAnsweredQst (Event->EvtCod,
|
|
|
|
|
Event->Status.QstInd));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Write number of participants *****/
|
|
|
|
|
if (Event->Status.Happening) // Event is being played
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/* Get current number of participants */
|
|
|
|
|
ExaEvt_GetNumParticipants (Event);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/* Show current number of participants */
|
|
|
|
|
HTM_TxtF ("/%u",Event->Status.NumParticipants);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** End block *****/
|
|
|
|
|
HTM_STRONG_End ();
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Write current countdown and hourglass icon ******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_PutCountdownAndHourglassIcon (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Countdown;
|
|
|
|
|
const char *Class;
|
|
|
|
|
const char *Icon;
|
|
|
|
|
|
|
|
|
|
/***** Set hourglass icon depending on countdown *****/
|
|
|
|
|
if (Event->Status.Showing == ExaEvt_END) // Event over
|
|
|
|
|
{
|
|
|
|
|
Class = "BT_LINK_OFF EXA_BUTTON_HIDDEN EXA_GREEN";
|
|
|
|
|
Icon = "fa-hourglass-start";
|
|
|
|
|
}
|
|
|
|
|
else if (Event->Status.Countdown < 0) // No countdown
|
|
|
|
|
{
|
|
|
|
|
Class = "BT_LINK_OFF EXA_BUTTON_OFF EXA_GREEN";
|
|
|
|
|
Icon = "fa-hourglass-start";
|
|
|
|
|
}
|
|
|
|
|
else if (Event->Status.Countdown > ExaEvt_COUNTDOWN_SECONDS_MEDIUM) // Countdown in progress
|
|
|
|
|
{
|
|
|
|
|
Class = "BT_LINK_OFF EXA_BUTTON_OFF EXA_LIMEGREEN";
|
|
|
|
|
Icon = "fa-hourglass-start";
|
|
|
|
|
}
|
|
|
|
|
else if (Event->Status.Countdown > ExaEvt_COUNTDOWN_SECONDS_SMALL) // Countdown in progress
|
|
|
|
|
{
|
|
|
|
|
Class = "BT_LINK_OFF EXA_BUTTON_OFF EXA_YELLOW";
|
|
|
|
|
Icon = "fa-hourglass-half";
|
|
|
|
|
}
|
|
|
|
|
else // Countdown about to end
|
|
|
|
|
{
|
|
|
|
|
Class = "BT_LINK_OFF EXA_BUTTON_OFF EXA_RED";
|
|
|
|
|
Icon = "fa-hourglass-end";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Write countdown and put hourglass icon *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_SHOW_HOURGLASS\"");
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_BIGBUTTON_CONT\"");
|
|
|
|
|
HTM_BUTTON_BUTTON_Begin (Txt_Countdown,Class,NULL);
|
|
|
|
|
|
|
|
|
|
/* Countdown */
|
|
|
|
|
if (Event->Status.Countdown > 0)
|
|
|
|
|
HTM_TxtF (" %02ld″",Event->Status.Countdown);
|
|
|
|
|
else
|
|
|
|
|
HTM_NBSP ();
|
|
|
|
|
HTM_BR ();
|
|
|
|
|
|
|
|
|
|
/* Icon */
|
|
|
|
|
HTM_TxtF ("<i class=\"fas %s\"></i>",Icon);
|
|
|
|
|
|
|
|
|
|
HTM_BUTTON_End ();
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Put all forms to start countdowns **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_PutFormsCountdown (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Start container *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_SHOW_HOURGLASS\"");
|
|
|
|
|
|
|
|
|
|
/***** Put forms to start countdown *****/
|
|
|
|
|
ExaEvt_PutFormCountdown (Event,-1 ,"EXA_GREEN" );
|
|
|
|
|
ExaEvt_PutFormCountdown (Event,ExaEvt_COUNTDOWN_SECONDS_LARGE ,"EXA_LIMEGREEN");
|
|
|
|
|
ExaEvt_PutFormCountdown (Event,ExaEvt_COUNTDOWN_SECONDS_MEDIUM,"EXA_YELLOW" );
|
|
|
|
|
ExaEvt_PutFormCountdown (Event,ExaEvt_COUNTDOWN_SECONDS_SMALL ,"EXA_RED" );
|
|
|
|
|
|
|
|
|
|
/***** End container *****/
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****** Put a form to start a countdown with a given number of seconds *******/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_PutFormCountdown (struct ExaEvt_Event *Event,long Seconds,const char *Color)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Countdown;
|
|
|
|
|
char *OnSubmit;
|
|
|
|
|
bool PutForm = Event->Status.Showing != ExaEvt_END;
|
|
|
|
|
|
|
|
|
|
if (PutForm)
|
|
|
|
|
{
|
|
|
|
|
/***** Start form *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (asprintf (&OnSubmit,"updateMatchTch('exam_event_left',"
|
2020-04-20 01:26:46 +02:00
|
|
|
|
"'act=%ld&ses=%s&EvtCod=%ld&Countdown=%ld');"
|
|
|
|
|
" return false;", // return false is necessary to not submit form
|
|
|
|
|
Act_GetActCod (ActExaEvtCntDwn),Gbl.Session.Id,
|
|
|
|
|
Event->EvtCod,Seconds) < 0)
|
|
|
|
|
Lay_NotEnoughMemoryExit ();
|
|
|
|
|
Frm_StartFormOnSubmit (ActUnk,OnSubmit);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Put icon *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_SMALLBUTTON_CONT\"");
|
|
|
|
|
|
|
|
|
|
HTM_BUTTON_SUBMIT_Begin (PutForm ? Txt_Countdown :
|
|
|
|
|
NULL,
|
|
|
|
|
Str_BuildStringStr (PutForm ? "BT_LINK EXA_BUTTON_ON %s" :
|
|
|
|
|
"BT_LINK_OFF EXA_BUTTON_HIDDEN %s",
|
|
|
|
|
Color),
|
|
|
|
|
NULL);
|
|
|
|
|
Str_FreeString ();
|
|
|
|
|
|
|
|
|
|
HTM_NBSP ();
|
|
|
|
|
if (Seconds >= 0)
|
|
|
|
|
HTM_TxtF ("%ld″",Seconds);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
HTM_Txt ("∞");
|
|
|
|
|
HTM_NBSP ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HTM_BUTTON_End ();
|
|
|
|
|
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
|
|
|
|
|
/***** End form *****/
|
|
|
|
|
if (PutForm)
|
|
|
|
|
{
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
free (OnSubmit);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******* Show right column when playing an exam event (as a teacher) *********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowRightColumnTch (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Start right container *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_RIGHT_TCH\"");
|
|
|
|
|
|
|
|
|
|
/***** Top row: exam event title *****/
|
|
|
|
|
ExaEvt_ShowEventTitleTch (Event);
|
|
|
|
|
|
|
|
|
|
/***** Bottom row: current question and possible answers *****/
|
|
|
|
|
if (Event->Status.Showing == ExaEvt_END) // Event over
|
|
|
|
|
ExaEvt_ShowEventScore (Event);
|
|
|
|
|
else // Event not over
|
|
|
|
|
ExaEvt_ShowQuestionAndAnswersTch (Event);
|
|
|
|
|
|
|
|
|
|
/***** End right container *****/
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******** Show left column when playing an exam event (as a student) *********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowLeftColumnStd (const struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
const struct ExaEvt_UsrAnswer *UsrAnswer)
|
|
|
|
|
{
|
|
|
|
|
bool Answered = UsrAnswer->NumOpt >= 0;
|
|
|
|
|
|
|
|
|
|
/***** Start left container *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_LEFT_STD\"");
|
|
|
|
|
|
|
|
|
|
/***** Top *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_TOP CT\"");
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
|
|
|
|
|
/***** Write number of question *****/
|
|
|
|
|
ExaEvt_ShowNumQstInEvt (Event);
|
|
|
|
|
|
|
|
|
|
switch (Event->Status.Showing)
|
|
|
|
|
{
|
|
|
|
|
case ExaEvt_START:
|
|
|
|
|
case ExaEvt_END:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/***** Write whether question is answered or not *****/
|
|
|
|
|
ExaEvt_PutIfAnswered (Event,Answered);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (Event->Status.Happening && // Event is being played
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event->Status.Showing == ExaEvt_ANSWERS && // Teacher's screen is showing question answers
|
|
|
|
|
Answered) // I have answered this question
|
|
|
|
|
/***** Put icon to remove my answet *****/
|
|
|
|
|
ExaEvt_PutIconToRemoveMyAnswer (Event);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** End left container *****/
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******* Show right column when playing an exam event (as a student) *********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowRightColumnStd (struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
const struct ExaEvt_UsrAnswer *UsrAnswer,
|
|
|
|
|
ExaEvt_Update_t Update)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Please_wait_;
|
|
|
|
|
|
|
|
|
|
/***** Start right container *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_RIGHT_STD\"");
|
|
|
|
|
|
|
|
|
|
/***** Top row *****/
|
|
|
|
|
ExaEvt_ShowEventTitleStd (Event);
|
|
|
|
|
|
|
|
|
|
/***** Bottom row *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (Event->Status.Happening) // Event is being played
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
if (Event->Status.Showing == ExaEvt_END) // Event over
|
|
|
|
|
ExaEvt_ShowWaitImage (Txt_Please_wait_);
|
|
|
|
|
else // Event not over
|
|
|
|
|
{
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_BOTTOM\"");
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Update participants ******/
|
|
|
|
|
if (ExaEvt_RegisterMeAsParticipantInEvent (Event))
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
if (Event->Status.Showing == ExaEvt_ANSWERS) // Teacher's screen is showing question answers
|
|
|
|
|
/* Show current question and possible answers */
|
2020-04-22 22:40:07 +02:00
|
|
|
|
ExaEvt_ShowQuestionAndAnswersStd (Event,UsrAnswer,Update);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
Ale_ShowAlert (Ale_ERROR,"You can not join this exam event.");
|
|
|
|
|
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else // Event is not being played
|
|
|
|
|
ExaEvt_ShowWaitImage (Txt_Please_wait_);
|
|
|
|
|
|
|
|
|
|
/***** End right container *****/
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Show number of question in exam ***********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowNumQstInEvt (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_MATCH_Start;
|
|
|
|
|
extern const char *Txt_MATCH_End;
|
2020-04-26 03:07:55 +02:00
|
|
|
|
unsigned NumQsts = ExaSet_GetNumQstsExam (Event->ExaCod);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_NUM_QST\"");
|
|
|
|
|
switch (Event->Status.Showing)
|
|
|
|
|
{
|
|
|
|
|
case ExaEvt_START: // Not started
|
|
|
|
|
HTM_Txt (Txt_MATCH_Start);
|
|
|
|
|
break;
|
|
|
|
|
case ExaEvt_END: // Event over
|
|
|
|
|
HTM_Txt (Txt_MATCH_End);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
HTM_TxtF ("%u/%u",Event->Status.QstInd,NumQsts);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************* Put buttons to control an exam event ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_PutMatchControlButtons (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Go_back;
|
|
|
|
|
extern const char *Txt_Go_forward;
|
|
|
|
|
extern const char *Txt_Pause;
|
|
|
|
|
extern const char *Txt_Start;
|
|
|
|
|
extern const char *Txt_Resume;
|
|
|
|
|
|
|
|
|
|
/***** Start buttons container *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_BUTTONS_CONT\"");
|
|
|
|
|
|
|
|
|
|
/***** Left button *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_BUTTON_LEFT_CONT\"");
|
|
|
|
|
switch (Event->Status.Showing)
|
|
|
|
|
{
|
|
|
|
|
case ExaEvt_START:
|
|
|
|
|
/* Put button to close browser tab */
|
|
|
|
|
ExaEvt_PutBigButtonClose ();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* Put button to go back */
|
|
|
|
|
ExaEvt_PutBigButton (ActBckExaEvt,"backward",Event->EvtCod,
|
|
|
|
|
ExaEvt_ICON_PREVIOUS,Txt_Go_back);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
|
|
|
|
|
/***** Center button *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_BUTTON_CENTER_CONT\"");
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (Event->Status.Happening) // Event is being played
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/* Put button to pause exam event */
|
|
|
|
|
ExaEvt_PutBigButton (ActPlyPauExaEvt,"play_pause",Event->EvtCod,
|
|
|
|
|
ExaEvt_ICON_PAUSE,Txt_Pause);
|
|
|
|
|
else // Event is paused, not being played
|
|
|
|
|
{
|
|
|
|
|
switch (Event->Status.Showing)
|
|
|
|
|
{
|
|
|
|
|
case ExaEvt_START: // Event just started, before first question
|
|
|
|
|
/* Put button to start playing exam event */
|
|
|
|
|
ExaEvt_PutBigButton (ActPlyPauExaEvt,"play_pause",Event->EvtCod,
|
|
|
|
|
ExaEvt_ICON_PLAY,Txt_Start);
|
|
|
|
|
break;
|
|
|
|
|
case ExaEvt_END: // Event over
|
|
|
|
|
/* Put disabled button to play exam event */
|
|
|
|
|
ExaEvt_PutBigButtonHidden (ExaEvt_ICON_PLAY);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* Put button to resume exam event */
|
|
|
|
|
ExaEvt_PutBigButton (ActPlyPauExaEvt,"play_pause",Event->EvtCod,
|
|
|
|
|
ExaEvt_ICON_PLAY,Txt_Resume);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
|
|
|
|
|
/***** Right button *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_BUTTON_RIGHT_CONT\"");
|
|
|
|
|
if (Event->Status.Showing == ExaEvt_END) // Event over
|
|
|
|
|
/* Put button to close browser tab */
|
|
|
|
|
ExaEvt_PutBigButtonClose ();
|
|
|
|
|
else // Event not over
|
|
|
|
|
/* Put button to show answers */
|
|
|
|
|
ExaEvt_PutBigButton (ActFwdExaEvt,"forward",Event->EvtCod,
|
|
|
|
|
ExaEvt_ICON_NEXT,Txt_Go_forward);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
|
|
|
|
|
/***** End buttons container *****/
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/** Show form to choice whether to show answers in one column or two columns */
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowFormColumns (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_column;
|
|
|
|
|
extern const char *Txt_columns;
|
|
|
|
|
unsigned NumCols;
|
|
|
|
|
static const char *NumColsIcon[1 + ExaEvt_MAX_COLS] =
|
|
|
|
|
{
|
|
|
|
|
"", // Not used
|
|
|
|
|
"1col.png", // 1 column
|
|
|
|
|
"2col.png", // 2 columns
|
|
|
|
|
"3col.png", // 3 columns
|
|
|
|
|
"4col.png", // 4 columns
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/***** Begin selector *****/
|
|
|
|
|
Set_StartOneSettingSelector ();
|
|
|
|
|
|
|
|
|
|
for (NumCols = 1;
|
|
|
|
|
NumCols <= ExaEvt_MAX_COLS;
|
|
|
|
|
NumCols++)
|
|
|
|
|
{
|
|
|
|
|
/* Begin container for this option */
|
|
|
|
|
HTM_DIV_Begin ("class=\"%s\"",
|
|
|
|
|
(Event->Status.NumCols == NumCols) ? "EXA_NUM_COL_ON" :
|
|
|
|
|
"EXA_NUM_COL_OFF");
|
|
|
|
|
|
|
|
|
|
/* Begin form */
|
|
|
|
|
Frm_StartForm (ActChgNumColExaEvt);
|
|
|
|
|
ExaEvt_PutParamEvtCod (Event->EvtCod); // Current exam event being played
|
|
|
|
|
ExaEvt_PutParamNumCols (NumCols); // Number of columns
|
|
|
|
|
|
|
|
|
|
/* Number of columns */
|
|
|
|
|
Ico_PutSettingIconLink (NumColsIcon[NumCols],
|
|
|
|
|
Str_BuildStringLongStr ((long) NumCols,
|
|
|
|
|
NumCols == 1 ? Txt_column :
|
|
|
|
|
Txt_columns));
|
|
|
|
|
Str_FreeString ();
|
|
|
|
|
|
|
|
|
|
/* End form */
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
|
|
|
|
|
/* End container for this option */
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** End selector *****/
|
|
|
|
|
Set_EndOneSettingSelector ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******** Write parameter with number of columns in answers of exam event *********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutParamNumCols (unsigned NumCols) // Number of columns
|
|
|
|
|
{
|
|
|
|
|
Par_PutHiddenParamUnsigned (NULL,"NumCols",NumCols);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Put checkbox to select if show results ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_PutCheckboxResult (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_View_results;
|
|
|
|
|
|
|
|
|
|
/***** Begin container *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_SHOW_RESULTS\"");
|
|
|
|
|
|
|
|
|
|
/***** Begin form *****/
|
|
|
|
|
Frm_StartForm (ActChgVisResExaEvtQst);
|
|
|
|
|
ExaEvt_PutParamEvtCod (Event->EvtCod); // Current exam event being played
|
|
|
|
|
|
|
|
|
|
/***** Put icon with link *****/
|
|
|
|
|
HTM_BUTTON_SUBMIT_Begin (Txt_View_results,"BT_LINK DAT ICO_HIGHLIGHT",NULL);
|
|
|
|
|
HTM_TxtF ("<i class=\"%s\"></i>",
|
|
|
|
|
Event->Status.ShowQstResults ? "fas fa-toggle-on" :
|
|
|
|
|
"fas fa-toggle-off");
|
|
|
|
|
HTM_TxtF (" %s",Txt_View_results);
|
|
|
|
|
HTM_BUTTON_End ();
|
|
|
|
|
|
|
|
|
|
/***** End form *****/
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
|
|
|
|
|
/***** End container *****/
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Put checkbox to select if show results ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_PutIfAnswered (const struct ExaEvt_Event *Event,bool Answered)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_View_my_answer;
|
|
|
|
|
extern const char *Txt_MATCH_QUESTION_Answered;
|
|
|
|
|
extern const char *Txt_MATCH_QUESTION_Unanswered;
|
|
|
|
|
|
|
|
|
|
/***** Start container *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_SHOW_ANSWERED\"");
|
|
|
|
|
|
|
|
|
|
/***** Put icon with link *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (Event->Status.Happening && // Event is being played
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event->Status.Showing == ExaEvt_ANSWERS && // Teacher's screen is showing question answers
|
|
|
|
|
Answered) // I have answered this question
|
|
|
|
|
{
|
|
|
|
|
/* Start form */
|
|
|
|
|
Frm_StartForm (ActSeeExaEvtAnsQstStd);
|
|
|
|
|
ExaEvt_PutParamEvtCod (Event->EvtCod); // Current exam event being played
|
|
|
|
|
|
|
|
|
|
HTM_BUTTON_OnMouseDown_Begin (Txt_View_my_answer,"BT_LINK DAT_SMALL_GREEN");
|
|
|
|
|
HTM_TxtF ("<i class=\"%s\"></i>","fas fa-check-circle");
|
|
|
|
|
HTM_TxtF (" %s",Txt_MATCH_QUESTION_Answered);
|
|
|
|
|
HTM_BUTTON_End ();
|
|
|
|
|
|
|
|
|
|
/* End form */
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
HTM_DIV_Begin ("class=\"%s\"",Answered ? "DAT_SMALL_GREEN" :
|
|
|
|
|
"DAT_SMALL_RED");
|
|
|
|
|
HTM_TxtF ("<i class=\"%s\" title=\"%s\"></i>",
|
|
|
|
|
Answered ? "fas fa-check-circle" :
|
|
|
|
|
"fas fa-exclamation-circle",
|
|
|
|
|
Answered ? Txt_MATCH_QUESTION_Answered :
|
|
|
|
|
Txt_MATCH_QUESTION_Unanswered);
|
|
|
|
|
HTM_TxtF (" %s",Answered ? Txt_MATCH_QUESTION_Answered :
|
|
|
|
|
Txt_MATCH_QUESTION_Unanswered);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** End container *****/
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Put checkbox to select if show results ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_PutIconToRemoveMyAnswer (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Delete_my_answer;
|
|
|
|
|
|
|
|
|
|
/***** Start container *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_REM_MY_ANS\"");
|
|
|
|
|
|
|
|
|
|
/***** Start form *****/
|
|
|
|
|
Frm_StartForm (ActRemExaEvtAnsQstStd);
|
|
|
|
|
ExaEvt_PutParamEvtCod (Event->EvtCod); // Current exam event being played
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Exa_PutParamQstInd (Event->Status.QstInd); // Current question index shown
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Put icon with link *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_BIGBUTTON_CONT\"");
|
|
|
|
|
HTM_BUTTON_OnMouseDown_Begin (Txt_Delete_my_answer,"BT_LINK EXA_BUTTON_ON ICO_DARKRED");
|
|
|
|
|
HTM_Txt ("<i class=\"fas fa-trash\"></i>");
|
|
|
|
|
HTM_BUTTON_End ();
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
|
|
|
|
|
/***** End form *****/
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
|
|
|
|
|
/***** End container *****/
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************************** Show exam event title ******************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowEventTitleTch (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Event title *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_TOP LT\"");
|
|
|
|
|
HTM_Txt (Event->Title);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowEventTitleStd (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Event title *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_TOP CT\"");
|
|
|
|
|
HTM_Txt (Event->Title);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***** Show question and its answers when playing an exam event (as a teacher) *****/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowQuestionAndAnswersTch (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_MATCH_Paused;
|
|
|
|
|
extern const char *Txt_Question_removed;
|
|
|
|
|
struct Tst_Question Question;
|
|
|
|
|
|
|
|
|
|
/***** Create test question *****/
|
|
|
|
|
Tst_QstConstructor (&Question);
|
|
|
|
|
Question.QstCod = Event->Status.QstCod;
|
|
|
|
|
|
|
|
|
|
/***** Trivial check: do not show anything on exam event start and end *****/
|
|
|
|
|
switch (Event->Status.Showing)
|
|
|
|
|
{
|
|
|
|
|
case ExaEvt_START:
|
|
|
|
|
case ExaEvt_END:
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Get data of question from database *****/
|
|
|
|
|
if (Tst_GetQstDataFromDB (&Question))
|
|
|
|
|
{
|
|
|
|
|
/***** Show question *****/
|
|
|
|
|
/* Check answer type */
|
|
|
|
|
if (Question.Answer.Type != Tst_ANS_UNIQUE_CHOICE)
|
|
|
|
|
Lay_ShowErrorAndExit ("Wrong answer type.");
|
|
|
|
|
|
|
|
|
|
/* Begin container */
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_BOTTOM\""); // Bottom
|
|
|
|
|
|
|
|
|
|
/* Write stem */
|
|
|
|
|
Tst_WriteQstStem (Question.Stem,"EXA_TCH_STEM",
|
|
|
|
|
true); // Visible
|
|
|
|
|
|
|
|
|
|
/* Show media */
|
|
|
|
|
Med_ShowMedia (&Question.Media,
|
|
|
|
|
"TEST_MED_EDIT_LIST_STEM_CONTAINER",
|
|
|
|
|
"TEST_MED_EDIT_LIST_STEM");
|
|
|
|
|
|
|
|
|
|
/***** Write answers? *****/
|
|
|
|
|
switch (Event->Status.Showing)
|
|
|
|
|
{
|
|
|
|
|
case ExaEvt_ANSWERS:
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (Event->Status.Happening) // Event is being played
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/* Write answers */
|
|
|
|
|
ExaEvt_WriteAnswersEventResult (Event,
|
|
|
|
|
&Question,
|
|
|
|
|
"EXA_TCH_ANS",
|
|
|
|
|
false); // Don't show result
|
|
|
|
|
else // Event is paused, not being played
|
|
|
|
|
ExaEvt_ShowWaitImage (Txt_MATCH_Paused);
|
|
|
|
|
break;
|
|
|
|
|
case ExaEvt_RESULTS:
|
|
|
|
|
/* Write answers with results */
|
|
|
|
|
ExaEvt_WriteAnswersEventResult (Event,
|
|
|
|
|
&Question,
|
|
|
|
|
"EXA_TCH_ANS",
|
|
|
|
|
true); // Show result
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* Don't write anything */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* End container */
|
|
|
|
|
HTM_DIV_End (); // Bottom
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
Ale_ShowAlert (Ale_WARNING,Txt_Question_removed);
|
|
|
|
|
|
|
|
|
|
/***** Destroy test question *****/
|
|
|
|
|
Tst_QstDestructor (&Question);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************* Write answers of a question when seeing an exam event ***************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_WriteAnswersEventResult (const struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
const struct Tst_Question *Question,
|
|
|
|
|
const char *Class,bool ShowResult)
|
|
|
|
|
{
|
|
|
|
|
/***** Write answer depending on type *****/
|
|
|
|
|
if (Question->Answer.Type == Tst_ANS_UNIQUE_CHOICE)
|
|
|
|
|
ExaEvt_WriteChoiceAnsViewEvent (Event,
|
|
|
|
|
Question,
|
|
|
|
|
Class,ShowResult);
|
|
|
|
|
else
|
|
|
|
|
Ale_ShowAlert (Ale_ERROR,"Type of answer not valid in an exam.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******** Write single or multiple choice answer when seeing an exam event *********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
void ExaEvt_WriteChoiceAnsViewEvent (const struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
const struct Tst_Question *Question,
|
|
|
|
|
const char *Class,bool ShowResult)
|
|
|
|
|
{
|
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
bool RowIsOpen = false;
|
|
|
|
|
unsigned NumRespondersQst;
|
|
|
|
|
unsigned NumRespondersAns;
|
|
|
|
|
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION]; // Indexes of all answers of this question
|
|
|
|
|
|
|
|
|
|
/***** Get number of users who have answered this question from database *****/
|
|
|
|
|
NumRespondersQst = ExaEvt_GetNumUsrsWhoAnsweredQst (Event->EvtCod,Event->Status.QstInd);
|
|
|
|
|
|
|
|
|
|
/***** Get indexes for this question in exam event *****/
|
|
|
|
|
ExaEvt_GetIndexes (Event->EvtCod,Event->Status.QstInd,Indexes);
|
|
|
|
|
|
|
|
|
|
/***** Begin table *****/
|
|
|
|
|
HTM_TABLE_BeginWidePadding (0);
|
|
|
|
|
|
|
|
|
|
/***** Show options distributed in columns *****/
|
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < Question->Answer.NumOptions;
|
|
|
|
|
NumOpt++)
|
|
|
|
|
{
|
|
|
|
|
/***** Start row? *****/
|
|
|
|
|
if (NumOpt % Event->Status.NumCols == 0)
|
|
|
|
|
{
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
RowIsOpen = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Write letter for this option *****/
|
|
|
|
|
HTM_TD_Begin ("class=\"EXA_TCH_BUTTON_TD\"");
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_TCH_BUTTON BT_%c\"",'A' + (char) NumOpt);
|
|
|
|
|
HTM_TxtF ("%c",'a' + (char) NumOpt);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/***** Write the option text and the result *****/
|
|
|
|
|
HTM_TD_Begin ("class=\"LT\"");
|
|
|
|
|
HTM_LABEL_Begin ("for=\"Ans%06u_%u\" class=\"%s\"",Event->Status.QstInd,NumOpt,Class);
|
|
|
|
|
|
|
|
|
|
/* Convert text, that is in HTML, to rigorous HTML */
|
|
|
|
|
Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
|
|
|
|
|
Question->Answer.Options[NumOpt].Text,
|
|
|
|
|
Tst_MAX_BYTES_ANSWER_OR_FEEDBACK,false);
|
|
|
|
|
HTM_Txt (Question->Answer.Options[Indexes[NumOpt]].Text);
|
|
|
|
|
|
|
|
|
|
HTM_LABEL_End ();
|
|
|
|
|
Med_ShowMedia (&Question->Answer.Options[Indexes[NumOpt]].Media,
|
|
|
|
|
"TEST_MED_SHOW_CONT",
|
|
|
|
|
"TEST_MED_SHOW");
|
|
|
|
|
|
|
|
|
|
/* Show result (number of users who answered? */
|
|
|
|
|
if (ShowResult)
|
|
|
|
|
{
|
|
|
|
|
/* Get number of users who selected this answer */
|
|
|
|
|
NumRespondersAns = ExaEvt_GetNumUsrsWhoHaveChosenAns (Event->EvtCod,Event->Status.QstInd,Indexes[NumOpt]);
|
|
|
|
|
|
|
|
|
|
/* Draw proportional bar for this answer */
|
|
|
|
|
ExaEvt_DrawBarNumUsrs (NumRespondersAns,NumRespondersQst,
|
|
|
|
|
Question->Answer.Options[Indexes[NumOpt]].Correct);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* Draw empty bar for this answer
|
|
|
|
|
in order to show the same layout that the one shown with results */
|
|
|
|
|
ExaEvt_DrawBarNumUsrs (0,0,
|
|
|
|
|
false); // Not used when length of bar is 0
|
|
|
|
|
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/***** End row? *****/
|
|
|
|
|
if (NumOpt % Event->Status.NumCols == Event->Status.NumCols - 1)
|
|
|
|
|
{
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
RowIsOpen = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** End row? *****/
|
|
|
|
|
if (RowIsOpen)
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
|
|
|
|
|
/***** End table *****/
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***** Show question and its answers when playing an exam event (as a student) *****/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
// Return true on valid question, false on invalid question
|
|
|
|
|
|
2020-04-22 22:40:07 +02:00
|
|
|
|
static void ExaEvt_ShowQuestionAndAnswersStd (const struct ExaEvt_Event *Event,
|
2020-04-20 01:26:46 +02:00
|
|
|
|
const struct ExaEvt_UsrAnswer *UsrAnswer,
|
|
|
|
|
ExaEvt_Update_t Update)
|
|
|
|
|
{
|
|
|
|
|
unsigned NumOptions;
|
|
|
|
|
unsigned NumOpt;
|
|
|
|
|
char *Class;
|
|
|
|
|
|
|
|
|
|
/***** Get number of options in this question *****/
|
|
|
|
|
NumOptions = Tst_GetNumAnswersQst (Event->Status.QstCod);
|
|
|
|
|
|
|
|
|
|
/***** Begin table *****/
|
|
|
|
|
HTM_TABLE_BeginWidePadding (8);
|
|
|
|
|
|
|
|
|
|
for (NumOpt = 0;
|
|
|
|
|
NumOpt < NumOptions;
|
|
|
|
|
NumOpt++)
|
|
|
|
|
{
|
|
|
|
|
/***** Start row *****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
/***** Write letter for this option *****/
|
|
|
|
|
/* Begin table cell */
|
|
|
|
|
HTM_TD_Begin ("class=\"EXA_STD_CELL\"");
|
|
|
|
|
|
|
|
|
|
/* Form with button.
|
|
|
|
|
Sumitting onmousedown instead of default onclick
|
|
|
|
|
is necessary in order to be fast
|
|
|
|
|
and not lose clicks due to refresh */
|
|
|
|
|
Frm_StartForm (ActAnsExaEvtQstStd);
|
|
|
|
|
ExaEvt_PutParamEvtCod (Event->EvtCod); // Current exam event being played
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Exa_PutParamQstInd (Event->Status.QstInd); // Current question index shown
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_PutParamNumOpt (NumOpt); // Number of button
|
|
|
|
|
|
|
|
|
|
if (asprintf (&Class,"EXA_STD_BUTTON%s BT_%c",
|
|
|
|
|
UsrAnswer->NumOpt == (int) NumOpt && // Student's answer
|
|
|
|
|
Update == ExaEvt_CHANGE_STATUS_BY_STUDENT ? " EXA_STD_ANSWER_SELECTED" :
|
|
|
|
|
"",
|
|
|
|
|
'A' + (char) NumOpt) < 0)
|
|
|
|
|
Lay_NotEnoughMemoryExit ();
|
|
|
|
|
HTM_BUTTON_OnMouseDown_Begin (NULL,Class);
|
|
|
|
|
HTM_TxtF ("%c",'a' + (char) NumOpt);
|
|
|
|
|
HTM_BUTTON_End ();
|
|
|
|
|
free (Class);
|
|
|
|
|
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
|
|
|
|
|
/* End table cell */
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/***** End row *****/
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** End table *****/
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************************** Show exam event scores *****************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#define ExaEvt_NUM_ROWS_SCORE 50
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ShowEventScore (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
unsigned NumScores;
|
|
|
|
|
unsigned NumScore;
|
|
|
|
|
double MinScore;
|
|
|
|
|
double MaxScore;
|
|
|
|
|
double Range;
|
|
|
|
|
double NumRowsPerScorePoint;
|
|
|
|
|
double Score;
|
|
|
|
|
unsigned MaxUsrs = 0;
|
|
|
|
|
unsigned NumUsrs;
|
|
|
|
|
unsigned NumRowForThisScore;
|
|
|
|
|
unsigned NumRow;
|
|
|
|
|
|
|
|
|
|
/***** Get minimum and maximum scores *****/
|
2020-04-22 22:40:07 +02:00
|
|
|
|
Exa_GetScoreRange (Event->ExaCod,&MinScore,&MaxScore);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Range = MaxScore - MinScore;
|
|
|
|
|
if (Range == 0.0)
|
|
|
|
|
return;
|
|
|
|
|
NumRowsPerScorePoint = (double) ExaEvt_NUM_ROWS_SCORE / Range;
|
|
|
|
|
|
|
|
|
|
/***** Get maximum number of users *****/
|
|
|
|
|
if (DB_QuerySELECT (&mysql_res,"can not get max users",
|
|
|
|
|
"SELECT MAX(NumUsrs)"
|
|
|
|
|
" FROM "
|
|
|
|
|
"(SELECT COUNT(*) AS NumUsrs" // row[1]
|
|
|
|
|
" FROM exa_results"
|
|
|
|
|
" WHERE EvtCod=%ld"
|
|
|
|
|
" GROUP BY Score"
|
|
|
|
|
" ORDER BY Score) AS Scores",
|
|
|
|
|
Event->EvtCod))
|
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
|
|
|
|
|
/* Get maximum number of users (row[0]) *****/
|
|
|
|
|
if (row)
|
|
|
|
|
if (row[0])
|
|
|
|
|
if (sscanf (row[0],"%u",&MaxUsrs) != 1)
|
|
|
|
|
MaxUsrs = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Free structure that stores the query result */
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
|
|
|
|
|
/***** Get scores from database *****/
|
|
|
|
|
NumScores = (unsigned)
|
|
|
|
|
DB_QuerySELECT (&mysql_res,"can not get scores",
|
|
|
|
|
"SELECT Score," // row[0]
|
|
|
|
|
"COUNT(*) AS NumUsrs" // row[1]
|
|
|
|
|
" FROM exa_results"
|
|
|
|
|
" WHERE EvtCod=%ld"
|
|
|
|
|
" GROUP BY Score"
|
|
|
|
|
" ORDER BY Score DESC",
|
|
|
|
|
Event->EvtCod);
|
|
|
|
|
|
|
|
|
|
/***** Begin table ****/
|
|
|
|
|
HTM_TABLE_BeginWide ();
|
|
|
|
|
|
|
|
|
|
/***** Get and draw scores *****/
|
|
|
|
|
for (NumScore = 0, NumRow = 0;
|
|
|
|
|
NumScore < NumScores;
|
|
|
|
|
NumScore++)
|
|
|
|
|
{
|
|
|
|
|
/***** Get score and number of users from database *****/
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
|
|
|
|
|
/* Get score (row[0]) */
|
|
|
|
|
Str_SetDecimalPointToUS (); // To get the decimal point as a dot
|
|
|
|
|
if (sscanf (row[0],"%lf",&Score) != 1)
|
|
|
|
|
Score = 0.0;
|
|
|
|
|
Str_SetDecimalPointToLocal (); // Return to local system
|
|
|
|
|
|
|
|
|
|
/* Get number of users (row[1]) *****/
|
|
|
|
|
if (sscanf (row[1],"%u",&NumUsrs) != 1)
|
|
|
|
|
NumUsrs = 0;
|
|
|
|
|
|
|
|
|
|
/***** Draw empty rows until reaching the adequate row *****/
|
|
|
|
|
NumRowForThisScore = (unsigned) ((MaxScore - Score) * NumRowsPerScorePoint);
|
|
|
|
|
if (NumRowForThisScore == ExaEvt_NUM_ROWS_SCORE)
|
|
|
|
|
NumRowForThisScore = ExaEvt_NUM_ROWS_SCORE - 1;
|
|
|
|
|
for (;
|
|
|
|
|
NumRow < NumRowForThisScore;
|
|
|
|
|
NumRow++)
|
|
|
|
|
ExaEvt_DrawEmptyScoreRow (NumRow,MinScore,MaxScore);
|
|
|
|
|
|
|
|
|
|
/***** Draw row for this score *****/
|
|
|
|
|
ExaEvt_DrawScoreRow (Score,MinScore,MaxScore,NumRow,NumUsrs,MaxUsrs);
|
|
|
|
|
NumRow++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Draw final empty rows *****/
|
|
|
|
|
for (;
|
|
|
|
|
NumRow < ExaEvt_NUM_ROWS_SCORE;
|
|
|
|
|
NumRow++)
|
|
|
|
|
ExaEvt_DrawEmptyScoreRow (NumRow,MinScore,MaxScore);
|
|
|
|
|
|
|
|
|
|
/***** End table *****/
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************************** Draw empty score row ****************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_DrawEmptyScoreRow (unsigned NumRow,double MinScore,double MaxScore)
|
|
|
|
|
{
|
|
|
|
|
/***** Draw row *****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
/* Write score */
|
|
|
|
|
HTM_TD_Begin ("class=\"EXA_SCO_SCO\"");
|
|
|
|
|
if (NumRow == 0)
|
|
|
|
|
{
|
|
|
|
|
HTM_DoubleFewDigits (MaxScore);
|
|
|
|
|
HTM_NBSP ();
|
|
|
|
|
}
|
|
|
|
|
else if (NumRow == ExaEvt_NUM_ROWS_SCORE - 1)
|
|
|
|
|
{
|
|
|
|
|
HTM_DoubleFewDigits (MinScore);
|
|
|
|
|
HTM_NBSP ();
|
|
|
|
|
}
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/* Empty column with bar and number of users */
|
|
|
|
|
HTM_TD_Begin ("class=\"EXA_SCO_NUM%s\"",ExaEvt_GetClassBorder (NumRow));
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************************* Draw score row ******************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_DrawScoreRow (double Score,double MinScore,double MaxScore,
|
|
|
|
|
unsigned NumRow,unsigned NumUsrs,unsigned MaxUsrs)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_ROLES_SINGUL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
|
|
|
|
|
extern const char *Txt_ROLES_PLURAL_abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
|
|
|
|
|
unsigned Color;
|
|
|
|
|
unsigned BarWidth;
|
|
|
|
|
char *Icon;
|
|
|
|
|
|
|
|
|
|
/***** Compute color *****/
|
|
|
|
|
/*
|
|
|
|
|
+----------------- MaxScore
|
|
|
|
|
| score9_1x1.png
|
|
|
|
|
+-----------------
|
|
|
|
|
| score8_1x1.png
|
|
|
|
|
+-----------------
|
|
|
|
|
| score7_1x1.png
|
|
|
|
|
+-----------------
|
|
|
|
|
| score6_1x1.png
|
|
|
|
|
+-----------------
|
|
|
|
|
| score5_1x1.png
|
|
|
|
|
+-----------------
|
|
|
|
|
| score4_1x1.png
|
|
|
|
|
+-----------------
|
|
|
|
|
| score3_1x1.png
|
|
|
|
|
+-----------------
|
|
|
|
|
| score2_1x1.png
|
|
|
|
|
+-----------------
|
|
|
|
|
| score1_1x1.png
|
|
|
|
|
+-----------------
|
|
|
|
|
| score0_1x1.png
|
|
|
|
|
+----------------- MinScore
|
|
|
|
|
*/
|
|
|
|
|
Color = (unsigned) (((Score - MinScore) / (MaxScore - MinScore)) * 10.0);
|
|
|
|
|
if (Color == 10)
|
|
|
|
|
Color = 9;
|
|
|
|
|
|
|
|
|
|
/***** Compute bar width *****/
|
|
|
|
|
if (MaxUsrs > 0)
|
|
|
|
|
{
|
|
|
|
|
BarWidth = (unsigned) (((NumUsrs * 95.0) / MaxUsrs) + 0.5);
|
|
|
|
|
if (BarWidth == 0)
|
|
|
|
|
BarWidth = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
BarWidth = 0;
|
|
|
|
|
|
|
|
|
|
/***** Draw row *****/
|
|
|
|
|
HTM_TR_Begin (NULL);
|
|
|
|
|
|
|
|
|
|
/* Write score */
|
|
|
|
|
HTM_TD_Begin ("class=\"EXA_SCO_SCO\"");
|
|
|
|
|
HTM_DoubleFewDigits (Score);
|
|
|
|
|
HTM_NBSP ();
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
/* Draw bar and write number of users for this score */
|
|
|
|
|
HTM_TD_Begin ("class=\"EXA_SCO_NUM%s\"",ExaEvt_GetClassBorder (NumRow));
|
|
|
|
|
if (asprintf (&Icon,"score%u_1x1.png",Color) < 0) // Background
|
|
|
|
|
Lay_NotEnoughMemoryExit ();
|
|
|
|
|
HTM_IMG (Cfg_URL_ICON_PUBLIC,Icon,
|
|
|
|
|
Str_BuildStringLongStr ((long) NumUsrs,
|
|
|
|
|
NumUsrs == 1 ? Txt_ROLES_SINGUL_abc[Rol_STD][Usr_SEX_UNKNOWN] :
|
|
|
|
|
Txt_ROLES_PLURAL_abc[Rol_STD][Usr_SEX_UNKNOWN]),
|
|
|
|
|
"class=\"EXA_SCO_BAR\" style=\"width:%u%%;\"",BarWidth);
|
|
|
|
|
Str_FreeString ();
|
|
|
|
|
free (Icon);
|
|
|
|
|
HTM_TxtF (" %u",NumUsrs);
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****** Write parameter with number of option (button) pressed by user *******/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static const char *ExaEvt_GetClassBorder (unsigned NumRow)
|
|
|
|
|
{
|
|
|
|
|
return NumRow == 0 ? " EXA_SCO_TOP" :
|
|
|
|
|
(NumRow == ExaEvt_NUM_ROWS_SCORE - 1 ? " EXA_SCO_BOT" :
|
|
|
|
|
" EXA_SCO_MID");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****** Write parameter with number of option (button) pressed by user *******/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutParamNumOpt (unsigned NumOpt)
|
|
|
|
|
{
|
|
|
|
|
Par_PutHiddenParamUnsigned (NULL,"NumOpt",NumOpt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******* Get parameter with number of option (button) pressed by user ********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static unsigned ExaEvt_GetParamNumOpt (void)
|
|
|
|
|
{
|
|
|
|
|
long NumOpt;
|
|
|
|
|
|
|
|
|
|
NumOpt = Par_GetParToLong ("NumOpt");
|
|
|
|
|
if (NumOpt < 0)
|
|
|
|
|
Lay_ShowErrorAndExit ("Wrong number of option.");
|
|
|
|
|
|
|
|
|
|
return (unsigned) NumOpt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Put a big button to do action ***********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutBigButton (Act_Action_t NextAction,const char *Id,
|
|
|
|
|
long EvtCod,const char *Icon,const char *Txt)
|
|
|
|
|
{
|
|
|
|
|
/***** Begin form *****/
|
|
|
|
|
Frm_StartFormId (NextAction,Id);
|
|
|
|
|
ExaEvt_PutParamEvtCod (EvtCod);
|
|
|
|
|
|
|
|
|
|
/***** Put icon with link *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_BIGBUTTON_CONT\"");
|
|
|
|
|
HTM_BUTTON_SUBMIT_Begin (Txt,"BT_LINK EXA_BUTTON_ON ICO_BLACK",NULL);
|
|
|
|
|
HTM_TxtF ("<i class=\"%s\"></i>",Icon);
|
|
|
|
|
HTM_BUTTON_End ();
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
|
|
|
|
|
/***** End form *****/
|
|
|
|
|
Frm_EndForm ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/************************** Put a big button hidden **************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutBigButtonHidden (const char *Icon)
|
|
|
|
|
{
|
|
|
|
|
/***** Put inactive icon *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_BIGBUTTON_CONT\"");
|
|
|
|
|
HTM_BUTTON_BUTTON_Begin (NULL,"BT_LINK_OFF EXA_BUTTON_HIDDEN ICO_BLACK",NULL);
|
|
|
|
|
HTM_TxtF ("<i class=\"%s\"></i>",Icon);
|
|
|
|
|
HTM_BUTTON_End ();
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************** Put a big button to close window *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_PutBigButtonClose (void)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_Close;
|
|
|
|
|
|
|
|
|
|
/***** Put icon with link *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_BIGBUTTON_CONT\"");
|
|
|
|
|
HTM_BUTTON_BUTTON_Begin (Txt_Close,"BT_LINK EXA_BUTTON_ON ICO_DARKRED","window.close();");
|
|
|
|
|
HTM_TxtF ("<i class=\"%s\"></i>",ExaEvt_ICON_CLOSE);
|
|
|
|
|
HTM_BUTTON_End ();
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************************** Show wait image ******************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_ShowWaitImage (const char *Txt)
|
|
|
|
|
{
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_WAIT_CONT\"");
|
|
|
|
|
Ico_PutIcon ("Spin-1s-200px.gif",Txt,"EXA_WAIT_IMG");
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/************************** Remove old participants **************************/
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
static void ExaEvt_RemoveOldParticipants (void)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Delete events not being played by teacher *****/
|
|
|
|
|
DB_QueryDELETE ("can not update events as not being played",
|
2020-04-22 22:40:07 +02:00
|
|
|
|
"DELETE FROM exa_happening"
|
2020-04-20 01:26:46 +02:00
|
|
|
|
" WHERE TS<FROM_UNIXTIME(UNIX_TIMESTAMP()-%lu)",
|
|
|
|
|
Cfg_SECONDS_TO_REFRESH_MATCH_TCH*3);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Delete participants (students) who have left events *****/
|
|
|
|
|
DB_QueryDELETE ("can not update exam event participants",
|
2020-04-22 22:40:07 +02:00
|
|
|
|
"DELETE FROM exa_participants"
|
2020-04-20 01:26:46 +02:00
|
|
|
|
" WHERE TS<FROM_UNIXTIME(UNIX_TIMESTAMP()-%lu)",
|
|
|
|
|
Cfg_SECONDS_TO_REFRESH_MATCH_STD*3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************** Update exam event as being played *************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_UpdateEventAsBeingPlayed (long EvtCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Insert exam event as being played *****/
|
|
|
|
|
DB_QueryREPLACE ("can not set exam event as being played",
|
2020-04-22 22:40:07 +02:00
|
|
|
|
"REPLACE exa_happening (EvtCod) VALUE (%ld)",
|
2020-04-20 01:26:46 +02:00
|
|
|
|
EvtCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**************** Update exam event as paused, not being played *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void ExaEvt_SetEventAsNotBeingPlayed (long EvtCod)
|
|
|
|
|
{
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Delete all exam event participants ******/
|
|
|
|
|
DB_QueryDELETE ("can not update exam event participants",
|
2020-04-22 22:40:07 +02:00
|
|
|
|
"DELETE FROM exa_participants"
|
2020-04-20 01:26:46 +02:00
|
|
|
|
" WHERE EvtCod=%ld",
|
|
|
|
|
EvtCod);
|
|
|
|
|
|
|
|
|
|
/***** Delete exam event as being played ******/
|
|
|
|
|
DB_QueryDELETE ("can not set exam event as not being played",
|
2020-04-22 22:40:07 +02:00
|
|
|
|
"DELETE FROM exa_happening"
|
2020-04-20 01:26:46 +02:00
|
|
|
|
" WHERE EvtCod=%ld",
|
|
|
|
|
EvtCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*********************** Get if exam event is being played ************************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static bool ExaEvt_GetIfEventIsBeingPlayed (long EvtCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Get if an exam event is being played or not *****/
|
|
|
|
|
return
|
|
|
|
|
(bool) (DB_QueryCOUNT ("can not get if exam event is being played",
|
2020-04-22 22:40:07 +02:00
|
|
|
|
"SELECT COUNT(*) FROM exa_happening"
|
2020-04-20 01:26:46 +02:00
|
|
|
|
" WHERE EvtCod=%ld",
|
|
|
|
|
EvtCod) != 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/************************** Get number of participants ***********************/
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
static void ExaEvt_GetNumParticipants (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Get number of participants who are playing an exam event *****/
|
|
|
|
|
Event->Status.NumParticipants =
|
|
|
|
|
(unsigned) DB_QueryCOUNT ("can not get number of participants",
|
2020-04-22 22:40:07 +02:00
|
|
|
|
"SELECT COUNT(*) FROM exa_participants"
|
2020-04-20 01:26:46 +02:00
|
|
|
|
" WHERE EvtCod=%ld",
|
|
|
|
|
Event->EvtCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/************** Register me as a participant in an exam event ****************/
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
// Return true on success
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
bool ExaEvt_RegisterMeAsParticipantInEvent (struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
/***** Trivial check: exam event code must be > 0 *****/
|
|
|
|
|
if (Event->EvtCod <= 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/***** Trivial check: exam event must be being played *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (!Event->Status.Happening) // Event is paused, not being played
|
2020-04-20 01:26:46 +02:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/***** Trivial check: exam event must not be over *****/
|
|
|
|
|
if (Event->Status.Showing == ExaEvt_END) // Event over
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
/***** Trivial check: only a student can join an exam event *****/
|
|
|
|
|
if (Gbl.Usrs.Me.Role.Logged != Rol_STD) // I am not logged as student
|
|
|
|
|
return false;
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Insert me as exam event participant *****/
|
|
|
|
|
DB_QueryREPLACE ("can not insert exam event participant",
|
2020-04-22 22:40:07 +02:00
|
|
|
|
"REPLACE exa_participants (EvtCod,UsrCod) VALUES (%ld,%ld)",
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event->EvtCod,Gbl.Usrs.Me.UsrDat.UsrCod);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************** Get code of exam event being played ***********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_GetEventBeingPlayed (void)
|
|
|
|
|
{
|
|
|
|
|
long EvtCodBeingPlayed;
|
|
|
|
|
|
|
|
|
|
/***** Get exam event code ****/
|
|
|
|
|
if ((EvtCodBeingPlayed = ExaEvt_GetParamEvtCod ()) <= 0)
|
|
|
|
|
Lay_ShowErrorAndExit ("Code of exam event is missing.");
|
|
|
|
|
|
|
|
|
|
ExaEvt_SetEvtCodBeingPlayed (EvtCodBeingPlayed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Show exam event being played as student ********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_JoinEventAsStd (void)
|
|
|
|
|
{
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/***** Get data of the exam event from database *****/
|
|
|
|
|
Event.EvtCod = ExaEvt_GetEvtCodBeingPlayed ();
|
|
|
|
|
ExaEvt_GetDataOfEventByCod (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam event status *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_DIV_Begin ("id=\"exam_event\" class=\"EXA_CONT\"");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ShowMatchStatusForStd (&Event,ExaEvt_CHANGE_STATUS_BY_STUDENT);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****** Remove student's answer to a question and show exam event as student ******/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_RemoveMyQuestionAnswer (void)
|
|
|
|
|
{
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
unsigned QstInd;
|
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/***** Get data of the exam event from database *****/
|
|
|
|
|
Event.EvtCod = ExaEvt_GetEvtCodBeingPlayed ();
|
|
|
|
|
ExaEvt_GetDataOfEventByCod (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Get question index from form *****/
|
|
|
|
|
QstInd = Exa_GetParamQstInd ();
|
|
|
|
|
|
|
|
|
|
/***** Check that teacher's screen is showing answers
|
|
|
|
|
and question index is the current one being played *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (Event.Status.Happening && // Event is being played
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event.Status.Showing == ExaEvt_ANSWERS && // Teacher's screen is showing answers
|
|
|
|
|
QstInd == Event.Status.QstInd) // Removing answer to the current question being played
|
|
|
|
|
/***** Remove answer to this question *****/
|
|
|
|
|
ExaEvt_RemoveMyAnswerToEventQuestion (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam event status *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_DIV_Begin ("id=\"exam_event\" class=\"EXA_CONT\"");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ShowMatchStatusForStd (&Event,ExaEvt_CHANGE_STATUS_BY_STUDENT);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Start exam event countdown (by a teacher) *******************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_StartCountdown (void)
|
|
|
|
|
{
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
long NewCountdown;
|
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/***** Get countdown parameter ****/
|
|
|
|
|
NewCountdown = Par_GetParToLong ("Countdown");
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Remove old participants.
|
2020-04-20 01:26:46 +02:00
|
|
|
|
This function must be called by a teacher
|
|
|
|
|
before getting exam event status. *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
ExaEvt_RemoveOldParticipants ();
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Get data of the exam event from database *****/
|
|
|
|
|
Event.EvtCod = ExaEvt_GetEvtCodBeingPlayed ();
|
|
|
|
|
ExaEvt_GetDataOfEventByCod (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Start countdown *****/
|
|
|
|
|
Event.Status.Countdown = NewCountdown;
|
|
|
|
|
|
|
|
|
|
/***** Update exam event status in database *****/
|
|
|
|
|
ExaEvt_UpdateEventStatusInDB (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam event status *****/
|
|
|
|
|
ExaEvt_ShowRefreshablePartTch (&Event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/****************** Refresh exam event for a teacher via AJAX *********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_RefreshEventTch (void)
|
|
|
|
|
{
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
enum {REFRESH_LEFT,REFRESH_ALL} WhatToRefresh;
|
|
|
|
|
|
|
|
|
|
if (!Gbl.Session.IsOpen) // If session has been closed, do not write anything
|
|
|
|
|
return;
|
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-23 02:28:28 +02:00
|
|
|
|
/***** Remove old participants.
|
2020-04-20 01:26:46 +02:00
|
|
|
|
This function must be called by a teacher
|
|
|
|
|
before getting exam event status. *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
ExaEvt_RemoveOldParticipants ();
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Get data of the exam event from database *****/
|
|
|
|
|
Event.EvtCod = ExaEvt_GetEvtCodBeingPlayed ();
|
|
|
|
|
ExaEvt_GetDataOfEventByCod (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Update countdown *****/
|
|
|
|
|
// If current countdown is < 0 ==> no countdown in progress
|
|
|
|
|
WhatToRefresh = REFRESH_LEFT;
|
2020-04-23 02:28:28 +02:00
|
|
|
|
if (Event.Status.Happening && // Event is being played
|
2020-04-20 01:26:46 +02:00
|
|
|
|
Event.Status.Countdown >= 0) // Countdown in progress
|
|
|
|
|
{
|
|
|
|
|
/* Decrease countdown */
|
|
|
|
|
Event.Status.Countdown -= Cfg_SECONDS_TO_REFRESH_MATCH_TCH;
|
|
|
|
|
|
|
|
|
|
/* On countdown reached, set exam event status to next (forward) status */
|
|
|
|
|
if (Event.Status.Countdown <= 0) // End of countdown reached
|
|
|
|
|
{
|
|
|
|
|
ExaEvt_SetMatchStatusToNext (&Event);
|
|
|
|
|
WhatToRefresh = REFRESH_ALL; // Refresh the whole page
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Update exam event status in database *****/
|
|
|
|
|
ExaEvt_UpdateEventStatusInDB (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Update elapsed time in this question *****/
|
|
|
|
|
ExaEvt_UpdateElapsedTimeInQuestion (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam event status *****/
|
|
|
|
|
switch (WhatToRefresh)
|
|
|
|
|
{
|
|
|
|
|
case REFRESH_LEFT: // Refresh only left part
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_Txt ("exam_event_left|0|"); // 0 ==> do not evaluate MatJax scripts after updating HTML
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ShowRefreshablePartTch (&Event);
|
|
|
|
|
break;
|
|
|
|
|
case REFRESH_ALL: // Refresh the whole page
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_Txt ("exam_event|1|"); // 1 ==> evaluate MatJax scripts after updating HTML
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ShowMatchStatusForTch (&Event);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/*************** Refresh current exam for a student via AJAX *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_RefreshEventStd (void)
|
|
|
|
|
{
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
if (!Gbl.Session.IsOpen) // If session has been closed, do not write anything
|
|
|
|
|
return;
|
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/***** Get data of the exam event from database *****/
|
|
|
|
|
Event.EvtCod = ExaEvt_GetEvtCodBeingPlayed ();
|
|
|
|
|
ExaEvt_GetDataOfEventByCod (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Show current exam event status *****/
|
|
|
|
|
ExaEvt_ShowMatchStatusForStd (&Event,ExaEvt_REFRESH_STATUS_BY_SERVER);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**** Receive previous question answer in an exam event question from database *****/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_GetQstAnsFromDB (long EvtCod,long UsrCod,unsigned QstInd,
|
|
|
|
|
struct ExaEvt_UsrAnswer *UsrAnswer)
|
|
|
|
|
{
|
|
|
|
|
MYSQL_RES *mysql_res;
|
|
|
|
|
MYSQL_ROW row;
|
|
|
|
|
unsigned NumRows;
|
|
|
|
|
|
|
|
|
|
/***** Set default values for number of option and answer index *****/
|
|
|
|
|
UsrAnswer->NumOpt = -1; // < 0 ==> no answer selected
|
|
|
|
|
UsrAnswer->AnsInd = -1; // < 0 ==> no answer selected
|
|
|
|
|
|
|
|
|
|
/***** Get student's answer *****/
|
|
|
|
|
NumRows = (unsigned) DB_QuerySELECT (&mysql_res,"can not get user's answer to an exam event question",
|
|
|
|
|
"SELECT NumOpt," // row[0]
|
|
|
|
|
"AnsInd" // row[1]
|
|
|
|
|
" FROM exa_answers"
|
|
|
|
|
" WHERE EvtCod=%ld"
|
|
|
|
|
" AND UsrCod=%ld"
|
|
|
|
|
" AND QstInd=%u",
|
|
|
|
|
EvtCod,UsrCod,QstInd);
|
|
|
|
|
if (NumRows) // Answer found...
|
|
|
|
|
{
|
|
|
|
|
row = mysql_fetch_row (mysql_res);
|
|
|
|
|
|
|
|
|
|
/***** Get number of option index (row[0]) *****/
|
|
|
|
|
if (sscanf (row[0],"%d",&(UsrAnswer->NumOpt)) != 1)
|
|
|
|
|
Lay_ShowErrorAndExit ("Error when getting student's answer to an exam event question.");
|
|
|
|
|
|
|
|
|
|
/***** Get answer index (row[1]) *****/
|
|
|
|
|
if (sscanf (row[1],"%d",&(UsrAnswer->AnsInd)) != 1)
|
|
|
|
|
Lay_ShowErrorAndExit ("Error when getting student's answer to an exam event question.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Free structure that stores the query result *****/
|
|
|
|
|
DB_FreeMySQLResult (&mysql_res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********* Receive question answer from student when playing an exam event *********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
void ExaEvt_ReceiveQuestionAnswer (void)
|
|
|
|
|
{
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct ExaEvt_Event Event;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
unsigned QstInd;
|
|
|
|
|
unsigned Indexes[Tst_MAX_OPTIONS_PER_QUESTION];
|
|
|
|
|
struct ExaEvt_UsrAnswer PreviousUsrAnswer;
|
|
|
|
|
struct ExaEvt_UsrAnswer UsrAnswer;
|
2020-04-22 03:15:04 +02:00
|
|
|
|
struct TstRes_Result Result;
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
2020-04-23 23:09:28 +02:00
|
|
|
|
/***** Reset event *****/
|
|
|
|
|
ExaEvt_ResetEvent (&Event);
|
|
|
|
|
|
2020-04-20 01:26:46 +02:00
|
|
|
|
/***** Get data of the exam event from database *****/
|
|
|
|
|
Event.EvtCod = ExaEvt_GetEvtCodBeingPlayed ();
|
|
|
|
|
ExaEvt_GetDataOfEventByCod (&Event);
|
|
|
|
|
|
|
|
|
|
/***** Get question index from form *****/
|
|
|
|
|
QstInd = Exa_GetParamQstInd ();
|
|
|
|
|
|
|
|
|
|
/***** Check that teacher's screen is showing answers
|
|
|
|
|
and question index is the current one being played *****/
|
|
|
|
|
if (Event.Status.Showing == ExaEvt_ANSWERS && // Teacher's screen is showing answers
|
|
|
|
|
QstInd == Event.Status.QstInd) // Receiving an answer to the current question being played
|
|
|
|
|
{
|
|
|
|
|
/***** Get indexes for this question from database *****/
|
|
|
|
|
ExaEvt_GetIndexes (Event.EvtCod,Event.Status.QstInd,Indexes);
|
|
|
|
|
|
|
|
|
|
/***** Get answer index *****/
|
|
|
|
|
/*
|
|
|
|
|
Indexes[4] = {0,3,1,2}
|
|
|
|
|
+-------+--------+----------+---------+
|
|
|
|
|
| Button | Option | Answer | Correct |
|
|
|
|
|
| letter | number | index | |
|
|
|
|
|
| screen | screen | database | |
|
|
|
|
|
+--------+--------+----------+---------+
|
|
|
|
|
| a | 0 | 0 | |
|
|
|
|
|
| b | 1 | 3 | |
|
|
|
|
|
| c | 2 | 1 | Y | <---- User press button #2 (index = 1, correct)
|
|
|
|
|
| d | 3 | 2 | |
|
|
|
|
|
+--------+--------+----------+---------+
|
|
|
|
|
UsrAnswer.NumOpt = 2
|
|
|
|
|
UsrAnswer.AnsInd = 1
|
|
|
|
|
*/
|
|
|
|
|
UsrAnswer.NumOpt = ExaEvt_GetParamNumOpt ();
|
|
|
|
|
UsrAnswer.AnsInd = Indexes[UsrAnswer.NumOpt];
|
|
|
|
|
|
|
|
|
|
/***** Get previous student's answer to this question
|
|
|
|
|
(<0 ==> no answer) *****/
|
|
|
|
|
ExaEvt_GetQstAnsFromDB (Event.EvtCod,Gbl.Usrs.Me.UsrDat.UsrCod,Event.Status.QstInd,
|
|
|
|
|
&PreviousUsrAnswer);
|
|
|
|
|
|
|
|
|
|
/***** Store student's answer *****/
|
|
|
|
|
if (UsrAnswer.NumOpt >= 0 &&
|
|
|
|
|
UsrAnswer.AnsInd >= 0 &&
|
|
|
|
|
UsrAnswer.AnsInd != PreviousUsrAnswer.AnsInd)
|
|
|
|
|
DB_QueryREPLACE ("can not register your answer to the exam event question",
|
|
|
|
|
"REPLACE exa_answers"
|
|
|
|
|
" (EvtCod,UsrCod,QstInd,NumOpt,AnsInd)"
|
|
|
|
|
" VALUES"
|
|
|
|
|
" (%ld,%ld,%u,%d,%d)",
|
|
|
|
|
Event.EvtCod,Gbl.Usrs.Me.UsrDat.UsrCod,Event.Status.QstInd,
|
|
|
|
|
UsrAnswer.NumOpt,
|
|
|
|
|
UsrAnswer.AnsInd);
|
|
|
|
|
|
|
|
|
|
/***** Update student's exam event result *****/
|
|
|
|
|
ExaRes_GetExamResultQuestionsFromDB (Event.EvtCod,Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
|
|
|
&Result);
|
|
|
|
|
ExaEvt_ComputeScore (&Result);
|
|
|
|
|
|
|
|
|
|
Str_SetDecimalPointToUS (); // To print the floating point as a dot
|
|
|
|
|
if (DB_QueryCOUNT ("can not get if exam event result exists",
|
|
|
|
|
"SELECT COUNT(*) FROM exa_results"
|
|
|
|
|
" WHERE EvtCod=%ld AND UsrCod=%ld",
|
|
|
|
|
Event.EvtCod,Gbl.Usrs.Me.UsrDat.UsrCod)) // Result exists
|
|
|
|
|
/* Update result */
|
|
|
|
|
DB_QueryUPDATE ("can not update exam event result",
|
|
|
|
|
"UPDATE exa_results"
|
|
|
|
|
" SET EndTime=NOW(),"
|
|
|
|
|
"NumQsts=%u,"
|
|
|
|
|
"NumQstsNotBlank=%u,"
|
|
|
|
|
"Score='%.15lg'"
|
|
|
|
|
" WHERE EvtCod=%ld AND UsrCod=%ld",
|
|
|
|
|
Result.NumQsts,
|
|
|
|
|
Result.NumQstsNotBlank,
|
|
|
|
|
Result.Score,
|
|
|
|
|
Event.EvtCod,Gbl.Usrs.Me.UsrDat.UsrCod);
|
|
|
|
|
else // Result doesn't exist
|
|
|
|
|
/* Create result */
|
|
|
|
|
DB_QueryINSERT ("can not create exam event result",
|
|
|
|
|
"INSERT exa_results "
|
|
|
|
|
"(EvtCod,UsrCod,StartTime,EndTime,NumQsts,NumQstsNotBlank,Score)"
|
|
|
|
|
" VALUES "
|
|
|
|
|
"(%ld," // EvtCod
|
|
|
|
|
"%ld," // UsrCod
|
|
|
|
|
"NOW()," // StartTime
|
|
|
|
|
"NOW()," // EndTime
|
|
|
|
|
"%u," // NumQsts
|
|
|
|
|
"%u," // NumQstsNotBlank
|
|
|
|
|
"'%.15lg')", // Score
|
|
|
|
|
Event.EvtCod,Gbl.Usrs.Me.UsrDat.UsrCod,
|
|
|
|
|
Result.NumQsts,
|
|
|
|
|
Result.NumQstsNotBlank,
|
|
|
|
|
Result.Score);
|
|
|
|
|
Str_SetDecimalPointToLocal (); // Return to local system
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/***** Show current exam event status *****/
|
2020-04-23 02:28:28 +02:00
|
|
|
|
HTM_DIV_Begin ("id=\"exam_event\" class=\"EXA_CONT\"");
|
2020-04-20 01:26:46 +02:00
|
|
|
|
ExaEvt_ShowMatchStatusForStd (&Event,ExaEvt_CHANGE_STATUS_BY_STUDENT);
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********************* Remove answer to exam event question ***********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_RemoveMyAnswerToEventQuestion (const struct ExaEvt_Event *Event)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
DB_QueryDELETE ("can not remove your answer to the exam event question",
|
|
|
|
|
"DELETE FROM exa_answers"
|
|
|
|
|
" WHERE EvtCod=%ld AND UsrCod=%ld AND QstInd=%u",
|
|
|
|
|
Event->EvtCod,Gbl.Usrs.Me.UsrDat.UsrCod,Event->Status.QstInd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/******************** Compute exam event score for a student **********************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-04-22 03:15:04 +02:00
|
|
|
|
static void ExaEvt_ComputeScore (struct TstRes_Result *Result)
|
2020-04-20 01:26:46 +02:00
|
|
|
|
{
|
|
|
|
|
unsigned NumQst;
|
|
|
|
|
struct Tst_Question Question;
|
|
|
|
|
|
|
|
|
|
for (NumQst = 0, Result->Score = 0.0;
|
|
|
|
|
NumQst < Result->NumQsts;
|
|
|
|
|
NumQst++)
|
|
|
|
|
{
|
|
|
|
|
/***** Create test question *****/
|
|
|
|
|
Tst_QstConstructor (&Question);
|
|
|
|
|
Question.QstCod = Result->Questions[NumQst].QstCod;
|
|
|
|
|
Question.Answer.Type = Tst_ANS_UNIQUE_CHOICE;
|
|
|
|
|
|
|
|
|
|
/***** Compute score for this answer ******/
|
2020-04-22 03:15:04 +02:00
|
|
|
|
TstRes_ComputeChoiceAnsScore (Result,NumQst,&Question);
|
2020-04-20 01:26:46 +02:00
|
|
|
|
|
|
|
|
|
/***** Update total score *****/
|
|
|
|
|
Result->Score += Result->Questions[NumQst].Score;
|
|
|
|
|
|
|
|
|
|
/***** Destroy test question *****/
|
|
|
|
|
Tst_QstDestructor (&Question);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/********** Get number of users who answered a question in an exam event ***********/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned ExaEvt_GetNumUsrsWhoAnsweredQst (long EvtCod,unsigned QstInd)
|
|
|
|
|
{
|
|
|
|
|
/***** Get number of users who answered
|
|
|
|
|
a question in an exam event from database *****/
|
|
|
|
|
return
|
|
|
|
|
(unsigned) DB_QueryCOUNT ("can not get number of users who answered a question",
|
|
|
|
|
"SELECT COUNT(*) FROM exa_answers"
|
|
|
|
|
" WHERE EvtCod=%ld AND QstInd=%u",
|
|
|
|
|
EvtCod,QstInd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/** Get number of users who have chosen a given answer of an exam question ***/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
unsigned ExaEvt_GetNumUsrsWhoHaveChosenAns (long EvtCod,unsigned QstInd,unsigned AnsInd)
|
|
|
|
|
{
|
|
|
|
|
/***** Get number of users who have chosen
|
|
|
|
|
an answer of a question from database *****/
|
|
|
|
|
return
|
|
|
|
|
(unsigned) DB_QueryCOUNT ("can not get number of users who have chosen an answer",
|
|
|
|
|
"SELECT COUNT(*) FROM exa_answers"
|
|
|
|
|
" WHERE EvtCod=%ld AND QstInd=%u AND AnsInd=%u",
|
|
|
|
|
EvtCod,QstInd,AnsInd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/**** Get number of users who have answered any question in an exam event ****/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static unsigned ExaEvt_GetNumUsrsWhoHaveAnswerEvt (long EvtCod)
|
|
|
|
|
{
|
|
|
|
|
/***** Get number of users who have answered
|
|
|
|
|
any question in exam event from database *****/
|
|
|
|
|
return
|
|
|
|
|
(unsigned) DB_QueryCOUNT ("can not get number of users who have answered an exam event",
|
|
|
|
|
"SELECT COUNT(DISTINCT UsrCod) FROM exa_answers"
|
|
|
|
|
" WHERE EvtCod=%ld",
|
|
|
|
|
EvtCod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/***************** Draw a bar with the percentage of answers *****************/
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#define ExaEvt_MAX_BAR_WIDTH 100
|
|
|
|
|
|
|
|
|
|
void ExaEvt_DrawBarNumUsrs (unsigned NumRespondersAns,unsigned NumRespondersQst,bool Correct)
|
|
|
|
|
{
|
|
|
|
|
extern const char *Txt_of_PART_OF_A_TOTAL;
|
|
|
|
|
unsigned i;
|
|
|
|
|
unsigned BarWidth = 0;
|
|
|
|
|
|
|
|
|
|
/***** Start container *****/
|
|
|
|
|
HTM_DIV_Begin ("class=\"EXA_RESULT\"");
|
|
|
|
|
|
|
|
|
|
/***** Draw bar with a with proportional to the number of clicks *****/
|
|
|
|
|
if (NumRespondersAns && NumRespondersQst)
|
|
|
|
|
BarWidth = (unsigned) ((((double) NumRespondersAns * (double) ExaEvt_MAX_BAR_WIDTH) /
|
|
|
|
|
(double) NumRespondersQst) + 0.5);
|
|
|
|
|
|
|
|
|
|
/***** Bar proportional to number of users *****/
|
|
|
|
|
HTM_TABLE_BeginWide ();
|
|
|
|
|
HTM_TR_Begin ("class=\"EXA_RES_TR\"");
|
|
|
|
|
for (i = 0;
|
|
|
|
|
i < 100;
|
|
|
|
|
i++)
|
|
|
|
|
{
|
|
|
|
|
HTM_TD_Begin ("class=\"%s\"",
|
|
|
|
|
(i < BarWidth) ? (Correct ? "EXA_RES_CORRECT" :
|
|
|
|
|
"EXA_RES_WRONG") :
|
|
|
|
|
"EXA_RES_VOID");
|
|
|
|
|
HTM_TD_End ();
|
|
|
|
|
}
|
|
|
|
|
HTM_TR_End ();
|
|
|
|
|
HTM_TABLE_End ();
|
|
|
|
|
|
|
|
|
|
/***** Write the number of users *****/
|
|
|
|
|
if (NumRespondersAns && NumRespondersQst)
|
|
|
|
|
HTM_TxtF ("%u (%u%% %s %u)",
|
|
|
|
|
NumRespondersAns,
|
|
|
|
|
(unsigned) ((((double) NumRespondersAns * 100.0) / (double) NumRespondersQst) + 0.5),
|
|
|
|
|
Txt_of_PART_OF_A_TOTAL,NumRespondersQst);
|
|
|
|
|
else
|
|
|
|
|
HTM_NBSP ();
|
|
|
|
|
|
|
|
|
|
/***** End container *****/
|
|
|
|
|
HTM_DIV_End ();
|
|
|
|
|
}
|