From a565e649898c629874ef812a13806b1c384f68e4 Mon Sep 17 00:00:00 2001 From: Nicolas BERNSTEIN Date: Sun, 18 Sep 2011 12:07:29 +0200 Subject: Added study program. Yay! --- design/style.css | 27 ++++++++++++++ lib/conf/apps.php | 3 ++ lib/deck/index.php | 6 ++-- lib/deck/mvent.php | 1 + lib/deck/rment.php | 2 ++ lib/deck/view.php | 8 +++++ lib/study/deck.php | 89 +++++++++++++++++++++++++++++++++++++++++++++-- lib/study/deckadd.php | 14 ++++++-- lib/study/deckrm.php | 17 +++++++++ lib/study/setcard.php | 37 ++++++++++++++++++++ lib/study/setrate.php | 19 ++++++++++ schema.sql | 47 ++++++++++++++++++++++--- tpl/deck/view.php | 8 +++-- tpl/study/deck.php | 68 ++++++++++++++++++++++++++++++++++++ tpl/study/index.php | 20 +++++++++-- tpl/study/lib_sidebar.php | 12 +++++-- tpl/study/setrate.php | 18 ++++++++++ 17 files changed, 375 insertions(+), 21 deletions(-) create mode 100644 lib/study/deckrm.php create mode 100644 lib/study/setcard.php create mode 100644 lib/study/setrate.php create mode 100644 tpl/study/deck.php create mode 100644 tpl/study/setrate.php diff --git a/design/style.css b/design/style.css index 64571fa..56a94f9 100644 --- a/design/style.css +++ b/design/style.css @@ -224,9 +224,36 @@ abbr { border-left: 1px solid #ccc; border-bottom: 1px solid #ccc; padding: 8px; + background: #F7F7F7; } .study_card code { margin-left: 16px; margin-bottom: 8px; + display: block; +} + +.scb1 { + border-left: 1px solid #DD00DD; + border-bottom: 1px solid #DD00DD; +} + +.scb2 { + border-left: 1px solid #FF7777; + border-bottom: 1px solid #FF7777; +} + +.scb3 { + border-left: 1px solid #FFAA00; + border-bottom: 1px solid #FFAA00; +} + +.scb4, .scb5, .scb6 { + border-left: 1px solid #00CC00; + border-bottom: 1px solid #00CC00; +} + +.scb7 { + border-left: 1px solid #0000FF; + border-bottom: 1px solid #0000FF; } diff --git a/lib/conf/apps.php b/lib/conf/apps.php index 734bfca..ef491ee 100644 --- a/lib/conf/apps.php +++ b/lib/conf/apps.php @@ -44,6 +44,9 @@ $apps = array( "index" => 1, "deckadd" => 1, "deck" => 1, + "deckrm" => 1, + "setrate" => 1, + "setcard" => 1, ), ); diff --git a/lib/deck/index.php b/lib/deck/index.php index 7a125e2..ad497d5 100644 --- a/lib/deck/index.php +++ b/lib/deck/index.php @@ -15,9 +15,9 @@ $fdefaults = array ( $decks = array(); $n = sql( - "SELECT decks.id AS id, decks.name AS name, account.login AS owner, 0 AS nbUsers ". - "FROM decks LEFT JOIN account ON decks.owner = account.id ". - "ORDER BY " . get_filter("order") . " " . get_filter("way") + "SELECT decks.id AS id, decks.name AS name, account.login AS owner, COUNT(deck_study.id) AS nbUsers ". + "FROM decks LEFT JOIN account ON decks.owner = account.id LEFT JOIN deck_study ON deck_study.deck = decks.id ". + "GROUP BY decks.id ORDER BY " . get_filter("order") . " " . get_filter("way") ); while ($nn = mysql_fetch_assoc($n)) $decks[] = $nn; diff --git a/lib/deck/mvent.php b/lib/deck/mvent.php index d4571e9..10d585a 100644 --- a/lib/deck/mvent.php +++ b/lib/deck/mvent.php @@ -26,6 +26,7 @@ if ($pos > $mn) { sql("UPDATE cards SET number = number - 1 WHERE number > " . $card['number']); sql("UPDATE cards SET number = number + 1 WHERE number >= $pos"); sql("UPDATE cards SET number = $pos WHERE id = $cardid"); + sql("UPDATE deck_study SET need_check = 1 WHERE deck = $deckid"); header("Location: view-deck-$deckid"); } diff --git a/lib/deck/rment.php b/lib/deck/rment.php index d3a87e0..6bb8300 100644 --- a/lib/deck/rment.php +++ b/lib/deck/rment.php @@ -15,4 +15,6 @@ assert_error($card && $card["deckowner"] == $user['id'], token_validate("Do you really want to delete this card ?", "view-deck-". $card['deckid']); sql("DELETE FROM cards WHERE id = $cardid"); sql("UPDATE cards SET number = number - 1 WHERE number > " . $card['number'] . " AND deck = " . $card['deckid']); +sql("UPDATE deck_study SET need_check = 1 WHERE deck = " . $card['deckid']); header("Location: view-deck-" . $card['deckid']); +die(); diff --git a/lib/deck/view.php b/lib/deck/view.php index eda2ca0..5604395 100644 --- a/lib/deck/view.php +++ b/lib/deck/view.php @@ -31,4 +31,12 @@ while ($nn = mysql_fetch_assoc($n)) $cards[] = $nn; $can_edit = false; if ($deck["owner_id"] == $user['id']) $can_edit = true; +$can_start_study = false; +if ($user['id'] != 0) { + if (!mysql_fetch_assoc(sql("SELECT id FROM deck_study WHERE deck = $deckid AND user = " . $user['id']))) + $can_start_study = true; +} else { + $message = "You should create an account in order to study this deck."; +} + require("tpl/deck/view.php"); diff --git a/lib/study/deck.php b/lib/study/deck.php index d7f8e9f..9b9c71f 100644 --- a/lib/study/deck.php +++ b/lib/study/deck.php @@ -1,5 +1,88 @@ study decks.'; -require("tpl/general/empty.php"); +assert_redir(count($args) == 3, 'study'); +$studyid = intval($args[2]); + +$study = mysql_fetch_assoc(sql( + "SELECT decks.id AS deckid, decks.name AS deckname, account.login AS deckowner, ". + " deck_study.learn_rate AS learn_rate, deck_study.user AS learn_user, deck_study.need_check AS need_check, deck_study.last_card AS last_card ". + "FROM deck_study LEFT JOIN decks ON deck_study.deck = decks.id LEFT JOIN account ON account.id = decks.owner ". + "WHERE deck_study.id = $studyid")); +assert_error($study && $study['learn_user'] == $user['id'], "You are not at the right place here."); + +if ($study['need_check']) { + /* Algorithm : + - Check that deck_study.last_card = max(card_study.card.number) + - Check that foreach (card where card.deck = deck_study.deck && card.number < deck_study.last_card), + exists corresponding card_study, if not exist, create level 0 (skipped) + */ + $mcn = mysql_fetch_assoc(sql("SELECT MAX(cards.number) AS mcn FROM deck_study ". + "LEFT JOIN card_study ON card_study.deck_study = deck_study.id LEFT JOIN cards ON cards.id = card_study.card ". + "WHERE deck_study.id = $studyid")); + $hasmoar = false; + if ($mcn) { + $mcn = $mcn['mcn']; + if ($study['last_card'] != $mcn) sql('UPDATE deck_study SET last_card = ' . $mcn . ' WHERE id = ' . $studyid); + + $cs = array(); + for($i = 1; $i < $mcn; $i++) $cs[$i] = 0; + + $d = sql("SELECT cards.number AS n, card_study.id AS i FROM card_study LEFT JOIN cards ON cards.id = card_study.card ". + "WHERE card_study.deck_study = $studyid"); + while($r = mysql_fetch_assoc($d)) $cs[$r['n']] = $r['i']; + + for ($i = 1; $i < $mcn; $i++) { + if ($cs[$i] == 0) { + $n = mysql_fetch_assoc(sql("SELECT cards.id AS id FROM deck_study LEFT JOIN cards ON cards.deck = deck_study.deck AND cards.number = $i ". + "WHERE deck_study.id = $studyid")); + assert_error($n, "Fucking deck inconsistency."); + sql("INSERT INTO card_study(deck_study, card, level, next_review) ". + "VALUES($studyid, " . $n['id'] . ", 0, ADDDATE(CURDATE(), INTERVAL 999999 DAY))"); + $hasmoar = true; + } + } + } + sql("UPDATE deck_study SET need_check = 0 WHERE id = $studyid"); + $message = "This deck has been checked. " . ($hasmoar ? "Some cards you hadn't studied before were added to your skipped list." : ""); +} + + +$load = mysql_fetch_assoc(sql("SELECT SUM(4 - level) AS l FROM card_study WHERE deck_study = $studyid AND level < 4 AND level > 0")); +$load = intval($load['l']); + +if ($load < $study['learn_rate']) { + $next_card = mysql_fetch_assoc(sql("SELECT * FROM cards WHERE deck = " . $study['deckid'] . " AND number = " . ($study['last_card'] + 1))); +} + + + +$filters = array( + "what" => array( + "level > 0 AND next_review <= CURDATE()" => "study today", + "level > 0 AND level < 4" => "learning", + "level > 0" => "all except skipped", + "level = 0" => "skipped", + ), + "order" => array( + "level" => "level", + "number" => "card number", + ), + "way" => $ord_ways, +); +$fdefaults = array( + "what" => "level > 0 AND level < 4", + "order" => "level", + "way" => "ASC", +); + +$study_cards = array(); +$s = sql( + "SELECT cards.id AS id, cards.name AS name, cards.text_html AS text, cards.number AS number, ". + "card_study.level AS level, card_study.next_review <= CURDATE() AS must_study ". + "FROM card_study LEFT JOIN cards ON card_study.card = cards.id WHERE deck_study = $studyid AND " . get_filter("what") . + " ORDER BY " . get_filter("order") . " " . get_filter('way')); +while ($ss = mysql_fetch_assoc($s)) $study_cards[] = $ss; + + + +require("tpl/study/deck.php"); diff --git a/lib/study/deckadd.php b/lib/study/deckadd.php index d7f8e9f..b95648c 100644 --- a/lib/study/deckadd.php +++ b/lib/study/deckadd.php @@ -1,5 +1,13 @@ study decks.'; -require("tpl/general/empty.php"); +assert_redir(count($args) == 3, 'deck'); +$deckid = intval($args[2]); +$deck = mysql_fetch_assoc(sql("SELECT id FROM decks WHERE id = $deckid")); +assert_error($deck, "This deck does not exist."); + +assert_error(!mysql_fetch_assoc(sql("SELECT id FROM deck_study WHERE deck = $deckid AND user = " . $user['id'])), + "You are already studying this deck."); + +sql("INSERT INTO deck_study(user, deck) VALUES(" . $user['id'] . ", $deckid)"); +header("Location: deck-study-".mysql_insert_id()); +die(); diff --git a/lib/study/deckrm.php b/lib/study/deckrm.php new file mode 100644 index 0000000..6b4e803 --- /dev/null +++ b/lib/study/deckrm.php @@ -0,0 +1,17 @@ += 3, 'study'); +$studyid = intval($args[2]); + +$study = mysql_fetch_assoc(sql( + "SELECT ". + " deck_study.user AS learn_user ". + "FROM deck_study ". + "WHERE deck_study.id = $studyid")); +assert_error($study && $study['learn_user'] == $user['id'], "You are not at the right place here."); + +token_validate("Do you really want to remove this deck from your studies ? ALL YOUR PROGRESS WILL BE LOST!", "deck-study-$studyid"); +sql("DELETE FROM card_study WHERE deck_study = $studyid"); +sql("DELETE FROM deck_study WHERE id = $studyid"); +header("Location: study"); +die(); diff --git a/lib/study/setcard.php b/lib/study/setcard.php new file mode 100644 index 0000000..13f3fc0 --- /dev/null +++ b/lib/study/setcard.php @@ -0,0 +1,37 @@ + 0, "You are not studying this deck."); + +assert_error($info['need_check'] == 0, + 'This deck needs checking first. Please return here first, it should do it.'); + +assert_error($info['cardnumber'] <= $info['last_card'] + 1, "You must first add previous cards to your study list."); + +$level = intval($args[3]); +assert_error($level >= 0 && $level <= 7, "That level is invalid."); +$intervals = array(999999, 1, 3, 5, 8, 16, 24, 999999); + +if ($info['cardnumber'] == $info['last_card'] + 1) { + sql("INSERT INTO card_study(deck_study, card, level, next_review) ". + "VALUES($studyid, $cardid, $level, ADDDATE(CURDATE(), INTERVAL " . $intervals[$level] . " DAY))"); + sql("UPDATE deck_study SET last_card = last_card + 1 WHERE id = $studyid"); +} else { + assert_error($info['csid'] > 0, "Deck is inconsistent."); + sql("UPDATE card_study SET level = $level, next_review = ADDDATE(CURDATE(), INTERVAL " . $intervals[$level] . " DAY) WHERE id = " . $info['csid']); +} + +header("Location: deck-study-$studyid"); +die(); diff --git a/lib/study/setrate.php b/lib/study/setrate.php new file mode 100644 index 0000000..f893eb9 --- /dev/null +++ b/lib/study/setrate.php @@ -0,0 +1,19 @@ += 3, 'study'); +$studyid = intval($args[2]); + +$study = mysql_fetch_assoc(sql( + "SELECT decks.id AS deckid, decks.name AS deckname, account.login AS deckowner, ". + " deck_study.learn_rate AS learn_rate, deck_study.user AS learn_user ". + "FROM deck_study LEFT JOIN decks ON deck_study.deck = decks.id LEFT JOIN account ON account.id = decks.owner ". + "WHERE deck_study.id = $studyid")); +assert_error($study && $study['learn_user'] == $user['id'], "You are not at the right place here."); + +if (isset($args[3]) && ($rate = intval($args[3])) > 0) { + sql("UPDATE deck_study SET learn_rate = $rate WHERE id = $studyid"); + header("Location: deck-study-$studyid"); + die(); +} + +include ("tpl/study/setrate.php"); diff --git a/schema.sql b/schema.sql index 2058771..d93fa1c 100644 --- a/schema.sql +++ b/schema.sql @@ -3,7 +3,7 @@ -- http://www.phpmyadmin.net -- -- Client: localhost --- Généré le : Sam 17 Septembre 2011 à 19:36 +-- Généré le : Dim 18 Septembre 2011 à 12:06 -- Version du serveur: 5.5.15 -- Version de PHP: 5.3.8 @@ -50,8 +50,25 @@ CREATE TABLE IF NOT EXISTS `cards` ( `text_html` text NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `unique_name` (`deck`,`name`), - UNIQUE KEY `unique_number` (`deck`,`number`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ; + UNIQUE KEY `unique_number` (`deck`,`number`), + KEY `deck_idx` (`deck`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ; + +-- -------------------------------------------------------- + +-- +-- Structure de la table `card_study` +-- + +CREATE TABLE IF NOT EXISTS `card_study` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `deck_study` int(11) NOT NULL, + `card` int(11) NOT NULL, + `level` int(11) NOT NULL, + `next_review` date NOT NULL, + PRIMARY KEY (`id`), + KEY `deck_study` (`deck_study`,`card`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=16 ; -- -------------------------------------------------------- @@ -66,11 +83,31 @@ CREATE TABLE IF NOT EXISTS `decks` ( `comment_md` text NOT NULL, `comment_html` text NOT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `unique_name` (`owner`,`name`) + UNIQUE KEY `unique_name` (`owner`,`name`), + KEY `owner_idx` (`owner`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ; -- -------------------------------------------------------- +-- +-- Structure de la table `deck_study` +-- + +CREATE TABLE IF NOT EXISTS `deck_study` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user` int(11) NOT NULL, + `deck` int(11) NOT NULL, + `last_card` int(11) NOT NULL DEFAULT '0', + `learn_rate` int(11) NOT NULL DEFAULT '10', + `need_check` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `unique_user_deck` (`user`,`deck`), + KEY `user_idx` (`user`), + KEY `deck_idx` (`deck`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ; + +-- -------------------------------------------------------- + -- -- Structure de la table `images` -- @@ -81,7 +118,7 @@ CREATE TABLE IF NOT EXISTS `images` ( `extension` varchar(5) NOT NULL, PRIMARY KEY (`id`), KEY `owner` (`owner`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=5 ; +) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=9 ; -- -------------------------------------------------------- diff --git a/tpl/deck/view.php b/tpl/deck/view.php index f54da3d..d15de4e 100644 --- a/tpl/deck/view.php +++ b/tpl/deck/view.php @@ -9,10 +9,12 @@ if ($can_edit) { echo 'add card'; } -echo '

Description

'; echo $deck["comment"]; -echo '

Cards

'; +if ($can_start_study) { + echo '

>> start studying this deck <<

'; +} + echo ''; foreach ($cards as $card) { echo '
'; @@ -22,7 +24,7 @@ foreach ($cards as $card) { echo '
'; } echo '#' . $card["number"] . ": " . $card["name"] . ''; - echo '
' . $card['text']; + echo $card['text']; echo ''; } if ($can_edit) diff --git a/tpl/study/deck.php b/tpl/study/deck.php new file mode 100644 index 0000000..d231705 --- /dev/null +++ b/tpl/study/deck.php @@ -0,0 +1,68 @@ +stop studying'; + +echo '
'; +echo 'Load : ' . $load . '/' . $study['learn_rate'] . ' - change study rate
'; + +if (isset($next_card)) { + if ($next_card) { + echo '
'; + echo ''; + echo '(next card) #'.$next_card['number'].': '.$next_card['name'].''; + echo $next_card['text_html']; + echo '
'; + } else { + echo '

No more cards to study !

'; + } +} + +echo ''; + +if (count($study_cards) == 0) { + echo '

Nothing in this list at the moment.

'; +} else { + foreach($study_cards as $card) { + echo '
'; + + $l = array(); + if ($card['level'] > 0) { + $l[] = ''. ($card['level'] >= 4 ? 'put in skip list' : 'skip this card') . ''; + } + if ($card['must_study'] || $card['level'] == 0 || $card['level'] == 7) { + $l[] = ''. ($card['level'] == 0 ? 'learn this card' : 'I forgot all about this'). ' (1)'; + if ($card['level'] > 0 and $card['level'] < 3) { + $l[] = 'I\'m learning this ('.($card['level']+1).')'; + } + if ($card['level'] < 4) { + $l[] = 'I know this (4)'; + } + if ($card['level'] > 3 and $card['level'] < 6) { + $l[] = 'I know this ('.($card['level']+1).')'; + } + } + if ($card['level'] < 7) { + $l[] = 'I totally know this (7)'; + } + echo '
'; + echo implode("
\n", $l); + echo '
'; + + echo '(' . ($card['level'] == 0 ? 'skipped' : 'level '.$card['level']) .') #'.$card['number'].': '.$card['name'].''; + echo $card['text']; + echo '
'; + } +} + +require ("lib_sidebar.php"); +require("tpl/general/bottom.php"); diff --git a/tpl/study/index.php b/tpl/study/index.php index 26ecd6d..be78f22 100644 --- a/tpl/study/index.php +++ b/tpl/study/index.php @@ -5,10 +5,26 @@ require("tpl/general/top.php"); ?> -

Welcome to the My studies section. This is a simple study program based on +

Welcome to the My studies section. This is a simple study program based on decks of cards, keeping track of your progress and everything.

-

Please take a look in the List of decks and look at whatever you want to learn.

+

Please take a look in the List of decks and start learning whatever you want to learn.

+ +

The cards you are studying are classified in the following boxes :

+ +

You can freely move cards into different boxes using the links that appear at the right of the card, the number in the parenthesis being + the level that card will have if you click it. +Cards you are supposed to study or review today appear with a bold title : that means that today, + you should decide to put it either in the next box, or somewhere else.

+

The load is calculated as a function of the number of cards with levels from 1 to 3 : it represents the quantity of + stuff you are learning right now. When the load is smaller than the study rate you ask for, new cards will be suggested for + you to learn.

'; echo '

Studying decks

'; echo '

My decks

'; } diff --git a/tpl/study/setrate.php b/tpl/study/setrate.php new file mode 100644 index 0000000..f632b33 --- /dev/null +++ b/tpl/study/setrate.php @@ -0,0 +1,18 @@ +Current learn rate : ' . $study['learn_rate'] . ' - keep this rate

'; + +echo '

Please choose how much you intend on studying :

'; + +require ("lib_sidebar.php"); +require("tpl/general/bottom.php"); -- cgit v1.2.3