I’ve been tooling around with coding an API for Udemy once I got my hands on the keys. I love parsing through data to create something useful. In this sense, I managed to code an extension page to my About Me section.
My Udemy Course Info – PC Affinity
What I did
I used 3 files to complete the information on that page. “udemy-include.php”, “udemyinfo.php”, and “udemyactivity-widget.php”. It’s a relatively simple PHP script that parses an API site using Curl. Most of the shared functions are within the udemy-include.php file; while extra functions are in each-other’s respective php file.
I coded this over the course of a few nights (on and off) using Notepad++. It has been years since I’ve coded in PHP, so it was a learning curve to figure out how to deal with json_decode’d arrays. After learning to view these arrays with ‘print_r’, learning how to pull the data using ‘->’ was a cinch. Udemy’s API is all over the place. I then had to split up the API calls within the foreach loops so that I could gather more data per course.
And the last thing that I wanted to mention was about wrapping my head around ‘date’ and ‘DateTime’. I learned the hard way that ‘date’ and ‘time’ are separate. And ‘strtotime’ was easily used to create new ‘date’ formats. It’s easier to think of ‘date’ as more of a formatted string. The ‘DateTime’ function, however, is closer to what I’m used to using in C#. It allowed me to get the exact date and time with the ability to view individual parts. This made calculation for my widget much easier.
My Code
udemy-include.php code:
<?php define('ID', "**********"); $token = 'Basic *************************************************************************************************************'; // This function uses curl to create a json decoded array from the url function CurlExec($url) { global $token; $ch = curl_init(); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch,CURLOPT_HTTPHEADER, array('Authorization: '.$token)); curl_setopt($ch, CURLINFO_HEADER_OUT, true); $result=curl_exec($ch); curl_close($ch); return json_decode($result); } // The following functions are just used to setup the url string to pass function GetUserCourses() { $url = "https://fmolhs.udemy.com/api-2.0/organizations/*****/analytics/user-course-activity/?user_email=".ID; return CurlExec($url); } function GetCourseInfo($cID) { $url = "https://www.udemy.com/api-2.0/courses/".$cID."?fields[course]=url,image_50x50,price&page_size=100"; return CurlExec($url); } function GetDayViewing($date) { if (!isset($date)) { $date = date('now'); } $url = "https://fmolhs.udemy.com/api-2.0/organizations/*****/analytics/user-activity/?user_email=".ID."&page_size=100&from_date=".$date."&to_date=".$date; return CurlExec($url); } function PrevActivity($date) { if (!isset($date)) { $date = date('now'); } $url = "https://fmolhs.udemy.com/api-2.0/organizations/*****/analytics/user-progress/?user_email=".ID."&page_size=100&from_date=".$date."&to_date=".$date; return CurlExec($url); } ?>
udemyinfo.php source:
<?php include_once('udemy-include.php'); // I decided to write all of the HTML code into a string. // You can use 'echo' instead, but I think this looks cleaner. // You can also move it into it's own function if doing it this way. $printString = ""; $printString .= "<hr>"; $result = GetUserCourses($user); $printString .= '<style type="text/css"> .info td { padding-top:20px; padding-bottom:20px; padding-right:20px; } .info td:first-child { padding-left:20px; padding-right:0; } </style>'; $sortedResults = $result->results; usort($sortedResults, "compare_course_title"); usort($sortedResults, "compare_completed_date"); usort($sortedResults, "compare_completion_ratio"); $cnt = 0; $printString .= "<table class=info>"; foreach ($sortedResults as $r) { $color = "#000000"; $completion = ""; $lastAccess = ""; if ($r->completion_ratio == 100.0) { $cnt++; $color = "#00ff00"; $completion = $r->course_completion_date; if ($completion != "") {$completion = " Completed (".explode('T',$completion,2)[0].")"; } } else if ($r->completion_ratio > 0) { $color = "#0000ff"; $lastAccess = $r->course_last_accessed_date; if ($lastAccess != "") {$lastAccess = " Last Viewed on (".explode('T',$lastAccess,2)[0].")"; } } $courseInfo = GetCourseInfo($r->course_id); $printString .= "<tr style='padding-bottom:10px;'><td>"; $printString .= "<a target=\"_new\" href=\"https://udemy.com".$courseInfo->url."\">"; $printString .= "<img src=\"".$courseInfo->image_50x50."\">"; $printString .= "</a></td><td>"; $printString .= "<a target=\"_new\" href=\"https://udemy.com".$courseInfo->url."\">"; $printString .= "<font color='".$color."';>"; $printString .= $font.$r->course_title; $printString .= "</font></a>"; $printString .= "<span style=\"vertical-align:super;padding-left:6px;\">"; $printString .= "<a style=\"text-decoration: none;\" target=\"_new\" href=\"https://fmolhs.udemy.com".$courseInfo->url."\">"; $printString .= "<font style=\"font-size: 7px;\">FMOL</font></a>"; $printString .= "</span>"; $printString .= "<br>".$r->completion_ratio."%".$completion.$lastAccess; if ($courseInfo->price == "") { $printString .= "<br>Course no longer available.<br><br>"; } #else #{ $printString .= "<br>".$courseInfo->price."<br><br>"; } $printString .= "</td></tr>"; } $printString .= "</table>"; $printString .= "<br> Completed ".$cnt."\\".$result->count; $printString .= "<br><br>"; // Print the HTML code: echo $printString; // The following functions are used to sort the results. function compare_course_title($a, $b) { return strcmp($a->course_title, $b->course_title); } function compare_completion_ratio($a, $b) { if ($a->completion_ratio == $b->completion_ratio) { return 0; } return ($a->completion_ratio < $b->completion_ratio) ? 1 : -1; } function compare_completed_date($a, $b) { if ($a->course_completion_date == $b->course_completion_date) { return 0; } return ($a->course_completion_date < $b->course_completion_date) ? 1 : -1; } function compare_accessed_date($a, $b) { if ($a->course_last_accessed_date == $b->course_last_accessed_date) { return 0; } return ($a->course_last_accessed_date < $b->course_last_accessed_date) ? 1 : -1; } ?>
PHP calling the widget:
<?php // My widget is unable to see the permalink or query_string. // So I pass both with the include so I can use them. include('https://pcaffinity.com/files/scripts/udemyactivity-widget.php?perm='.get_permalink().'&'.$_SERVER['QUERY_STRING']); ?>
udemyactivity-widget.php
<?php include_once('udemy-include.php'); $url_components = parse_url($_SERVER['HTTP_HOST'].'?'.$_SERVER['QUERY_STRING']); // Use parse_str() function to parse the string passed via URL parse_str($url_components['query'], $params); // Permalink saved from the widget call. $perm = $params['perm']; // ID updated from forms $dtID = $params['dtID']; $prev = 0; // API is updated at 12pm CST // Do not allow date before API updates $dt = new DateTime(); if ((int)$dt->H >= 12) { $prev = -1; } else { $prev = -2; } if ($dtID == "") { $dtID = $prev; } // Set past date depending on when the API was updated. // Or depending on if a dtID parameter was found. $date = date("Y-m-d h:i:s", strtotime(strval($dtID)." Days")); // I could have saved all HTML to a string before using echo. // However, this works just fine and is an alternate example. echo "<div style='float:left; padding-right:10px;'>"; echo "Activity for ".date('Y-m-d', strtotime($date)); echo "</div>"; echo "<div style='float:left;'>"; echo '<form action="'.$perm.'?dtID='.strval($dtID - 1).'" method="post">'; echo '<input type="submit" name="dtadj" value="-" />'; echo '</form>'; echo "</div>"; echo "<div style='float:left;'>"; echo '<form action="'.$perm.'?dtID='.strval($dtID + 1).'" method="post">'; if ($dtID >= $prev) { $dtID = $prev; echo '<input type="submit" name="dtadjp" value=" " disabled/>'; } else { echo '<input type="submit" name="dtadjm" value="+"/>'; } echo '</form>'; echo "</div>"; $printString = GetPrevActivity($date); //print all Activity HTML echo $printString; // Code used to get and print the Activity. // Turned into a function so it can be reusable in the future. function GetPrevActivity($d) { $activityResult = PrevActivity($d); #print_r($activityResult); $cnt = $activityResult->{'count'}; $tdstyle = "font-size:10px; padding-top:0px; padding-bottom:0px; padding-right:0px; border-width:1px; border-style:solid;"; $tablestyle = "width:98%; order-width:1px; border-style:solid;"; $divstyle = "width:280px; height:".($cnt > 0 ? '150' : '20')."px; overflow:scroll; overflow-x:hidden; border-width:1px; border-style:solid;"; $pString = ""; $pString .= "<div style='".$divstyle."'><table style='".$tablestyle."'>"; foreach ($activityResult->results as $r) { $d1 = new DateTime($r->item_start_time); #print_r($d1); $d2 = new DateTime($r->item_completion_time); $diff = $d1->diff($d2); $pString .= "<tr><td style='".$tdstyle."'>"; $pString .= $r->course_title."<br>"; $pString .= $r->item_title."<br>"; $pString .= $r->item_type." - ".$diff->i.":".$diff->s."s"; $pString .= "</td></tr>"; } $pString .= "</table></div>"; $viewingResult = GetDayViewing($d); $track = 0; foreach ($viewingResult->results as $r) { $track += $r->num_video_consumed_minutes; } $trackTime = convertToHoursMins($track); if ($trackTime == "") { $trackTime = "0 Hours, 0 Minutes"; } $pString .= "<center>".$trackTime."</center>"; return $pString; } // Simple function to return a time format used above. function convertToHoursMins($time, $format = '%02d Hour(s), %02d Minute(s)') { if ($time < 1) { return; } $hours = floor($time / 60); $minutes = ($time % 60); return sprintf($format, $hours, $minutes); } ?>
Hope you enjoyed. If you have any questions I will try my best to answer them.
Thanks!