This commit was generated by cvs2svn to compensate for changes in r8625,
which included commits to RCS files with non-trunk default branches. git-svn-id: svn://10.65.10.50/trunk@8626 c028cbd2-c16b-5b4b-a496-9718f37d4682
This commit is contained in:
parent
8fa72f5d1a
commit
3dbdc66142
BIN
webcgi/eurocampo/documentazione.doc
Executable file
BIN
webcgi/eurocampo/documentazione.doc
Executable file
Binary file not shown.
135
webcgi/formazione/README
Executable file
135
webcgi/formazione/README
Executable file
@ -0,0 +1,135 @@
|
|||||||
|
Software di gestione moduli didattici per la formazione a distanza ver. 0.1
|
||||||
|
==================================================================
|
||||||
|
Copyright Aga informatica 1999.
|
||||||
|
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
----------
|
||||||
|
Le seguenti note sono solo indicative e vanno prese così come sono; ovvero
|
||||||
|
come promemoria. Quando avrò un poco più di tempo provvederò a farne una
|
||||||
|
stesura completa ed esauriente, con tanto di discussione della logica dei
|
||||||
|
programmi, per la gioia ed il gaudio di tutti. Per ora accontentatevi...
|
||||||
|
Teoricamente potrebbe essere possibile compilarlo anche sotto NT, basta avere
|
||||||
|
gcc. Al posto di postgres si dovrebbe adattare il sorgente creando un piccolo
|
||||||
|
middleware per utilizzare SQL server od un DBMS generico.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
NOTE
|
||||||
|
----
|
||||||
|
Per l'installazione del software cambiare l'install directory presente nel
|
||||||
|
Makefile e quindi dare il comando make install come root.
|
||||||
|
|
||||||
|
Per l'installazione dei tracciati, dopo aver installato Postgres 6.3,
|
||||||
|
creare il database corsi:
|
||||||
|
|
||||||
|
createdb corsi
|
||||||
|
|
||||||
|
creare le tabelle:
|
||||||
|
|
||||||
|
psql -e corsi < tracciato
|
||||||
|
|
||||||
|
impostare eventuali opzioni di sicurezza/accessi esterni in pg_hba.conf e garantire l'accesso
|
||||||
|
all'utente nobody, o comunque a quello per mezzo del quale vengono eseguiti
|
||||||
|
i CGI dall'http server tramite lo statement SQL:
|
||||||
|
|
||||||
|
GRANT ALL ON <tabelle> TO PUBLIC;
|
||||||
|
|
||||||
|
La struttura dei direttori deve essere siffatta:
|
||||||
|
|
||||||
|
|
||||||
|
<Root directory HTTP>---/corsi/----/cgi-bin/
|
||||||
|
|
|
||||||
|
|-/corso_<x1>
|
||||||
|
|
|
||||||
|
|-/corso_<x2>
|
||||||
|
|
|
||||||
|
|-/corso_<x3>
|
||||||
|
|
||||||
|
Ogni direttorio del corso deve avere un direttorio documenti ed eventualmente
|
||||||
|
un direttorio upload. Tali direttori devono essere protetti in lettura, ovvero
|
||||||
|
non possono essere letti da chiunque se non dal gruppo appartenente al personale
|
||||||
|
di servizio.
|
||||||
|
Il direttorio cgi-bin invece, oltre ad essere configurato dal server http come
|
||||||
|
direttorio di esecuzione CGI, deve avere l'accesso possibile oltre che dal personale
|
||||||
|
di servizio, anche dai membri dei corsi.
|
||||||
|
In cgi-bin andrà anche installato il direttorio HyperNews debitamente configurato.
|
||||||
|
Ricordarsi di compilare per ogni utente anche il corso del quale fa parte. Tale
|
||||||
|
indicazione deve essere uguale ai vari direttori dei corsi <x1>, <x2> o <x3> etc.
|
||||||
|
All'interno del direttorio documenti vi sono tanti direttori quanti i moduli
|
||||||
|
validi per il corso; ognuno è contraddistinto dal suo numero registrato sul
|
||||||
|
database con una "M" davanti. All'interno di ognuno di questi direttori, M1,M2,
|
||||||
|
M3 etc, vi sarà la pagina iniziale (index.htm) di presentazione e riferimento
|
||||||
|
ai vari materiali. I materiali presenti in tale direttorio possono essere files
|
||||||
|
di qualsiasi tipo. Infine oltre ai materiali vi sono anche i test necessari
|
||||||
|
per l'ingresso e passaggio da una unità didattica all'altra: test_a.htm, test_b.htm
|
||||||
|
test_c.htm... test_z.htm, dove test_a.htm è quello obbligatorio per l'ingresso
|
||||||
|
e test_z.htm è quello per verificare la conoscenza finale del modulo.o
|
||||||
|
|
||||||
|
|
||||||
|
<Root directory HTTP>--/corsi/--/corso_<x1>/upload/
|
||||||
|
|
|
||||||
|
/documenti/
|
||||||
|
|
|
||||||
|
|-/M1/..index.htm, materiali, test
|
||||||
|
|
|
||||||
|
|-/M2/
|
||||||
|
|
|
||||||
|
|-/M3/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
I riferimenti alle class-room per i singoli moduli devono avere lo stesso
|
||||||
|
nome col quale il modulo stesso viene registrato nel database. Fare attenzione
|
||||||
|
perche' la cosa e' case sensitive.
|
||||||
|
|
||||||
|
TODO
|
||||||
|
----
|
||||||
|
|
||||||
|
*) Possibilità di gestione utente tutore, in modo che possa accedere liberamente
|
||||||
|
ad ogni corso, ad ogni modulo, ad ogni classroom, ad ogni test senza memorizzarne
|
||||||
|
risultati etc. Questa modifica è collegata alla memorizzazione delle informazioni
|
||||||
|
relative ad ogni singolo corso. E' da decidere se ci vuole una tabella in più
|
||||||
|
(corsi, con nome e descrizione) e dove mettere le informazioni di appartenenza
|
||||||
|
al corso: sul modulo o sull'utente. Se si mettono sull'utente è necessario
|
||||||
|
trovare un modo, nel caso del tutor, affinchè si possa ricostruire il path
|
||||||
|
completo del modulo. Probabilmente si dovrà unificare tutti i direttori documenti
|
||||||
|
dei vari corsi, e tener distinti i moduli, visto che ognuno è cmq diverso.
|
||||||
|
In tal modo probabilmente si avrà un unico direttorio "documenti" sotto "corsi"
|
||||||
|
e non tanti ognuno sotto corso_<xxx>, facilitando la composizione del path del
|
||||||
|
file da reperire, siano essi tests o materiali. In effetti visto che i moduli
|
||||||
|
sono n, non vi è motivo di tenerli separati per corso. Per coerenza l'unica
|
||||||
|
cosa che si può implementare è un campo in più sulla tabella moduli nel
|
||||||
|
quale evidenziare di quale corso fa parte ma sarebbe una informazione ridondante.
|
||||||
|
|
||||||
|
*) Interfaccia NSAPI per Netscape Fasttrack, in modo che l'autenticazione sia effettuata
|
||||||
|
controllando il database utenti postgres. Così facendo si evitano duplicazioni
|
||||||
|
di user-ID. Ovvio che tale interfaccia NSAPI, sarà valida solo per Fasttrack
|
||||||
|
server. Nel caso di altro http server, ad esempio Apache, la cosa è da studiare.
|
||||||
|
|
||||||
|
*) Verificare le potenzialità di HyperNews ed eventualmente cercare una message board
|
||||||
|
più flessibile per i nostri scopi. Attualmente il sistema di autenticazione
|
||||||
|
manuale incapsulato all'interno non è un gran ché; fare in modo che utilizzi
|
||||||
|
quello del browser. In genere per HyperNews, basta impostare un unico utente,
|
||||||
|
il creatore dei forum ed amministratore del sito.
|
||||||
|
|
||||||
|
*) Implementare un miglior metodo per far sì che i client non mettano in cache
|
||||||
|
i risultati dei vari cgi. Attualmente l'header expires non funziona molto bene,
|
||||||
|
infatti quando si torna indietro col browser, dice sempre che il documento
|
||||||
|
è spirato. Analogamente se si decide di stampare una qualsiasi pagina. Forse
|
||||||
|
tramite i cookies...
|
||||||
|
|
||||||
|
*) Maggior parametrizzazione per l'installazione, in modo da fare un bell'RPM
|
||||||
|
cosicchè si possa vendere il prodotto!
|
||||||
|
|
||||||
|
*) Maggior documentazione... ma questo si sapeva già...
|
||||||
|
|
||||||
|
*) Se possibile trovare un modo per togliere i pulsanti di azione dalle pagine
|
||||||
|
di ogni modulo (quelli per scaricare le lezioni, accedere ai tests ecc) e
|
||||||
|
sostituirli con hyperlink. Il problema è che si tratta di forms che spediscono
|
||||||
|
col metodo POST. Forse implementando una riga unica con i paramateri necessari
|
||||||
|
ci si può riuscire.
|
||||||
|
|
||||||
|
|
||||||
|
--Angelo
|
||||||
|
|
56
webcgi/formazione/applicat.cpp
Executable file
56
webcgi/formazione/applicat.cpp
Executable file
@ -0,0 +1,56 @@
|
|||||||
|
#include "applicat.h"
|
||||||
|
|
||||||
|
|
||||||
|
void Application::print_content()
|
||||||
|
{
|
||||||
|
cout << "Content-type: text/html" << endl ;
|
||||||
|
// Force expiring action, since these are CGI outputs...
|
||||||
|
// Guess this date...
|
||||||
|
cout << "Expires: Tue, 10 Nov 1992 00:00:00 GMT" << endl << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::print_bin_content()
|
||||||
|
{
|
||||||
|
cout << "Content-type: application/octet-stream" << endl << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::print_database_error()
|
||||||
|
{
|
||||||
|
cout << "<BR><BR><H1>Errore sul database</H1><BR><BR>" << endl;
|
||||||
|
cout << "<P>Connessione al database fallita.</P><BR>" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::print_header(const char* title)
|
||||||
|
{
|
||||||
|
print_content();
|
||||||
|
cout << "<HTML>" << endl;
|
||||||
|
cout << "<HEAD>" << endl;
|
||||||
|
cout << "<TITLE>" << title << "</TITLE>" << endl;
|
||||||
|
cout << "<link rel=\"stylesheet\" href=\"/def_style.css\" type=\"text/css\">" << endl;
|
||||||
|
cout << "</HEAD>" << endl;
|
||||||
|
cout << "<BODY>" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::print_footer()
|
||||||
|
{
|
||||||
|
cout << "<BR><BR>" << endl;
|
||||||
|
//cout << "Torna <A HREF=\"javascript:history.back(1)\">indietro</A>" << endl;
|
||||||
|
cout << "</BODY></HTML>" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::run(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
__argc = argc;
|
||||||
|
__argv = (const char**)argv;
|
||||||
|
if (create())
|
||||||
|
main_func();
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
37
webcgi/formazione/applicat.h
Executable file
37
webcgi/formazione/applicat.h
Executable file
@ -0,0 +1,37 @@
|
|||||||
|
// Libreria generica per applicazioni CGI della formazione a distanza
|
||||||
|
|
||||||
|
// Applicazione generica
|
||||||
|
////////////////////////
|
||||||
|
|
||||||
|
#include "defines.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Application
|
||||||
|
{
|
||||||
|
int __argc;
|
||||||
|
const char** __argv;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool create() { return TRUE; }
|
||||||
|
virtual bool destroy() { return TRUE; }
|
||||||
|
virtual void main_func() {};
|
||||||
|
virtual void print_content();
|
||||||
|
virtual void print_bin_content();
|
||||||
|
virtual void print_database_error();
|
||||||
|
virtual void print_header(const char* title);
|
||||||
|
virtual void print_footer();
|
||||||
|
public:
|
||||||
|
int argc() const { return __argc; }
|
||||||
|
const char** argv() const { return __argv; }
|
||||||
|
const char* argv(int i) const { return __argv[i]; }
|
||||||
|
|
||||||
|
virtual void run(int argc, char* argv[]);
|
||||||
|
Application() {};
|
||||||
|
virtual ~Application() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
319
webcgi/formazione/check_answers.cpp
Executable file
319
webcgi/formazione/check_answers.cpp
Executable file
@ -0,0 +1,319 @@
|
|||||||
|
// check_answers.cgi: applicazione per controllare i punteggi e memorizzare le risposte
|
||||||
|
// dei test.
|
||||||
|
|
||||||
|
#include <libpq++.h>
|
||||||
|
#include "applicat.h"
|
||||||
|
#include "questionnaire.h"
|
||||||
|
|
||||||
|
class Check_answers_Application : public Application
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
String _dbname;
|
||||||
|
String _utente; // Utente corrente
|
||||||
|
String _modulo; // Numero del modulo del quale controllare le risposte
|
||||||
|
String _testnum; // Numero del test del quale controllare le risposte
|
||||||
|
PgEnv _environment;
|
||||||
|
bool _blank_test;
|
||||||
|
String _user_answers;
|
||||||
|
Questionnaire _questionario;
|
||||||
|
PgTransaction *_db;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool create();
|
||||||
|
virtual bool destroy();
|
||||||
|
virtual void main_func();
|
||||||
|
void check_answers();
|
||||||
|
void print_access_error();
|
||||||
|
bool load_corrector();
|
||||||
|
public:
|
||||||
|
Check_answers_Application() {_db = NULL;}
|
||||||
|
virtual ~Check_answers_Application() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bool Check_answers_Application::create()
|
||||||
|
{
|
||||||
|
String separator, ws;
|
||||||
|
String qs[3];
|
||||||
|
char answers[MAXQUESTIONS][MAXANSWERS];
|
||||||
|
|
||||||
|
for (int i=0; i<MAXQUESTIONS; i++)
|
||||||
|
for (int j=0; j<MAXANSWERS; j++)
|
||||||
|
answers[i][j] = 'F';
|
||||||
|
|
||||||
|
_user_answers = "";
|
||||||
|
separator = "_";
|
||||||
|
_dbname = POSTGRES_DB;
|
||||||
|
_environment.Port(POSTGRES_PORT);
|
||||||
|
_environment.Host(POSTGRES_HOST);
|
||||||
|
print_header("Controllo punteggi");
|
||||||
|
_utente = getenv("REMOTE_USER");
|
||||||
|
_blank_test = FALSE;
|
||||||
|
char *t1, *t2;
|
||||||
|
char *ccc = getenv("CONTENT_LENGTH");
|
||||||
|
int cl = ccc != NULL ? atoi(ccc) : 0;
|
||||||
|
|
||||||
|
#ifdef DBG
|
||||||
|
cout << "<BR>Lunghezza del buffer: " << cl << "<BR>" << endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (cl < 8192) { // Evita di ricevere troppa roba sullo stdin (8kb sono abbastanza??)
|
||||||
|
for (int x = 0; cl && (!feof(stdin)); x++) {
|
||||||
|
t1 = fmakeword(stdin, '&', &cl);
|
||||||
|
t2 = makeword(t1, '=');
|
||||||
|
unescape_url(t1);
|
||||||
|
unescape_url(t2);
|
||||||
|
if (!strcmp(t2,"MODULO"))
|
||||||
|
_modulo = t1;
|
||||||
|
if (!strcmp(t2,"TESTNUM"))
|
||||||
|
_testnum = t1;
|
||||||
|
if (!strcmp(t2,"BLANK_TEST"))
|
||||||
|
_blank_test = !strcmp(t1,"ON");
|
||||||
|
// Carica le risposte fornite
|
||||||
|
if (t2[0] == 'Q') { // Si tratta di una risposta del test; formato Q_<numero_domanda>_<numero_risposta>
|
||||||
|
// Estrae il numero relativo alla domanda ed il numero della risposta
|
||||||
|
ws = t2;
|
||||||
|
const int e = split(ws, qs, 3, separator);
|
||||||
|
if (e == 3) {
|
||||||
|
const int qnum = atoi(qs[1]) - 1; // riferimento alla domanda
|
||||||
|
const int anum = atoi(qs[2]) - 1; // riferimento alla risposta
|
||||||
|
if (qnum < MAXQUESTIONS && anum < MAXANSWERS)
|
||||||
|
if (!strcmp(t1,"ON"))
|
||||||
|
answers[qnum][anum] = 'V';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Compone _user_answers
|
||||||
|
for (int i=0; i<MAXQUESTIONS; i++) {
|
||||||
|
_user_answers += "Q_";
|
||||||
|
_user_answers += itoa(i+1);
|
||||||
|
_user_answers += "=";
|
||||||
|
for (int j=0; j<MAXANSWERS; j++)
|
||||||
|
_user_answers += answers[i][j];
|
||||||
|
_user_answers += ";";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Debug stuff, useful in many wayz
|
||||||
|
#ifdef DBG
|
||||||
|
cout << "<H2>Risposte fornite</H2><BR><BR>" << endl;
|
||||||
|
for (int j = 0; j < MAXQUESTIONS; j++) {
|
||||||
|
cout << "Domanda " << j+1 << ":<BR>";
|
||||||
|
for (int k = 0; k < MAXANSWERS; k++)
|
||||||
|
if (answers[j][k] == 'V')
|
||||||
|
cout << "VERO ";
|
||||||
|
else
|
||||||
|
cout << "FALSO ";
|
||||||
|
cout << "<BR>" << endl;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cout << "<H2>Troppi dati inviati</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>L'applicazione ha ricevuto troppi dati sul buffer d'ingresso.</P><BR>" << endl;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Check_answers_Application::destroy()
|
||||||
|
{
|
||||||
|
print_footer();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Check_answers_Application::load_corrector()
|
||||||
|
{
|
||||||
|
bool loaded = FALSE;
|
||||||
|
String command, correct_answers, separator;
|
||||||
|
|
||||||
|
separator = ";";
|
||||||
|
command = "SELECT * FROM CORRETTORI WHERE modulo=";
|
||||||
|
command += _modulo;
|
||||||
|
command += " AND testnum='";
|
||||||
|
command += _testnum;
|
||||||
|
command += "'";
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
const int tuples = _db->Tuples();
|
||||||
|
|
||||||
|
|
||||||
|
if (tuples > 0) {
|
||||||
|
correct_answers = _db->GetValue(0, "risposte");
|
||||||
|
loaded = _questionario.load(correct_answers, _user_answers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Check_answers_Application::check_answers()
|
||||||
|
{
|
||||||
|
if (!load_corrector()) {
|
||||||
|
cout << "<H2>Correttore inesistente</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>Impossibile caricare il correttore per il test immesso.</P>";
|
||||||
|
cout << "<P>Rivolgersi al docente della lezione odierna.</P>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 of theze dayz I'll fall to pieces anywayz
|
||||||
|
String command, verifiche, corso;
|
||||||
|
// Compone la stringa di selezione utente.
|
||||||
|
command = "SELECT * FROM UTENTI WHERE loginname='";
|
||||||
|
command += _utente;
|
||||||
|
command += "' AND logged='t'";
|
||||||
|
// Esegue il comando SQL, dovrebbe venir ritornata una sola tupla
|
||||||
|
// al limite se ne vengono ritornate piu' di una verranno ignorate
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
const int tuples = _db->Tuples();
|
||||||
|
if (tuples > 0) {
|
||||||
|
// Aggiorna lo stato dei test effettuati per modulo sulla tabella UTENTI
|
||||||
|
verifiche = _db->GetValue(0, "verifiche");
|
||||||
|
corso = _db->GetValue(0, "course");
|
||||||
|
trim(corso);
|
||||||
|
|
||||||
|
const unsigned int mod_int = atoi(_modulo);
|
||||||
|
const unsigned int l = verifiche.length();
|
||||||
|
if ((l < mod_int || verifiche[mod_int-1] != _testnum[0]) && mod_int <= MAXMODULES) {
|
||||||
|
if (l< mod_int)
|
||||||
|
for (unsigned int j=l; j < mod_int; j++)
|
||||||
|
verifiche += ' ';
|
||||||
|
verifiche[mod_int-1] = _testnum[0];
|
||||||
|
command = "UPDATE UTENTI SET verifiche='";
|
||||||
|
command += verifiche;
|
||||||
|
command += "' WHERE loginname='";
|
||||||
|
command += _utente;
|
||||||
|
command += "'";
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
|
||||||
|
// Controlla i punteggi inseriti nella matrice con quelli memorizzati nel correttore
|
||||||
|
// Compone le risposte fornite da memorizzare sulla tabella VERIFICHE in un formato più "umano"
|
||||||
|
Rational punteggio;
|
||||||
|
punteggio = _blank_test ? ZERO : _questionario.calc_score();
|
||||||
|
|
||||||
|
long progressivo = 0L;
|
||||||
|
// Blocca la tabella dei progressivi e prende il progressivo
|
||||||
|
command = "LOCK PROGRESSIVI";
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
|
||||||
|
command = "SELECT * FROM PROGRESSIVI";
|
||||||
|
if (_db->ExecTuplesOk(command) && _db->Tuples() > 0){
|
||||||
|
progressivo = atol(_db->GetValue(0, "progverifiche"));
|
||||||
|
}
|
||||||
|
else { // Se non c'è nessuna riga viene aggiunta
|
||||||
|
command = "INSERT INTO PROGRESSIVI VALUES(0,0)";
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
}
|
||||||
|
progressivo++;
|
||||||
|
command = "UPDATE PROGRESSIVI SET progverifiche=progverifiche+1"; // Incrementa il progressivo
|
||||||
|
_db->ExecCommandOk(command); // Aggiorna la tabella PROGRESSIVI
|
||||||
|
// Memorizza il punteggio e le risposte fornite sulla tabella VERIFICHE
|
||||||
|
command = "INSERT INTO VERIFICHE VALUES (";
|
||||||
|
command += ltoa(progressivo);
|
||||||
|
command += ",'";
|
||||||
|
command += _utente;
|
||||||
|
command += "',current_timestamp,";
|
||||||
|
command += _modulo;
|
||||||
|
command += ",'";
|
||||||
|
command += _testnum;
|
||||||
|
command += "','";
|
||||||
|
if (_blank_test)
|
||||||
|
command += " " ;
|
||||||
|
else
|
||||||
|
command += _user_answers; // Risposte fornite in formato "umano"
|
||||||
|
command += "',";
|
||||||
|
command += dtoa((double) punteggio); // Punteggio sottoforma di double
|
||||||
|
command += ")";
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
|
||||||
|
|
||||||
|
cout << "<H2>Risultato del test</H2><BR><BR>" << endl;
|
||||||
|
if (!_blank_test)
|
||||||
|
_questionario.dump_html(corso, atoi(_modulo), _testnum[0]);
|
||||||
|
else
|
||||||
|
cout << "<P>Test consegnato <STRONG> in bianco </STRONG><BR><BR><BR>";
|
||||||
|
|
||||||
|
cout << "<TABLE><TR><TD><FORM METHOD=\"POST\" ACTION=";
|
||||||
|
cout << GET_MODULE_CGI << ">";
|
||||||
|
cout << "Cliccate sull'icona qui sotto per ritornare all'area formativa del modulo.";
|
||||||
|
cout << "<BR><BR><INPUT TYPE=HIDDEN NAME=\"MODULO\" VALUE=\"" << _modulo << "\">";
|
||||||
|
cout << "<INPUT TYPE=\"IMAGE\" SRC=\"/images/area_" << mod_int;
|
||||||
|
cout << ".gif\" ALIGN=top ALT=\"Accesso al modulo\" BORDER=0>";
|
||||||
|
cout << "</FORM></TD></TR></TABLE></P>" << endl;
|
||||||
|
|
||||||
|
}
|
||||||
|
else { // Se il test è già stato eseguito, nega l'aggiornamento il calcolo punteggio e tutto il resto
|
||||||
|
cout << "<H2>Test già eseguito</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>Il test selezionato risulta già eseguito.</P>";
|
||||||
|
cout << "<P>Si prega di riselezionare l'area corretta dalla pagina di <A HREF=\"";
|
||||||
|
cout << LOGIN_CGI << "\">selezione moduli.</A></P>";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else { // Se non trova i dati dell'utente indicato
|
||||||
|
// visualizza la mancanza di informazioni per completare la pagina.
|
||||||
|
cout << "<H2>Mancanza di informazioni</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>Impossibile reperire le informazioni relative al percorso formativo dell'utente " << _utente;
|
||||||
|
cout << ".</P><P>Controllare l'esattezza delle informazioni inserite.</P>";
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Check_answers_Application::print_access_error()
|
||||||
|
{
|
||||||
|
|
||||||
|
cout << "<H2>Errore di accesso</H2><BR><BR>";
|
||||||
|
cout << "<P>Utilizzare la normale procedura di <A HREF=\"";
|
||||||
|
cout << LOGIN_CGI << "\">accesso</A> ai corsi.</P><BR>";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Check_answers_Application::main_func()
|
||||||
|
{
|
||||||
|
// Controllo utente: se il CGI viene chiamato senza tutte le indicazioni necessarie
|
||||||
|
// visualizza l'errore
|
||||||
|
if (_utente.empty() || _modulo.empty() || _testnum.empty() || _user_answers.empty()) {
|
||||||
|
print_access_error();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Se tutto è OK...
|
||||||
|
// Inizia la transazione
|
||||||
|
_db = new PgTransaction(_environment, _dbname);
|
||||||
|
if ( _db->ConnectionBad() )
|
||||||
|
print_database_error();
|
||||||
|
else
|
||||||
|
check_answers();
|
||||||
|
delete _db; // Termina la transazione
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
Check_answers_Application* a = new Check_answers_Application();
|
||||||
|
|
||||||
|
a->run(argc, argv);
|
||||||
|
|
||||||
|
delete a;
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
76
webcgi/formazione/check_module_user.pl
Executable file
76
webcgi/formazione/check_module_user.pl
Executable file
@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/perl -T
|
||||||
|
|
||||||
|
$dbname = 'corsi';
|
||||||
|
$dbhost = 'localhost';
|
||||||
|
|
||||||
|
use Pg;
|
||||||
|
|
||||||
|
if (check_user("utente_fad", "Excel-base") eq 0) {
|
||||||
|
print "Can't find it\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print "Found it\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
print get_course("utente_fad");
|
||||||
|
|
||||||
|
sub get_course {
|
||||||
|
my $retval;
|
||||||
|
my $loginname=$_[0];
|
||||||
|
|
||||||
|
$retval = "no_course";
|
||||||
|
|
||||||
|
# Compose query string and connect
|
||||||
|
|
||||||
|
$commandos = "SELECT * FROM UTENTI WHERE loginname='";
|
||||||
|
$commandos = "$commandos$loginname'";
|
||||||
|
|
||||||
|
$conn = Pg::connectdb("dbname=$dbname host=$dbhost");
|
||||||
|
|
||||||
|
if ($conn->status == PGRES_CONNECTION_OK) {
|
||||||
|
$result = $conn->exec($commandos);
|
||||||
|
# Did you find anyone?
|
||||||
|
if ($result->ntuples > 0) {
|
||||||
|
$retval = $result->getvalue(0,$result->fnumber("course"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$retval =~ tr/ //d;
|
||||||
|
$retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub check_user {
|
||||||
|
my $commandos;
|
||||||
|
my $modules;
|
||||||
|
my $ret_val;
|
||||||
|
my $loginname= $_[0];
|
||||||
|
my $modulename= $_[1];
|
||||||
|
|
||||||
|
$ret_val = 0;
|
||||||
|
|
||||||
|
# Compose query string and connect
|
||||||
|
|
||||||
|
$commandos = "SELECT * FROM UTENTI WHERE loginname='";
|
||||||
|
$commandos = "$commandos$loginname'";
|
||||||
|
|
||||||
|
$conn = Pg::connectdb("dbname=$dbname host=$dbhost");
|
||||||
|
|
||||||
|
if ($conn->status == PGRES_CONNECTION_OK) {
|
||||||
|
$result = $conn->exec($commandos);
|
||||||
|
# Did you find anyone?
|
||||||
|
if ($result->ntuples > 0) {
|
||||||
|
$modules = $result->getvalue(0,$result->fnumber("modules"));
|
||||||
|
$commandos = "SELECT * FROM MODULI WHERE modulename='";
|
||||||
|
# Search for $module_name and fetch its number
|
||||||
|
$commandos = "$commandos$modulename'";
|
||||||
|
$result = $conn->exec($commandos);
|
||||||
|
if ($result->ntuples > 0) {
|
||||||
|
$mod_num = $result->getvalue(0,$result->fnumber("modulenum"));
|
||||||
|
# Now check if $mod_num is enabled for this $loginname
|
||||||
|
$ret_val = 1 if (substr($modules, $mod_num-1,1) eq "X");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret_val;
|
||||||
|
}
|
52
webcgi/formazione/defines.h
Executable file
52
webcgi/formazione/defines.h
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
#ifndef _DEFINES_H
|
||||||
|
#define _DEFINES_H
|
||||||
|
|
||||||
|
#include <fstream.h>
|
||||||
|
#include <bool.h>
|
||||||
|
#include <String.h>
|
||||||
|
#include <Rational.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define POSTGRES_HOST "localhost"
|
||||||
|
#define POSTGRES_PORT ""
|
||||||
|
#define POSTGRES_DB "corsi"
|
||||||
|
#define FAD_DIR "/corsi/"
|
||||||
|
#define FAD_ROOT "/disk2/html/corsi/corso_"
|
||||||
|
#define FAD_CGI FAD_DIR"cgi-bin/"
|
||||||
|
#define GET_MODULE_CGI FAD_CGI"get_module.cgi"
|
||||||
|
#define LOGIN_CGI FAD_CGI"login.cgi"
|
||||||
|
#define LOGOUT_CGI FAD_CGI"logout.cgi"
|
||||||
|
#define CHECK_ANSWERS_CGI FAD_CGI"check_answers.cgi"
|
||||||
|
#define MAXMODULES 64
|
||||||
|
#define MAXQUESTIONS 20
|
||||||
|
#define MAXANSWERS 5
|
||||||
|
#define ERROR_FILE "__error" // File fittizio per segnalare gli errori
|
||||||
|
|
||||||
|
static Rational ZERO(0);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
void getword(char *word, char *line, char stop);
|
||||||
|
char *makeword(char *line, char stop);
|
||||||
|
char *fmakeword(FILE *f, char stop, int *cl);
|
||||||
|
char x2c(char *what);
|
||||||
|
void unescape_url(char *url);
|
||||||
|
void plustospace(char *str);
|
||||||
|
int rind(char *s, char c);
|
||||||
|
int getline(char *s, int n, FILE *f);
|
||||||
|
void send_fd(FILE *f, FILE *fd);
|
||||||
|
off_t fsize(const char* n);
|
||||||
|
char * itoa(int);
|
||||||
|
char * ltoa(long);
|
||||||
|
char * dtoa(double);
|
||||||
|
void trim(String& s);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
319
webcgi/formazione/get_module.cpp
Executable file
319
webcgi/formazione/get_module.cpp
Executable file
@ -0,0 +1,319 @@
|
|||||||
|
// get_module.cgi: applicazione per reperire la pagina (costruita dal docente)
|
||||||
|
// relativa al modulo passato. Tale pagina rimane fuori dall'albero del sito WWW
|
||||||
|
// Serve inoltre per ritornare le lezioni da scaricare ed i test da eseguire.
|
||||||
|
// Il comportamento del programma viene determinato in base alla presenza o meno
|
||||||
|
// di variabili passate sullo stdin:
|
||||||
|
// - se è presente solo la variabile MODULO allora introduce nella pagina iniziale del modulo
|
||||||
|
// - se sono presenti sia la variabile MODULO che TESTNUM allora ritorna il test indicato per tale modulo (eseguendo controlli se il test è già stato eseguito
|
||||||
|
// - se sono presenti MODULO TESTNUM e LEZIONE ritorna la lezione indicata, controllando il livello di test necessario per scaricarla
|
||||||
|
|
||||||
|
#include <libpq++.h>
|
||||||
|
#include "applicat.h"
|
||||||
|
|
||||||
|
|
||||||
|
enum behaviour { normal, get_test, get_lesson };
|
||||||
|
|
||||||
|
class Get_module_Application : public Application
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
String _dbname;
|
||||||
|
String _utente; // Utente corrente
|
||||||
|
String _modulo; // Modulo
|
||||||
|
String _testnum; // Livello del test
|
||||||
|
String _lezione; // Nome della lezione
|
||||||
|
PgEnv _environment;
|
||||||
|
PgTransaction *_db;
|
||||||
|
behaviour _what; // Comportamento: reperisce l'indice del modulo, i test o le lezioni
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool create();
|
||||||
|
virtual bool destroy();
|
||||||
|
virtual void main_func();
|
||||||
|
virtual void print_header(const char* title);
|
||||||
|
void print_access_error();
|
||||||
|
void get_module();
|
||||||
|
public:
|
||||||
|
Get_module_Application() {_db = NULL;}
|
||||||
|
virtual ~Get_module_Application() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Get_module_Application::create()
|
||||||
|
{
|
||||||
|
|
||||||
|
_dbname = POSTGRES_DB;
|
||||||
|
_environment.Port(POSTGRES_PORT);
|
||||||
|
_environment.Host(POSTGRES_HOST);
|
||||||
|
_utente = getenv("REMOTE_USER");
|
||||||
|
_modulo = "";
|
||||||
|
_what = normal;
|
||||||
|
char *t1, *t2;
|
||||||
|
char *ccc = getenv("CONTENT_LENGTH");
|
||||||
|
int cl = ccc != NULL ? atoi(ccc) : 0;
|
||||||
|
if (cl < 512) { // Evita di ricevere troppa roba sullo stdin
|
||||||
|
for (int x = 0; cl && (!feof(stdin)); x++) {
|
||||||
|
t1 = fmakeword(stdin, '&', &cl);
|
||||||
|
t2 = makeword(t1, '=');
|
||||||
|
unescape_url(t1);
|
||||||
|
unescape_url(t2);
|
||||||
|
if (!strcmp(t2,"MODULO"))
|
||||||
|
_modulo = t1;
|
||||||
|
if (!strcmp(t2,"TESTNUM"))
|
||||||
|
_testnum = t1;
|
||||||
|
if (!strcmp(t2,"LEZIONE"))
|
||||||
|
_lezione = t1;
|
||||||
|
}
|
||||||
|
if (!_testnum.empty())
|
||||||
|
_what = get_test;
|
||||||
|
if (!_lezione.empty())
|
||||||
|
_what = get_lesson;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print_header("Troppi dati inviati");
|
||||||
|
cout << "<H2>Troppi dati inviati</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>L'applicazione ha ricevuto troppi dati sul buffer d'ingresso.</P><BR>" << endl;
|
||||||
|
print_footer();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Get_module_Application::destroy()
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Get_module_Application::print_header(const char * title)
|
||||||
|
{
|
||||||
|
print_content();
|
||||||
|
cout << "<HTML>" << endl;
|
||||||
|
cout << "<HEAD>" << endl;
|
||||||
|
cout << "<TITLE>" << title << "</TITLE>" << endl;
|
||||||
|
cout << "<link rel=\"stylesheet\" href=\"/def_style.css\" type=\"text/css\">" << endl;
|
||||||
|
cout << "</HEAD>" << endl;
|
||||||
|
cout << "<BODY>" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Get_module_Application::print_access_error()
|
||||||
|
{
|
||||||
|
print_header("Errore di accesso");
|
||||||
|
cout << "<H2>Errore di accesso</H2><BR><BR>";
|
||||||
|
cout << "<P>Utilizzare la normale procedura di <A HREF=\"";
|
||||||
|
cout << LOGIN_CGI << "\">accesso</A> ai corsi.</P><BR>";
|
||||||
|
print_footer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Get_module_Application::get_module()
|
||||||
|
{
|
||||||
|
// Another one bites the dust...
|
||||||
|
String command;
|
||||||
|
// Compone la stringa di selezione utente: nome utente & logged
|
||||||
|
command = "SELECT * FROM UTENTI WHERE loginname='";
|
||||||
|
command += _utente;
|
||||||
|
command += "' AND logged='t'";
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
const int tuples = _db->Tuples();
|
||||||
|
if (tuples > 0) { // E' loggato oppure no...
|
||||||
|
// Cosa deve fare:
|
||||||
|
// Verifica che il modulo passato sia effettivamente attivo per l'utente indicato
|
||||||
|
String moduli;
|
||||||
|
String corso;
|
||||||
|
moduli = _db->GetValue(0, "modules");
|
||||||
|
corso = _db->GetValue(0, "course");
|
||||||
|
trim(corso);
|
||||||
|
const unsigned int int_mod = atoi(_modulo)-1;
|
||||||
|
if (moduli.length() >= int_mod && moduli[int_mod] == 'X' && int_mod <= MAXMODULES -1) {
|
||||||
|
// Se si tratta del primo accesso al modulo, spedisce il questionario di inizio modulo
|
||||||
|
// Legge da FAD_ROOT il documento indicato da _modulo e lo restituisce sullo stdout così com'è
|
||||||
|
String filename, base_path;
|
||||||
|
FILE * html_page;
|
||||||
|
// La pagina base di ogni modulo sarà in FAD_ROOT/M<modulo>/index.htm
|
||||||
|
// Il test di inizio modulo sarà in FAD_ROOT/M<modulo>/test_a.htm
|
||||||
|
|
||||||
|
const char * sv = _db->GetValue(0, "verifiche");
|
||||||
|
const char level = strlen(sv) > int_mod ? sv[int_mod] : ' ';
|
||||||
|
const bool first_test = (level == ' ');
|
||||||
|
base_path = FAD_ROOT;
|
||||||
|
base_path += corso;
|
||||||
|
base_path += "/documenti/M";
|
||||||
|
base_path += _modulo;
|
||||||
|
base_path += "/";
|
||||||
|
filename = base_path;
|
||||||
|
|
||||||
|
switch (_what) {
|
||||||
|
case normal:
|
||||||
|
if (first_test) {
|
||||||
|
command = "SELECT * FROM MODULI WHERE modulenum=";
|
||||||
|
command += _modulo;
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
bool faked = 0;
|
||||||
|
if (_db->Tuples() > 0)
|
||||||
|
faked = _db->GetValue(0,"faked")[0] == 't';
|
||||||
|
if (faked)
|
||||||
|
filename += "index.htm";
|
||||||
|
else
|
||||||
|
filename += "test_a.htm";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
filename += "index.htm";
|
||||||
|
break;
|
||||||
|
case get_test:
|
||||||
|
if (level+1 == _testnum[0] || _testnum == "z") { // Caso speciale per l'ultimo test (z)
|
||||||
|
filename += "test_";
|
||||||
|
filename += _testnum;
|
||||||
|
filename += ".htm";
|
||||||
|
// Last test Handling: controlla qual'è l'ultimo test possibile per questo modulo
|
||||||
|
if (_testnum == "z") {
|
||||||
|
command = "SELECT * FROM MODULI WHERE modulenum=";
|
||||||
|
command += _modulo;
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
if (_db->Tuples() > 0) { // Modulo presente?
|
||||||
|
const char * last_test = _db->GetValue(0,"lasttest");
|
||||||
|
if (last_test[0] != level) // Se non ha raggiunto l'ultimo test possibile, nega l'accesso all'ultimo test
|
||||||
|
filename = ERROR_FILE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
filename = ERROR_FILE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
filename = ERROR_FILE; // File inesistente per segnalare l'errore di livello sul test richiesto
|
||||||
|
break;
|
||||||
|
case get_lesson: // Reperisce la lezione indicata
|
||||||
|
if (level >= _testnum[0]) {
|
||||||
|
filename += _lezione;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
filename = ERROR_FILE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
html_page = fopen(filename, "r");
|
||||||
|
if (html_page != NULL) {
|
||||||
|
// Save some system resources, since sending the lessons may take some time
|
||||||
|
// So it seems to be quite reasonable freeing semaphores and shared memory allocated by Postgres connections.
|
||||||
|
delete _db;
|
||||||
|
_db = NULL;
|
||||||
|
|
||||||
|
const off_t sz = fsize(filename);
|
||||||
|
if (sz > 0)
|
||||||
|
cout << "Content-length: " << sz << endl;
|
||||||
|
if (_what == get_lesson) {
|
||||||
|
print_bin_content(); // Necessario per inviare files binari
|
||||||
|
}
|
||||||
|
else
|
||||||
|
print_content(); // Necessario per i files testo/html
|
||||||
|
send_fd(html_page, stdout);
|
||||||
|
fclose(html_page);
|
||||||
|
}
|
||||||
|
else { // Se non trova la risorsa da inviare, visualizza l'errore
|
||||||
|
switch (_what) {
|
||||||
|
case normal:
|
||||||
|
print_header(first_test ? "Questionario iniziale non implementato" : "Modulo non implementato");
|
||||||
|
if (first_test){
|
||||||
|
cout << "<H2>Questionario iniziale non implementato</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>Il questionario iniziale per il modulo selezionato non è presente.</P><BR>";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cout << "<H2>Modulo non implementato</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>I materiali didattici per il modulo selezionato non sono presenti.</P><BR>";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case get_test:
|
||||||
|
if (filename == ERROR_FILE) {
|
||||||
|
print_header("Accesso al livello di test non consentito");
|
||||||
|
cout << "<H2>Accesso al livello di test non consentito</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>L'utente <STRONG>" << _utente << "</STRONG> non ha i permessi necessari per accedere al livello di test richiesto oppure il test è già stato svolto.</P><BR>" << endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print_header("Questionario non ancora implementato");
|
||||||
|
cout << "<H2>Questionario non ancora implementato</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>Il questionario per il modulo selezionato non è presente.</P><BR>";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case get_lesson:
|
||||||
|
if (filename == ERROR_FILE) {
|
||||||
|
print_header("Accesso alla lezione non consentito");
|
||||||
|
cout << "<H2>Accesso alla lezione non consentito</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>L'utente <STRONG>" << _utente << "</STRONG> non ha i permesso necessari per accedere al livello di lezione richiesto.</P><BR>" << endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print_header("Lezione non ancora implementata");
|
||||||
|
cout << "<H2>Lezione non ancora implementata</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>La lezione per il modulo selezionato non è presente.</P><BR>";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
print_footer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print_header("Accesso al modulo non consentito");
|
||||||
|
cout << "<H2>Accesso al modulo non consentito</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>L'utente <STRONG>"<< _utente << "</STRONG> non ha i permessi necessari per accedere al modulo indicato.</P><BR>";
|
||||||
|
print_footer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Se non trova l'utente indicato oppure non era loggato
|
||||||
|
// visualizza ugualmente un messaggio ingannatore
|
||||||
|
print_access_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Get_module_Application::main_func()
|
||||||
|
{
|
||||||
|
// Controllo utente: se il CGI viene chiamato senza indicazioni d'utente e/o modulo
|
||||||
|
// rimanda al normale login
|
||||||
|
if (_utente.empty() || _modulo.empty()) {
|
||||||
|
print_access_error();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Se e' stato impostato l'utente, prende il documento indicato dal numero di modulo,
|
||||||
|
// non prima di aver controllato che l'utente sia loggato!
|
||||||
|
// Inizia la transazione
|
||||||
|
_db = new PgTransaction(_environment, _dbname);
|
||||||
|
if ( _db->ConnectionBad() ) {
|
||||||
|
print_header("Errore sul database");
|
||||||
|
print_database_error();
|
||||||
|
print_footer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
get_module();
|
||||||
|
if (_db != NULL)
|
||||||
|
delete _db; // Termina la transazione
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
Get_module_Application* a = new Get_module_Application();
|
||||||
|
|
||||||
|
a->run(argc, argv);
|
||||||
|
|
||||||
|
delete a;
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
72
webcgi/formazione/html_parser.cpp
Executable file
72
webcgi/formazione/html_parser.cpp
Executable file
@ -0,0 +1,72 @@
|
|||||||
|
// Applicazione di prova per la classe Questionnaire
|
||||||
|
|
||||||
|
#include "applicat.h"
|
||||||
|
#include "questionnaire.h"
|
||||||
|
|
||||||
|
|
||||||
|
class Parsing_Application : public Application
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool create();
|
||||||
|
virtual bool destroy();
|
||||||
|
virtual void main_func();
|
||||||
|
public:
|
||||||
|
Parsing_Application() {}
|
||||||
|
virtual ~Parsing_Application() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bool Parsing_Application::create()
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Parsing_Application::destroy()
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parsing_Application::main_func()
|
||||||
|
{
|
||||||
|
Questionnaire q;
|
||||||
|
|
||||||
|
String s1,s2,s3;
|
||||||
|
s1 = "Q_1=VVVF;Q_2=FVFV;Q_3=VVVV;Q_4=VVFF;Q_5=VFFF;";
|
||||||
|
s2 = "Q_1=VVFFF;Q_2=FFFVF;Q_3=VVVVF;Q_4=VVFFF;Q_5=VFFFF;";
|
||||||
|
s3 = "volare";
|
||||||
|
|
||||||
|
q.load(s1,s2);
|
||||||
|
q.dump_html(s3, 1, 'a');
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
Parsing_Application* a = new Parsing_Application();
|
||||||
|
|
||||||
|
a->run(argc, argv);
|
||||||
|
|
||||||
|
delete a;
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
193
webcgi/formazione/login.cpp
Executable file
193
webcgi/formazione/login.cpp
Executable file
@ -0,0 +1,193 @@
|
|||||||
|
// login.cgi: applicazione per registrare l'ingresso nell'area
|
||||||
|
// dei corsi. Modifica il template con i dati personali
|
||||||
|
// dell'utente (percorso formativo con moduli disponibili)
|
||||||
|
|
||||||
|
#include <libpq++.h>
|
||||||
|
#include "applicat.h"
|
||||||
|
|
||||||
|
class Login_Application : public Application
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
String _dbname;
|
||||||
|
String _utente; // Utente corrente
|
||||||
|
String _realname;
|
||||||
|
String _moduli; // Moduli attivi per l'utente corrente
|
||||||
|
PgEnv _environment;
|
||||||
|
PgTransaction *_db;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool create();
|
||||||
|
virtual bool destroy();
|
||||||
|
virtual void main_func();
|
||||||
|
void login();
|
||||||
|
void print_access_error();
|
||||||
|
public:
|
||||||
|
Login_Application() {_db = NULL;}
|
||||||
|
virtual ~Login_Application() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
void Login_Application::print_access_error()
|
||||||
|
{
|
||||||
|
cout << "<CENTER><H2>Accesso al corso</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>Si prega di effettuare regolarmente l'accesso al corso, fornendo le indicazioni<BR>";
|
||||||
|
cout << "di <B><I>utente</I></B> e <B><I>password</I></B> in vostro possesso.</P><BR>" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Login_Application::create()
|
||||||
|
{
|
||||||
|
|
||||||
|
_dbname = POSTGRES_DB;
|
||||||
|
_environment.Port(POSTGRES_PORT);
|
||||||
|
_environment.Host(POSTGRES_HOST);
|
||||||
|
print_header("Accesso all'area corsi");
|
||||||
|
_utente = getenv("REMOTE_USER");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Login_Application::destroy()
|
||||||
|
{
|
||||||
|
print_footer();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Login_Application::login()
|
||||||
|
{
|
||||||
|
// Controlla se l'utente fornito è' un utente valido
|
||||||
|
String command;
|
||||||
|
// Compone la stringa di selezione utente.
|
||||||
|
command = "SELECT * FROM UTENTI WHERE loginname='";
|
||||||
|
command += _utente;
|
||||||
|
command += "'";
|
||||||
|
// Esegue il comando SQL, dovrebbe venir ritornata una sola tupla
|
||||||
|
// al limite se ne vengono ritornate piu' di una verranno ignorate
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
const int tuples = _db->Tuples();
|
||||||
|
if (tuples > 0) {
|
||||||
|
// Cosa deve fare:
|
||||||
|
// Sistema le informazioni di logging dell'utente (anche sulla tabella ACCESSI)
|
||||||
|
// Carica il percorso formativo (campo modules) e compone la pagina
|
||||||
|
const bool logged = (_db->GetValue(0, "logged"))[0] == 't';
|
||||||
|
_realname = _db->GetValue(0, "realname");
|
||||||
|
_moduli = _db->GetValue(0, "modules");
|
||||||
|
// Aggiorna le informazioni di logging
|
||||||
|
if (!logged){
|
||||||
|
// Prende il progressivo per il logging sulla tabella accessi
|
||||||
|
long progressivo = 0L;
|
||||||
|
command = "LOCK PROGRESSIVI"; // Blocca la tabell all'interno di questa transazione per evitare sovrapposizioni
|
||||||
|
_db->ExecCommandOk(command); // Verrà automaticamente sbloccata al termine della transazione
|
||||||
|
command = "SELECT * FROM PROGRESSIVI"; // In caso il CGI si pianti la transazione viene abortita dal server, e di fatto non verrà fatto nessun aggiornamento alle tabelle
|
||||||
|
if (_db->ExecTuplesOk(command) && _db->Tuples() > 0){
|
||||||
|
progressivo = atol(_db->GetValue(0, "progaccessi"));
|
||||||
|
}
|
||||||
|
else { // Se non c'è nessuna riga viene aggiunta
|
||||||
|
command = "INSERT INTO PROGRESSIVI VALUES(0,0)";
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
}
|
||||||
|
progressivo++;
|
||||||
|
command = "UPDATE PROGRESSIVI SET progaccessi=progaccessi+1"; // Incrementa il progressivo
|
||||||
|
_db->ExecCommandOk(command); // Aggiorna la tabella relativa
|
||||||
|
command = "UPDATE UTENTI SET logged=1, logindate=current_timestamp, ";
|
||||||
|
command += "progaccesso=";
|
||||||
|
command += ltoa(progressivo);
|
||||||
|
command += " WHERE loginname='";
|
||||||
|
command += _utente;
|
||||||
|
command += "'";
|
||||||
|
_db->ExecCommandOk(command); // Aggiorna la tabella utenti
|
||||||
|
command = "INSERT INTO ACCESSI VALUES (";
|
||||||
|
command += ltoa(progressivo);
|
||||||
|
command += ",'";
|
||||||
|
command += _utente;
|
||||||
|
command += "',current_timestamp, null)";
|
||||||
|
_db->ExecCommandOk(command); // Aggiunge un record alla tabella accessi
|
||||||
|
}
|
||||||
|
// Seleziona i moduli abilitati
|
||||||
|
command = "SELECT * FROM MODULI ORDER BY modulenum";
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
cout << "<H2>Percorso formativo</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>Benvenuto/a, <STRONG>" << _realname << "</STRONG>.</P>" << endl;
|
||||||
|
cout << "<P>Ecco l'elenco dei moduli disponibili, clicca sull'immagine relativa al modulo per entrare nell'area di formazione prescelta. Si ricorda che i moduli contraddistinti da una piccola spirale fanno parte delle lezioni avanzate.</P>" << endl;
|
||||||
|
const int nt = _db->Tuples();
|
||||||
|
if (nt > 0) {
|
||||||
|
// Seleziona i moduli abilitati per costui
|
||||||
|
cout << "<BR><CENTER><TABLE WIDTH=50% CELLPADDING=4>" << endl;
|
||||||
|
int curr_module=0;
|
||||||
|
for (int i=0; i<nt;i++) {
|
||||||
|
const unsigned int mod_num = atoi(_db->GetValue(i, "modulenum"))-1;
|
||||||
|
if (_moduli.length() > mod_num+1 && _moduli[mod_num] == 'X') { // Se il modulo e' abilitato visualizza
|
||||||
|
const bool new_row = curr_module % 2 == 0;
|
||||||
|
curr_module++;
|
||||||
|
if (new_row)
|
||||||
|
cout << "<TR>" << endl;
|
||||||
|
cout << "<TD><FORM METHOD=\"POST\" ACTION=";
|
||||||
|
cout << GET_MODULE_CGI << ">";
|
||||||
|
cout << "<INPUT TYPE=HIDDEN NAME=\"MODULO\" VALUE=\"" << mod_num + 1 << "\">";
|
||||||
|
cout << "<INPUT TYPE=\"IMAGE\" SRC=\"/images/area_";
|
||||||
|
cout << mod_num+1 << ".gif\" ALIGN=top ALT=\"Accesso al modulo ";
|
||||||
|
cout << _db->GetValue(i, "modulename") << "\" BORDER=0>";
|
||||||
|
cout << "</FORM></TD>" << endl;
|
||||||
|
if (!new_row || i == nt - 1) // Se non è nuova riga o è l'ultimo elemento
|
||||||
|
cout << "</TR>" << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cout << "</TABLE></CENTER>" << endl;
|
||||||
|
}
|
||||||
|
cout << "<BR><BR>" << endl;
|
||||||
|
}
|
||||||
|
else { // Se non trova i dati dell'utente indicato (con il percorso formativo)
|
||||||
|
// visualizza la mancanza di informazioni per completare la pagina.
|
||||||
|
cout << "<H2>Mancanza di informazioni</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>Impossibile reperire le informazioni relative al percorso formativo dell'utente <STRONG>" << _utente << "</STRONG>.</P>";
|
||||||
|
cout << "<P>Controllare l'esattezza delle informazioni inserite.</P><BR>";
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Login_Application::main_func()
|
||||||
|
{
|
||||||
|
// Controllo utente: se il CGI viene chiamato senza indicazioni d'utente (impossibile o quasi)
|
||||||
|
// Allora mostra un avvertimento
|
||||||
|
if (_utente.empty()) {
|
||||||
|
print_access_error();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Se e' stato impostato l'utente, effettua la login()
|
||||||
|
// Inizia la transazione
|
||||||
|
_db = new PgTransaction(_environment, _dbname);
|
||||||
|
if ( _db->ConnectionBad() )
|
||||||
|
print_database_error();
|
||||||
|
else
|
||||||
|
login();
|
||||||
|
delete _db; // Termina la transazione
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
Login_Application* a = new Login_Application();
|
||||||
|
|
||||||
|
a->run(argc, argv);
|
||||||
|
|
||||||
|
delete a;
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
127
webcgi/formazione/logout.cpp
Executable file
127
webcgi/formazione/logout.cpp
Executable file
@ -0,0 +1,127 @@
|
|||||||
|
// logout.cgi: applicazione per registrare l'uscita dall'area
|
||||||
|
// dei corsi.
|
||||||
|
|
||||||
|
#include <libpq++.h>
|
||||||
|
#include "applicat.h"
|
||||||
|
|
||||||
|
class Logout_Application : public Application
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
String _dbname;
|
||||||
|
String _utente; // Utente corrente
|
||||||
|
PgEnv _environment;
|
||||||
|
PgTransaction *_db;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool create();
|
||||||
|
virtual bool destroy();
|
||||||
|
virtual void main_func();
|
||||||
|
void logout();
|
||||||
|
public:
|
||||||
|
Logout_Application() {_db = NULL;}
|
||||||
|
virtual ~Logout_Application() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Logout_Application::create()
|
||||||
|
{
|
||||||
|
|
||||||
|
_dbname = POSTGRES_DB;
|
||||||
|
_environment.Port(POSTGRES_PORT);
|
||||||
|
_environment.Host(POSTGRES_HOST);
|
||||||
|
print_header("Uscita dall'area corsi");
|
||||||
|
_utente = getenv("REMOTE_USER");;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Logout_Application::destroy()
|
||||||
|
{
|
||||||
|
print_footer();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logout_Application::logout()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
String command;
|
||||||
|
// Compone la stringa di selezione utente: nome utente & logged altrimenti nisba logout!
|
||||||
|
command = "SELECT * FROM UTENTI WHERE loginname='";
|
||||||
|
command += _utente;
|
||||||
|
command += "' AND logged='t'";
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
const int tuples = _db->Tuples();
|
||||||
|
if (tuples > 0) { // E' loggato oppure no...
|
||||||
|
// Cosa deve fare:
|
||||||
|
// Sistema le informazioni di logging dell'utente (anche sulla tabella ACCESSI)
|
||||||
|
String progressivo;
|
||||||
|
progressivo = _db->GetValue(0, "progaccesso");
|
||||||
|
command = "UPDATE UTENTI SET logged=0, logindate=null ";
|
||||||
|
command += " WHERE loginname='";
|
||||||
|
command += _utente;
|
||||||
|
command += "'";
|
||||||
|
_db->ExecCommandOk(command); // Aggiorna la tabella utenti
|
||||||
|
command = "UPDATE ACCESSI SET logoutdate=current_timestamp WHERE loginname='";
|
||||||
|
command += _utente;
|
||||||
|
command += "' AND progressivo=";
|
||||||
|
command += progressivo;
|
||||||
|
_db->ExecCommandOk(command); // Aggiorna la tabella accessi
|
||||||
|
}
|
||||||
|
// Se non trova l'utente indicato oppure non era loggato
|
||||||
|
// visualizza ugualmente un messaggio ingannatoreà
|
||||||
|
cout << "<H2>Uscita dall'area corsi</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>Uscita dall'area corsi effettuata con successo.</P><BR>";
|
||||||
|
cout << "<P>Le informazioni necessarie sono state registrate.</P><BR>";
|
||||||
|
cout << "<P><B>Grazie.</B></P><BR>";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logout_Application::main_func()
|
||||||
|
{
|
||||||
|
// Controllo utente: se il CGI viene chiamato senza indicazioni d'utente
|
||||||
|
// non mostra proprio nulla (null logout)
|
||||||
|
if (_utente.empty()) {
|
||||||
|
cout << "<H2>Uscita dall'area corsi</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>Uscita dall'area corsi effettuata con successo.</P><BR>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Se e' stato impostato l'utente, effettua la logout()
|
||||||
|
// Inizia la transazione
|
||||||
|
_db = new PgTransaction(_environment, _dbname);
|
||||||
|
if ( _db->ConnectionBad() )
|
||||||
|
print_database_error();
|
||||||
|
else
|
||||||
|
logout();
|
||||||
|
delete _db; // Termina la transazione
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
Logout_Application* a = new Logout_Application();
|
||||||
|
|
||||||
|
a->run(argc, argv);
|
||||||
|
|
||||||
|
delete a;
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
73
webcgi/formazione/makefile
Executable file
73
webcgi/formazione/makefile
Executable file
@ -0,0 +1,73 @@
|
|||||||
|
# Makefile for compiling CGI-FAD software
|
||||||
|
|
||||||
|
# Decommentare il seguente per includere le informazioni di debug
|
||||||
|
#CFLAGS=-Wall -g
|
||||||
|
|
||||||
|
|
||||||
|
# Usare il seguente per non includere le informazioni di debug
|
||||||
|
CFLAGS=-Wall -O2 -fomit-frame-pointer
|
||||||
|
PGINCLUDE=/usr/local/pgsql/include
|
||||||
|
PGLIB=/usr/local/pgsql/lib
|
||||||
|
INSTALLDIR=/disk2/html/corsi/cgi-bin
|
||||||
|
|
||||||
|
all: login.cgi logout.cgi get_module.cgi check_answers.cgi report.cgi html_parser score_corrector
|
||||||
|
|
||||||
|
install: all
|
||||||
|
install -m 750 -o nobody -g nobody -s login.cgi $(INSTALLDIR)
|
||||||
|
install -m 750 -o nobody -g nobody -s logout.cgi $(INSTALLDIR)
|
||||||
|
install -m 750 -o nobody -g nobody -s get_module.cgi $(INSTALLDIR)
|
||||||
|
install -m 750 -o nobody -g nobody -s check_answers.cgi $(INSTALLDIR)
|
||||||
|
install -m 750 -o nobody -g nobody -s report.cgi $(INSTALLDIR)
|
||||||
|
|
||||||
|
util.o: util.cpp
|
||||||
|
gcc $(CFLAGS) -c util.cpp
|
||||||
|
|
||||||
|
applicat.o: applicat.cpp applicat.h
|
||||||
|
g++ $(CFLAGS) -c applicat.cpp
|
||||||
|
|
||||||
|
login.o: login.cpp applicat.h
|
||||||
|
g++ $(CFLAGS) -c login.cpp -I$(PGINCLUDE)
|
||||||
|
|
||||||
|
login.cgi: login.o applicat.o util.o
|
||||||
|
g++ $(CFLAGS) -o login.cgi login.o applicat.o util.o -lpq -lpq++ -L$(PGLIB)
|
||||||
|
|
||||||
|
logout.o: logout.cpp applicat.h
|
||||||
|
g++ $(CFLAGS) -c logout.cpp -I$(PGINCLUDE)
|
||||||
|
|
||||||
|
logout.cgi: logout.o applicat.o util.o
|
||||||
|
g++ $(CFLAGS) -o logout.cgi logout.o applicat.o util.o -lpq -lpq++ -L$(PGLIB)
|
||||||
|
|
||||||
|
get_module.o: get_module.cpp applicat.h
|
||||||
|
g++ $(CFLAGS) -c get_module.cpp -I$(PGINCLUDE)
|
||||||
|
|
||||||
|
get_module.cgi: get_module.o applicat.o util.o
|
||||||
|
g++ $(CFLAGS) -o get_module.cgi get_module.o applicat.o util.o -lpq -lpq++ -L$(PGLIB)
|
||||||
|
|
||||||
|
check_answers.o: check_answers.cpp applicat.h
|
||||||
|
g++ $(CFLAGS) -c check_answers.cpp -I$(PGINCLUDE)
|
||||||
|
|
||||||
|
check_answers.cgi: check_answers.o applicat.o util.o questionnaire.o
|
||||||
|
g++ $(CFLAGS) -o check_answers.cgi check_answers.o applicat.o questionnaire.o util.o -lpq -lpq++ -L$(PGLIB)
|
||||||
|
|
||||||
|
report.o: report.cpp applicat.h
|
||||||
|
g++ $(CFLAGS) -c report.cpp -I$(PGINCLUDE)
|
||||||
|
|
||||||
|
report.cgi: report.o applicat.o util.o questionnaire.o
|
||||||
|
g++ $(CFLAGS) -o report.cgi report.o applicat.o questionnaire.o util.o -lpq -lpq++ -L$(PGLIB)
|
||||||
|
|
||||||
|
html_parser.o: html_parser.cpp applicat.h
|
||||||
|
g++ $(CFLAGS) -c html_parser.cpp
|
||||||
|
|
||||||
|
score_corrector.o: score_corrector.cpp applicat.h
|
||||||
|
g++ $(CFLAGS) -c score_corrector.cpp -I$(PGINCLUDE)
|
||||||
|
|
||||||
|
questionnaire.o: questionnaire.cpp questionnaire.h
|
||||||
|
g++ $(CFLAGS) -c questionnaire.cpp
|
||||||
|
|
||||||
|
html_parser: html_parser.o applicat.o questionnaire.o util.o
|
||||||
|
g++ $(CFLAGS) -o html_parser html_parser.o applicat.o questionnaire.o util.o
|
||||||
|
|
||||||
|
score_corrector: score_corrector.o applicat.o questionnaire.o util.o
|
||||||
|
g++ $(CFLAGS) -o score_corrector score_corrector.o applicat.o questionnaire.o util.o -lpq -lpq++ -L$(PGLIB)
|
||||||
|
|
||||||
|
|
393
webcgi/formazione/questionnaire.cpp
Executable file
393
webcgi/formazione/questionnaire.cpp
Executable file
@ -0,0 +1,393 @@
|
|||||||
|
// Classe per la gestione/parsing questionari
|
||||||
|
#include "questionnaire.h"
|
||||||
|
|
||||||
|
// In alcuni casi la parentesi finale non viene messa perchè possono esistere forme del tipo:
|
||||||
|
// <FORM method= ..> oppure <p align ...> oppure <input type=...>
|
||||||
|
#define INPUT_TAG "<INPUT"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const char* tag_list[] = { "<TITLE>",
|
||||||
|
"</TITLE>",
|
||||||
|
"<FORM",
|
||||||
|
"</FORM",
|
||||||
|
"<P",
|
||||||
|
"</P>",
|
||||||
|
"<INPUT",
|
||||||
|
"<TABLE",
|
||||||
|
"</TABLE>",
|
||||||
|
"<DIV",
|
||||||
|
"</DIV>",
|
||||||
|
NULL };
|
||||||
|
|
||||||
|
// Implementazione metodi della classe
|
||||||
|
|
||||||
|
void Questionnaire::reset()
|
||||||
|
{
|
||||||
|
int i,j; // Ma porca Eva, si può sapere perchè le specifiche ANSI vietano di definire variabili nel' inizializzazione di un for!?!?!?
|
||||||
|
|
||||||
|
_title = "";
|
||||||
|
_loaded = _parsed = _computed = FALSE;
|
||||||
|
_score = ZERO;
|
||||||
|
_max_score = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < MAXQUESTIONS; i++)
|
||||||
|
{
|
||||||
|
Question_struct& qq = _questions[i];
|
||||||
|
qq.text = ""; // Azzera il testo della domanda
|
||||||
|
for (j = 0 ; j < MAXANSWERS; j++)
|
||||||
|
{
|
||||||
|
Answer_struct& aa = qq.answers[j];
|
||||||
|
aa.text = ""; // Azzera il testo della risposta
|
||||||
|
aa.user_answer = 'F';
|
||||||
|
aa.corrector_answer = 'F';
|
||||||
|
aa.p = ZERO; // Azzera il peso di questo item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abbozzo di semplice parser html ad uso e consumo AGA, per modificare
|
||||||
|
// gli html dei questionari "al volo"
|
||||||
|
|
||||||
|
void Questionnaire::parse_html(const char* name)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
bool in_form, in_paragraph, in_table;
|
||||||
|
bracket_state bstate = none;
|
||||||
|
String current_string_tag, paragraph;
|
||||||
|
valid_tags current_tag = no_tag;
|
||||||
|
short progressivo_domande, progressivo_risposte;
|
||||||
|
|
||||||
|
progressivo_domande = progressivo_risposte = 0;
|
||||||
|
|
||||||
|
//reset();
|
||||||
|
|
||||||
|
ifstream html(name);
|
||||||
|
|
||||||
|
if (html.bad()) {
|
||||||
|
cout << "<p>Errore: impossibile leggere il file: " << name << "</p>" << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
in_table = in_form = in_paragraph = FALSE;
|
||||||
|
|
||||||
|
// 2 parse or not 2 parse... This is the question...
|
||||||
|
// Note: this is a VERY VERY simple parser, designed only to find questions
|
||||||
|
// and answers in html files like test_a.htm.
|
||||||
|
|
||||||
|
// Si suppone che il file html sia sintatticamente corretto.
|
||||||
|
|
||||||
|
while (!html.eof())
|
||||||
|
{
|
||||||
|
html.get(c);
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '<': bstate = open_bracket; break;
|
||||||
|
case '>': if (bstate == open_bracket) bstate = close_bracket; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bstate != none) // Compose current tag
|
||||||
|
current_string_tag += c;
|
||||||
|
|
||||||
|
if (bstate == open_bracket)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bstate == close_bracket)
|
||||||
|
{
|
||||||
|
// Traduce il tag da lettera a numero.
|
||||||
|
// Se non lo trova, tiene l'ultimo valido.
|
||||||
|
current_string_tag.upcase();
|
||||||
|
short i;
|
||||||
|
for ( i = 0; tag_list[i] != NULL; i++)
|
||||||
|
if (current_string_tag.index(tag_list[i]) >= 0) // Tag was recognized.
|
||||||
|
break;
|
||||||
|
|
||||||
|
i++; // Il primo elemento è no_tag
|
||||||
|
|
||||||
|
// La cosa più giusta sarebbe avere una pila di tag riconosciuti e farne il pop
|
||||||
|
|
||||||
|
if ((valid_tags) i != ignored_tag)
|
||||||
|
current_tag = (valid_tags) i;
|
||||||
|
|
||||||
|
if (current_tag == input_tag && in_paragraph)
|
||||||
|
{
|
||||||
|
paragraph += current_string_tag;
|
||||||
|
current_tag = paragraph_tag_begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_string_tag = "";
|
||||||
|
bstate = none; // Reset bracket state
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Che razza di tag sei?
|
||||||
|
switch (current_tag)
|
||||||
|
{
|
||||||
|
case title_tag_begin: // Match <TITLE>
|
||||||
|
_title += c;
|
||||||
|
break;
|
||||||
|
case title_tag_end: // Match </TITLE>
|
||||||
|
current_tag = no_tag;
|
||||||
|
break;
|
||||||
|
case form_tag_begin: // Match <FORM
|
||||||
|
current_tag = no_tag;
|
||||||
|
in_form = TRUE;
|
||||||
|
in_paragraph = FALSE;
|
||||||
|
break;
|
||||||
|
case form_tag_end: // Match </FORM>
|
||||||
|
current_tag = no_tag;
|
||||||
|
in_form = FALSE;
|
||||||
|
in_paragraph = FALSE;
|
||||||
|
break;
|
||||||
|
case paragraph_tag_begin: // Match <P
|
||||||
|
if (in_form && !in_table) // Ignora i paragrafi che non sono compresi tra <FORM> e </FORM>
|
||||||
|
{
|
||||||
|
paragraph += c; // Compone tutto il paragrafo tra <P> e </P>
|
||||||
|
in_paragraph = TRUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case paragraph_tag_end: // Match </P>
|
||||||
|
if (in_form && !in_table)
|
||||||
|
{
|
||||||
|
current_tag = no_tag;
|
||||||
|
in_paragraph = FALSE;
|
||||||
|
// Controlla se il paragrafo contiene eventuali <INPUT
|
||||||
|
// Per estrarre il testo deve prima togliere tutto ciò che fa parte di <INPUT ....>
|
||||||
|
// se invece non contiene nessun <INPUT allora si tratta del testo della domanda
|
||||||
|
// e lo memorizza così com'è
|
||||||
|
const int start_pos = paragraph.index(INPUT_TAG);
|
||||||
|
if (start_pos >= 0) // <INPUT... >
|
||||||
|
{
|
||||||
|
const int end_pos = paragraph.index('>', start_pos); //
|
||||||
|
paragraph.del(start_pos, end_pos - start_pos + 1);
|
||||||
|
_questions[progressivo_domande].answers[progressivo_risposte++].text = paragraph;
|
||||||
|
// Ogni MAXANSWERS cambia la domanda
|
||||||
|
// Questo tipo di logica è molto debole perchè si basa su
|
||||||
|
// di un numero fisso di possibili risposte (MAXANSWERS)
|
||||||
|
// Quindi è necessario che esse siano sempre in tal numero (MAXANSWERS appunto)
|
||||||
|
|
||||||
|
if (progressivo_risposte == MAXANSWERS)
|
||||||
|
{
|
||||||
|
progressivo_risposte = 0;
|
||||||
|
progressivo_domande++;
|
||||||
|
if (progressivo_domande == MAXQUESTIONS) // Non eccedere MAXQUESTIONS
|
||||||
|
progressivo_domande--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_questions[progressivo_domande].text = paragraph;
|
||||||
|
|
||||||
|
paragraph = "";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case table_tag_begin:
|
||||||
|
in_table = TRUE;
|
||||||
|
break;
|
||||||
|
case table_tag_end:
|
||||||
|
in_table = FALSE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break; // Ignore any other tag...(ignored_tag or no_tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_parsed = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// S1 contiene il correttore, S2 contiene le risposte fornite
|
||||||
|
bool Questionnaire::load(const String& s1, const String& s2)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
|
||||||
|
_loaded = !s1.empty() && !s2.empty();
|
||||||
|
|
||||||
|
if (!_loaded) {
|
||||||
|
cout << "<p>Errore: impossibile caricare il correttore o le risposte fornite</p>" << endl;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
// Il formato delle stringhe da passare in S1 ed S2 (correttore e risposte) è siffatto:
|
||||||
|
// Q_<numero domanda>=<V|F><V|F><V|F><V|F>;Q_<numero domanda>=<V|F><V|F><V|F><V|F>; ...etc
|
||||||
|
// I valori delle risposte possono essre V (vero) oppure F (falso); se indicato un blank
|
||||||
|
// lo si tratta come risposta non presente, ovvero per identificare la fine delle risposte
|
||||||
|
// possibili per ogni domanda;
|
||||||
|
// serve per riconoscere quando fermare il test per ogni risposta data in calc_score().
|
||||||
|
// Esempio: Q_1=VVFV;Q_2=VFFV;Q_3=FFVF
|
||||||
|
// Attenzione, le risposte possibili sono sempre 5, ma nel correttore ne vengono indicate solo 4;
|
||||||
|
// questo perche' la quinta viene presa in considerazione solo nel caso le precedenti
|
||||||
|
// siano tutte V o tutte F. La quinta domanda corrisponde a "nessuna delle precedenti"
|
||||||
|
// In questa funzione si carica la matrice correttore con i punteggi possibili per le
|
||||||
|
// varie risposte fornite; ecco gli esempi possibili con i vari punteggi:
|
||||||
|
// VVVV ---> +1/4, +1/4, +1/4, +1/4, -1
|
||||||
|
// FFFF ---> -1/4, -1/4, -1/4, -1/4, +1
|
||||||
|
// VFFF ---> +1, -1/3, -1/3, -1/3, 0
|
||||||
|
// VVFF ---> +1/2, +1/2, -1/2, -1/2, 0
|
||||||
|
// VVVF ---> +1/3, +1/3, +1/3, -1, 0
|
||||||
|
// Ecc. ecc. per tutte le varie combinazioni possibili. In pratica il concetto è: 1 punto
|
||||||
|
// totale per ogni domanda, tale punto va suddiviso a seconda delle risposte corrette. Nel
|
||||||
|
// caso di risposte tutte sbagliate o tutte giuste entra in gioco anche la domanda nr. 5
|
||||||
|
// Quindi per un questionario con 20 domande avremo un massimo di 20 punti ed un minimo
|
||||||
|
// assoluto di -20.
|
||||||
|
String separator, risposte;
|
||||||
|
String rs[MAXQUESTIONS], qs[2], ns[2];
|
||||||
|
int numr, numi;
|
||||||
|
|
||||||
|
for (int x = 0; x < 2 ; x++) {
|
||||||
|
|
||||||
|
const bool parse_corrector = x == 0;
|
||||||
|
risposte = parse_corrector ? s1 : s2;
|
||||||
|
trim(risposte);
|
||||||
|
separator = ";";
|
||||||
|
|
||||||
|
numr = split(risposte, rs, MAXQUESTIONS, separator);
|
||||||
|
|
||||||
|
for (int i=0; i < numr; i++) {
|
||||||
|
separator = "=";
|
||||||
|
numi = split(rs[i], qs, 2, separator);
|
||||||
|
// qs[0] contiene Q_<numero domanda>
|
||||||
|
// qs[1] contiene VVVV FFFF VFFV etc
|
||||||
|
if (numi == 2) {
|
||||||
|
|
||||||
|
if (parse_corrector)
|
||||||
|
_max_score++;
|
||||||
|
|
||||||
|
separator = "_";
|
||||||
|
split(qs[0],ns,2,separator);
|
||||||
|
const int qn = atoi(ns[1]) -1;
|
||||||
|
String& qs1 = qs[1];
|
||||||
|
qs1.upcase();
|
||||||
|
|
||||||
|
const int r_vere = qs1.freq('V'); // Numero di risposte vere
|
||||||
|
const int r_false = MAXANSWERS - r_vere - 1; // Numero di risposte false
|
||||||
|
Rational p_v(1, r_vere != 0 ? r_vere : 1); // 1/4, 1/3, 1/2, 1
|
||||||
|
Rational p_f(1, r_false != 0 ? r_false : 1); // -1/4, -1/3, -1/2. -1
|
||||||
|
p_f.negate(); // Cambia segno per i punti in negativo
|
||||||
|
|
||||||
|
const int l = qs1.length();
|
||||||
|
|
||||||
|
if (parse_corrector && r_vere == 0)
|
||||||
|
_questions[qn].answers[MAXANSWERS-1].corrector_answer = 'V';
|
||||||
|
|
||||||
|
for (int j = 0; j < MAXANSWERS; j++) {
|
||||||
|
Answer_struct& ans = _questions[qn].answers[j];
|
||||||
|
if (parse_corrector) {
|
||||||
|
if (j<l) {
|
||||||
|
ans.p = qs1[j] == 'V' ? p_v : p_f;
|
||||||
|
ans.corrector_answer = qs1[j]; // Memorizza le risposte del correttore ed il "peso"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (r_vere == 0)
|
||||||
|
ans.p = p_v;
|
||||||
|
else
|
||||||
|
if (r_false == 0)
|
||||||
|
ans.p = p_f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (j<l)
|
||||||
|
ans.user_answer = qs1[j]; // Memorizza le risposte dell'utente
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_loaded = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Debug Stuff
|
||||||
|
#ifdef DBG
|
||||||
|
cout << "<H2>Correttore</H2><BR><BR>" << endl;
|
||||||
|
for (int j = 0; j < MAXQUESTIONS; j++) {
|
||||||
|
cout << "Domanda " << j+1 << ":<BR>";
|
||||||
|
for (int k = 0; k < MAXANSWERS; k++) {
|
||||||
|
cout << _questions[j].answers[k].corrector_answer << "__";
|
||||||
|
//Rational& rr = _questions[j].answers[k].p;
|
||||||
|
//cout << rr.numerator() << "/" << rr.denominator() << ";";
|
||||||
|
}
|
||||||
|
cout << "<BR>" << endl;
|
||||||
|
}
|
||||||
|
_loaded = FALSE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return _loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Questionnaire::dump_html(const String& corso, const int modulo, const char testnum)
|
||||||
|
{
|
||||||
|
if (!_parsed) {
|
||||||
|
String nome;
|
||||||
|
|
||||||
|
nome = FAD_ROOT;
|
||||||
|
nome += corso;
|
||||||
|
nome += "/documenti/M";
|
||||||
|
nome += itoa(modulo);
|
||||||
|
nome += "/test_";
|
||||||
|
nome += testnum;
|
||||||
|
nome += ".htm";
|
||||||
|
parse_html(nome);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_parsed && _loaded)
|
||||||
|
{
|
||||||
|
int i,j;
|
||||||
|
Rational punteggio;
|
||||||
|
|
||||||
|
punteggio = calc_score();
|
||||||
|
cout << "<CENTER>" << endl;
|
||||||
|
cout << "<H2>" << _title << "</H2>" << endl;
|
||||||
|
cout << "</CENTER>" << endl;
|
||||||
|
cout << "<P>Punteggio ottenuto nel test: <STRONG>" << dtoa((double)punteggio) ;
|
||||||
|
cout << " su di un massimo di " << itoa(_max_score) << "</STRONG><BR><BR><BR>" << endl;
|
||||||
|
cout << "<P>Le caselle di sinistra identificano le risposte corrette, quelle di destra le risposte fornite</P>" << endl;
|
||||||
|
cout << "<P>Ogni risposta che differisce dal correttore viene videnziata in rosso</p>" <<endl;
|
||||||
|
cout << "<BR><BR>" << endl;
|
||||||
|
cout << "<FORM>" << endl; // Senza il tag <FORM> non visualizza i checkbox!
|
||||||
|
for (i=0;i<MAXQUESTIONS;i++)
|
||||||
|
if (!_questions[i].text.empty())
|
||||||
|
{
|
||||||
|
cout << "<P><STRONG>" << _questions[i].text << "</STRONG></P>" << endl; // Test della domanda
|
||||||
|
for (j=0;j<MAXANSWERS;j++)
|
||||||
|
{
|
||||||
|
Answer_struct& ans = _questions[i].answers[j];
|
||||||
|
const bool is_right = ans.user_answer == ans.corrector_answer;
|
||||||
|
cout << "<P><INPUT TYPE=\"CHECKBOX\" NAME=\"C_" << i+1 << "_" << j+1;
|
||||||
|
cout << "\" VALUE=\"ON\" ";
|
||||||
|
if (ans.corrector_answer == 'V')
|
||||||
|
cout << "CHECKED ";
|
||||||
|
cout << "onClick=\"return false;\">"; // Risposte del correttore
|
||||||
|
cout << "<INPUT TYPE=\"CHECKBOX\" NAME=\"U_" << i+1 << "_" << j+1;
|
||||||
|
cout << "\" VALUE=\"ON\" ";
|
||||||
|
if (ans.user_answer == 'V')
|
||||||
|
cout << "CHECKED ";
|
||||||
|
cout << "onClick=\"return false;\">"; // Risposte dell'utente
|
||||||
|
if (!is_right) // Se la risposta fornita non corrisponde a quella del correttore, evidenzia in rosso la risposta data.
|
||||||
|
cout << "<font color=\"#FF0000\">";
|
||||||
|
|
||||||
|
cout << _questions[i].answers[j].text;
|
||||||
|
if (!is_right)
|
||||||
|
cout << "</font>";
|
||||||
|
cout << "</P>" << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cout << "</FORM>" << endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cout << "<p>Errore: per eseguire il dump dei tests è necessario caricare il correttore, le risposte e specificare il corso, il modulo ed il numero di test.</p>" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rational Questionnaire::calc_score()
|
||||||
|
{
|
||||||
|
if (_loaded && !_computed)
|
||||||
|
{
|
||||||
|
// Calcolo punteggio: per ogni risposta corrispondente a quella del correttore
|
||||||
|
// si ottiene un punto.
|
||||||
|
_computed = TRUE;
|
||||||
|
for (int i=0; i < MAXQUESTIONS; i++)
|
||||||
|
{
|
||||||
|
for (int j=0; j < MAXANSWERS; j++) {
|
||||||
|
Answer_struct& ans = _questions[i].answers[j];
|
||||||
|
if (((Rational&)ans.p).numerator() != 0)
|
||||||
|
if (ans.user_answer == 'V') // Per ogni risposta vera fornita, aggiunge il punteggio fornito dal correttore
|
||||||
|
_score += ((Rational&) ans.p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _score;
|
||||||
|
}
|
79
webcgi/formazione/questionnaire.h
Executable file
79
webcgi/formazione/questionnaire.h
Executable file
@ -0,0 +1,79 @@
|
|||||||
|
#include "defines.h"
|
||||||
|
|
||||||
|
struct Answer_struct
|
||||||
|
{
|
||||||
|
String text; // Testo dell'item di risposta
|
||||||
|
char user_answer; // Risposta data dall'utente (V o F)
|
||||||
|
char corrector_answer; // Risposta memorizzata nel correttore
|
||||||
|
Rational p; // "Peso" della risposta giusta/sbagliata: 1, 1/4, 1/2, 1/3, -1/3 -1/2 ecc
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Question_struct
|
||||||
|
{
|
||||||
|
String text; // Testo della domanda
|
||||||
|
Answer_struct answers[MAXANSWERS];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Modo di utilizzo:
|
||||||
|
|
||||||
|
Questionnaire q;
|
||||||
|
|
||||||
|
s1 = "Q_1=VFFF;Q_2=VVVV;Q_3=FVFV;..."
|
||||||
|
s2 = "Q_1=FFFVF;Q_2=FVFVF;Q_3=VVFFF;..."
|
||||||
|
|
||||||
|
q.load(s1,s2);
|
||||||
|
|
||||||
|
Il metodo load() azzera il questionario ed è praticamente il primo metodo da
|
||||||
|
invocare; dove s1 è il correttore ed s2 sono le risposte fornite dall'utente.
|
||||||
|
Questo metodo carica il correttore e le risposte fornite, predisponendo il calcolo
|
||||||
|
del punteggio.
|
||||||
|
|
||||||
|
Dopo aver chiamato load() è si possono chiamare i metodi dump_html() o calc_score()
|
||||||
|
indifferentemente l'uno dall'altro.
|
||||||
|
|
||||||
|
|
||||||
|
Rational r = q.calc_score();
|
||||||
|
|
||||||
|
il metodo calc_score() ritorna il punteggio calcolato nel test in base al correttore ed alle
|
||||||
|
risposte fornite in load()
|
||||||
|
|
||||||
|
|
||||||
|
q.dump_html("volare", 10, 'a');
|
||||||
|
|
||||||
|
il metodo dump_html() scrive sullo stdout l'html del test eseguito, contrassegnando
|
||||||
|
con le "X" i checkbox del correttore e delle risposte fornite dall'utente, evidenziando in
|
||||||
|
rosso le risposte errate rispetto al correttore. Si ricorda che tale html non è una
|
||||||
|
pagina completa, per permettere di fare aggiunte prima o dopo di esso.
|
||||||
|
|
||||||
|
In conclusione prima di usare i 2 metodi dump_html() o calc_score() è necessario
|
||||||
|
aver fatto comunque un load() del correttore.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Questionnaire
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
enum bracket_state { none, open_bracket, close_bracket };
|
||||||
|
|
||||||
|
enum valid_tags { no_tag, title_tag_begin, title_tag_end, form_tag_begin, form_tag_end,
|
||||||
|
paragraph_tag_begin, paragraph_tag_end, input_tag, table_tag_begin,
|
||||||
|
table_tag_end, div_tag_begin, div_tag_end, ignored_tag };
|
||||||
|
String _title;
|
||||||
|
Question_struct _questions[MAXQUESTIONS];
|
||||||
|
bool _loaded, _parsed, _computed;
|
||||||
|
Rational _score;
|
||||||
|
int _max_score;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void parse_html(const char* name);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void reset();
|
||||||
|
Rational calc_score();
|
||||||
|
bool load(const String& s1, const String& s2);
|
||||||
|
void dump_html(const String& corso, const int modulo, const char testnum);
|
||||||
|
Questionnaire() { reset();}
|
||||||
|
virtual ~Questionnaire() {};
|
||||||
|
};
|
219
webcgi/formazione/report.cpp
Executable file
219
webcgi/formazione/report.cpp
Executable file
@ -0,0 +1,219 @@
|
|||||||
|
// report.cgi: applicazione per avere qualche piccolo report/status
|
||||||
|
// sui partecipanti ai corsi
|
||||||
|
|
||||||
|
#include <libpq++.h>
|
||||||
|
#include "applicat.h"
|
||||||
|
#include "questionnaire.h"
|
||||||
|
|
||||||
|
class Report_Application : public Application
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
String _dbname;
|
||||||
|
String _utente;
|
||||||
|
String _azione; //ACCESSI, VERIFICHE, LOGGATI
|
||||||
|
String _modulo; // Numero del modulo
|
||||||
|
String _testnum; // Livello di test
|
||||||
|
String _correct_answers;
|
||||||
|
String _corso;
|
||||||
|
Questionnaire _questionario;
|
||||||
|
PgEnv _environment;
|
||||||
|
PgDatabase *_db;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool create();
|
||||||
|
virtual bool destroy();
|
||||||
|
virtual void main_func();
|
||||||
|
void report();
|
||||||
|
public:
|
||||||
|
Report_Application() {_db = NULL;}
|
||||||
|
virtual ~Report_Application() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Report_Application::create()
|
||||||
|
{
|
||||||
|
_modulo = "";
|
||||||
|
_testnum = "";
|
||||||
|
_corso = "";
|
||||||
|
_dbname = POSTGRES_DB;
|
||||||
|
_environment.Port(POSTGRES_PORT);
|
||||||
|
_environment.Host(POSTGRES_HOST);
|
||||||
|
print_header("Report per utenti");
|
||||||
|
char *t1, *t2;
|
||||||
|
char *ccc = getenv("CONTENT_LENGTH");
|
||||||
|
int cl = ccc != NULL ? atoi(ccc) : 0;
|
||||||
|
if (cl < 512)
|
||||||
|
for (int x = 0; cl && (!feof(stdin)); x++) {
|
||||||
|
t1 = fmakeword(stdin, '&', &cl);
|
||||||
|
t2 = makeword(t1, '=');
|
||||||
|
unescape_url(t1);
|
||||||
|
unescape_url(t2);
|
||||||
|
if (!strcmp(t2,"AZIONE"))
|
||||||
|
_azione = t1;
|
||||||
|
if (!strcmp(t2,"UTENTE"))
|
||||||
|
_utente = t1;
|
||||||
|
if (!strcmp(t2,"MODULO"))
|
||||||
|
_modulo = t1;
|
||||||
|
if (!strcmp(t2,"TESTNUM"))
|
||||||
|
_testnum = t1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cout << "<H2>Troppi dati inviati</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>L'applicazione ha ricevuto troppi dati sul buffer d'ingresso.</P><BR>" << endl;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Report_Application::destroy()
|
||||||
|
{
|
||||||
|
print_footer();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Report_Application::report()
|
||||||
|
{
|
||||||
|
// The Last One... Query di selezione per ogni azione diversa
|
||||||
|
String command, command1;
|
||||||
|
if (_azione == "LOGGATI") {
|
||||||
|
command = "SELECT * FROM UTENTI WHERE logged='t'";
|
||||||
|
cout << "<H2>Elenco utenti collegati</H2><BR><BR>" << endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (_azione == "ACCESSI") {
|
||||||
|
command = "SELECT * FROM ACCESSI WHERE loginname='";
|
||||||
|
command += _utente;
|
||||||
|
command += "'";
|
||||||
|
cout << "<H2>Elenco accessi effettuati per l'utente " << _utente << "</H2><BR><BR>" << endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (_azione == "VERIFICHE") {
|
||||||
|
command = "SELECT * FROM VERIFICHE WHERE loginname='";
|
||||||
|
command += _utente;
|
||||||
|
command += "'";
|
||||||
|
if (!_modulo.empty() && !_testnum.empty()){
|
||||||
|
command += " AND MODULO=";
|
||||||
|
command += _modulo;
|
||||||
|
command += " AND TESTNUM='";
|
||||||
|
command += _testnum;
|
||||||
|
command += "'";
|
||||||
|
|
||||||
|
// Selezione per reperire il correttore
|
||||||
|
command1 = "SELECT * FROM CORRETTORI WHERE MODULO=";
|
||||||
|
command1 += _modulo;
|
||||||
|
command1 += " AND TESTNUM='";
|
||||||
|
command1 += _testnum;
|
||||||
|
command1 += "'";
|
||||||
|
_db->ExecCommandOk(command1);
|
||||||
|
if (_db->Tuples() > 0)
|
||||||
|
_correct_answers = _db->GetValue(0,"risposte");
|
||||||
|
|
||||||
|
// Selezione per reperire a quale corso appartiene l'utente
|
||||||
|
command1 = "SELECT * FROM UTENTI WHERE loginname='";
|
||||||
|
command1 += _utente;
|
||||||
|
command1 += "'";
|
||||||
|
_db->ExecCommandOk(command1);
|
||||||
|
if (_db->Tuples() > 0)
|
||||||
|
_corso = _db->GetValue(0,"course");
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << "<H2>Elenco verifiche effettuate per l'utente " << _utente << "</H2><BR><BR>" << endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
command = "SELECT * FROM UTENTI WHERE loginname='_'"; // Comando per ritornare 0 Tuple
|
||||||
|
|
||||||
|
_db->ExecCommandOk(command);
|
||||||
|
const int tuples = _db->Tuples();
|
||||||
|
|
||||||
|
if (tuples > 0) {
|
||||||
|
// Intestazione tabella
|
||||||
|
cout << "<CENTER><TABLE BORDER>" << endl;
|
||||||
|
cout << "<TR>";
|
||||||
|
if (_azione == "LOGGATI")
|
||||||
|
cout << "<TH>Log-name</TH><TH>Accesso in data</TH></TR>" << endl;
|
||||||
|
else
|
||||||
|
if (_azione == "ACCESSI")
|
||||||
|
cout << "<TH>Log-Name</TH><TH>Ingresso</TH><TH>Uscita</TH></TR>" << endl;
|
||||||
|
else
|
||||||
|
if (_azione == "VERIFICHE")
|
||||||
|
cout << "<TH>Log-name</TH><TH>Modulo</TH><TH>Liv. Test</TH><TH>Data verifica</TH><TH>Punteggio</TH></TR>" << endl;
|
||||||
|
for (int i = 0; i < tuples; i++) { // Ciclo sulle tuple ritornate
|
||||||
|
cout << "<TR>";
|
||||||
|
if (_azione == "LOGGATI")
|
||||||
|
cout << "<TD>" << _db->GetValue(i,"loginname") << "</TD><TD>" << _db->GetValue(i,"logindate") << "</TD>" << endl;
|
||||||
|
else
|
||||||
|
if (_azione == "ACCESSI")
|
||||||
|
cout << "<TD>" << _db->GetValue(i,"loginname") << "</TD><TD>" << _db->GetValue(i,"logindate") << "</TD><TD>" << _db->GetValue(i,"logoutdate") << "</TD>" << endl;
|
||||||
|
else
|
||||||
|
if (_azione == "VERIFICHE")
|
||||||
|
cout << "<TD>" << _db->GetValue(i,"loginname")
|
||||||
|
<< "</TD><TD>" << _db->GetValue(i,"modulo") << "</TD><TD>"
|
||||||
|
<< _db->GetValue(i,"testnum") << "</TD><TD>"
|
||||||
|
<< _db->GetValue(i,"data") << "</TD><TD>"
|
||||||
|
<< _db->GetValue(i,"punteggio") << "</TD>" << endl;
|
||||||
|
cout << "</TR>" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << "</TABLE></CENTER>" << endl;
|
||||||
|
|
||||||
|
if (_azione == "VERIFICHE" && !_modulo.empty() && !_testnum.empty()) {
|
||||||
|
String user_answers, corso;
|
||||||
|
|
||||||
|
trim(_corso);
|
||||||
|
for (int i=0; i<tuples; i++) {
|
||||||
|
user_answers = _db->GetValue(i,"risposte");
|
||||||
|
_questionario.load(_correct_answers,user_answers);
|
||||||
|
_questionario.dump_html(_corso, atoi(_modulo), _testnum[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cout << "<p>Nessun record presente per la query selezionata</p>" << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Report_Application::main_func()
|
||||||
|
{
|
||||||
|
if (_azione.empty() || (_azione != "LOGGATI" && _utente.empty())) {
|
||||||
|
cout << "<H2>Impossibile eseguire il report</H2><BR><BR>" << endl;
|
||||||
|
cout << "<P>E' necessario specificare il tipo di report e/o l'utente.</P><BR>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_db = new PgDatabase(_environment, _dbname);
|
||||||
|
if ( _db->ConnectionBad() )
|
||||||
|
print_database_error();
|
||||||
|
else
|
||||||
|
report();
|
||||||
|
delete _db;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
Report_Application* a = new Report_Application();
|
||||||
|
|
||||||
|
a->run(argc, argv);
|
||||||
|
|
||||||
|
delete a;
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
3363
webcgi/formazione/rfc1945.txt
Executable file
3363
webcgi/formazione/rfc1945.txt
Executable file
File diff suppressed because it is too large
Load Diff
151
webcgi/formazione/score_corrector.cpp
Executable file
151
webcgi/formazione/score_corrector.cpp
Executable file
@ -0,0 +1,151 @@
|
|||||||
|
// Applicazione per la correzione dei punteggi
|
||||||
|
#include <libpq++.h>
|
||||||
|
#include "applicat.h"
|
||||||
|
#include "questionnaire.h"
|
||||||
|
|
||||||
|
|
||||||
|
class Score_Application : public Application
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
PgDatabase *_db1, *_db2, *_db3;
|
||||||
|
Questionnaire _questionario;
|
||||||
|
PgEnv _environment;
|
||||||
|
String _dbname;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool create();
|
||||||
|
virtual bool destroy();
|
||||||
|
virtual void main_func();
|
||||||
|
void correct();
|
||||||
|
public:
|
||||||
|
Score_Application() {}
|
||||||
|
virtual ~Score_Application() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bool Score_Application::create()
|
||||||
|
{
|
||||||
|
_dbname = POSTGRES_DB;
|
||||||
|
_environment.Port(POSTGRES_PORT);
|
||||||
|
_environment.Host(POSTGRES_HOST);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Score_Application::destroy()
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Score_Application::correct()
|
||||||
|
{
|
||||||
|
String command, utente, modulo, testnum, user_answers, corrector_answers;
|
||||||
|
double punteggio;
|
||||||
|
int num=0, nv = 0;
|
||||||
|
|
||||||
|
//command = "SELECT * FROM VERIFICHE";
|
||||||
|
|
||||||
|
command = "SELECT * FROM CORRETTORI";
|
||||||
|
/*command += modulo;
|
||||||
|
command += " AND testnum='";
|
||||||
|
command += testnum;
|
||||||
|
command += "'";*/
|
||||||
|
|
||||||
|
|
||||||
|
_db1->ExecCommandOk(command);
|
||||||
|
const int tuples = _db1->Tuples();
|
||||||
|
if (tuples > 0) {
|
||||||
|
for (int i = 0; i<tuples; i++) // Ciclo su tutti i correttori
|
||||||
|
{
|
||||||
|
modulo = _db1->GetValue(i,"modulo");
|
||||||
|
testnum = _db1->GetValue(i,"testnum");
|
||||||
|
corrector_answers = _db1->GetValue(i,"risposte");
|
||||||
|
|
||||||
|
command = "SELECT * FROM VERIFICHE WHERE modulo=";
|
||||||
|
command += modulo;
|
||||||
|
command += " AND TESTNUM='";
|
||||||
|
command += testnum;
|
||||||
|
command += "'";
|
||||||
|
|
||||||
|
_db2->ExecCommandOk(command);
|
||||||
|
const int tups = _db2->Tuples();
|
||||||
|
if (tups > 0) {
|
||||||
|
for (int j=0;j<tups;j++) {
|
||||||
|
double score;
|
||||||
|
nv++;
|
||||||
|
utente = _db2->GetValue(j,"loginname");
|
||||||
|
punteggio = strtod(_db2->GetValue(j,"punteggio"),NULL);
|
||||||
|
user_answers = _db2->GetValue(j,"risposte");
|
||||||
|
_questionario.load(corrector_answers, user_answers);
|
||||||
|
score = strtod(dtoa(_questionario.calc_score()),NULL);
|
||||||
|
if (score != punteggio) {
|
||||||
|
num++;
|
||||||
|
cout << "Punteggio non congruente:" << endl << " Utente: " << utente << endl;
|
||||||
|
cout << " Punteggio calcolato= " << dtoa(score) << endl;
|
||||||
|
cout << " Punteggio mem.= " << dtoa(punteggio) << endl;
|
||||||
|
cout << " Modulo: " << modulo << " Livello : " << testnum << endl;
|
||||||
|
command = "UPDATE VERIFICHE SET punteggio=";
|
||||||
|
command += dtoa(_questionario.calc_score());
|
||||||
|
command += " WHERE loginname='";
|
||||||
|
command += utente;
|
||||||
|
command += "' AND MODULO=";
|
||||||
|
command += modulo;
|
||||||
|
command += " AND TESTNUM='";
|
||||||
|
command += testnum;
|
||||||
|
command += "'";
|
||||||
|
_db3->ExecCommandOk(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cout << endl << "Numero totale verifiche : " << nv << " di cui errate: " << num << endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cout << "Nessuna verifica presente nel database" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Score_Application::main_func()
|
||||||
|
{
|
||||||
|
_db1 = new PgDatabase(_environment, _dbname);
|
||||||
|
_db2 = new PgDatabase(_environment, _dbname);
|
||||||
|
_db3 = new PgDatabase(_environment, _dbname);
|
||||||
|
if ( _db1->ConnectionBad() || _db2->ConnectionBad()|| _db3->ConnectionBad())
|
||||||
|
cout << "Errore in connessione al database" << endl;
|
||||||
|
else
|
||||||
|
correct();
|
||||||
|
|
||||||
|
delete _db1; // Termina la transazione
|
||||||
|
delete _db2;
|
||||||
|
delete _db3;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
Score_Application* a = new Score_Application();
|
||||||
|
|
||||||
|
a->run(argc, argv);
|
||||||
|
|
||||||
|
delete a;
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
12
webcgi/formazione/tracciato
Executable file
12
webcgi/formazione/tracciato
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
\connect - angelo
|
||||||
|
CREATE TABLE accessi (progressivo int4, loginname char(30), logindate timestamp, logoutdate timestamp);
|
||||||
|
\connect - angelo
|
||||||
|
CREATE TABLE correttori (progressivo int4, modulo int4, testnum char, risposte char(254));
|
||||||
|
\connect - angelo
|
||||||
|
CREATE TABLE moduli (modulenum int4, modulename char(30), descrizione char(50), lasttest char, faked bool);
|
||||||
|
\connect - angelo
|
||||||
|
CREATE TABLE progressivi (progaccessi int4, progverifiche int4);
|
||||||
|
\connect - angelo
|
||||||
|
CREATE TABLE utenti (loginname char(30), realname char(50), email char(30), modules char(64), logindate timestamp, logged bool, progaccesso int4, verifiche char(64), telefono char(50), course char(20));
|
||||||
|
\connect - angelo
|
||||||
|
CREATE TABLE verifiche (progressivo int4, loginname char(30), data timestamp, modulo int4, testnum char, risposte char(254), punteggio float8);
|
166
webcgi/formazione/util.cpp
Executable file
166
webcgi/formazione/util.cpp
Executable file
@ -0,0 +1,166 @@
|
|||||||
|
#include "defines.h"
|
||||||
|
|
||||||
|
#define LF 10
|
||||||
|
#define CR 13
|
||||||
|
|
||||||
|
static char __stringa[64];
|
||||||
|
|
||||||
|
char* itoa(int i)
|
||||||
|
{
|
||||||
|
sprintf(__stringa, "%d", i);
|
||||||
|
return __stringa;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* ltoa(long l)
|
||||||
|
{
|
||||||
|
sprintf(__stringa, "%ld", l);
|
||||||
|
return __stringa;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* dtoa(double d)
|
||||||
|
{
|
||||||
|
sprintf(__stringa, "%6.4f", d);
|
||||||
|
return __stringa;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getword(char *word, char *line, char stop) {
|
||||||
|
int x = 0,y;
|
||||||
|
|
||||||
|
for(x=0;((line[x]) && (line[x] != stop));x++)
|
||||||
|
word[x] = line[x];
|
||||||
|
|
||||||
|
word[x] = '\0';
|
||||||
|
if(line[x]) ++x;
|
||||||
|
y=0;
|
||||||
|
|
||||||
|
while((line[y++] = line[x++]));
|
||||||
|
}
|
||||||
|
|
||||||
|
char *makeword(char *line, char stop) {
|
||||||
|
int x = 0,y;
|
||||||
|
char *word = (char *) malloc(sizeof(char) * (strlen(line) + 1));
|
||||||
|
|
||||||
|
for(x=0;((line[x]) && (line[x] != stop));x++)
|
||||||
|
word[x] = line[x];
|
||||||
|
|
||||||
|
word[x] = '\0';
|
||||||
|
if(line[x]) ++x;
|
||||||
|
y=0;
|
||||||
|
|
||||||
|
while((line[y++] = line[x++]));
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *fmakeword(FILE *f, char stop, int *cl) {
|
||||||
|
int wsize;
|
||||||
|
char *word;
|
||||||
|
int ll;
|
||||||
|
|
||||||
|
wsize = 102400;
|
||||||
|
ll=0;
|
||||||
|
word = (char *) malloc(sizeof(char) * (wsize + 1));
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
word[ll] = (char)fgetc(f);
|
||||||
|
if(ll==wsize) {
|
||||||
|
word[ll+1] = '\0';
|
||||||
|
wsize+=102400;
|
||||||
|
word = (char *)realloc(word,sizeof(char)*(wsize+1));
|
||||||
|
}
|
||||||
|
--(*cl);
|
||||||
|
if((word[ll] == stop) || (feof(f)) || (!(*cl))) {
|
||||||
|
if(word[ll] != stop) ll++;
|
||||||
|
word[ll] = '\0';
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
++ll;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char x2c(char *what) {
|
||||||
|
register char digit;
|
||||||
|
|
||||||
|
digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
|
||||||
|
digit *= 16;
|
||||||
|
digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
|
||||||
|
return(digit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unescape_url(char *url) {
|
||||||
|
register int x,y;
|
||||||
|
|
||||||
|
for(x=0,y=0;url[y];++x,++y) {
|
||||||
|
if((url[x] = url[y]) == '%') {
|
||||||
|
url[x] = x2c(&url[y+1]);
|
||||||
|
y+=2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
url[x] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void plustospace(char *str) {
|
||||||
|
register int x;
|
||||||
|
|
||||||
|
for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
int rind(char *s, char c) {
|
||||||
|
register int x;
|
||||||
|
for(x=strlen(s) - 1;x != -1; x--)
|
||||||
|
if(s[x] == c) return x;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getline(char *s, int n, FILE *f) {
|
||||||
|
register int i=0;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
s[i] = (char)fgetc(f);
|
||||||
|
|
||||||
|
if(s[i] == CR)
|
||||||
|
s[i] = fgetc(f);
|
||||||
|
|
||||||
|
if((s[i] == 0x4) || (s[i] == LF) || (i == (n-1))) {
|
||||||
|
s[i] = '\0';
|
||||||
|
return (feof(f) ? 1 : 0);
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_fd(FILE *f, FILE *fd)
|
||||||
|
{
|
||||||
|
|
||||||
|
char c;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
c = fgetc(f);
|
||||||
|
if(feof(f))
|
||||||
|
return;
|
||||||
|
fputc(c,fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t fsize(const char* n)
|
||||||
|
{
|
||||||
|
struct stat statbuf;
|
||||||
|
stat(n, &statbuf);
|
||||||
|
return statbuf.st_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void trim(String& s)
|
||||||
|
{
|
||||||
|
const int l = s.length();
|
||||||
|
int i,f,n;
|
||||||
|
f = 0;
|
||||||
|
n = 0;
|
||||||
|
for (i = l-1; i>=0; i--)
|
||||||
|
if (s[i] != ' ')
|
||||||
|
break;
|
||||||
|
if (i >= 0 && i < l-1)
|
||||||
|
{
|
||||||
|
f = i+1;
|
||||||
|
n = l-i-1;
|
||||||
|
}
|
||||||
|
s.del(f, n);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user