From 73ee662ec3a00efd56b3833a482c91dfddf6fc02 Mon Sep 17 00:00:00 2001 From: Pablo Ogando Ferreira Date: Fri, 13 Dec 2024 11:01:22 +0100 Subject: [PATCH] fix: add sync command to save on PMK recordings and then download and import --- Command/SyncMediaCommand.php | 79 ++++++++++++++++--- Resources/config/pumukit_blackboard.yaml | 8 +- Services/CollaborateAPIAuth.php | 11 +-- Services/CollaborateAPIConfiguration.php | 16 ++++ ...php => CollaborateAPICourseRecordings.php} | 7 +- Services/CollaborateAPIRecording.php | 52 ++++++++++++ Services/CollaborateAPISessionSearch.php | 76 ++++++++++++++++++ Services/LearnAPICourse.php | 2 +- ValueObject/CollaborateRecording.php | 55 +++++++++++++ 9 files changed, 282 insertions(+), 24 deletions(-) rename Services/{CollaborateAPIRecordings.php => CollaborateAPICourseRecordings.php} (84%) create mode 100644 Services/CollaborateAPIRecording.php create mode 100644 Services/CollaborateAPISessionSearch.php create mode 100644 ValueObject/CollaborateRecording.php diff --git a/Command/SyncMediaCommand.php b/Command/SyncMediaCommand.php index f9ec74f..3af7957 100644 --- a/Command/SyncMediaCommand.php +++ b/Command/SyncMediaCommand.php @@ -3,9 +3,12 @@ namespace Pumukit\BlackboardBundle\Command; use Pumukit\BlackboardBundle\Services\CollaborateAPIAuth; -use Pumukit\BlackboardBundle\Services\CollaborateAPIRecordings; +use Pumukit\BlackboardBundle\Services\CollaborateAPICourseRecordings; +use Pumukit\BlackboardBundle\Services\CollaborateAPIRecording; +use Pumukit\BlackboardBundle\Services\CollaborateAPISessionSearch; use Pumukit\BlackboardBundle\Services\LearnAPIAuth; use Pumukit\BlackboardBundle\Services\LearnAPICourse; +use Pumukit\BlackboardBundle\ValueObject\CollaborateRecording; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -15,19 +18,25 @@ class SyncMediaCommand extends Command private LearnAPIAuth $learnAPIAuth; private LearnAPICourse $learnAPICourse; private CollaborateAPIAuth $collaborateAPIAuth; - private CollaborateAPIRecordings $collaborateAPIRecordings; + private CollaborateAPICourseRecordings $collaborateAPICourseRecordings; + private CollaborateAPIRecording $collaborateAPIRecording; + private CollaborateAPISessionSearch $collaborateAPISessionSearch; public function __construct( LearnAPIAuth $learnAPIAuth, LearnAPICourse $learnAPICourse, CollaborateAPIAuth $collaborateAPIAuth, - CollaborateAPIRecordings $collaborateAPIRecordings, + CollaborateAPICourseRecordings $collaborateAPICourseRecordings, + CollaborateAPIRecording $collaborateAPIRecording, + CollaborateAPISessionSearch $collaborateAPISessionSearch, string $name = null ) { $this->learnAPIAuth = $learnAPIAuth; $this->learnAPICourse = $learnAPICourse; $this->collaborateAPIAuth = $collaborateAPIAuth; - $this->collaborateAPIRecordings = $collaborateAPIRecordings; + $this->collaborateAPICourseRecordings = $collaborateAPICourseRecordings; + $this->collaborateAPIRecording = $collaborateAPIRecording; + $this->collaborateAPISessionSearch = $collaborateAPISessionSearch; parent::__construct($name); } @@ -42,18 +51,66 @@ public function configure(): void public function execute(InputInterface $input, OutputInterface $output): int { - $output->writeln('***** Getting learn token to use api *****'); + $output->writeln('***** Getting learn token to use api *****'); $learnToken = $this->learnAPIAuth->getToken(); - $output->writeln('***** Getting list of courses *****'); - $courses = $this->learnAPICourse->getIdsFromCourses($learnToken); - - $output->writeln('***** Getting collaborate token to use api *****'); + $output->writeln('***** Getting collaborate token to use api *****'); $collaborateToken = $this->collaborateAPIAuth->getToken(); + $output->writeln('***** Getting list of courses *****'); + $courses = $this->learnAPICourse->getIdsFromCourses($learnToken); + $output->writeln('[COLLABORATE] Courses found '. count($courses) .''); + + $courseRecordings = []; + $output->writeln('***** Getting recordings from course *****'); foreach ($courses as $course) { - $output->writeln('***** Getting recordings from course *****'); - $this->collaborateAPIRecordings->getCourseRecordings($collaborateToken, $course); + $courseData = $this->collaborateAPICourseRecordings->getCourseRecordings($collaborateToken, $course); + if(isset($courseData['results']) && 0 !== count($courseData['results'])) { + $courseRecordings[$course] = $courseData['results']; + } + } + + foreach ($courseRecordings as $key => $recordings) { + foreach ($recordings as $element) { + $output->writeln('***** Getting data from recording '. $element['id'] .''); + + $recording = $this->collaborateAPIRecording->getRecordingData($collaborateToken, $element['id']); + if(!$recording || !isset($recording['mediaDownloadUrl'])) { + continue; + } + + $downloadUrl = $recording['mediaDownloadUrl']; + $session = $this->collaborateAPISessionSearch->searchBySessionName($collaborateToken, $element['sessionName']); + + //$enrollments = $this->collaborateAPISessionSearch->getEnrollmentsBySessionId($collaborateToken, '4d0d37cb688f431e973b6ac19ff10599'); + $enrollments = $this->collaborateAPISessionSearch->getEnrollmentsBySessionId($collaborateToken, '2f74e38cb29042a4af2da9d9c8398caa'); + var_dump($enrollments);die; + $collaborateRecording = CollaborateRecording::create($element['id'], $key, $element['sessionName'], $downloadUrl); + } + } + + die; + + $sessions = []; + foreach ($recordingSession as $key => $element) { + $session = $this->collaborateAPISessionSearch->searchBySessionName($collaborateToken, $element); + if(!isset($session['results'][0])) { + continue; + } + $sessions[] = $this->collaborateAPISessionSearch->getEnrollmentsBySessionId($collaborateToken, $session['results'][0]['id']); + } + + $users = []; + foreach ($sessions as $session) { + if(!isset($session['results'])) { + continue; + } + + foreach($session['results'] as $sessionResult) { + if($sessionResult['launchingRole'] === 'moderator') { + $users[] = $sessionResult['userId']; + } + } } return Command::SUCCESS; diff --git a/Resources/config/pumukit_blackboard.yaml b/Resources/config/pumukit_blackboard.yaml index aa3ba8d..c768daf 100644 --- a/Resources/config/pumukit_blackboard.yaml +++ b/Resources/config/pumukit_blackboard.yaml @@ -35,7 +35,11 @@ services: Pumukit\BlackboardBundle\Services\CollaborateAPIAuth: class: Pumukit\BlackboardBundle\Services\CollaborateAPIAuth - Pumukit\BlackboardBundle\Services\CollaborateAPIRecordings: - class: Pumukit\BlackboardBundle\Services\CollaborateAPIRecordings + Pumukit\BlackboardBundle\Services\CollaborateAPICourseRecordings: + class: Pumukit\BlackboardBundle\Services\CollaborateAPICourseRecordings + Pumukit\BlackboardBundle\Services\CollaborateAPIRecording: + class: Pumukit\BlackboardBundle\Services\CollaborateAPIRecording + Pumukit\BlackboardBundle\Services\CollaborateAPISessionSearch: + class: Pumukit\BlackboardBundle\Services\CollaborateAPISessionSearch diff --git a/Services/CollaborateAPIAuth.php b/Services/CollaborateAPIAuth.php index e94a587..95b9091 100644 --- a/Services/CollaborateAPIAuth.php +++ b/Services/CollaborateAPIAuth.php @@ -27,11 +27,8 @@ public function getToken(): string { try { $response = $this->client->request('POST', $this->configuration->apiTokenUrl(), [ - 'headers' => [ - 'Authorization' => 'Bearer '.$this->assertion, - 'Content-Type' => 'application/json', - ], - 'json' => $this->payload, + 'auth_basic' => [$this->configuration->key(), $this->configuration->secret()], + 'body' => $this->payload, 'verify_peer' => $this->verify_cert, ]); @@ -57,11 +54,11 @@ private function generateJWTPayload(): void ]; $this->header = [ - 'alg' => 'RS256', + 'alg' => 'HS256', 'typ' => 'JWT', ]; - $this->assertion = JWT::encode($claims, $this->configuration->secret(), 'RS256'); + $this->assertion = JWT::encode($claims, $this->configuration->secret(), 'HS256'); $this->grant_type = 'urn:ietf:params:oauth:grant-type:jwt-bearer'; $this->payload = [ diff --git a/Services/CollaborateAPIConfiguration.php b/Services/CollaborateAPIConfiguration.php index 9c6aacd..7627e24 100644 --- a/Services/CollaborateAPIConfiguration.php +++ b/Services/CollaborateAPIConfiguration.php @@ -45,4 +45,20 @@ public function apiRecordingUrl(): string { return $this->host().self::API_BASE_PATH.'/recordings'; } + + public function recordingDataUrl(string $recordingId): string + { + return $this->host().self::API_BASE_PATH.'/recordings/'.$recordingId.'/data'; + } + + public function recordingInfoUrl(string $recordingId): string + { + return $this->host().self::API_BASE_PATH.'/recordings/'.$recordingId; + } + + public function sessionDataUrl(): string + { + return $this->host().self::API_BASE_PATH.'/sessions'; + } + } diff --git a/Services/CollaborateAPIRecordings.php b/Services/CollaborateAPICourseRecordings.php similarity index 84% rename from Services/CollaborateAPIRecordings.php rename to Services/CollaborateAPICourseRecordings.php index 0b64e48..22ad2c6 100644 --- a/Services/CollaborateAPIRecordings.php +++ b/Services/CollaborateAPICourseRecordings.php @@ -5,7 +5,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Contracts\HttpClient\HttpClientInterface; -class CollaborateAPIRecordings +class CollaborateAPICourseRecordings { private HttpClientInterface $client; private CollaborateAPIConfiguration $configuration; @@ -32,12 +32,13 @@ private function recordings(string $accessToken, string $courseId): string 'Accept' => 'application/json', ], 'query' => [ - 'externalCourseId' => $courseId, +// 'contextId' => $courseId, + 'contextExtId' => $courseId, ], ]); if (Response::HTTP_OK !== $response->getStatusCode()) { - throw new \Exception('Unable to get courses. Response status code: '.$response->getStatusCode()); + throw new \Exception('Unable to get course recordings. Response status code: '.$response->getStatusCode()); } return $response->getContent(); diff --git a/Services/CollaborateAPIRecording.php b/Services/CollaborateAPIRecording.php new file mode 100644 index 0000000..ff59582 --- /dev/null +++ b/Services/CollaborateAPIRecording.php @@ -0,0 +1,52 @@ +client = $client; + $this->configuration = $configuration; + } + + public function getRecordingData(string $accessToken, string $recording): ?array + { + $path = $this->configuration->recordingDataUrl($recording); + $recordingsResponse = $this->recordings($accessToken, $path); + + return json_decode($recordingsResponse, true); + } + + public function getRecordingInfo(string $accessToken, string $recording): ?array + { + $path = $this->configuration->recordingInfoUrl($recording); + $recordingsResponse = $this->recordings($accessToken, $path); + + return json_decode($recordingsResponse, true); + } + + private function recordings(string $accessToken, string $path): ?string + { + $response = $this->client->request('GET', $path, [ + 'headers' => [ + 'Authorization' => 'Bearer '.$accessToken, + 'Accept' => 'application/json', + 'Content-Type' => 'application/json' + ], + 'verify_peer' => true, + ]); + + if (Response::HTTP_OK !== $response->getStatusCode()) { + return null; + } + + return $response->getContent(); + } +} diff --git a/Services/CollaborateAPISessionSearch.php b/Services/CollaborateAPISessionSearch.php new file mode 100644 index 0000000..1040536 --- /dev/null +++ b/Services/CollaborateAPISessionSearch.php @@ -0,0 +1,76 @@ +client = $client; + $this->configuration = $configuration; + } + + public function searchBySessionName(string $accessToken, string $sessionName): ?array + { + $sessionResponse = $this->sessionByName($accessToken, $sessionName); + + return json_decode($sessionResponse, true); + } + + public function getEnrollmentsBySessionId(string $accessToken, string $sessionId): ?array + { + $sessionResponse = $this->sessionById($accessToken, $sessionId); + + return json_decode($sessionResponse, true); + } + + private function sessionByName(string $accessToken, string $sessionName): ?string + { + try { + $response = $this->client->request('GET', $this->configuration->sessionDataUrl(), [ + 'headers' => [ + 'Authorization' => 'Bearer '.$accessToken, + 'Accept' => 'application/json', + ], +// 'query' => [ +// 'name' => $sessionName, +// ], + ]); + + if (Response::HTTP_OK !== $response->getStatusCode()) { + throw new \Exception('Unable to get session. Response status code: '.$response->getStatusCode()); + } + + return $response->getContent(); + } catch (\Exception $exception) { + throw new \Exception($exception->getMessage()); + } + } + + private function sessionById(string $accessToken, string $sesionId): ?string + { + try { + $response = $this->client->request('GET', $this->configuration->sessionDataUrl().'/'.$sesionId.'/enrollments', [ + 'headers' => [ + 'Authorization' => 'Bearer '.$accessToken, + 'Accept' => 'application/json', + ] + ]); + + if (Response::HTTP_OK !== $response->getStatusCode()) { + throw new \Exception('Unable to get session. Response status code: '.$response->getStatusCode()); + } + + return $response->getContent(); + } catch (\Exception $exception) { + throw new \Exception($exception->getMessage()); + } + } + +} diff --git a/Services/LearnAPICourse.php b/Services/LearnAPICourse.php index 646cce7..d685608 100644 --- a/Services/LearnAPICourse.php +++ b/Services/LearnAPICourse.php @@ -23,7 +23,7 @@ public function getIdsFromCourses(string $accessToken): array $courseIds = []; foreach ($courses['results'] as $course) { - $courseIds[] = $course['courseId']; + $courseIds[] = $course['uuid']; } return $courseIds; diff --git a/ValueObject/CollaborateRecording.php b/ValueObject/CollaborateRecording.php new file mode 100644 index 0000000..83c81b8 --- /dev/null +++ b/ValueObject/CollaborateRecording.php @@ -0,0 +1,55 @@ +id = $id; + $this->courseUUID = $courseUUID; + $this->downloadUrl = $downloadUrl; + $this->sessionName = $sessionName; + } + + public static function create(string $id, string $courseUUID, string $downloadUrl, string $sessionName): CollaborateRecording + { + return new self($id, $courseUUID, $downloadUrl, $sessionName); + } + + public function addUser(string $user): void + { + $this->user = $user; + } + + public function id(): string + { + return $this->id; + } + + public function courseUUID(): string + { + return $this->courseUUID; + } + + public function downloadUrl(): string + { + return $this->downloadUrl; + } + + public function sessionName(): string + { + return $this->sessionName; + } + + public function user(): ?string + { + return $this->user; + } +}