diff --git a/examples/data.php b/examples/data.php
new file mode 100644
index 0000000..6d41c12
--- /dev/null
+++ b/examples/data.php
@@ -0,0 +1,279 @@
+ OAUTH2_CLIENT_ID,
+ 'redirect_uri' => $scriptPath,
+ 'scope' => 'read',
+ 'state' => $_SESSION['state'],
+ 'response_type' => 'code'
+ );
+
+ # Redirect the user to Github's authorization page
+ header('Location: ' . $authorizeURL . '?' . http_build_query($params));
+ die();
+}
+
+# When Github redirects the user back here, there will be a "code" and "state" parameter in the query string
+
+if (get('code')) {
+ # Verify the state matches our stored state
+ if (!get('state') || $_SESSION['state'] != get('state')) {
+ header('Location: ' . $scriptPath);
+ die();
+ }
+
+ # Exchange the auth code for a token
+ $token = apiRequest($tokenURL, array(
+ 'client_id' => OAUTH2_CLIENT_ID,
+ 'client_secret' => OAUTH2_CLIENT_SECRET,
+ 'redirect_uri' => $scriptPath,
+ 'state' => $_SESSION['state'],
+ 'code' => get('code'),
+ 'grant_type' => 'authorization_code'
+ ));
+ $_SESSION['access_token'] = $token->access_token;
+
+ header('Location: ' . $scriptPath);
+}
+
+if (session('access_token')) {
+
+ # =========
+ # WELCOME!
+ # =========
+
+ $stepics = apiRequest($apiURLBase . 'stepics/1');
+ $user_id = $stepics->profiles[0]->id;
+ $first_name = $stepics->profiles[0]->first_name;
+ $last_name = $stepics->profiles[0]->last_name;
+ echo '
';
+ echo 'Личный кабинет онлайн-программы «Анализ данных»';
+ echo '';
+ echo '';
+ echo '';
+ echo '';
+ echo '';
+ echo '
';
+
+ # =============
+ # COURSES LIST
+ # =============
+
+ $courses = array(
+ array('id' => 217, 'required' => True, 'sections' => array(2, 4), 'need_to_pass' => 85, 'exams' => 1536), # Алгоритмы: теория и практика. Методы
+ array('id' => 129, 'required' => True, 'sections' => array(1, 2, 3), 'need_to_pass' => 85, 'exams' => 1425), # Анализ данных в R
+ array('id' => 724, 'required' => True, 'sections' => array(1, 2, 3), 'need_to_pass' => 85, 'exams' => null), # Анализ данных в R. Часть 2
+ array('id' => 1240, 'required' => True, 'sections' => array(1, 2, 5, 6), 'need_to_pass' => 85, 'exams' => 1534), # Введение в базы данных
+ array('id' => 253, 'required' => True, 'sections' => array(1, 3, 4, 5), 'need_to_pass' => 85, 'exams' => 1535), # Введение в архитектуру ЭВМ. Элементы операционных систем.
+ array('id' => 902, 'required' => True, 'sections' => array(1, 2, 3, 4), 'need_to_pass' => 90, 'exams' => 1424), # Дискретная математика
+ array('id' => 73, 'required' => True, 'sections' => array(1, 2, 3), 'need_to_pass' => 90, 'exams' => 1427), # Введение в Linux
+ array('id' => 497, 'required' => True, 'sections' => array(1, 2, 3), 'need_to_pass' => 85, 'exams' => 1426), # Основы программирования на R
+ array('id' => 76, 'required' => True, 'sections' => array(1, 2, 3), 'need_to_pass' => 90, 'exams' => 1423), # Основы статистики
+ array('id' => 524, 'required' => True, 'sections' => array(1, 2, 3), 'need_to_pass' => 85, 'exams' => null), # Основы статистики. Часть 2
+ array('id' => 67, 'required' => True, 'sections' => array(1, 2, 3), 'need_to_pass' => 90, 'exams' => 1428), # Программирование на Python
+ array('id' => 512, 'required' => True, 'sections' => array(1, 2, 3), 'need_to_pass' => 90, 'exams' => null), # Python: основы и применение
+ array('id' => 217, 'required' => False, 'sections' => array(6, 8), 'need_to_pass' => 85, 'exams' => 1537), # Алгоритмы: теория и практика. Методы
+ array('id' => 253, 'required' => False, 'sections' => array(2, 6, 7), 'need_to_pass' => 85, 'exams' => 1538), # Введение в архитектуру ЭВМ. Элементы операционных систем.
+ array('id' => 1240, 'required' => False, 'sections' => array(3, 4, 7), 'need_to_pass' => 85, 'exams' => 1544), # Введение в базы данных
+ array('id' => 95, 'required' => False, 'sections' => array(1, 2, 3, 4), 'need_to_pass' => 85, 'exams' => 1429), # Введение в математический анализ
+ array('id' => 401, 'required' => False, 'sections' => array(1, 2, 3, 4), 'need_to_pass' => 85, 'exams' => 1432), # Нейронные сети
+ array('id' => 7, 'required' => False, 'sections' => array(1, 2, 3, 4, 5, 6), 'need_to_pass' => 85, 'exams' => 1430), # Программирование на языке C++
+ array('id' => 187, 'required' => False, 'sections' => array(1, 2, 3, 4, 5, 6), 'need_to_pass' => 85, 'exams' => 1431), # Java. Базовый курс
+ # TO ADD WHEN READY: Управление вычислениями (required)
+ # TO ADD WHEN READY: Основы статистики, часть 3 (not required)
+ # TO ADD WHEN READY: Machine Learning (not required)
+ );
+
+ echo '';
+ echo '';
+ echo '| Обязательные курсы: |
';
+ echo '| Курс | Количество модулей | Модули | Ваши баллы | Ваш процент | Допуск к экзамену |
';
+
+ $previousCourse = null;
+ foreach ($courses as $course) {
+ $info = apiRequest($apiURLBase . 'courses/' . $course['id'])->courses[0];
+ $section_ids = array();
+ foreach ($course['sections'] as $i) {
+ $section_ids[] = $info->sections[$i - 1];
+ }
+ $sections = apiRequest($apiURLBase . 'sections?ids[]=' . implode('&ids[]=', $section_ids))->sections;
+ $progress_ids = array();
+ foreach ($sections as $section) {
+ $progress_ids[] = $section->progress;
+ }
+ $progresses = apiRequest($apiURLBase . 'progresses?ids[]=' . implode('&ids[]=', $progress_ids))->progresses;
+ $score = 0;
+ $cost = 0;
+ foreach ($progresses as $progress) {
+ $score += $progress->score;
+ $cost += $progress->cost;
+ }
+
+ $percentage = $cost ? round(100 * $score / $cost, 2) : 0;
+ if ($percentage >= $course['need_to_pass']) {
+ try {
+ $exam = apiRequest($apiURLBase . 'courses/' . $course['exams'])->courses[0];
+ $progress = apiRequest($apiURLBase . 'progresses/' . $exam->progress)->progresses[0];
+ $exam_message = $progress->score . ' / ' . $progress->cost;
+ } catch (Exception $e) {
+ $exam_message = 'можно начать';
+ }
+ } else {
+ $exam_message = 'нет доступа (нужно ' . $course['need_to_pass'] . '% за курс)';
+ }
+ if ($previousCourse['required'] and !$course['required']) {
+ echo '| Курсы по выбору: |
';
+ echo '| Курс | Количество модулей | Модули | Ваши баллы | Ваш процент | Экзамен |
';
+ }
+
+ echo '';
+ echo '| ' . $info->title . ' | ';
+ echo '' . count($course['sections']) . ' | ';
+ echo '' . implode(", ", $course['sections']) . ' | ';
+
+ if ($cost) {
+ echo '' . $score . ' / ' . $cost . ' | ';
+ echo '' . $percentage . '% | ';
+ } else {
+ echo 'Запишитесь на курс | ';
+ }
+
+ echo '' . $exam_message . ' | ';
+ echo '
';
+ $previousCourse = $course;
+ }
+
+ echo '
';
+
+ # =============
+ # PAYMENTS LOG
+ # =============
+
+ $subscription_plans = array(10 => 0, 1999 => 1, 5397 => 3, 6000 => 3, 9595 => 6, 16791 => 12);
+ $format = 'M d, Y (H:i)';
+ $page = 1;
+ $payments_list = array();
+ $payment_start = 0;
+ $payment_valid_until = 0;
+ do {
+ $payments = apiRequest($apiURLBase . 'payments?page=' . $page);
+ foreach ($payments->payments as $payment) {
+ if ($payment->destination_type != 'au_data') {
+ continue;
+ }
+
+ $payment_date = strtotime($payment->payment_date);
+ $payment_start = max($payment_date, $payment_start);
+ $months = $subscription_plans[intval($payment->amount)];
+ $payment_valid_until = paymentDue($payment_start, $months);
+ $payments_list[] = array(
+ intval($payment->amount),
+ date($format, $payment_date),
+ date($format, $payment_start),
+ date($format, $payment_valid_until)
+ );
+ $payment_start = $payment_valid_until;
+ }
+ $page += 1;
+ } while ($payments->meta->has_next);
+
+ if ($payment_valid_until >= time()) {
+ $color = ($payment_valid_until - time() >= 60 * 60 * 24 * 7) ? "#66cc66" : "cccc00"; # yellow if less than one week left
+ echo 'Ваша подписка на программу активна до ' . date($format, $payment_valid_until) . '
';
+ } else {
+ echo 'Ваша подписка на программу не активна
';
+ }
+
+ echo 'Ваши платежи:
';
+ echo '';
+ echo '| Сумма (руб) | Дата и время платежа (UTC) | Действует от | Действует до |
';
+ foreach (array_reverse($payments_list) as $payment) {
+ echo '| ';
+ echo implode(' | ', $payment);
+ echo ' |
';
+ }
+ echo '
';
+
+ # LOGOUT
+ echo '[Выйти из кабинета]
';
+ echo '';
+} else {
+ echo '';
+ echo 'Вы не вошли
';
+ echo 'Войти через Stepik.org
';
+}
+
+# =================
+# HELPER FUNCTIONS
+# =================
+
+function apiRequest($url, $post = FALSE, $headers = array())
+{
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
+
+ if ($post)
+ curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));
+
+ $headers[] = 'Accept: application/json';
+
+ if (session('access_token'))
+ $headers[] = 'Authorization: Bearer ' . session('access_token');
+
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+
+ $response = curl_exec($ch);
+ return json_decode($response);
+}
+
+function get($key, $default = NULL)
+{
+ return array_key_exists($key, $_GET) ? $_GET[$key] : $default;
+}
+
+function session($key, $default = NULL)
+{
+ return array_key_exists($key, $_SESSION) ? $_SESSION[$key] : $default;
+}
+
+# Based on http://stackoverflow.com/a/24014541/92396 but fixed and simplified:
+function paymentDue($timestamp, $months)
+{
+ $date = new DateTime('@' . $timestamp);
+ $next = clone $date;
+ $next->modify('last day of +' . $months . ' month');
+ if ($date->format('d') > $next->format('d')) {
+ $add = $date->diff($next);
+ } else {
+ $add = new DateInterval('P' . $months . 'M');
+ }
+ $newDate = $date->add($add);
+ return $newDate->getTimestamp();
+}