Jadi, Tin Can API adalah spesifikasi untuk program pembelajaran jarak jauh yang memungkinkan sistem pelatihan untuk berkomunikasi satu sama lain dengan melacak dan merekam semua sesi pelatihan. Informasi tentang kegiatan belajar disimpan dalam basis data khusus - LRS.
Detail dapat ditemukan di books.ifmo.ru/file/pdf/1772.pdf Bagian
2 dari artikel ini - https://habr.com/en/post/508882/
Fitur API Tin Can:
API Tin Can - penggantian yang diusulkan untuk spesifikasi SCORM
API Tin Can memungkinkan kami mencatat pengalaman belajar apa pun, yang memberi kami gambaran yang lebih lengkap tentang pembelajaran orang tertentu.
Tin Can API menghapus batasan data yang diberlakukan oleh LMS
Tin Can API dapat memberikan bantuan yang tak ternilai kepada departemen pendidikan dengan membandingkan data kualitas melakukan pekerjaan dengan data pelatihan, sehingga meningkatkan efektivitas pelatihan.
Ini adalah teori, sekarang praktik.
Ketika bekerja dengan SCORM, semuanya relatif sederhana, perlu "mengatur" nilai variabel tetap atau mendapatkan nilai variabel tetap.
Yah, misalnya ...
min = 0
max= 100
raw_score = 100
scaled = raw_score / max -- 0..1.
ScormSetValue("cmi.score.min", ""..min); --
ScormSetValue("cmi.score.max", ""..max); --
ScormSetValue("cmi.score.raw", ""..raw_score); --
ScormSetValue("cmi.score.scaled", ""..scaled); -- 0..1.
-- () 0..1
ScormSetValue("cmi.progress_measure", "1");
ScormSetValue("cmi.success_status", "passed");
ScormSetValue("cmi.completion_status", "completed");
ScormGetValue("cmi.learner_name");
ScormGetValue("cmi.learner_id");
ScormGetValue("cmi.suspend_data");
ScormGetValue("cmi.scaled_passing_score");
ScormGetValue("cmi.completion_threshold");
print ( ScormGetValue("cmi._version"))
print ( ScormGetValue("cmi.total_time"))
print ( ScormGetValue("cmi.time_limit_action"))
print ( ScormGetValue("cmi.max_time_allowed"))
--
ScormSetValue("cmi.interactions.0.id","Step1");
ScormSetValue("cmi.interactions.0.description", "17:14:28 ")
ScormSetValue("cmi.interactions.0.result","correct");
ScormSetValue("cmi.interactions.1.id","Step2");
ScormSetValue("cmi.interactions.1.type","fill-in");
ScormSetValue("cmi.interactions.1.objectives.0.id","urn:ADL:objectiveid-0001");
ScormSetValue("cmi.interactions.1.description", "privet");
ScormSetValue("cmi.interactions.1.learner_response", "privet");
ScormSetValue("cmi.interactions.1.timestamp", "2005-10-11T09:00:30");
ScormSetValue("cmi.interactions.1.correct_responses.0.pattern", "privet");
ScormSetValue("cmi.interactions.1.weighting", "1");
--correct, incorrect, unanticipated, neutral , number 0..1
ScormSetValue("cmi.interactions.1.result","unanticipated");
ScormSetValue("cmi.interactions.1.latency", "PT0H0M5.0S");
ScormSetValue ("cmi.comments_from_learner.0.comment",q1);
ScormSetValue ("cmi.comments_from_learner.1.comment",q2);
Begitulah semuanya terjadi ... Sekarang di xAPI ...
Berikutnya adalah daftar LRS di mana saya melakukan pengujian interaksi (pendaftaran dan penerimaan login / pass masing-masing diperlukan) ...
- host = " cloud.scorm.com/ScormEngineInterface/TCAPI/public/statements "
- host = " lrs.adlnet.gov/xapi/statements "
- // view - lrs.adlnet.gov/prototypes/StatementViewer/index.html
- watershed.ws/tc
- tincanapi.com/public-lrs
- host = " cloud.scorm.com/tc/QXTBX7C4VK/sandbox/statements "
- // view - cloud.scorm.com/sc/user/LRSView
Untuk berinteraksi dengan xAPI di C ++, kita perlu CURL dan beberapa pustaka untuk bekerja dengan JSON (cJSON misalnya) ...
Kemudian menggunakan xAPI dapat dilakukan seperti ini:
TinCanAddRecord("actor:::mbox:::mailto:mathmodel@mathmodel.com") TinCanAddRecord("actor:::name:::mathmodel")
TinCanAddRecord("actor:::objectType:::Agent")
TinCanAddRecord("verb:::id:::http://adlnet.gov/expapi/verbs/interacted")
TinCanAddRecord("object:::id:::http://lcontent.ru/lms1/simulator2")
TinCanAddRecord("object:::objectType:::Activity")
TinCanAddRecord("object:::definition:::type:::http://www.lcontent.ru/lms1/simulator1")
TinCanAddRecord("object:::definition:::name:::en-US:::mathmodel")
TinCanAddRecord("object:::definition:::description:::en-US:::mathmodel log")
TinCanAddRecord("object:::extensions:::http://lcontent.ru/upsv/Teapot1 angle:::" .. a1)
TinCanAddRecord("object:::extensions:::http://lcontent.ru/upsv/Teapot2 angle:::" .. a2)
TinCanAddRecord("object:::extensions:::http://lcontent.ru/upsv/Teapot3 angle:::" .. a3)
TinCanAddRecord("object:::extensions:::http://lcontent.ru/upsv/time:::" .. (os.clock() - veryoldtime))
TinCanAddRecord("actor:::mbox:::mailto:maxgammer@gmail.com")
TinCanAddRecord("actor:::name:::Maxim Gammer")
TinCanAddRecord("actor:::objectType:::Agent")
TinCanAddRecord("verb:::id:::http://adlnet.gov/expapi/verbs/interacted")
TinCanAddRecord("object:::id:::http://lcontent.ru/lms1/simulator2")
TinCanAddRecord("object:::objectType:::Activity")
TinCanAddRecord("object:::definition:::type:::http://lcontent.ru/lms1/simulator1")
TinCanAddRecord("object:::definition:::name:::en-US:::User move")
TinCanAddRecord("object:::definition:::description:::en-US:::User coordinates")
TinCanAddRecord("object:::extensions:::http://lcontent.ru/upsv/time:::" .. (os.clock() - veryoldtime))
TinCanAddRecord("object:::extensions:::http://lcontent.ru/upsv/X:::" .. UserData.X)
TinCanAddRecord("object:::extensions:::http://lcontent.ru/upsv/Y:::" .. UserData.Y)
TinCanAddRecord("object:::extensions:::http://lcontent.ru/upsv/Z:::" .. UserData.Z)
TinCanAddRecord("object:::extensions:::http://lcontent.ru/upsv/HeadYaw:::" .. UserData.HeadYaw)
TinCanAddRecord("object:::extensions:::http://lcontent.ru/upsv/HeadPitch:::" .. UserData.HeadPitch)
TinCanAddRecord("object:::extensions:::http://lcontent.ru/upsv/HeadRoll:::" .. UserData.HeadRoll)
Semuanya, kita melihat catatan di LRS.
"
InterfaceForTinCan.cpp
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string>
#include <string.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <algorithm>
#include <iterator>
#include <curl/curl.h>
//using namespace std;
#ifdef WIN32
#include "./cJSON.h"
#else
#include "./cJSON.h"
#endif
class InterfaceForTinCan
{
public:
InterfaceForTinCan();
void AddTinCanRecord(std::string str, std::string type);
void PostToLRS(std::string host, std::string login, std::string password);
void PostFileToLRS(std::string filename);
void PostToFile (std::string filename);
private:
std::vector<std::string> split(const std::string& s, const std::string& delim, const bool keep_empty = true ) ;
//
std::map <std::string, cJSON *> OBJECTS;
//
cJSON *top;
std::string LRS_host;
std::string LRS_login;
std::string LRS_password;
void PostStringToLRS(std::string zzz);
};
InterfaceForTinCan::InterfaceForTinCan()
{
top=cJSON_CreateObject();
}
void InterfaceForTinCan::AddTinCanRecord(std::string str, std::string type)
{
//1. (::: @@@)
std::vector<std::string> words = split(str, ":::");
// 2 =
int numOfObject = words.size();
//
std::string z = words [0];
if( OBJECTS.end() != OBJECTS.find(z))
{
//
}
else
{
//
OBJECTS[z] =cJSON_CreateObject();
// root
cJSON_AddItemToObject(top,z.c_str(), OBJECTS[z]);
}
for (int i=1; i < numOfObject -2; i++)
{
std::string oldz = z;
z = z + ":::" + words [i];
if( OBJECTS.end() != OBJECTS.find(z))
{
//
}
else
{
//
OBJECTS[z] =cJSON_CreateObject();
//
cJSON_AddItemToObject(OBJECTS[oldz], words [i].c_str(), OBJECTS[z]);
}
}
std::string value = words [numOfObject-1];
if (type=="string")
{
cJSON_AddStringToObject(OBJECTS[z], words [numOfObject-2].c_str(), value.c_str());
}
else if (type=="number")
{
cJSON_AddNumberToObject(OBJECTS[z], words [numOfObject-2].c_str(), std::stod(value));
}
else if (type=="bool")
{
bool val = false;
if ((value=="true")||(value=="TRUE")) val = true;
cJSON_AddBoolToObject(OBJECTS[z], words [numOfObject-2].c_str(), val);
}
}
void InterfaceForTinCan::PostToLRS(std::string host, std::string login, std::string password)
{
char* out=cJSON_Print(top);
std::string zzz = out;
cJSON_Delete(top);
OBJECTS.clear();
printf("%s\n",out);
free(out);
top=cJSON_CreateObject();
LRS_host = host;
LRS_login = login;
LRS_password = password;
PostStringToLRS(zzz);
}
void InterfaceForTinCan::PostFileToLRS(std::string filename)
{
std::string zzz;
std::string line;
std::ifstream myfile (filename.c_str());
if (myfile.is_open())
{
while ( myfile.good() )
{
getline (myfile,line);
zzz = zzz + line;
}
myfile.close();
}
//
PostStringToLRS(zzz);
}
void InterfaceForTinCan::PostToFile(std::string filename)
{
char* out=cJSON_Print(top);
std::string zzz = out;
cJSON_Delete(top);
OBJECTS.clear();
std::ofstream myfile;
myfile.open (filename);
myfile << zzz;
myfile.close();
free(out);
top=cJSON_CreateObject();
}
void InterfaceForTinCan::PostStringToLRS(std::string zzz)
{
std::string URL = LRS_host; //"https://cloud.scorm.com/ScormEngineInterface/TCAPI/public/statements";
std::string loginpassword = LRS_login + ":" + LRS_password; //"test:test"
CURL *curl;
struct curl_slist *headers=NULL;
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append( headers, "Content-Type: application/json");
headers = curl_slist_append( headers, "X-Experience-API-Version:1.0.0");
headers = curl_slist_append( headers, "charsets: utf-8");
curl = curl_easy_init();
if (curl)
{
/* enable verbose for easier tracing */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); //PUT
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
//
curl_easy_setopt( curl, CURLOPT_USERPWD, loginpassword.c_str() ); //"test:test"
// With the curl command line tool, you disable this with -k/--insecure.
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, zzz.c_str());
std::cout<< "..." << std::endl;
CURLcode res = curl_easy_perform(curl);
std::cout<< std::endl << "..." << std::endl;
/* Check for errors */
if(res != CURLE_OK)
{
std::cout<< "error:" << std::endl;
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
std::cout << std::endl;
}
curl_easy_cleanup(curl);
}
else
{
std::cout << "false" << std::endl;
}
}
std::vector<std::string> InterfaceForTinCan::split(const std::string& s, const std::string& delim, const bool keep_empty)
{
std::vector <std::string> result;
if (delim.empty())
{
result.push_back(s);
return result;
}
std::string::const_iterator substart = s.begin(), subend;
while (true)
{
subend = search(substart, s.end(), delim.begin(), delim.end());
std::string temp(substart, subend);
if (keep_empty || !temp.empty()) {
result.push_back(temp);
}
if (subend == s.end()) {
break;
}
substart = subend + delim.size();
}
return result;
}