Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [JavaScript][PHP]test z ograniczoną ilością czasu
Forum PHP.pl > Forum > Przedszkole
blade-mrn
Jestem tu nowy więc Witam wszystkich i do rzeczy.
Piszę właśnie stronę na której będzie można przeprowadzać testy i stanąłem w jednym miejscu, mianowicie na rozwiązanie każdego testu jest określona ilość czasu i nie było by tu dużego problemu gdyby nie fakt iż pytania są wyświetlane pojedynczo (stronicowanie) i po każdej udzielonej odpowiedzi skrypt jest przekierowywany do innego pliku gdzie następuje zapis udzielonej odpowiedzi do bazy MySql oraz inkrementacja zmiennych stronicowania i na końcu przekierowanie z powrotem. Mój problem polega na tym, że początkowo użyłem JS do odliczania czasu i przekierowania na stronę z wynikiem gdy on upłynie ale po wprowadzeniu opisanego powyżej rozwiązania to się nie sprawdza bo JS po każdym odświeżeniu strony zaczyna odliczanie od początku. Myślałem nad zapisem aktualnego czasu do zmiennej w sesji przed przekierowanie a potem ponowne jej odczytanie tyle, że nie wiem jak to można zrobić :/ Czy ktoś mi podsunie rozwiązanie tego problemu, a może ktoś ma lepszy pomysł questionmark.gif
Poniżej przedstawiam kod źródłowy wspomnianych wcześniej plików.
Plik wyświetlający test:
  1. <?php
  2. @$db = new mysqli ($db_host, $db_user, $db_pass, $db_name);
  3. if (mysqli_connect_errno()) {echo 'Błąd połączenia z bazą!'; exit;}
  4. $zap_test = $db->query("SELECT * FROM testy WHERE id_test=".$id_test);
  5. $rek_test = $zap_test->fetch_assoc();
  6. for ($i=0;$i<=strlen($rek_test['czas']);$i++)
  7. {
  8. if ($i<2) {$godz = $godz.$rek_test['czas'][$i];}
  9. if (($i>2) && ($i<5)) {$min = $min.$rek_test['czas'][$i];}
  10. if ($i>5) {$sek = $sek.$rek_test['czas'][$i];}
  11. }
  12. if (($godz == 0) && ($min == 0) && ($sek == 0))
  13. { echo 'Brak limitu czasu.';}
  14. else
  15. {
  16. echo 'Pozostało Ci <span id="sekundy" style="font-size: 20px"></span> sekund. ';
  17. echo $godz.'h '.$min.' min '.$sek;
  18. }
  19. if (is_numeric($_REQUEST['page']))
  20. {
  21. $page = (int) $_REQUEST['page'];
  22. if ($page < 1) { $page = 1; }
  23. }
  24. else { $page = 1; }
  25. $start = ($page - 1);
  26. $_SESSION['page'] = $page;
  27. $zap_ile_pyt = $db->query("SELECT * FROM pytania WHERE id_test=".$rek_test['id_test']);
  28. $result = $db->query("SELECT * FROM pytania WHERE id_test = ".$rek_test['id_test']." LIMIT $start, 1");
  29. echo '<form action="index.php?id=7" method="POST" id="form">';
  30. echo '<ol type="I">';
  31. for ($i=1;$i<=$result->num_rows;$i++)
  32. {
  33. $rek_pyt = $result->fetch_assoc();
  34. echo '<input type="hidden" name="id_pyt" value="'.$rek_pyt['id_pytania'].'">';
  35. echo $rek_pyt['pytanie'].'<br />';
  36. $zap_odp = $db->query("SELECT * FROM odpowiedzi WHERE id_test=".$rek_test['id_test']." AND id_pyt=".$rek_pyt['id_pytania']);
  37. echo '<ol>';
  38. for ($j=1; $j<=$zap_odp->num_rows; $j++)
  39. {
  40. $rek_odp = $zap_odp->fetch_assoc();
  41. echo '<li>'.$rek_odp['odpowiedz'].'<input type="checkbox" name=checkodp'.$j.'></li>';
  42. }
  43. echo '</ol>';
  44. }
  45. echo '<input type="submit" value="dalej">';
  46. echo "<br /><a href='index.php?id=0'>Wróć</a></div>";
  47. echo '</form>';
  48. ?>
  49. <script type="text/javascript">
  50. o=document.getElementById('sekundy')
  51. function odliczaj(o,sek)
  52. {
  53. o.innerHTML=sek
  54. if(sek>0)setTimeout(function(){odliczaj(o,--sek)},1e3)
  55. if(sek==0){window.location ="index.php?id=6";}
  56. }
  57. odliczaj(document.getElementById('sekundy'),<?php echo ($godz*60*60)+($min*60)+$sek; ?>)
  58. </script>
  59. </body>
  60. </html>

Plik dokonujący zapisu i inkrementacji:
  1. <?php
  2. require_once ('common.php');
  3. $id_test = $_SESSION['ide'];
  4. $nr_ucznia = $_SESSION['nr_ucznia'];
  5. $klasa = $_SESSION['klasa'];
  6. $id_pyt = $_POST['id_pyt'];
  7. date_default_timezone_set ("Europe/Warsaw");
  8. $data = date('Ymd');
  9. $czas = date('His');
  10. $ip = getenv('REMOTE_ADDR');
  11. @$db = new mysqli ($db_host, $db_user, $db_pass, $db_name);
  12. if (mysqli_connect_errno()) {echo 'Błąd połączenia z bazą!'; exit;}
  13. $zap_odp = $db->query("SELECT * FROM odpowiedzi WHERE id_test=".$id_test." AND id_pyt=".$id_pyt);
  14. for ($j=1; $j<=$zap_odp->num_rows; $j++)
  15. {
  16. $rek_odp = $zap_odp->fetch_assoc();
  17. if ($_POST['checkodp'.$j] == on)
  18. {
  19. $zap_roz = $db->query("INSERT INTO roz VALUES (NULL,'".strtoupper($klasa)."',".$nr_ucznia.",".$id_test.",".$rek_odp['id_odp'].",".$data.",".$czas.",'".$ip."'".")");
  20. }
  21. }
  22. //----------------START Zmiana strony w stronicowaniu---------------
  23. $next = $_SESSION['next'];
  24. $next = $_SESSION['page'] + 1;
  25. $_SESSION['next'] = $next;
  26. $zap_ile_pyt = $db->query("SELECT id_pytania FROM pytania WHERE id_test=".$id_test);
  27. if ($next <= $zap_ile_pyt->num_rows)
  28. {
  29. echo '<META HTTP-EQUIV="Refresh" CONTENT="0;URL='.$_SERVER['PHP_SELF'].'?id=5&page='.$next.'">';
  30. }
  31. else
  32. {
  33. echo '<META HTTP-EQUIV="Refresh" CONTENT="0;URL=index.php?id=6">';
  34. }
  35. //-------------STOP zmiana strony w stronicowaniu-------------------
  36. ?>

robal94
A może czas do bazy mysql?
thek
Uważam, że nie tyle powinieneś zapisywać aktualny czas, co jego początek lub czas zakończenia i na podstawie tego kontrolować upływ. Tę informację można zapisać, jak sam zauważasz, w sesji. JS mając ją sam sobie może czas liczyć, bo jest ona dla niego niezmienna. A o to Ci chodzi.
blade-mrn
Dzięki za sugestię, zaraz się pobawię.

Moich zmagań ciąg dalszy. Wykombinowałem coś takiego, że w chwili wyświetlenia pierwszego pytania testu do pliku cooki zapisywany jest aktualny czas Unix. Następnie do tego czasu dodaję czas trwania testu w sekundach teoretycznie otrzymując czas w którym ma się zakończyć test. Aby to sprawdzić porównuję wynik z aktualnym czasem. Cały problem w tym, że test ma trwać np. 10 sekund a przekierowanie następuje już po 1 sekundzie. Czy to założenie jest zle czy ja coś knocę questionmark.gif
Wklejam tylko zmienione/dopisane fragmenty kodu bo reszta jest wyżej.
[JAVASCRIPT] pobierz, plaintext
  1. <head>
  2. <?php
  3. naglowek_strony ();
  4. ?>
  5. <script type="text/javascript">
  6. function zap_czas ()
  7. {
  8. offset = document.cookie.indexOf("czas=");
  9. if (offset == -1) // jeżeli ciasteczko istnieje
  10. {
  11. var today = new Date();
  12. document.cookie="czas="+today.getTime()
  13. }
  14. }
  15. function getCookie(Name)
  16. {
  17. var search = Name + "=";
  18. if (document.cookie.length > 0)
  19. {
  20. offset = document.cookie.indexOf(search);
  21. if (offset != -1)
  22. {
  23. offset += search.length;
  24. end = document.cookie.indexOf(";", offset)
  25. if (end == -1) end = document.cookie.length
  26. return unescape(document.cookie.substring(offset, end))
  27. }
  28. }
  29. }
  30. </script>
  31. </head>
  32. <body onLoad="zap_czas ()">
  33. .
  34. .
  35. .
  36. <script type="text/javascript">
  37. o=document.getElementById('sekundy');
  38. function odliczaj(o,sek)
  39. {
  40. var obecny_czas = new Date ();
  41. var czas_startu = parseFloat(getCookie("czas"));
  42. var ilosc_czasu = <?php echo (($godz*60*60))+(($min*60))+($sek*10); ?>;
  43. o.innerHTML=sek
  44. if(sek>0){setTimeout(function(){odliczaj(o,--sek)},1e3)}
  45. var licz = czas_startu + ilosc_czasu;
  46. if(licz < obecny_czas.getTime()){window.location ="index.php?id=6";}
  47. }
  48. odliczaj(document.getElementById('sekundy'),<?php echo ($godz*60*60)+($min*60)+$sek; ?>);
  49. </script>
[JAVASCRIPT] pobierz, plaintext

Wydaje mi się że to już bardzie pasuje do działu "Po stronie przeglądarki" ale chyba tworzenie kolejnego tematu nie ma sensu.
thek
Problem w tym wypadku polega na tym, że czas startu nie jest równoznaczny z czasem przez Ciebie wybranym. Pamiętaj, że ileś czasu trwa renderowanie strony. Stąd licznik powinien startować dopiero PO jej pełnym wyświetleniu, a więc mogą nastąpić różnice czasu z tego powodu. Z tego co kojarzę to zdarzenie onLoad jest wywoływane w momencie gdy strona zostanie załadowana w pełni, czyli sadzę, że w tym momencie powinien startować licznik czasu na pytanie w teście, ale działo by się to niezależnie od znacznika startu/końca testu w cookie/na serwerze. Nie pamiętam czy było je można stosować do konkretnego elementu, bo wtedy czas wystartowałby po załadowaniu odpowiedniego elementu strony (choćby obrazków testowych). Zawsze bowiem może zajść sytuacja, że user zna odpowiedź na pytanie, zanim strona się załaduje w pełni, wpisze odpowiedź i da "Dalej". W ten sposób mimo faktu, iż licznik pytania nawet nie ruszył, czas całości testu upływa niezależnie od usera na serwerze. Problemem może być wolna strona, gdzie czas ładowania może stanowić dużą część całości testu, zmniejszając możliwy na odpowiedź. Dlatego ogranicz na stronach testu grafikę i wszelkie wodotryski/spowalniacze do niezbędnych. A timestamp startu/końca niech będzie niemożliwy do edycji w żaden sposób (zapis na serwerze) co sprawi, że będzie to miarodajne i uczciwe wobec większości userów. Dlatego to rozwiązanie jest mieszane, gdyż część rzeczy dzieje się po stronie serwera (kontrola całości testu i jego trwania), a część po stronie usera (ograniczony czas lokalny na jedno pytanie). To moim zdaniem najsensowniejsze rozwiązanie problemu.
blade-mrn
thek dzięki za uwagi ale z racji tego iż jestem raczej początkujący czy mógłbyś nieco bardziej rozjaśnić rozwiązanie tego problemu questionmark.gif
thek
Najprościej pisząc:
Na starcie testu zapisz sobie czas startu lub końca. W zdarzeniu onLoad uruchom liczniki czasu na pojedyncze pytanie i/lub całości testu jaki Ci pozostał. Nigdy nie modyfikuj czasu startu/końca, bo jest on dla Ciebie wzorcowym. Jeśli upłynie czas na jedno pytanie lub czas całego testu to przerwij go przekierowując na stronę wyniku/następnego pytania, o ile sam user tego nie zrobi dając submit w formularzu (czy to poprzez guzik submit czy wybór opcji, która wywoła zdarzenie submit() ).
Tak więc masz na stronie niejako 2 liczniki:
- lokalny dla pytania,
- globalny dla testu.
W zależności od sytuacji wywołają one określone zdarzenia. Upłynięcie pierwszego -> przekierowanie na następne pytanie, a jeśli to było ostatnie to do podsumowania testu. Koniec drugiego - od razu do podsumowania. Każde pytanie bez odpowiedzi niech przyjmuje jakąs z góry określona wartość. Choćby NULL, pustą czy inną nieokreśloną. To pozwoli oznaczyć pytania z odpowiedziami i bez nich.

Oczywiście pisze tutaj o sytuacji gdy każde pytanie to kolejna strona. Jeśli masz tak, że masz wszystkie pytania na jednej stronie, to wystarczy jeden licznik, ten lokalny, bo będzie on miał w sumie długi czas i te kilka sekund na przesłanie netem strony i jej rendering usera by nie zbawiło winksmiley.jpg
blade-mrn
Tyle że ja właśnie coś takiego próbuję zrobić, mianowicie tak jak pisałem wcześniej.
1. Gdy zostanie wyświetlona strona z pierwszym pytaniem (oznacza to początek testu) zostaje zapisany do pliku cooki(jeśli już nie istnieje) aktualny czas Unix;
2. Potem skrypt odliczający dynamicznie czas na stronie co sekundę sprawdza czy czas początkowy plus czas trwania testu nie jest większy od czasu obecnego jeśli tak przekierowuje na stronę końcową jeśli nie nie robi nic.
Więc czas początkowy nigdy w trakcie rozwiązywania testu nie jest edytowany.
Cały problem polega na tym, że z dodawania czas_startu + ilosc_czasu; nie wychodzi mi czas zakończenia testu(czyli czas rozpoczęcia powiększony o czas trwania) i nie wiem dlaczego tak się dzieje co_jest.gif .
thek
Myślę, że drugi przykład stąd (http://www.w3schools.com/js/js_timing.asp) Cię naprowadzi na rozwiązanie winksmiley.jpg Tutaj leci w górę od 0... Ty zrobisz to samo, ale w dół. Gdy dojdzie do 0 -> przekierowanie smile.gif Zerknij sobie tam a zauważysz, że całość metody jest banalna. Tylko sobie dopasuj do potrzeb ten prosty kod.

EDIT:
Przykładowo masz czas_startu testu i wiesz, że trwać on ma 5 minut (czas_testu) w sumie na 10 pytań, ale maksymalnie jest minuta na pytanie. Robisz więc na każdej stronie counter odliczający w dół od 60 (limit na pytanie). Masz też czas startu testu. Liczysz więc:
czas_testu + czas_startu - czas_obecny_w_chwili_startu_pytania
lub inaczej:
czas_testu - ( czas_obecny_w_chwili_startu_pytania - czas_zapisany)
co da nam czas jaki pozostaje na całość testu. Oba te liczniki sobie lecą w dół i dojście do 0 któregoś z nich wywołuje odpowiednie zdarzenie (albo nowe pytanie, albo koniec testu). Chyba prosto to opisałem?
blade-mrn
Na razie dzięki za pomoc, zobaczę co mi z tego wyjdzie.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.