Menggunakan xAPI (Tin Can) dan CMI5 di simulator

gambar
Meskipun SCORM 2004 masih bertahan, sudah waktunya untuk mulai mendukung standar baru. Hari ini kami akan mencoba menangani xAPI / TinCab / CMI5. Pastikan untuk menguji kode di situs web resmi www.SCORM.com dan www.adlnet.gov .



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) ...





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;
}





All Articles