From dea3e74b6adb0f983a00d08e5a026d7dad50477f Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Tue, 28 Nov 2023 10:19:55 +0000 Subject: [PATCH 01/21] Implement Job based hamster --- Dockerfile | 3 +- app/cli.php | 4 + app/init.php | 4 + app/worker.php | 4 + bin/worker-hamster | 3 + docker-compose.yml | 31 ++ src/Appwrite/Event/Event.php | 3 + src/Appwrite/Event/Hamster.php | 153 +++++++ src/Appwrite/Platform/Services/Workers.php | 2 + src/Appwrite/Platform/Tasks/Hamster.php | 373 +++-------------- src/Appwrite/Platform/Workers/Hamster.php | 454 +++++++++++++++++++++ 11 files changed, 708 insertions(+), 326 deletions(-) create mode 100644 bin/worker-hamster create mode 100644 src/Appwrite/Event/Hamster.php create mode 100644 src/Appwrite/Platform/Workers/Hamster.php diff --git a/Dockerfile b/Dockerfile index 059c499bd..599e4ea70 100755 --- a/Dockerfile +++ b/Dockerfile @@ -105,7 +105,8 @@ RUN chmod +x /usr/local/bin/hamster && \ chmod +x /usr/local/bin/delete-orphaned-projects && \ chmod +x /usr/local/bin/clear-card-cache && \ chmod +x /usr/local/bin/calc-users-stats && \ - chmod +x /usr/local/bin/calc-tier-stats + chmod +x /usr/local/bin/calc-tier-stats && \ + chmod +x /usr/local/bin/worker-hamster # Letsencrypt Permissions RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/ diff --git a/app/cli.php b/app/cli.php index 643a615c4..003f3a1f7 100644 --- a/app/cli.php +++ b/app/cli.php @@ -6,6 +6,7 @@ require_once __DIR__ . '/controllers/general.php'; use Appwrite\Event\Delete; use Appwrite\Event\Certificate; use Appwrite\Event\Func; +use Appwrite\Event\Hamster; use Appwrite\Platform\Appwrite; use Utopia\CLI\CLI; use Utopia\Database\Validator\Authorization; @@ -154,6 +155,9 @@ CLI::setResource('queue', function (Group $pools) { CLI::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); +CLI::setResource('queueForHamster', function (Connection $queue) { + return new Hamster($queue); +}, ['queue']); CLI::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); diff --git a/app/init.php b/app/init.php index 2c0219eec..c30eb77e8 100644 --- a/app/init.php +++ b/app/init.php @@ -72,6 +72,7 @@ use Ahc\Jwt\JWTException; use Appwrite\Event\Build; use Appwrite\Event\Certificate; use Appwrite\Event\Func; +use Appwrite\Event\Hamster; use MaxMind\Db\Reader; use PHPMailer\PHPMailer\PHPMailer; use Swoole\Database\PDOProxy; @@ -916,6 +917,9 @@ App::setResource('queueForCertificates', function (Connection $queue) { App::setResource('queueForMigrations', function (Connection $queue) { return new Migration($queue); }, ['queue']); +App::setResource('queueForHamster', function (Connection $queue) { + return new Hamster($queue); +}, ['queue']); App::setResource('usage', function ($register) { return new Stats($register->get('statsd')); }, ['register']); diff --git a/app/worker.php b/app/worker.php index 32a8b9804..4f7355311 100644 --- a/app/worker.php +++ b/app/worker.php @@ -9,6 +9,7 @@ use Appwrite\Event\Certificate; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Delete; use Appwrite\Event\Func; +use Appwrite\Event\Hamster; use Appwrite\Event\Mail; use Appwrite\Event\Messaging; use Appwrite\Event\Migration; @@ -155,6 +156,9 @@ Server::setResource('queueForCertificates', function (Connection $queue) { Server::setResource('queueForMigrations', function (Connection $queue) { return new Migration($queue); }, ['queue']); +Server::setResource('queueForHamster', function (Connection $queue) { + return new Hamster($queue); +}, ['queue']); Server::setResource('logger', function (Registry $register) { return $register->get('logger'); }, ['register']); diff --git a/bin/worker-hamster b/bin/worker-hamster new file mode 100644 index 000000000..b388dd13c --- /dev/null +++ b/bin/worker-hamster @@ -0,0 +1,3 @@ +#!/bin/sh + +php /usr/src/code/app/worker.php hamster $@ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 42091e5e4..da9b0e51e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -717,6 +717,37 @@ services: environment: - _APP_ASSISTANT_OPENAI_API_KEY + appwrite-worker-hamster: + entrypoint: worker-hamster + <<: *x-logging + container_name: appwrite-worker-hamster + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_LOGGING_PROVIDER + - _APP_MIXPANEL_TOKEN + openruntimes-executor: container_name: openruntimes-executor hostname: appwrite-executor diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index 46b430d12..fc12c5b5b 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -42,6 +42,9 @@ class Event public const MIGRATIONS_QUEUE_NAME = 'v1-migrations'; public const MIGRATIONS_CLASS_NAME = 'MigrationsV1'; + public const HAMSTER_QUEUE_NAME = 'v1-hamster'; + public const HAMSTER_CLASS_NAME = 'HamsterV1'; + protected string $queue = ''; protected string $class = ''; protected string $event = ''; diff --git a/src/Appwrite/Event/Hamster.php b/src/Appwrite/Event/Hamster.php new file mode 100644 index 000000000..9ae730367 --- /dev/null +++ b/src/Appwrite/Event/Hamster.php @@ -0,0 +1,153 @@ +setQueue(Event::HAMSTER_QUEUE_NAME) + ->setClass(Event::HAMSTER_CLASS_NAME); + } + + /** + * Sets the type for the hamster event. + * + * @param string $type + * @return self + */ + public function setType(string $type): self + { + $this->type = $type; + + return $this; + } + + /** + * Returns the set type for the hamster event. + * + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * Sets the project for the hamster event. + * + * @param Document $project + */ + public function setProject(Document $project): self + { + $this->project = $project; + + return $this; + } + + /** + * Returns the set project for the hamster event. + * + * @return Document + */ + public function getProject(): Document + { + return $this->project; + } + + /** + * Sets the organization for the hamster event. + * + * @param Document $organization + */ + public function setOrganization(Document $organization): self + { + $this->organization = $organization; + + return $this; + } + + /** + * Returns the set organization for the hamster event. + * + * @return string + */ + public function getOrganization(): Document + { + return $this->organization; + } + + /** + * Sets the user for the hamster event. + * + * @param Document $user + */ + public function setUser(Document $user): self + { + $this->user = $user; + + return $this; + } + + /** + * Returns the set user for the hamster event. + * + * @return Document + */ + public function getUser(): Document + { + return $this->user; + } + + /** + * Executes the function event and sends it to the functions worker. + * + * @return string|bool + * @throws \InvalidArgumentException + */ + public function trigger(): string|bool + { + if ($this->paused) { + return false; + } + + $client = new Client($this->queue, $this->connection); + + $events = $this->getEvent() ? Event::generateEvents($this->getEvent(), $this->getParams()) : null; + + return $client->enqueue([ + 'type' => $this->type, + 'project' => $this->project, + 'organization' => $this->organization, + 'user' => $this->user, + 'events' => $events, + ]); + } + + /** + * Generate a function event from a base event + * + * @param Event $event + * + * @return self + * + */ + public function from(Event $event): self + { + $this->event = $event->getEvent(); + $this->params = $event->getParams(); + return $this; + } +} diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index 07fc25434..c5a051476 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -12,6 +12,7 @@ use Appwrite\Platform\Workers\Databases; use Appwrite\Platform\Workers\Functions; use Appwrite\Platform\Workers\Builds; use Appwrite\Platform\Workers\Deletes; +use Appwrite\Platform\Workers\Hamster; use Appwrite\Platform\Workers\Migrations; class Workers extends Service @@ -30,6 +31,7 @@ class Workers extends Service ->addAction(Builds::getName(), new Builds()) ->addAction(Deletes::getName(), new Deletes()) ->addAction(Migrations::getName(), new Migrations()) + ->addAction(Hamster::getName(), new Hamster()) ; } diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index 1d5d3b0b2..fea4c88ad 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -2,6 +2,7 @@ namespace Appwrite\Platform\Tasks; +use Appwrite\Event\Hamster as EventHamster; use Appwrite\Network\Validator\Origin; use Exception; use Utopia\App; @@ -19,20 +20,6 @@ use Utopia\Pools\Group; class Hamster extends Action { - private array $metrics = [ - 'usage_files' => 'files.$all.count.total', - 'usage_buckets' => 'buckets.$all.count.total', - 'usage_databases' => 'databases.$all.count.total', - 'usage_documents' => 'documents.$all.count.total', - 'usage_collections' => 'collections.$all.count.total', - 'usage_storage' => 'project.$all.storage.size', - 'usage_requests' => 'project.$all.network.requests', - 'usage_bandwidth' => 'project.$all.network.bandwidth', - 'usage_users' => 'users.$all.count.total', - 'usage_sessions' => 'sessions.email.requests.create', - 'usage_executions' => 'executions.$all.compute.total', - ]; - protected string $directory = '/usr/local'; protected string $path; @@ -60,233 +47,20 @@ class Hamster extends Action }); } - private function getStatsPerProject(Group $pools, Cache $cache, Database $dbForConsole) + private function getStatsPerProject(Group $pools, Database $dbForConsole) { - $this->calculateByGroup('projects', $dbForConsole, function (Database $dbForConsole, Document $project) use ($pools, $cache) { - /** - * Skip user projects with id 'console' - */ - if ($project->getId() === 'console') { - Console::info("Skipping project console"); - return; - } + $this->calculateByGroup('projects', $dbForConsole, function (Database $dbForConsole, Document $project) use ($pools) { + $queue = $pools->get('queue')->pop(); + $connection = $queue->getResource(); - Console::log("Getting stats for {$project->getId()}"); + $hamsterTask = new EventHamster($connection); - try { - $db = $project->getAttribute('database'); - $adapter = $pools - ->get($db) - ->pop() - ->getResource(); + $hamsterTask + ->setType('project') + ->setProject($project) + ->trigger(); - $dbForProject = new Database($adapter, $cache); - $dbForProject->setDefaultDatabase('appwrite'); - $dbForProject->setNamespace('_' . $project->getInternalId()); - - $statsPerProject = []; - - $statsPerProject['time'] = microtime(true); - - /** Get Project ID */ - $statsPerProject['project_id'] = $project->getId(); - - /** Get project created time */ - $statsPerProject['project_created'] = $project->getAttribute('$createdAt'); - - /** Get Project Name */ - $statsPerProject['project_name'] = $project->getAttribute('name'); - - /** Total Project Variables */ - $statsPerProject['custom_variables'] = $dbForProject->count('variables', [], APP_LIMIT_COUNT); - - /** Total Migrations */ - $statsPerProject['custom_migrations'] = $dbForProject->count('migrations', [], APP_LIMIT_COUNT); - - /** Get Custom SMTP */ - $smtp = $project->getAttribute('smtp', null); - if ($smtp) { - $statsPerProject['custom_smtp_status'] = $smtp['enabled'] === true ? 'enabled' : 'disabled'; - - /** Get Custom Templates Count */ - $templates = array_keys($project->getAttribute('templates', [])); - $statsPerProject['custom_email_templates'] = array_filter($templates, function ($template) { - return str_contains($template, 'email'); - }); - $statsPerProject['custom_sms_templates'] = array_filter($templates, function ($template) { - return str_contains($template, 'sms'); - }); - } - - /** Get total relationship attributes */ - $statsPerProject['custom_relationship_attributes'] = $dbForProject->count('attributes', [ - Query::equal('type', ['relationship']) - ], APP_LIMIT_COUNT); - - /** Get Total Functions */ - $statsPerProject['custom_functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT); - - foreach (\array_keys(Config::getParam('runtimes')) as $runtime) { - $statsPerProject['custom_functions_' . $runtime] = $dbForProject->count('functions', [ - Query::equal('runtime', [$runtime]), - ], APP_LIMIT_COUNT); - } - - /** Get Total Deployments */ - $statsPerProject['custom_deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT); - $statsPerProject['custom_deployments_manual'] = $dbForProject->count('deployments', [ - Query::equal('type', ['manual']) - ], APP_LIMIT_COUNT); - $statsPerProject['custom_deployments_git'] = $dbForProject->count('deployments', [ - Query::equal('type', ['vcs']) - ], APP_LIMIT_COUNT); - - /** Get VCS repos connected */ - $statsPerProject['custom_vcs_repositories'] = $dbForConsole->count('repositories', [ - Query::equal('projectInternalId', [$project->getInternalId()]) - ], APP_LIMIT_COUNT); - - /** Get Total Teams */ - $statsPerProject['custom_teams'] = $dbForProject->count('teams', [], APP_LIMIT_COUNT); - - /** Get Total Members */ - $teamInternalId = $project->getAttribute('teamInternalId', null); - if ($teamInternalId) { - $statsPerProject['custom_organization_members'] = $dbForConsole->count('memberships', [ - Query::equal('teamInternalId', [$teamInternalId]) - ], APP_LIMIT_COUNT); - } else { - $statsPerProject['custom_organization_members'] = 0; - } - - /** Get Email and Name of the project owner */ - if ($teamInternalId) { - $membership = $dbForConsole->findOne('memberships', [ - Query::equal('teamInternalId', [$teamInternalId]), - ]); - - if (!$membership || $membership->isEmpty()) { - throw new Exception('Membership not found. Skipping project : ' . $project->getId()); - } - - $userId = $membership->getAttribute('userId', null); - if ($userId) { - $user = $dbForConsole->getDocument('users', $userId); - $statsPerProject['email'] = $user->getAttribute('email', null); - $statsPerProject['name'] = $user->getAttribute('name', null); - } - } - - /** Get Domains */ - $statsPerProject['custom_domains'] = $dbForConsole->count('rules', [ - Query::equal('projectInternalId', [$project->getInternalId()]), - Query::limit(APP_LIMIT_COUNT) - ]); - - /** Get Platforms */ - $platforms = $dbForConsole->find('platforms', [ - Query::equal('projectInternalId', [$project->getInternalId()]), - Query::limit(APP_LIMIT_COUNT) - ]); - - $statsPerProject['custom_platforms_web'] = sizeof(array_filter($platforms, function ($platform) { - return $platform['type'] === 'web'; - })); - - $statsPerProject['custom_platforms_android'] = sizeof(array_filter($platforms, function ($platform) { - return $platform['type'] === 'android'; - })); - - $statsPerProject['custom_platforms_apple'] = sizeof(array_filter($platforms, function ($platform) { - return str_contains($platform['type'], 'apple'); - })); - - $statsPerProject['custom_platforms_flutter'] = sizeof(array_filter($platforms, function ($platform) { - return str_contains($platform['type'], 'flutter'); - })); - - $flutterPlatforms = [Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_FLUTTER_LINUX]; - - foreach ($flutterPlatforms as $flutterPlatform) { - $statsPerProject['custom_platforms_' . $flutterPlatform] = sizeof(array_filter($platforms, function ($platform) use ($flutterPlatform) { - return $platform['type'] === $flutterPlatform; - })); - } - - $statsPerProject['custom_platforms_api_keys'] = $dbForConsole->count('keys', [ - Query::equal('projectInternalId', [$project->getInternalId()]), - Query::limit(APP_LIMIT_COUNT) - ]); - - /** Get Usage $statsPerProject */ - $periods = [ - 'infinity' => [ - 'period' => '1d', - 'limit' => 90, - ], - '24h' => [ - 'period' => '1h', - 'limit' => 24, - ], - ]; - - Authorization::skip(function () use ($dbForProject, $periods, &$statsPerProject) { - foreach ($this->metrics as $key => $metric) { - foreach ($periods as $periodKey => $periodValue) { - $limit = $periodValue['limit']; - $period = $periodValue['period']; - - $requestDocs = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); - - $statsPerProject[$key . '_' . $periodKey] = []; - foreach ($requestDocs as $requestDoc) { - $statsPerProject[$key . '_' . $periodKey][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; - } - - $statsPerProject[$key . '_' . $periodKey] = array_reverse($statsPerProject[$key . '_' . $periodKey]); - // Calculate aggregate of each metric - $statsPerProject[$key . '_' . $periodKey] = array_sum(array_column($statsPerProject[$key . '_' . $periodKey], 'value')); - } - } - }); - - if (isset($statsPerProject['email'])) { - /** Send data to mixpanel */ - $res = $this->mixpanel->createProfile($statsPerProject['email'], '', [ - 'name' => $statsPerProject['name'], - 'email' => $statsPerProject['email'] - ]); - - if (!$res) { - Console::error('Failed to create user profile for project: ' . $project->getId()); - } - } - - $event = new Event(); - $event - ->setName('Project Daily Usage') - ->setProps($statsPerProject); - $res = $this->mixpanel->createEvent($event); - - if (!$res) { - Console::error('Failed to create event for project: ' . $project->getId()); - } - } catch (Exception $e) { - Console::error('Failed to send stats for project: ' . $project->getId()); - Console::error($e->getMessage()); - } finally { - $pools - ->get($db) - ->reclaim(); - } + $queue->reclaim(); }); } @@ -305,6 +79,8 @@ class Hamster extends Action $next->setTimezone(new \DateTimeZone(date_default_timezone_get())); $delay = $next->getTimestamp() - $now->getTimestamp(); + $delay = 5; + /** * If time passed for the target day. */ @@ -323,17 +99,17 @@ class Hamster extends Action /* Initialise new Utopia app */ $app = new App('UTC'); - Console::info('Getting stats for all projects'); - $this->getStatsPerProject($pools, $cache, $dbForConsole); - Console::success('Completed getting stats for all projects'); + Console::info('Queuing stats for all projects'); + $this->getStatsPerProject($pools, $dbForConsole); + Console::success('Completed queuing stats for all projects'); - Console::info('Getting stats for all organizations'); - $this->getStatsPerOrganization($dbForConsole); - Console::success('Completed getting stats for all organizations'); + Console::info('Queuing stats for all organizations'); + $this->getStatsPerOrganization($pools, $dbForConsole); + Console::success('Completed queuing stats for all organizations'); - Console::info('Getting stats for all users'); - $this->getStatsPerUser($dbForConsole); - Console::success('Completed getting stats for all users'); + Console::info('Queuing stats for all users'); + $this->getStatsPerUser($pools, $dbForConsole); + Console::success('Completed queuing stats for all users'); $pools ->get('console') @@ -378,96 +154,43 @@ class Hamster extends Action Console::log("Processed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); } - protected function getStatsPerOrganization(Database $dbForConsole) + protected function getStatsPerOrganization(Group $pools, Database $dbForConsole) { - $this->calculateByGroup('teams', $dbForConsole, function (Database $dbForConsole, Document $document) { + $this->calculateByGroup('teams', $dbForConsole, function (Database $dbForConsole, Document $organization) use ($pools) { try { - $statsPerOrganization = []; - - /** Organization name */ - $statsPerOrganization['name'] = $document->getAttribute('name'); - - /** Get Email and of the organization owner */ - $membership = $dbForConsole->findOne('memberships', [ - Query::equal('teamInternalId', [$document->getInternalId()]), - ]); - - if (!$membership || $membership->isEmpty()) { - throw new Exception('Membership not found. Skipping organization : ' . $document->getId()); - } - - $userId = $membership->getAttribute('userId', null); - if ($userId) { - $user = $dbForConsole->getDocument('users', $userId); - $statsPerOrganization['email'] = $user->getAttribute('email', null); - } - - /** Organization Creation Date */ - $statsPerOrganization['created'] = $document->getAttribute('$createdAt'); - - /** Number of team members */ - $statsPerOrganization['members'] = $document->getAttribute('total'); - - /** Number of projects in this organization */ - $statsPerOrganization['projects'] = $dbForConsole->count('projects', [ - Query::equal('teamId', [$document->getId()]), - Query::limit(APP_LIMIT_COUNT) - ]); - - if (!isset($statsPerOrganization['email'])) { - throw new Exception('Email not found. Skipping organization : ' . $document->getId()); - } - - $event = new Event(); - $event - ->setName('Organization Daily Usage') - ->setProps($statsPerOrganization); - $res = $this->mixpanel->createEvent($event); - if (!$res) { - throw new Exception('Failed to create event for organization : ' . $document->getId()); - } + $queue = $pools->get('queue')->pop(); + $connection = $queue->getResource(); + + $hamsterTask = new EventHamster($connection); + + $hamsterTask + ->setType('organization') + ->setOrganization($organization) + ->trigger(); + + $queue->reclaim(); } catch (Exception $e) { Console::error($e->getMessage()); } }); } - protected function getStatsPerUser(Database $dbForConsole) + protected function getStatsPerUser(Group $pools, Database $dbForConsole) { - $this->calculateByGroup('users', $dbForConsole, function (Database $dbForConsole, Document $document) { + $this->calculateByGroup('users', $dbForConsole, function (Database $dbForConsole, Document $user) use ($pools) { try { - $statsPerUser = []; - - /** Organization name */ - $statsPerUser['name'] = $document->getAttribute('name'); - - /** Organization ID (needs to be stored as an email since mixpanel uses the email attribute as a distinctID) */ - $statsPerUser['email'] = $document->getAttribute('email'); - - /** Organization Creation Date */ - $statsPerUser['created'] = $document->getAttribute('$createdAt'); - - /** Number of teams this user is a part of */ - $statsPerUser['memberships'] = $dbForConsole->count('memberships', [ - Query::equal('userInternalId', [$document->getInternalId()]), - Query::limit(APP_LIMIT_COUNT) - ]); - - if (!isset($statsPerUser['email'])) { - throw new Exception('User has no email: ' . $document->getId()); - } - - /** Send data to mixpanel */ - $event = new Event(); - $event - ->setName('User Daily Usage') - ->setProps($statsPerUser); - $res = $this->mixpanel->createEvent($event); - - if (!$res) { - throw new Exception('Failed to create user profile for user: ' . $document->getId()); - } + $queue = $pools->get('queue')->pop(); + $connection = $queue->getResource(); + + $hamsterTask = new EventHamster($connection); + + $hamsterTask + ->setType('user') + ->setUser($user) + ->trigger(); + + $queue->reclaim(); } catch (Exception $e) { Console::error($e->getMessage()); } diff --git a/src/Appwrite/Platform/Workers/Hamster.php b/src/Appwrite/Platform/Workers/Hamster.php new file mode 100644 index 000000000..d1c03744c --- /dev/null +++ b/src/Appwrite/Platform/Workers/Hamster.php @@ -0,0 +1,454 @@ + 'files.$all.count.total', + 'usage_buckets' => 'buckets.$all.count.total', + 'usage_databases' => 'databases.$all.count.total', + 'usage_documents' => 'documents.$all.count.total', + 'usage_collections' => 'collections.$all.count.total', + 'usage_storage' => 'project.$all.storage.size', + 'usage_requests' => 'project.$all.network.requests', + 'usage_bandwidth' => 'project.$all.network.bandwidth', + 'usage_users' => 'users.$all.count.total', + 'usage_sessions' => 'sessions.email.requests.create', + 'usage_executions' => 'executions.$all.compute.total', + ]; + + protected string $directory = '/usr/local'; + + protected string $path; + + protected string $date; + + protected Mixpanel $mixpanel; + + public static function getName(): string + { + return 'hamster'; + } + + /** + * @throws \Exception + */ + public function __construct() + { + $this->mixpanel = new Mixpanel(App::getEnv('_APP_MIXPANEL_TOKEN', '')); + + $this + ->desc('Hamster worker') + ->inject('message') + ->inject('pools') + ->inject('cache') + ->inject('dbForConsole') + ->inject('queueForHamster') + ->inject('queueForEvents') + ->inject('usage') + ->inject('log') + ->callback(fn (Message $message, Group $pools, Cache $cache, Database $dbForConsole, EventHamster $queueForHamster, Event $queueForEvents, Stats $usage, Log $log) => $this->action($message, $pools, $cache, $dbForConsole, $queueForHamster, $queueForEvents, $usage, $log)); + } + + /** + * @param Message $message + * @param Group $pools + * @param Cache $cache + * @param Database $dbForConsole + * @param EventHamster $queueForHamster + * @param Event $queueForEvents + * @param Stats $usage + * @param Log $log + * @return void + * @throws Authorization + * @throws Structure + * @throws \Utopia\Database\Exception + * @throws Conflict + */ + public function action(Message $message, Group $pools, Cache $cache, Database $dbForConsole, EventHamster $queueForHamster, Event $queueForEvents, Stats $usage, Log $log): void + { + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new \Exception('Missing payload'); + } + + + $payload = $message->getPayload() ?? []; + + if (empty($payload)) { + throw new \Exception('Missing payload'); + } + + $type = $payload['type'] ?? ''; + + switch ($type) { + case 'project': + $this->getStatsForProject(new Document($payload['project']), $pools, $cache, $dbForConsole, $log); + break; + case 'organization': + $this->getStatsForOrganization(new Document($payload['organization']), $pools, $cache, $dbForConsole, $log); + break; + case 'user': + $this->getStatsPerUser(new Document($payload['user']), $dbForConsole); + break; + } + } + + /** + * @param Document $project + * @param Group $pools + * @param Cache $cache + * @param Database $dbForConsole + * @param Log $log + * @throws \Utopia\Database\Exception + */ + private function getStatsForProject(Document $project, Group $pools, Cache $cache, Database $dbForConsole, Log $log): void + { + /** + * Skip user projects with id 'console' + */ + if ($project->getId() === 'console') { + Console::info("Skipping project console"); + return; + } + + Console::log("Getting stats for {$project->getId()}"); + + + try { + $db = $project->getAttribute('database'); + $adapter = $pools + ->get($db) + ->pop() + ->getResource(); + + $dbForProject = new Database($adapter, $cache); + $dbForProject->setDefaultDatabase('appwrite'); + $dbForProject->setNamespace('_' . $project->getInternalId()); + + $statsPerProject = []; + + $statsPerProject['time'] = microtime(true); + + /** Get Project ID */ + $statsPerProject['project_id'] = $project->getId(); + + /** Get project created time */ + $statsPerProject['project_created'] = $project->getAttribute('$createdAt'); + + /** Get Project Name */ + $statsPerProject['project_name'] = $project->getAttribute('name'); + + /** Total Project Variables */ + $statsPerProject['custom_variables'] = $dbForProject->count('variables', [], APP_LIMIT_COUNT); + + /** Total Migrations */ + $statsPerProject['custom_migrations'] = $dbForProject->count('migrations', [], APP_LIMIT_COUNT); + + /** Get Custom SMTP */ + $smtp = $project->getAttribute('smtp', null); + if ($smtp) { + $statsPerProject['custom_smtp_status'] = $smtp['enabled'] === true ? 'enabled' : 'disabled'; + + /** Get Custom Templates Count */ + $templates = array_keys($project->getAttribute('templates', [])); + $statsPerProject['custom_email_templates'] = array_filter($templates, function ($template) { + return str_contains($template, 'email'); + }); + $statsPerProject['custom_sms_templates'] = array_filter($templates, function ($template) { + return str_contains($template, 'sms'); + }); + } + + /** Get total relationship attributes */ + $statsPerProject['custom_relationship_attributes'] = $dbForProject->count('attributes', [ + Query::equal('type', ['relationship']) + ], APP_LIMIT_COUNT); + + /** Get Total Functions */ + $statsPerProject['custom_functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT); + + foreach (\array_keys(Config::getParam('runtimes')) as $runtime) { + $statsPerProject['custom_functions_' . $runtime] = $dbForProject->count('functions', [ + Query::equal('runtime', [$runtime]), + ], APP_LIMIT_COUNT); + } + + /** Get Total Deployments */ + $statsPerProject['custom_deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT); + $statsPerProject['custom_deployments_manual'] = $dbForProject->count('deployments', [ + Query::equal('type', ['manual']) + ], APP_LIMIT_COUNT); + $statsPerProject['custom_deployments_git'] = $dbForProject->count('deployments', [ + Query::equal('type', ['vcs']) + ], APP_LIMIT_COUNT); + + /** Get VCS repos connected */ + $statsPerProject['custom_vcs_repositories'] = $dbForConsole->count('repositories', [ + Query::equal('projectInternalId', [$project->getInternalId()]) + ], APP_LIMIT_COUNT); + + /** Get Total Teams */ + $statsPerProject['custom_teams'] = $dbForProject->count('teams', [], APP_LIMIT_COUNT); + + /** Get Total Members */ + $teamInternalId = $project->getAttribute('teamInternalId', null); + if ($teamInternalId) { + $statsPerProject['custom_organization_members'] = $dbForConsole->count('memberships', [ + Query::equal('teamInternalId', [$teamInternalId]) + ], APP_LIMIT_COUNT); + } else { + $statsPerProject['custom_organization_members'] = 0; + } + + /** Get Email and Name of the project owner */ + if ($teamInternalId) { + $membership = $dbForConsole->findOne('memberships', [ + Query::equal('teamInternalId', [$teamInternalId]), + ]); + + if (!$membership || $membership->isEmpty()) { + throw new \Exception('Membership not found. Skipping project : ' . $project->getId()); + } + + $userId = $membership->getAttribute('userId', null); + if ($userId) { + $user = $dbForConsole->getDocument('users', $userId); + $statsPerProject['email'] = $user->getAttribute('email', null); + $statsPerProject['name'] = $user->getAttribute('name', null); + } + } + + /** Get Domains */ + $statsPerProject['custom_domains'] = $dbForConsole->count('rules', [ + Query::equal('projectInternalId', [$project->getInternalId()]), + Query::limit(APP_LIMIT_COUNT) + ]); + + /** Get Platforms */ + $platforms = $dbForConsole->find('platforms', [ + Query::equal('projectInternalId', [$project->getInternalId()]), + Query::limit(APP_LIMIT_COUNT) + ]); + + $statsPerProject['custom_platforms_web'] = sizeof(array_filter($platforms, function ($platform) { + return $platform['type'] === 'web'; + })); + + $statsPerProject['custom_platforms_android'] = sizeof(array_filter($platforms, function ($platform) { + return $platform['type'] === 'android'; + })); + + $statsPerProject['custom_platforms_apple'] = sizeof(array_filter($platforms, function ($platform) { + return str_contains($platform['type'], 'apple'); + })); + + $statsPerProject['custom_platforms_flutter'] = sizeof(array_filter($platforms, function ($platform) { + return str_contains($platform['type'], 'flutter'); + })); + + $flutterPlatforms = [Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_FLUTTER_LINUX]; + + foreach ($flutterPlatforms as $flutterPlatform) { + $statsPerProject['custom_platforms_' . $flutterPlatform] = sizeof(array_filter($platforms, function ($platform) use ($flutterPlatform) { + return $platform['type'] === $flutterPlatform; + })); + } + + $statsPerProject['custom_platforms_api_keys'] = $dbForConsole->count('keys', [ + Query::equal('projectInternalId', [$project->getInternalId()]), + Query::limit(APP_LIMIT_COUNT) + ]); + + /** Get Usage $statsPerProject */ + $periods = [ + 'infinity' => [ + 'period' => '1d', + 'limit' => 90, + ], + '24h' => [ + 'period' => '1h', + 'limit' => 24, + ], + ]; + + Authorization::skip(function () use ($dbForProject, $periods, &$statsPerProject) { + foreach ($this->metrics as $key => $metric) { + foreach ($periods as $periodKey => $periodValue) { + $limit = $periodValue['limit']; + $period = $periodValue['period']; + + $requestDocs = $dbForProject->find('stats', [ + Query::equal('period', [$period]), + Query::equal('metric', [$metric]), + Query::limit($limit), + Query::orderDesc('time'), + ]); + + $statsPerProject[$key . '_' . $periodKey] = []; + foreach ($requestDocs as $requestDoc) { + $statsPerProject[$key . '_' . $periodKey][] = [ + 'value' => $requestDoc->getAttribute('value'), + 'date' => $requestDoc->getAttribute('time'), + ]; + } + + $statsPerProject[$key . '_' . $periodKey] = array_reverse($statsPerProject[$key . '_' . $periodKey]); + // Calculate aggregate of each metric + $statsPerProject[$key . '_' . $periodKey] = array_sum(array_column($statsPerProject[$key . '_' . $periodKey], 'value')); + } + } + }); + + if (isset($statsPerProject['email'])) { + /** Send data to mixpanel */ + $res = $this->mixpanel->createProfile($statsPerProject['email'], '', [ + 'name' => $statsPerProject['name'], + 'email' => $statsPerProject['email'] + ]); + + if (!$res) { + Console::error('Failed to create user profile for project: ' . $project->getId()); + } + } + + $event = new AnalyticsEvent(); + $event + ->setName('Project Daily Usage') + ->setProps($statsPerProject); + $res = $this->mixpanel->createEvent($event); + + if (!$res) { + Console::error('Failed to create event for project: ' . $project->getId()); + } + } catch (\Exception $e) { + Console::error('Failed to send stats for project: ' . $project->getId()); + Console::error($e->getMessage()); + } finally { + $pools + ->get($db) + ->reclaim(); + } + } + + /** + * @param Document $organization + * @param Group $pools + * @param Cache $cache + * @param Database $dbForConsole + * @param Log $log + * @throws \Utopia\Database\Exception + */ + private function getStatsForOrganization(Document $organization, Group $pools, Cache $cache, Database $dbForConsole, Log $log): void + { + try { + $statsPerOrganization = []; + + /** Organization name */ + $statsPerOrganization['name'] = $organization->getAttribute('name'); + + /** Get Email and of the organization owner */ + $membership = $dbForConsole->findOne('memberships', [ + Query::equal('teamInternalId', [$organization->getInternalId()]), + ]); + + if (!$membership || $membership->isEmpty()) { + throw new \Exception('Membership not found. Skipping organization : ' . $organization->getId()); + } + + $userId = $membership->getAttribute('userId', null); + if ($userId) { + $user = $dbForConsole->getDocument('users', $userId); + $statsPerOrganization['email'] = $user->getAttribute('email', null); + } + + + /** Organization Creation Date */ + $statsPerOrganization['created'] = $organization->getAttribute('$createdAt'); + + /** Number of team members */ + $statsPerOrganization['members'] = $organization->getAttribute('total'); + + /** Number of projects in this organization */ + $statsPerOrganization['projects'] = $dbForConsole->count('projects', [ + Query::equal('teamId', [$organization->getId()]), + Query::limit(APP_LIMIT_COUNT) + ]); + + if (!isset($statsPerOrganization['email'])) { + throw new \Exception('Email not found. Skipping organization : ' . $organization->getId()); + } + + $event = new AnalyticsEvent(); + $event + ->setName('Organization Daily Usage') + ->setProps($statsPerOrganization); + $res = $this->mixpanel->createEvent($event); + if (!$res) { + throw new \Exception('Failed to create event for organization : ' . $organization->getId()); + } + } catch (\Exception $e) { + Console::error($e->getMessage()); + } + } + + protected function getStatsPerUser(Document $user, Database $dbForConsole) + { + try { + $statsPerUser = []; + + /** Organization name */ + $statsPerUser['name'] = $user->getAttribute('name'); + + /** Organization ID (needs to be stored as an email since mixpanel uses the email attribute as a distinctID) */ + $statsPerUser['email'] = $user->getAttribute('email'); + + /** Organization Creation Date */ + $statsPerUser['created'] = $user->getAttribute('$createdAt'); + + /** Number of teams this user is a part of */ + $statsPerUser['memberships'] = $dbForConsole->count('memberships', [ + Query::equal('userInternalId', [$user->getInternalId()]), + Query::limit(APP_LIMIT_COUNT) + ]); + + if (!isset($statsPerUser['email'])) { + throw new \Exception('User has no email: ' . $user->getId()); + } + + /** Send data to mixpanel */ + $event = new AnalyticsEvent(); + $event + ->setName('User Daily Usage') + ->setProps($statsPerUser); + $res = $this->mixpanel->createEvent($event); + + if (!$res) { + throw new \Exception('Failed to create user profile for user: ' . $user->getId()); + } + } catch (\Exception $e) { + Console::error($e->getMessage()); + } + } +} From 8bec64b2a2063f6384a578715ddb980226000532 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Thu, 30 Nov 2023 11:05:15 +0000 Subject: [PATCH 02/21] Update Hamster.php --- src/Appwrite/Platform/Tasks/Hamster.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index fea4c88ad..1df16fe9f 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -78,9 +78,6 @@ class Hamster extends Action $next = new \DateTime($now->format("Y-m-d $jobInitTime")); $next->setTimezone(new \DateTimeZone(date_default_timezone_get())); $delay = $next->getTimestamp() - $now->getTimestamp(); - - $delay = 5; - /** * If time passed for the target day. */ From d34050a5dff79bc37d38dd5c66776e66dda8b8b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Thu, 30 Nov 2023 16:22:26 +0000 Subject: [PATCH 03/21] chore: address review comments --- src/Appwrite/Event/Hamster.php | 16 ++-- src/Appwrite/Platform/Tasks/Hamster.php | 115 ++++++++---------------- 2 files changed, 48 insertions(+), 83 deletions(-) diff --git a/src/Appwrite/Event/Hamster.php b/src/Appwrite/Event/Hamster.php index 9ae730367..54cf0012f 100644 --- a/src/Appwrite/Event/Hamster.php +++ b/src/Appwrite/Event/Hamster.php @@ -13,6 +13,10 @@ class Hamster extends Event protected ?Document $organization = null; protected ?Document $user = null; + const TYPE_PROJECT = 'project'; + const TYPE_ORGANISATION = 'organisation'; + const TYPE_USER = 'user'; + public function __construct(protected Connection $connection) { parent::__construct($connection); @@ -47,7 +51,7 @@ class Hamster extends Event /** * Sets the project for the hamster event. - * + * * @param Document $project */ public function setProject(Document $project): self @@ -59,7 +63,7 @@ class Hamster extends Event /** * Returns the set project for the hamster event. - * + * * @return Document */ public function getProject(): Document @@ -69,7 +73,7 @@ class Hamster extends Event /** * Sets the organization for the hamster event. - * + * * @param Document $organization */ public function setOrganization(Document $organization): self @@ -81,7 +85,7 @@ class Hamster extends Event /** * Returns the set organization for the hamster event. - * + * * @return string */ public function getOrganization(): Document @@ -91,7 +95,7 @@ class Hamster extends Event /** * Sets the user for the hamster event. - * + * * @param Document $user */ public function setUser(Document $user): self @@ -103,7 +107,7 @@ class Hamster extends Event /** * Returns the set user for the hamster event. - * + * * @return Document */ public function getUser(): Document diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index fea4c88ad..7a105ba45 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -3,31 +3,16 @@ namespace Appwrite\Platform\Tasks; use Appwrite\Event\Hamster as EventHamster; -use Appwrite\Network\Validator\Origin; use Exception; use Utopia\App; use Utopia\Platform\Action; -use Utopia\Cache\Cache; use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Query; -use Utopia\Database\Validator\Authorization; -use Utopia\Analytics\Adapter\Mixpanel; -use Utopia\Analytics\Event; -use Utopia\Config\Config; use Utopia\Database\Document; -use Utopia\Pools\Group; class Hamster extends Action { - protected string $directory = '/usr/local'; - - protected string $path; - - protected string $date; - - protected Mixpanel $mixpanel; - public static function getName(): string { return 'hamster'; @@ -35,48 +20,30 @@ class Hamster extends Action public function __construct() { - $this->mixpanel = new Mixpanel(App::getEnv('_APP_MIXPANEL_TOKEN', '')); - $this ->desc('Get stats for projects') - ->inject('pools') - ->inject('cache') + ->inject('queueForHamster') ->inject('dbForConsole') - ->callback(function (Group $pools, Cache $cache, Database $dbForConsole) { - $this->action($pools, $cache, $dbForConsole); + ->callback(function (EventHamster $queueForHamster, Database $dbForConsole) { + $this->action($queueForHamster, $dbForConsole); }); } - private function getStatsPerProject(Group $pools, Database $dbForConsole) + public function action(EventHamster $queueForHamster, Database $dbForConsole): void { - $this->calculateByGroup('projects', $dbForConsole, function (Database $dbForConsole, Document $project) use ($pools) { - $queue = $pools->get('queue')->pop(); - $connection = $queue->getResource(); - - $hamsterTask = new EventHamster($connection); - - $hamsterTask - ->setType('project') - ->setProject($project) - ->trigger(); - - $queue->reclaim(); - }); - } - - public function action(Group $pools, Cache $cache, Database $dbForConsole): void - { - Console::title('Cloud Hamster V1'); Console::success(APP_NAME . ' cloud hamster process has started'); $sleep = (int) App::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default) $jobInitTime = App::getEnv('_APP_HAMSTER_TIME', '22:00'); // (hour:minutes) + $now = new \DateTime(); $now->setTimezone(new \DateTimeZone(date_default_timezone_get())); + $next = new \DateTime($now->format("Y-m-d $jobInitTime")); $next->setTimezone(new \DateTimeZone(date_default_timezone_get())); + $delay = $next->getTimestamp() - $now->getTimestamp(); $delay = 5; @@ -91,29 +58,24 @@ class Hamster extends Action Console::log('[' . $now->format("Y-m-d H:i:s.v") . '] Delaying for ' . $delay . ' setting loop to [' . $next->format("Y-m-d H:i:s.v") . ']'); - Console::loop(function () use ($pools, $cache, $dbForConsole, $sleep) { + Console::loop(function () use ($queueForHamster, $dbForConsole, $sleep) { $now = date('d-m-Y H:i:s', time()); - Console::info("[{$now}] Getting Cloud Usage Stats every {$sleep} seconds"); + Console::info("[{$now}] Queuing Cloud Usage Stats every {$sleep} seconds"); $loopStart = microtime(true); - /* Initialise new Utopia app */ - $app = new App('UTC'); - Console::info('Queuing stats for all projects'); - $this->getStatsPerProject($pools, $dbForConsole); + $this->getStatsPerProject($queueForHamster, $dbForConsole); Console::success('Completed queuing stats for all projects'); Console::info('Queuing stats for all organizations'); - $this->getStatsPerOrganization($pools, $dbForConsole); + $this->getStatsPerOrganization($queueForHamster, $dbForConsole); Console::success('Completed queuing stats for all organizations'); Console::info('Queuing stats for all users'); - $this->getStatsPerUser($pools, $dbForConsole); + $this->getStatsPerUser($queueForHamster, $dbForConsole); Console::success('Completed queuing stats for all users'); - $pools - ->get('console') - ->reclaim(); + $queue->reclaim(); $loopTook = microtime(true) - $loopStart; $now = date('d-m-Y H:i:s', time()); @@ -121,7 +83,7 @@ class Hamster extends Action }, $sleep, $delay); } - protected function calculateByGroup(string $collection, Database $dbForConsole, callable $callback) + protected function calculateByGroup(string $collection, Database $database, callable $callback) { $count = 0; $chunk = 0; @@ -134,7 +96,7 @@ class Hamster extends Action while ($sum === $limit) { $chunk++; - $results = $dbForConsole->find($collection, \array_merge([ + $results = $database->find($collection, \array_merge([ Query::limit($limit), Query::offset($count) ])); @@ -144,7 +106,7 @@ class Hamster extends Action Console::log('Processing chunk #' . $chunk . '. Found ' . $sum . ' documents'); foreach ($results as $document) { - call_user_func($callback, $dbForConsole, $document); + call_user_func($callback, $database, $document); $count++; } } @@ -154,43 +116,42 @@ class Hamster extends Action Console::log("Processed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); } - protected function getStatsPerOrganization(Group $pools, Database $dbForConsole) + protected function getStatsPerOrganization(EventHamster $hamster, Database $dbForConsole) { - - $this->calculateByGroup('teams', $dbForConsole, function (Database $dbForConsole, Document $organization) use ($pools) { + $this->calculateByGroup('teams', $dbForConsole, function (Database $dbForConsole, Document $organization) use ($hamster) { try { - $queue = $pools->get('queue')->pop(); - $connection = $queue->getResource(); - - $hamsterTask = new EventHamster($connection); - - $hamsterTask - ->setType('organization') + $hamster + ->setType(EventHamster::TYPE_ORGANISATION) ->setOrganization($organization) ->trigger(); - - $queue->reclaim(); } catch (Exception $e) { Console::error($e->getMessage()); } }); } - protected function getStatsPerUser(Group $pools, Database $dbForConsole) + private function getStatsPerProject(EventHamster $hamster, Database $dbForConsole) { - $this->calculateByGroup('users', $dbForConsole, function (Database $dbForConsole, Document $user) use ($pools) { + $this->calculateByGroup('projects', $dbForConsole, function (Database $dbForConsole, Document $project) use ($hamster) { try { - $queue = $pools->get('queue')->pop(); - $connection = $queue->getResource(); - - $hamsterTask = new EventHamster($connection); - - $hamsterTask - ->setType('user') + $hamster + ->setType(EventHamster::TYPE_PROJECT) + ->setProject($project) + ->trigger(); + } catch (Exception $e) { + Console::error($e->getMessage()); + } + }); + } + + protected function getStatsPerUser(EventHamster $hamster, Database $dbForConsole) + { + $this->calculateByGroup('users', $dbForConsole, function (Database $dbForConsole, Document $user) use ($hamster) { + try { + $hamster + ->setType(EventHamster::TYPE_USER) ->setUser($user) ->trigger(); - - $queue->reclaim(); } catch (Exception $e) { Console::error($e->getMessage()); } From 4b7676158e540bc1711fc45981fc2fbc54d4466e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Thu, 30 Nov 2023 16:24:11 +0000 Subject: [PATCH 04/21] chore: address review comments --- src/Appwrite/Platform/Tasks/Hamster.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index 157f3db39..9ecab942a 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -72,8 +72,6 @@ class Hamster extends Action $this->getStatsPerUser($queueForHamster, $dbForConsole); Console::success('Completed queuing stats for all users'); - $queue->reclaim(); - $loopTook = microtime(true) - $loopStart; $now = date('d-m-Y H:i:s', time()); Console::info("[{$now}] Cloud Stats took {$loopTook} seconds"); From f3544485e54c44c63f228f53f6354016b374e364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Thu, 30 Nov 2023 17:41:23 +0000 Subject: [PATCH 05/21] chore: address review comments --- app/init.php | 3 -- src/Appwrite/Platform/Tasks/Hamster.php | 21 ++++---- src/Appwrite/Platform/Workers/Hamster.php | 62 ++++++++--------------- 3 files changed, 33 insertions(+), 53 deletions(-) diff --git a/app/init.php b/app/init.php index c30eb77e8..2beeb2845 100644 --- a/app/init.php +++ b/app/init.php @@ -917,9 +917,6 @@ App::setResource('queueForCertificates', function (Connection $queue) { App::setResource('queueForMigrations', function (Connection $queue) { return new Migration($queue); }, ['queue']); -App::setResource('queueForHamster', function (Connection $queue) { - return new Hamster($queue); -}, ['queue']); App::setResource('usage', function ($register) { return new Stats($register->get('statsd')); }, ['register']); diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index 9ecab942a..1dca095e9 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -61,15 +61,15 @@ class Hamster extends Action $loopStart = microtime(true); Console::info('Queuing stats for all projects'); - $this->getStatsPerProject($queueForHamster, $dbForConsole); + $this->getStatsPerProject($queueForHamster, $dbForConsole, $loopStart); Console::success('Completed queuing stats for all projects'); Console::info('Queuing stats for all organizations'); - $this->getStatsPerOrganization($queueForHamster, $dbForConsole); + $this->getStatsPerOrganization($queueForHamster, $dbForConsole, $loopStart); Console::success('Completed queuing stats for all organizations'); Console::info('Queuing stats for all users'); - $this->getStatsPerUser($queueForHamster, $dbForConsole); + $this->getStatsPerUser($queueForHamster, $dbForConsole, $loopStart); Console::success('Completed queuing stats for all users'); $loopTook = microtime(true) - $loopStart; @@ -111,10 +111,11 @@ class Hamster extends Action Console::log("Processed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); } - protected function getStatsPerOrganization(EventHamster $hamster, Database $dbForConsole) + protected function getStatsPerOrganization(EventHamster $hamster, Database $dbForConsole, float $loopStart) { - $this->calculateByGroup('teams', $dbForConsole, function (Database $dbForConsole, Document $organization) use ($hamster) { + $this->calculateByGroup('teams', $dbForConsole, function (Database $dbForConsole, Document $organization) use ($hamster, $loopStart) { try { + $organization->setAttribute('$time', $loopStart); $hamster ->setType(EventHamster::TYPE_ORGANISATION) ->setOrganization($organization) @@ -125,10 +126,11 @@ class Hamster extends Action }); } - private function getStatsPerProject(EventHamster $hamster, Database $dbForConsole) + private function getStatsPerProject(EventHamster $hamster, Database $dbForConsole, float $loopStart) { - $this->calculateByGroup('projects', $dbForConsole, function (Database $dbForConsole, Document $project) use ($hamster) { + $this->calculateByGroup('projects', $dbForConsole, function (Database $dbForConsole, Document $project) use ($hamster, $loopStart) { try { + $project->setAttribute('$time', $loopStart); $hamster ->setType(EventHamster::TYPE_PROJECT) ->setProject($project) @@ -139,10 +141,11 @@ class Hamster extends Action }); } - protected function getStatsPerUser(EventHamster $hamster, Database $dbForConsole) + protected function getStatsPerUser(EventHamster $hamster, Database $dbForConsole, float $loopStart) { - $this->calculateByGroup('users', $dbForConsole, function (Database $dbForConsole, Document $user) use ($hamster) { + $this->calculateByGroup('users', $dbForConsole, function (Database $dbForConsole, Document $user) use ($hamster, $loopStart) { try { + $user->setAttribute('$time', $loopStart); $hamster ->setType(EventHamster::TYPE_USER) ->setUser($user) diff --git a/src/Appwrite/Platform/Workers/Hamster.php b/src/Appwrite/Platform/Workers/Hamster.php index d1c03744c..2931741c8 100644 --- a/src/Appwrite/Platform/Workers/Hamster.php +++ b/src/Appwrite/Platform/Workers/Hamster.php @@ -2,8 +2,6 @@ namespace Appwrite\Platform\Workers; -use Appwrite\Usage\Stats; -use Appwrite\Event\Event; use Appwrite\Event\Hamster as EventHamster; use Appwrite\Network\Validator\Origin; use Utopia\Analytics\Adapter\Mixpanel; @@ -37,12 +35,6 @@ class Hamster extends Action 'usage_executions' => 'executions.$all.compute.total', ]; - protected string $directory = '/usr/local'; - - protected string $path; - - protected string $date; - protected Mixpanel $mixpanel; public static function getName(): string @@ -55,19 +47,13 @@ class Hamster extends Action */ public function __construct() { - $this->mixpanel = new Mixpanel(App::getEnv('_APP_MIXPANEL_TOKEN', '')); - $this ->desc('Hamster worker') ->inject('message') ->inject('pools') ->inject('cache') ->inject('dbForConsole') - ->inject('queueForHamster') - ->inject('queueForEvents') - ->inject('usage') - ->inject('log') - ->callback(fn (Message $message, Group $pools, Cache $cache, Database $dbForConsole, EventHamster $queueForHamster, Event $queueForEvents, Stats $usage, Log $log) => $this->action($message, $pools, $cache, $dbForConsole, $queueForHamster, $queueForEvents, $usage, $log)); + ->callback(fn (Message $message, Group $pools, Cache $cache, Database $dbForConsole) => $this->action($message, $pools, $cache, $dbForConsole)); } /** @@ -75,24 +61,17 @@ class Hamster extends Action * @param Group $pools * @param Cache $cache * @param Database $dbForConsole - * @param EventHamster $queueForHamster - * @param Event $queueForEvents - * @param Stats $usage - * @param Log $log + * * @return void - * @throws Authorization - * @throws Structure * @throws \Utopia\Database\Exception - * @throws Conflict */ - public function action(Message $message, Group $pools, Cache $cache, Database $dbForConsole, EventHamster $queueForHamster, Event $queueForEvents, Stats $usage, Log $log): void + public function action(Message $message, Group $pools, Cache $cache, Database $dbForConsole): void { - $payload = $message->getPayload() ?? []; - - if (empty($payload)) { - throw new \Exception('Missing payload'); + $token = App::getEnv('_APP_MIXPANEL_TOKEN', ''); + if (empty($token)) { + throw new \Exception('Missing MixPanel Token'); } - + $this->mixpanel = new Mixpanel($token); $payload = $message->getPayload() ?? []; @@ -103,13 +82,13 @@ class Hamster extends Action $type = $payload['type'] ?? ''; switch ($type) { - case 'project': - $this->getStatsForProject(new Document($payload['project']), $pools, $cache, $dbForConsole, $log); + case EventHamster::TYPE_PROJECT: + $this->getStatsForProject(new Document($payload['project']), $pools, $cache, $dbForConsole); break; - case 'organization': - $this->getStatsForOrganization(new Document($payload['organization']), $pools, $cache, $dbForConsole, $log); + case EventHamster::TYPE_ORGANISATION: + $this->getStatsForOrganization(new Document($payload['organization']), $dbForConsole); break; - case 'user': + case EventHamster::TYPE_USER: $this->getStatsPerUser(new Document($payload['user']), $dbForConsole); break; } @@ -120,10 +99,9 @@ class Hamster extends Action * @param Group $pools * @param Cache $cache * @param Database $dbForConsole - * @param Log $log * @throws \Utopia\Database\Exception */ - private function getStatsForProject(Document $project, Group $pools, Cache $cache, Database $dbForConsole, Log $log): void + private function getStatsForProject(Document $project, Group $pools, Cache $cache, Database $dbForConsole): void { /** * Skip user projects with id 'console' @@ -149,7 +127,7 @@ class Hamster extends Action $statsPerProject = []; - $statsPerProject['time'] = microtime(true); + $statsPerProject['time'] = $project->getAttribute('$time'); /** Get Project ID */ $statsPerProject['project_id'] = $project->getId(); @@ -354,20 +332,20 @@ class Hamster extends Action /** * @param Document $organization - * @param Group $pools - * @param Cache $cache * @param Database $dbForConsole - * @param Log $log * @throws \Utopia\Database\Exception */ - private function getStatsForOrganization(Document $organization, Group $pools, Cache $cache, Database $dbForConsole, Log $log): void + private function getStatsForOrganization(Document $organization, Database $dbForConsole): void { try { $statsPerOrganization = []; + $statsPerOrganization['time'] = $organization->getAttribute('$time'); + /** Organization name */ $statsPerOrganization['name'] = $organization->getAttribute('name'); + /** Get Email and of the organization owner */ $membership = $dbForConsole->findOne('memberships', [ Query::equal('teamInternalId', [$organization->getInternalId()]), @@ -383,7 +361,6 @@ class Hamster extends Action $statsPerOrganization['email'] = $user->getAttribute('email', null); } - /** Organization Creation Date */ $statsPerOrganization['created'] = $organization->getAttribute('$createdAt'); @@ -418,6 +395,8 @@ class Hamster extends Action try { $statsPerUser = []; + $statsPerUser['time'] = $user->getAttribute('$time'); + /** Organization name */ $statsPerUser['name'] = $user->getAttribute('name'); @@ -442,6 +421,7 @@ class Hamster extends Action $event ->setName('User Daily Usage') ->setProps($statsPerUser); + $res = $this->mixpanel->createEvent($event); if (!$res) { From 799fe2acca6871821256f8f340e596674def159f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Thu, 30 Nov 2023 17:43:07 +0000 Subject: [PATCH 06/21] chore: address review comments --- app/init.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/init.php b/app/init.php index 2beeb2845..2c0219eec 100644 --- a/app/init.php +++ b/app/init.php @@ -72,7 +72,6 @@ use Ahc\Jwt\JWTException; use Appwrite\Event\Build; use Appwrite\Event\Certificate; use Appwrite\Event\Func; -use Appwrite\Event\Hamster; use MaxMind\Db\Reader; use PHPMailer\PHPMailer\PHPMailer; use Swoole\Database\PDOProxy; From 0382b2250aec40974e810e3365903f126b6e38ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Thu, 30 Nov 2023 19:22:35 +0000 Subject: [PATCH 07/21] chore: address review comments --- src/Appwrite/Platform/Workers/Hamster.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Workers/Hamster.php b/src/Appwrite/Platform/Workers/Hamster.php index 2931741c8..c8ab1952c 100644 --- a/src/Appwrite/Platform/Workers/Hamster.php +++ b/src/Appwrite/Platform/Workers/Hamster.php @@ -61,7 +61,7 @@ class Hamster extends Action * @param Group $pools * @param Cache $cache * @param Database $dbForConsole - * + * * @return void * @throws \Utopia\Database\Exception */ From d4ff6961734e16b4159f5a8cadf8a9dfdd1f0bd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Thu, 30 Nov 2023 20:35:18 +0000 Subject: [PATCH 08/21] chore: fix linter issues --- src/Appwrite/Event/Hamster.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Appwrite/Event/Hamster.php b/src/Appwrite/Event/Hamster.php index 54cf0012f..5d79fce56 100644 --- a/src/Appwrite/Event/Hamster.php +++ b/src/Appwrite/Event/Hamster.php @@ -13,9 +13,9 @@ class Hamster extends Event protected ?Document $organization = null; protected ?Document $user = null; - const TYPE_PROJECT = 'project'; - const TYPE_ORGANISATION = 'organisation'; - const TYPE_USER = 'user'; + public const TYPE_PROJECT = 'project'; + public const TYPE_ORGANISATION = 'organisation'; + public const TYPE_USER = 'user'; public function __construct(protected Connection $connection) { From e382a9d58bccec9359aaa947d847f9cc830bcef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Thu, 30 Nov 2023 20:37:59 +0000 Subject: [PATCH 09/21] feat: update console --- .gitmodules | 2 +- app/console | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index dad7c9ed3..39b60d5a0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 3.2.9 + branch = 3.2.10 diff --git a/app/console b/app/console index 212b74299..ab9ef73fe 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 212b7429926d097a31ed71d2410e39c600c56f3b +Subproject commit ab9ef73fe0e74bdbb195331bb868af3ede7d1aa3 From 49bb3444bf1d7194871e0285e59427c430f6a253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Thu, 30 Nov 2023 21:11:31 +0000 Subject: [PATCH 10/21] chore: add logs --- docker-compose.yml | 34 ++++++++++++++++++++--- src/Appwrite/Platform/Workers/Hamster.php | 7 +++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index da9b0e51e..97cf7e513 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -722,6 +722,34 @@ services: <<: *x-logging container_name: appwrite-worker-hamster image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_MIXPANEL_TOKEN + + appwrite-hamster-scheduler: + entrypoint: hamster + <<: *x-logging + container_name: appwrite-hamster-scheduler + image: appwrite-dev networks: - appwrite volumes: @@ -743,10 +771,8 @@ services: - _APP_DB_SCHEMA - _APP_DB_USER - _APP_DB_PASS - - _APP_USAGE_STATS - - _APP_LOGGING_CONFIG - - _APP_LOGGING_PROVIDER - - _APP_MIXPANEL_TOKEN + - _APP_HAMSTER_TIME + - _APP_HAMSTER_INTERVAL openruntimes-executor: container_name: openruntimes-executor diff --git a/src/Appwrite/Platform/Workers/Hamster.php b/src/Appwrite/Platform/Workers/Hamster.php index c8ab1952c..e911bb6c7 100644 --- a/src/Appwrite/Platform/Workers/Hamster.php +++ b/src/Appwrite/Platform/Workers/Hamster.php @@ -111,8 +111,7 @@ class Hamster extends Action return; } - Console::log("Getting stats for {$project->getId()}"); - + Console::log("Getting stats for Project {$project->getId()}"); try { $db = $project->getAttribute('database'); @@ -337,6 +336,8 @@ class Hamster extends Action */ private function getStatsForOrganization(Document $organization, Database $dbForConsole): void { + Console::log("Getting stats for Organization {$organization->getId()}"); + try { $statsPerOrganization = []; @@ -392,6 +393,8 @@ class Hamster extends Action protected function getStatsPerUser(Document $user, Database $dbForConsole) { + Console::log("Getting stats for User {$user->getId()}"); + try { $statsPerUser = []; From 11a9583c72ee092de97cc778dd83a2e940619bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Thu, 30 Nov 2023 21:16:17 +0000 Subject: [PATCH 11/21] feat: update cache buster --- app/init.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/init.php b/app/init.php index 4cbf08cf2..72bf75d41 100644 --- a/app/init.php +++ b/app/init.php @@ -109,7 +109,7 @@ const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours -const APP_CACHE_BUSTER = 328; +const APP_CACHE_BUSTER = 329; const APP_VERSION_STABLE = '1.4.13'; const APP_DATABASE_ATTRIBUTE_EMAIL = 'email'; const APP_DATABASE_ATTRIBUTE_ENUM = 'enum'; From 8218e95f17b3c4449d45177928d0efa5b652a0f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Thu, 30 Nov 2023 21:26:07 +0000 Subject: [PATCH 12/21] chore: revert console --- app/console | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/console b/app/console index 49d039ed0..ab9ef73fe 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 49d039ed07628155e7f56e2c997fcef90ecde267 +Subproject commit ab9ef73fe0e74bdbb195331bb868af3ede7d1aa3 From eb87b951c692f0897aa3744512d94cfe4ad51180 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Mon, 4 Dec 2023 10:23:29 +0100 Subject: [PATCH 13/21] chore: bump console version --- .gitmodules | 2 +- app/console | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 39b60d5a0..1b780fe9c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 3.2.10 + branch = 3.2.11 diff --git a/app/console b/app/console index ab9ef73fe..50d4e4790 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit ab9ef73fe0e74bdbb195331bb868af3ede7d1aa3 +Subproject commit 50d4e4790c7bdf4bb9578b34cf7efcc052917531 From d79411813a28b6a4329071af94be86ff64a736ab Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Mon, 4 Dec 2023 10:45:18 +0100 Subject: [PATCH 14/21] bump: version --- .gitmodules | 2 +- app/console | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 39b60d5a0..1b780fe9c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 3.2.10 + branch = 3.2.11 diff --git a/app/console b/app/console index ab9ef73fe..50d4e4790 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit ab9ef73fe0e74bdbb195331bb868af3ede7d1aa3 +Subproject commit 50d4e4790c7bdf4bb9578b34cf7efcc052917531 From f127ca651f49a06eb758a849e79ac0ffb648bbb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Tue, 5 Dec 2023 00:54:52 +0000 Subject: [PATCH 15/21] chore: update console version --- .gitmodules | 2 +- app/console | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 1b780fe9c..a44e37790 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 3.2.11 + branch = 3.2.13 diff --git a/app/console b/app/console index 50d4e4790..cd96fcad2 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 50d4e4790c7bdf4bb9578b34cf7efcc052917531 +Subproject commit cd96fcad23a54a90006c9383e85c232ab89eacb0 From 38a3c087720a533e7343d7e17dde85912d61789d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Tue, 5 Dec 2023 09:16:35 +0000 Subject: [PATCH 16/21] chore: update docker base image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 2f85f2cc4..76d434330 100755 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT RUN npm ci RUN npm run build -FROM appwrite/base:0.4.3 as final +FROM appwrite/base:0.4.5 as final LABEL maintainer="team@appwrite.io" From 1595d41490dedb307b01ff2d64b78e18f15e2e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Tue, 5 Dec 2023 14:13:24 +0000 Subject: [PATCH 17/21] fix: update qr code test images due to Imagemagic extension upgrade in the base image --- app/controllers/api/avatars.php | 2 +- tests/resources/qr/qr-default.png | Bin 14593 -> 29121 bytes tests/resources/qr/qr-size-200-margin-10.png | Bin 3750 -> 5512 bytes tests/resources/qr/qr-size-200.png | Bin 6075 -> 10906 bytes 4 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index e0d967eb0..540d85dd2 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -485,7 +485,7 @@ App::get('/v1/avatars/qr') $response ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache ->setContentType('image/png') - ->send($image->output('png', 9)); + ->send($image->output('png', 5)); }); App::get('/v1/avatars/initials') diff --git a/tests/resources/qr/qr-default.png b/tests/resources/qr/qr-default.png index a7da496d9fd62ecb6b0afc8d1f2bfb154a660cbf..d6bbba4d12458ffecd85d1221b6717b710213f88 100644 GIT binary patch delta 28982 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 z⁢Z-|35x-796t>Cujd>S3vPoEz|Z#S3P>yA||Tqz8wmZ>G0|s^rn~7w_{G3X>>GqxJ4d-^+UIg4b2QZkE-K zhe>%ozFd9&+k(%|{=G`ya{Nfj4EZnAt#OgQ0@gl=34*&AKceXQ&*6e>?JsDs=z`L@=Vwm9|O_mI<(YSxd?NC5D4y`=v7hX2sl3f|WG1gqLA)I&{meVr)aD{~z4 z?}xG=?O%>-Tp%KzsvXS&7s5?fmMF8BcqhC2DsPuDf{UZS1V2nW8VFt7_62na{IQ6v z{&Gy-`|Lj{cB}rJYBcew408|={`2TJF9sL;njee-J`}u{1k5a+P*c~?am}+fA1;0B z_(!_?5$TriRNhwJALvd}Nl#O>Yb-pAZqFkB+y7{EA?N&cX%KZ_QWiZkI2M1FI2PNQsrvzzyJdq|eh?j@DLs z$7tgMs3m@4h_u55fgf)M4ttq9v0QnCm{}fTk#mplc~D}Lk>`W0d$2l8ZHFQ(dC(x} z&IUI3$AhstMYS?Iwdc9ICjT}rW?z}YnlMvMIbvKStk1g=!8tin>*?4*S%a>xlMN(0 zmd8*HH^}k!19>F@W$p8paFoZp-p|g@cN9{7Uxjz{vt6h@n9+Ktqm=`H4K|CxlJID@ z2q*OkLfBDYqSc)r$dvdDqoA$M>oSNgc$vGZNHAT)t!$?I)KzU(|4eOkIA&QXcM3Zx zIdjVG7dx6Mf8BUkJ?3^U6I81y)^bP*=@)N@6CgyqrohUNr+`P*%nrEw0i09ckRXmV z(Td`DGL=}(+*n(VDyEZ6&`w%tr0}e=G%#W91GVNrso7SxJ{%{6Qg*e@Lk=fLT? zm%;qT8y)=iqIeYDn8nl)X8B9}EC1qV?H{Du-(ZlhIenBAV(&^}erdsgv7Y z+M?N8YW!$gPuDrpASMk@p{*k#SWW|^363a8kCrnsY|4}BBRs8{#1q*aYKjo`aO_Gz zHJaS6N}y~wU@QSnIvQC4V!yjMrPwmT`iy@503|Z4zuYV4+-PSibA|H@e6e$(=Qv*c(u$4 z<`)vrHmx?_e1-Eg?9m#4Us`HMyI0l7&!dvD(lKey*f6|+pu`0$nB+eZuK$n474@65 zIsuy|&7)y`41>7-v9pG;;4PHKt#J3sT)r7uIWEZ$JrG0A;1VnX!6KJ_ZMwj;ROV$? zYAO>_z-6yPFkq8)K;31Dr5PxjM zY)shgpcI)bjQJ1xmrWlJnM55?wi_v92)avdranlmQG@7_2oEsISLxNHpwT1pq5UMH z@Qz}|OsM_&fc4Cz4)@CxX41Nz`zC8MlxqkK zEasU7vkZ6EBz>PRRQ{}G}6(WYX)SYRy0tkv1N*WzQ`V%CCh z@qrF-C|At#^zn$wDh*S3_sgDP4@A;@qjYL}8@-X_X97pm!bMjHiWE#(fjf1 z_;gyRHOE2zNH+agkHV+jfoDaf9LiKzlHIIaT>-%4X$iuno1A6tmOw%;cqQ_xhd8rq zf#!SL7kKcQ$jQJdZW;D*Cz}hneY3(+fz$lly#(}cweVOx26nf1=86Mx8!wSQknNl5 zFUY=+H?eruqfgIgE53a4zPJ1YLH8z*!dZ{N^l8hO9msXf3=oiu&oEpMsE42Z)-0R( z2)g!Uwt$BY;DT|Su$V7jp@xw)nV}j3zg=grlNq5mNwZmTJ);r;wj;Z2pu1ncku@-I ziUQ4k{>^g6?7oMJl6-OQt=$UIs)jwTIT8}3wJ&U3`>{Cm){}T@c1c~fD$6%PM z)KY3il$oy1Fs$&1s2_;aGIl{Fuhvpz&=6dWXcWkPt``eh01Mo}5f(%eEIM*T?WiYK zk>M!L@m1KXhb0`O7E^w}2{f0co?+;~!lPw_1aX-WE&vlwP+J*}svZZ9x;&#|t27i#CaC@+Yj4K6AjeN+nIObyABj^uk9UUE`wrbgADXPdVoXTkz9mqo)A`@VL3 zYDC6LYQev~VihX^AoQs%v~nLJWK*e0*IH6=F=0O&{+dAJf#+}iR^gmLyo{lJLERj4 z)Sq3dZf%+J(=lU{&2O5-VJi4N~;(h}^T6B@g2Nt=&-z z0md_MnA)!3pl} z9^BoX-~`tMz3hF@eK-&Ish?`}?4F~0)U5jducY*(SIcb{53!Qn_s2hkR$z_UHNpAR zEpPxnWsyHPiEogvycJf9Nj0pGDm`7Db<<7iz7~wMz@Pl;V&uM0G46oKO96het7~o? zZgZG-XbU4-KyAH(eXn(E_`js;pv63KWB5ad;O!nfFo#&f(G&2;n&U? zCWDYL05E*qM>b*)z zlU!y$M;6;2Yl!+Q*;wT%y}SPgafwe84{J=QH2Hk0eJ@hLnPrf=bTPc#m-QUsbCeME z8B|T@#>SV7iw$UL;se7-BJk1A?#J8lFL5SZijnu5Pcxj|Gk*S9iGo+tT3xGEu~OYr zEJ46&=Y1B&G;QRmG3qYOMm^*a8J*OmCWA)uHv2o@^9U~Lv~S-*w$@!Rvfp67kuUUR zDLBB-R|`0|5rNnv%)BJNd{WAa;G?z%^51qFwLq!gc${H7d`?`3QGlsJ-}5>=zivS= zd#UmjYP}vQn6!lG-W4Bm3nL_m6EF-a;k3!?+ zNz%oqKBWdoEpOq90o%8lk0N^e9wao^r=E2$0BbORGc}`_99>GnVdS7dSBWe+Pb zG)H{|@haebs;kxaokU-0V^{lRjP?iw3o|TBnWXFfGX$@R;Cag7!95AZU&dNy?P>&eEv$tgU8gp^4f1{O#~sy3P3orR$vGlT(_}y< zaSNADaS%3P&qHPvn%o@VWg$)a{i$1L)N8kK`d=oF5(q#rAw8V4$PR;@%|4i;!{oAx zjDS^7E3+$9!f7O)P=EZy$?C={f;7DcuF7aup(t2FN9svvKQs@c1S83K5U@?A%wR#- zk)fOm=4IZS?QgU)9gjbX>xwZ+LMCiJW3{m^hl>M>a&K|RnM#+Hb9dKe?Y ztU-=+hNlx*u-j?A3)qcOHUWWD^uCZq!Q^E#O%^;-apjS221W|0cb5?;iOeYE!V{vEu@CQK?%zY`;`qzq!o-g|fI?$FAJ20@h4^`_>QE@jb?4$?(WT1h{4_O!;lDTu4 zz{j>td_RWJD4+dCPGjq0M%a;nZFX|tEHOr`@1~El|3^!h<;68ME9Fwk$fGe0yXSKC z{o;EMVz+}XQj`*@A_H0E0;2EzjoDOYO%1U&S%{_6$M=Afe!~qmo6xrMZ6S_8hk=)H z#W5sxtJ5T3Ao^X}{FKTtu|rE}#<)GTz8jl)$0!z#4#aduWt>Npqx?s4qqW&-NrQ_? z5Qiq@JbPn>PCCn>`{ntT2?H7(9p3VKnZhc=D@B9Q$L0Day7JxsuQL8$K~H`&{q^I%!&f zIl7&$XE-d9h2Ab7x{n_;5$harRi&e7CPIZ?7IrQ1%*{XfH!rr_@6Mq36(Hz6EiRUQ zFo)_L`T4#3?i6B_bq}C?F#{|4-nE8bhM6SQFScFH9dqif1sA}s)4V_}73R*FLxqO4M;CW_)#0jNwu)^(Hp!Tw|h z#Orv!LKbX*<;P|hJJdRChG4=|d8O~QZkVimHuI(sU{_06|EGx6+Ijk`@qS@;^`yB(F0uN1tEbZa@_F})|HwFLiOrHM4@wS-G0Hp`Sy#tP!2N1wM+p!?U*p*B z%<_eVRx18_E6vgV)fNjqda`R0Ai+U|K{%t`8Esx&wtb^wdRPcCn?s)Kipg@%0oi;ZnlPa^qHdpRridsSZF|l9m03XPGd^A*K z3EFW0U7Mxq7T>BOuqe{0L&!=}oGbE$qn_z9<#TVwze~-Iyyjl4yqQF+!5-cB#xyIW zsGhzM1s?6opg9hooji_DsJOlF-#9e_Y08EzJf`~03YBmwd^>SyPE-?a$Nhso7&8V_ z-4t=QMprhfG^AqEpbBpWrn!6M#qc|3bzd@L@uCCUD>((??Lzqt2_9+rVSstu;*+*x z1U?(!dAf2tR%1yUYFT$Jo8vOLh@1)u_^ufF1F?#+rnv|1dDb&Z44D>BTmQr~m@)U= zpos~`NXPhHXSVJ3;&@3VQ;m2O9-a)jJ6)E6z%-4wNaC92q6Y#qLbWJKW_! zmS(yqLUx}P`0)hS%Mqh&v?rGWH^hUc}F6y zS*K5#^EmTeq0s}`N}n|(!WG{WTbcdsIEt^Kb{YF>GG>^etd23FT*7>fAteZY@g;R7 zPXKG}+Drgf37ubj#lRPOT|da$Nbf&5=4nSLlqe#Y77%N?#Bq?elkIjG(JNGUDC z9hR5C=8xn`P>KnJ;#X_PNA~8tS2$JPr6?O}=eY!>l!zudSn3Pw#x`(2gdm{&d-1lC1XFB`Fu&(&*a!>&&k(T>Or-|u z_UtBf>_kM9g#j{% zoX`FERFrJeuu#6ggB7$dzhDCCO0RbuqQw&g34;_5r->8FWRP^|n!CYv{=75u1LH-- zsn(ZOg*6HE0?wIbi5A=i%Zaf{^=H zS5+N`tIK4Jamvf(F8@tBAPX_JxWBwUWlMWlxf*|Rgv5LtOC14Mb=)92ehkJsh*$<* z=2$jk73TAzU%OXP4a=HOd7JO1t%x#dPY0mheFE9q&QP``K}a!AZao z-6-8=p*>+^X=63;nXo#i3NPVwU{*FKS^c&@;V%@F4rZdiCAFhax>IR?q)8tVeFs8( zY!K)Y;@aHR{{o(Fk62;@1Z9W*;EF|mKsVLZ8cfuF`1W(FNT}G)r$@Rye=?KPBNQDo0CG<2HiV6J8?*NIevSTBcBF;iAMABn=~0LualTv8+XNhGdm zk=|oL&lrXa6^{s$g}xtFrR#gx(`0k*g!`O-rR?mH0cD!2S6G3`&m*?@g z${3^C!hACo1ii|IpL_pgK0 zLQJ~d@N{b_j%-Z(fJsmz?8Nq`n^-u6;lz@V8jr~&YLc8xqBezVJg@gmRsHy8INU6H zArX}OSnICtF!i+qm`)VhPR%TitWZH7Z7Gtw{j${LeA!=Pc6H%dTapx%#haf;-Bbg4&)t}@B^37gpj~KG`PT_u>Y+i7?WhNk$Keh# zWHAQJ6Vgk0xP0wY65<$x$B)Sq3`e+S|MaA(TZQMNXwO)l0#&$Y^YTw=BP$~|-lxk-UVg_SWC zZrg>_by4xR9z0alzbrZk0kn}J_RFA<>eu_S^H0knrtJ0te@m^>{U#yuA{C%ZxGhLE zKOv#aAkRmqrwOJYOY@(8_w_96hf&3p($lV|n{VnLB!G5l@os*??nR1>3P zu1ORjde=GvO|aHvX9P|6X;xtPU&cnZ6}E4le;)XvzAXzQ06h1>xZu(9GQv+WoI-!k z3(){F_G-34P)PSnbv5MAM3stTZTs4b{KE%@J)m3HW_e&u7}ZH!8@}WUo(o%48m1xV z-xQ1In(q#JkFIBy_wvR2`#5u1Tiy_)w-YZgd<%$0PlvS~R?vRoCfOo2-jX&X!&ru& z(qrk`JzZhGHVyAf=+!dH*^yLtXn4D4ie5+I{T!EVKIV}TYyf$ipJO75O%>;)%1G&` z^O929w@#8u*~-bA{c*33KAJ#^jxb5y?$2E0S~%5++}=kGu36DJo&P}i8P+e$Tbm}C zg?@#f5I7nNsGP$LQQmbybt<{S+Rwrvr|2PPrLHZlM&VSnhZDJgUqa`pn>yk$#a7La zax$%9Y42$%%ZA7+kmK1Q@1HKoPg|R^&IF@@^JY3hx-+9V6lb0m)QmgurmP@&QZv`t zDq-kpTxR%j%F(M=_M#8(bR+C8Cb6U)d26pAH*z#wP@woPRZ8;_$)Si|SYx|}4d--c zpO|nx853t+T+4iR72mlLBFCTfLzg0y@oDjgID}1Z8W1!)6gqYo1Rfph9jvqxJUYKJ z&VqFUdVRp&!nCn4BLDPf^unxhb8u+HVRa)X=BQs|)=JQ_h2t0XBrDSAL4<%cYs`!5-H3SLnd zMTtvq_Ykpl8Wd-?^0Fy9%7Jp?VgehRL`*eW)6$XKAO z@E`3(c(6GW%(!}BfrdbpiYQA~n+PmNZqxj(Tv98~jKI8di+*6-9JeWK5gE9TD7RKs zj8X5X&1;3FMC49EP`M6fB*9jfm7w0RZF6S8hs?6s;0UTH$0bJM<(F-H&l>^bp4_i% zn+>G%!rdlVfrvjBbtU_Mr>43-n)EH=%EYH-S&cZI6@9ippy^uwxc5A?9%ypL)M!m< z+BP~#R`i!!(iK%ELi>3$k1nzYcJfhD%G$SPBu$6s)5c1+#ObPh(+#niIGzN$rc1y48Bl3&lnYz4Svc{#!N z@E|2ZZ@|flb}Ketq*o6^_B-Infh-foK+q0^*$4lG+jPGkhx&;?t1bL;s|HbaK3-4>^yCa6n%|l#~C+A7LM1;+NNdQ?~#WrA
    P!Nwrpd-qT`N? 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 diff --git a/tests/resources/qr/qr-size-200.png b/tests/resources/qr/qr-size-200.png index 202c0cc1970196532f0fde06446de707ad7ab718..94f08d81700f4d845fd699e1f343c5f7452ee60d 100644 GIT binary patch delta 10634 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 z zlg}&NnKeILPdvbmO6l0RTz+@Q|jrcpeW2~M!+xwafuHyhZg~p3l;$9C(}?=YIy==>YjgRVT9Y zjh3Ps6M5M0AVmp7cz4UMZrfb?F#eG9UeWPtSv%SW!p*;-+bQ05hUE>rG8A%P)}>68 zRn119Bd>`O)+Y}PCastsjE1}jaBx1eGce$V>{(Qpn^Q*&?^$okn*{^7f8uaEODCzHjbRr}2* zXAayl*7B%oCOCq!;y<5;e1Bz2Z7pt~TTSWA6a?N|`n|^S8hEY$`=5_N0WjzRfLW;% z+7NK-0)ap@u2R~rX2!1O{HEW`|0^JNC_4u;8#^kAtvKa>yP=Yldqq5M#26LW~c z|0V1l&8#iF{vYwbV8BUe`#(VczY(t1_U10G#`aGCf0JNNEc~A#{~zPxXyNMdqZjY- F{{XOSDT4q2 delta 5717 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 CHP^cU From fa7f9be6f7966e243d1cfc4e89d122b4730296ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Tue, 5 Dec 2023 14:25:19 +0000 Subject: [PATCH 18/21] fix: update image sizes in graphQL test --- tests/e2e/Services/GraphQL/AvatarsTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/e2e/Services/GraphQL/AvatarsTest.php b/tests/e2e/Services/GraphQL/AvatarsTest.php index b95e3b251..e496cf7ac 100644 --- a/tests/e2e/Services/GraphQL/AvatarsTest.php +++ b/tests/e2e/Services/GraphQL/AvatarsTest.php @@ -29,7 +29,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(18767, \strlen($creditCardIcon['body'])); + $this->assertEquals(18546, \strlen($creditCardIcon['body'])); return $creditCardIcon['body']; } @@ -50,7 +50,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(11100, \strlen($browserIcon['body'])); + $this->assertEquals(13328, \strlen($browserIcon['body'])); return $browserIcon['body']; } @@ -71,7 +71,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(7460, \strlen($countryFlag['body'])); + $this->assertEquals(8830, \strlen($countryFlag['body'])); return $countryFlag['body']; } @@ -92,7 +92,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(36036, \strlen($image['body'])); + $this->assertEquals(52601, \strlen($image['body'])); return $image['body']; } @@ -134,7 +134,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(14771, \strlen($qrCode['body'])); + $this->assertEquals(29444, \strlen($qrCode['body'])); return $qrCode['body']; } From 298d6ea917809fea6b18327e5ed194617834bff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E5=8D=8E=20=E5=88=98?= Date: Tue, 5 Dec 2023 14:41:05 +0000 Subject: [PATCH 19/21] fix: revert qr code quality --- app/controllers/api/avatars.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 540d85dd2..e0d967eb0 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -485,7 +485,7 @@ App::get('/v1/avatars/qr') $response ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache ->setContentType('image/png') - ->send($image->output('png', 5)); + ->send($image->output('png', 9)); }); App::get('/v1/avatars/initials') From 14e2488f984cd66f5085f644e93a78680858113c Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Tue, 5 Dec 2023 18:20:47 +0100 Subject: [PATCH 20/21] chore: bump version --- .gitmodules | 2 +- app/console | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index a44e37790..1d47a44db 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "app/console"] path = app/console url = https://github.com/appwrite/console - branch = 3.2.13 + branch = 3.2.14 diff --git a/app/console b/app/console index cd96fcad2..11244b1ad 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit cd96fcad23a54a90006c9383e85c232ab89eacb0 +Subproject commit 11244b1ad32648ef080f914ff5936a8380b5e199 From ea3b9ca0ec6a86c59a5ac396d9ba36ec7a11e1fc Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 7 Dec 2023 17:37:15 +0100 Subject: [PATCH 21/21] Revert "Update appwrite base image" --- Dockerfile | 2 +- tests/e2e/Services/GraphQL/AvatarsTest.php | 10 +++++----- tests/resources/qr/qr-default.png | Bin 29121 -> 14593 bytes tests/resources/qr/qr-size-200-margin-10.png | Bin 5512 -> 3750 bytes tests/resources/qr/qr-size-200.png | Bin 10906 -> 6075 bytes 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 76d434330..2f85f2cc4 100755 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT RUN npm ci RUN npm run build -FROM appwrite/base:0.4.5 as final +FROM appwrite/base:0.4.3 as final LABEL maintainer="team@appwrite.io" diff --git a/tests/e2e/Services/GraphQL/AvatarsTest.php b/tests/e2e/Services/GraphQL/AvatarsTest.php index e496cf7ac..b95e3b251 100644 --- a/tests/e2e/Services/GraphQL/AvatarsTest.php +++ b/tests/e2e/Services/GraphQL/AvatarsTest.php @@ -29,7 +29,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(18546, \strlen($creditCardIcon['body'])); + $this->assertEquals(18767, \strlen($creditCardIcon['body'])); return $creditCardIcon['body']; } @@ -50,7 +50,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(13328, \strlen($browserIcon['body'])); + $this->assertEquals(11100, \strlen($browserIcon['body'])); return $browserIcon['body']; } @@ -71,7 +71,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(8830, \strlen($countryFlag['body'])); + $this->assertEquals(7460, \strlen($countryFlag['body'])); return $countryFlag['body']; } @@ -92,7 +92,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(52601, \strlen($image['body'])); + $this->assertEquals(36036, \strlen($image['body'])); return $image['body']; } @@ -134,7 +134,7 @@ class AvatarsTest extends Scope 'x-appwrite-project' => $projectId, ], $this->getHeaders()), $graphQLPayload); - $this->assertEquals(29444, \strlen($qrCode['body'])); + $this->assertEquals(14771, \strlen($qrCode['body'])); return $qrCode['body']; } diff --git a/tests/resources/qr/qr-default.png b/tests/resources/qr/qr-default.png index d6bbba4d12458ffecd85d1221b6717b710213f88..a7da496d9fd62ecb6b0afc8d1f2bfb154a660cbf 100644 GIT binary patch literal 14593 zcmZ{Lbx>T*x9vcX!NOp{WpD@%!QI_0xLa^{5AN;+cemgY+})kv?hcRd)_t#j_q+A} zICZMK*X}b@)wO%?z19wumlZ>T$AJd`07w$z!ioR@M9e=A@M8i{Fl!F_ctM#8$Or%c zHPHw!`p_R^B13US834eY902eQ1OT2srhE?o04D|j;7AVu;7kDkFl{qh6u3VYAPuC% zgaPlt(W9LoBUncX84=iB2zY2BgjM|c4`ViD31Ia7kWEfoc;c zC56G0m;>|7rt z1ReoOkj-ekI%}4IZmCR$(F`6oSrmC)&9ao95bSRCEDdh7qW4`Q zqg%pgoZRFuh!DBq*V!19f?b#GHqO;AD;tHe9bx33i2rT|pCkMJ-Bm;y^G?GrdQ*74 zEprmWVFVY(wm_0|1e(D{ca$OIQRLT8=DsWFKHDWa!ZF&yXwQ9WQL?%{Badm3(;It z8O$v*x>O%@a)|`SxIJx}BsCbCu}mPBH63Ojk`6qyi=Y(8aTAXSt>|`ZNuMIoQg`R5 zhSA2oD1K00769~sJ>Oh6@4?*=&8LHKP7m7WLhEAxI{SgKpuunH zXsGaIC&eI9!I<8q5!CWXZtIgw);DdnU&zv#bv^<9FRI%P>liOAl}aV0>ySNgAm--3 zzD7I!dwEn}*JA*TiE;{Lp@veCK@H!7e<@WomTdIbEu|Q%`Au3T{x~bN&eJi+nf%FvsZywarVpbbQ(27SO1dsOUCypxb6&47~UCOOG{os?U`r4OEN85FEW`9wj zMS-QYnmJF~Z$EbDeTr5Qx>GdAmwR<`ANRnbSXmCZLz}|c<0N=D+NKd2bRDpl7g$mk zO81zxaZr2r5;zeZfmht|_FBsXS-l=-mu}nO^Rwv7^6~7X#ssv!bMosfi?P_Dj7Ell zZq1zLjvEpS`hU%Toz4~7>Av*L)KLvxqIs>@`73B?Z^%3PW*R{|%~IuD~G zfYUlrmQ}@jWjKAqTmhQ7@T|~>YG9vh4l)?RCYC|g>_uMbhgl<7i{^`YJ%rQV8ksr> z22r|mT0j5*{7L>Aom8uF%1LxRSc^#36hZ>ID|ruaEn;!em7v$(3NKFS_i_5RC?~7_ zz%5U53Pc(4rggQSuPhU~Vu5yqSHYtH(*^$V;19_T@CQsSzymqQk)}6@2Zb47FoPM< zd*pz^yp4QOkvWD4f2{LK2ZcEvm$9NgZ?PGp;aFYf!Uy)2RCX{Y%7Kk405Kk2o^TNgg`n3|3y!GalTI` zkH)+_g06vE6v7)X3l^(kbjPensLZCv0viXV-;1dzE6 z*faCqr#aejvgCa2Ud&dDFCmN*XE6%IFM*q^hF>W9KJOc7S2g=caE{8|Iy=*O{z$u+ zJAleCVz^OIWbk{1*l8yrWpC|>82v@O{HfPNO#BzHZ#=r-UA__pq>$Eclbe2!l{kY0nUQ@4XVT6A#9+C z!0g{<^`Ex2)jsY?W+|VnfwjX=0`2tk4f0!v`=I{( zy^&^xDokJiLH1kAe4{yaA!W_>)e03VSG6 z@{GaCBDsUG*vuYiUPsrUMahTd928wU4G0*g$kMMbDYCtz%6(;Ve@Zg8+clA7>d@@r z6$=)x)-@P;nl}zDCI$JWBl9qz3?C0kq5~vo`18}T`iE= zl)NIwweoH~ZjsF0j&UAxhC}5%dJa5~#TXMCEAHB4tj)L&|p@M1vvd+``^Gmdf$k}|uV=%>Lke|s|a#a87Y zc@=$b|CBDJZ&yb{3wT=3cVJPSu4=5lYUtA}q)!1BRc&+mNiSDJC>T@p z-2s4A5eC8e=@mW95n+1?tLoBn;;&MGsVaUZ8!!5E%w`<>6C*nAABt@(h#moPJ#*RD z6K_a_0w8)05vd^ZzoI2iK|T2a;!iWlT!*|QTm@g05oC`P>A>K;RHlhA#d>F4b5C$m6D=3s|2k z|L&lr16Qc&Ru2C`xDbp*_n~#OMoeSp%dQP#^{*J)UpbR8>u`57()0kU7DU^`pE=3_ z!_?kZRF}I*o%dc=ebJDqUg7(8pkadX0dM%SX`kR`=0*V7<_v)_q& z#|#2opp!vPHhjf%vC$k@#^YWL&@E^M0Lg+@yqVIL=L5f`TRS~stxhlRzO(}9$*E57 z`cEyLtOBghTfb)L%~{wsWaUCAUf6$WJosZtLF&cFY-`NctTXx1&Y6}n$kHksz*|G; z#ZQ_c^X|7bWC3^jV~{>?QNS60d-WAZEF=>5`6{&qwB09;2&^7rkOLJN)wvRN9cZA6 zN^_G^ojd|bz&>XGiUQbyZc?KB13jVbUwLyrM}SxLZ3fz2=*In*GIGWXJ2L}wZ5-L9 zfQ+|I_N?lLNJf^q#4`J|jyXhA>EE%LFr0z@0 z+n)Ife}*%LM8Z5F`nr3KL`ma(Uwen`EB=D{t?mqR;zf2O4p= zyx9X(@O>r-U9|aP3kMyn{2xK~Kc3cg1Frkr3jxn{BmKH{1&cFH==X?wWn%cl5(=U| zecoqv5Tt>8AMnO9(i`4+HsxJuKK3vB?pE1py{!Ol)ni;eUTD;;_N6=M|{cA`Nh6CL$W!QoqtX>PhQp68+z zf(b<)Q|0I%{X~WlATUpoq0nmz!tBER{POGAc`wM+X4TfFqQ>Lg9LWuZohXRiYp4HL zU@xZ4*Z|En7pdrRa8<<)us6oUM(18Sq{Z20&bl08pvo)8@l;8%?foKUG3cdtx*}Y8 z;t=;<=NnJM8iZv(rnUASXnN^kPYe?lV?H7LvH)(OcI=g~ll!VrJ%9M?703CV_Lt!o zU2BEUrDIiRo%wjm$x2c?I#P;k9qpQ@9L0OxFXk?SlASO6CKJI)&ojLSx%U-~udnyJ z0x#}Cv2V7Xv2F}YilfWprO%cr)DEkozneSzBkN4v8(6CUXpD~2P0I+v;?52@F11!n zm7kYSuj`$q=E~Z6b>1JkR?5DkvfZh|*4q(`&n|{|n$E(}clgqa2vpyzzsEyLx$5;e zLHJvm`4c{}Huk@8UhYIL*}QH>es+Q4AWC(}{pq2FLL9oY?~#}9m%P$Do8pjpQmb~i z<@nRToPlq5E|lk{gx#O&rG9M+t(F9GRRPZ0;o%s_CsrJAjmjnG_VoPi=1pm^)j4I4 z-TS%22GPgmUq{ClO4CqhTn5dy{8MgrB25!1#Ul4FS$$qsih^waOofI)F$DDj1-lDJ zi6955o#IbwIp5d?5eZ*K)ia%7!BM0+g%nOZ9pCN>dWTG`5@C=zyz>4mN%E6rmNYNd z#^sktY3TNo=1gx=`SDvk=*vj@6_Wp;TNN6_Oa7@y?XqgcH^tA|t&H5q#3*Hf_oD{N z94q(8xT&(sLdjG%y7<;vtLgp>c-h$PUKiJ;eH|S^Z7DH+2OP92#}Os3xEMXZnpK$v zJxA#ngrPl5Wlj1>q6IGR2#s47NoNd)tOduX^sswspg(CE0F$m}91&aD?Ih};dz=^= zMguqd<$PkMS}sp}(J5}lS_hR3wBgWo6Ap14oxQF5S}4SUqK73^_w34A-dWg9Su!Yz zSxM|@Ql1u^WH)SwDWXSL=s$4nggfbT%UVV}Lu_|0$_k}k82yjlUG*75n`q`r8k4sPSN$bicP+q3?ck^kk3+AI(o}J7zmhUm& zQh1DFZ68K&p9NyIZdj4JKe3-?;*S#zYCW3CPOph>ptC(`^D>vjzXbs(%O>6@qN{G- zmQaYgT80^2`}g?Xh?X+*ewBAVMQ7`peSA8?()f({7Wq(FV89v31v2`1cG0_S9uQ~h z{?vJ1=_a)afS6Zy*J@gD7CH9CQ%DSdlse<<7i*wUo;NHn!TV!je@L&E@HRsUA+PBA zapBsnavhS`Yh6vwdV>_>m1YMZ*?FDY8x<_4@u4xYwq!$QvML=3Fh}$lHl5Qedj&F? zJMbg~A3-xphD5u!7P{EZFqYH+o3N&flYXJaBXN1+Sii(io_Fblf`|+cHC*M)6rbFv zg@-{njOELt3_!X|ip%&!s^yYkq<;l3AHx7bLE+6I$=P?_h@TOfaY3-Utxpo7!?0f~ zz-?qDjy{3>4AuOf9fg%HS*5W4bZefIu#f6SZm4oXR3Zb8Tg-98j2d9BMbv~)7~QUN zOv;ocokT`*a#i?QD{e7S86zweze@I6F+-DuDw#6!=&;fRu#yHExpkgc z&#o8@Vp39Z9ejM5MtnGeMauf0F89v#mL33RZBNchC)1(5<7Hz42v=|&vkx;>YF79* zN|ZF7SwgSvSh?~Z4;U0TO}A}s3!K9I(%`0xUsU znXk(jUO-3EUu=L+?eP~y&GSAp+bs}An7SS2z~w2M&hnFQX-umN(jRx7nse=?_CesL zXG|bvWI^ijeYpeJ2@aE(Y=d0OXlCTnL{)v4zoQ1N>cJC^WXcyt{oM&n5R+#A*|oxR z;EkFZ#x3YLihzG96mmv^_Cp+WIxF-~vFSfE4i_OHpU4PTiK2QJnNdhxs;BLanvr!^ zun_$V58x(|^hR6eyAWQux7astDZM5=b*mrF<*5c|3GMs)jsPmgrM_bU7IR2+2m5z3 zH-&NnmIEsiA5P^=<$WCTLZZ$aIZPQXqCc-)6{LwCQD^)GQdMx*wW>i!WtOT#LjEgZ5U`+v zt%}RZP(h(!Q%s@}0C!pMK{gGyp2~VwZO!`a3|oj4=2rHvNin1E{k3NlbI@mgRTK-O zX71)Knx{?dh7F*pUDyN=0!8rmHh*FEF6&k%B22mU;-G!D?N@!oN^4S}=VdDJ9Oqvi z#LxICu)I4)HzvD*ilbX~g4zmdL&ygEV>$tPbg1;u7piSTbeHZ5pDNn*vhN%=VxaX0UXokzh-N3Hs7 zA~P0Tt0Om5B`WCe+2ARhNMAwB0%ggXK1;AFuFU$)rZ^49`vM2oOi_pweb!m)TO4|J zTlq2xzJZ}hk7^f##NG=!kK;;J*4dH2mvk4YPm2t9q%m4ivgv`O^L*AxaZ=l7QuS7+ zfH<6Nj)_=lkSQH4sl0$SB;~&#!(QbU^ln>-Ke%@59ahfy>ys!_fz-G zK6g=&kmsIOhxziSrPY^#ZolIU>8LZWW~mlI?!wfTi~Xvi51@{(>= zd_w5P*WVuM4-OAtAPmUm)N0#53a&WL5|DEovpj;cYCw0l%#y<}XF*Ljxm}QPK;x)JC$;C;)n$WSA zLo3B^cjaGdtY5i4d*paz2%{&IkM=z7UzMri6Ipuvp**}$GRDG#IgIv|t$1QvXrlCu zf`F@fSr3kv$=*C`obKv}(CN#@mWjl|QbmkKcIrA|D zB~ynoS#@u(d@dIZHENR{9r#6r6jug}kaCS07*Z0HZG4_iHuXv=K)BCZJwDY_udExM#V?y%^-L-BVUC31Xkw zQGo(}>#jtxv7M8SS$9y~5jnzyTI~w{(%kt~(%F_K#Hp>P#xBUh`;5dSS>?GTPufPZ zp19+$pOa&v43CW*Q*w#c+EPZ3u_-BUoMjY{FY`~${`81K-Fh)&e?n(=V8th|d z+DwLULB9C~2#sGwNOviaSxdaj_`2_DuepfiL%j6&pZ=(GctufstE_xKCOH0;+gZV@ zlU$K2P2znS&NVYhuqVhp#(3SATxbS6*T2A5ejB9wVqp&!Yq+}@pud0qkqN?=U4PtG z?5Sq$3eVDyP!o+@3 zv9$_8{~^wiap5=f%Tc`jj`2Xc!f3}#@9DqQAa!#ju_SD3yb1}dre=T}StOz*IdK3H zYnCrAbk{knSak!Dn@%)5F}7<-FP%pYi4f9uW?TY@Bq1NIu*aLp>+>g4#!$W`<8yfw zwsGm~^#bT%a}KVEjo%bisN)Y+IUfkb1L>Ec`Oy0jw2o+Lihq-Xe{fTIdCr9*!1FKV z?j$BIEW`_RpED&`@lSKup!^xAc`SNv;w7*W#s7-)9X*iU)eZLq-A8V1ipgazpIds% z#uDCyV|~U;XP^wJOoo9Q?L|Bc-8 zH0SqJVW||#R`lyp&9>FsBA?C%YN#kM&M5V{bko`*aI>OH4SDR$)9P~OrGhc?-UA_^ zuVc!MtdvT@6UEVY<-TkJ<$N65C&mo{14K*Abk^1kcO`rMuep=P0-*G0WEfWR`lzTA znzTUR;gH?1#?Pyff?SLvn>gjI&4J&$s2CCj--2h~1{kXeG*h<UvT2_Z)gAEkosNTW2xnE7(2)BRl$O_6zA6n* zv2m<# z8Jj*cX@{q8EqI*-9|NH5<52#w!sn@Wn7x-`Hod)JL;L+oTHJ!~G1{id_TDbQ*UW1C z_n9T$V0E(5%mIV8jzHvpA%|Ica)TO zmV@f?@Dh6+2Bq?hr=&Je&+Ks#!soom1~_>`s4$5q4>b+3%KAEHs!HvX?tChEK_JDY zC7YHAXt*Q-!kGYaRxeH)ilo`TW(SEDp445r@vSofXW;FQ)w$~R!>HvKx2pt!VtgyY zNF!_XqRKoCmo#Fme5ERjIOE6x1|H^PAuA7C!HcQ2?9USK{9;fm9<40k(Cciv?&PsQ3Qm?~!x21SoNsvW!M`c7H$c;c}n&t;LBJjQpJ{j^&(U5#~1+qug zKcO|_27$T%bGcP_1GCc;H5N|$CvL6d*;fLJj5;^uMk zhu29f_4q*?m!@K_=z?PYb1lv?CGt-|{@xdDDr!ezc4c_x*2yTEKKEccxS=p3BPyesPceMHp)|Gcm(7vDuH3{@}??pUp?TD2<@d?oa_S>_)=^q zZm6i3C}xTp8=QZ6EFTG(%}-}HYG*M(X>wKFm$bW?zP7%qzeh54Z!h+IAI6I&uMCKD zB8Tk0J3HnX#>&s&VAF@yG)#%{_OddzB2}lP$@m~nzh|^i&ACIfx#$qet5{ZfwU#-S z0Ywa-$!Na{4o4?SEc%4?lk$34FMI7-c09TN`8HF;(qJ1Ddh2BJ^s;YgGk_98HKEm( z3H^#hOicZ@NTs;ZkhQ>M;1dp`i`i9*Q#hVt4p> z9~|UVEs&hd`_&f6@tQ1^rcw8FJx5a{#P;=Fg}rnJwKlaSct*tIO6{*q*7{X6#H}rMEa2SSibv2wMFIHCS)Kth2gFa+PPpEsgFjh*_rP;9&J^t%w}$}f zlZ&ElA$!OAcYxAF<*G44ngep|0=6i~#5n9V(OAY$h#dc|0QtX3c}6O4`}3q z?>n{w2DmsjA0GMFKaiEP?A>XmBTXfjNs+y?Dph1CpX|5Q@R}3wLit{I(F}gMO6Id0 znBnYMBj!Xye|uL9Y2)H2k{cmF6CGWzv37w9ep~`UHPYk3e9#!J$mmRU;Z9YJHl#J5bvJn4mmMhd) zCqRWvobMz}J|u*!W9Bm+fW-M_>y}ZeYyTLIphn4Xv8wi9RyQ1#hu}1%e9-oxFo{jk#>&ke;;RIgNovQ z6n_<7%wPAm(^a}Y#H@x{@-wdv z6MqL4hP-w&k5qvi-^Ise1ca9Hih57gYk}Y@;|MJHec+^<6oI6_k%-o&5v;ch3-DPA zhv7qo_v#IM;Be!VUS!=Gm<{v;NxRPR#~^x=&Q65FiKWrdNuQ7~^t<7{L4+}YQ2hk| zr@ldm5_sugKVvgwQveb5Lid80doIB1v`wxrVMp zHTBgC-M7Ite#_AkTW)i2*d6=Lu6(Oc6JOv#ZFeNt^F=Ypq)r1_6dy=#N^hk7=_b;> z;gDB-&Z1%_9!Km_Yk4hAPj#MfzucBF_2h)X*^47R^!w$edNggMgIqips#N7`%hIKh zHz8y%h`dTGEB~p`mPEB*Rk>wy{oz*RM|Cc~FIv;IirTfuXEqk>+lrt zlD2Z`*7ik0OF~0LqvUZc-;mkVyUM@qc%A!t%N8c2Xp|is$yekj@sm zg|3=t0OOp)QnLyE;w!tSX*MWC>pN>NYt%Ukhjjg1Z$ zszDB&{I`w|2!}#s7|9kIO}Vw%RceJCJ2$JA;6Mo%tFD5ZIYx2QxVeh;*-mganspbB zsixaX>UX&JlN3a~BUqtH+MirJ%$-??N|gPm_si((w3>r=xask7tuJ1(n^3ZcW^Mz+ z5zh^UvcFUa6Kj?*gyjrKL8>pvgJaBKh}DTCr#-ou(^O@&_poQmi?gwbA*)bhmV?(? zv8H{fhLPGibvB$#Gd}=C#TAetHx^}f03s!TVvgG zwIP`mDHPejfU7JH9k>LcX?*a{kFtLl?)LpN52As4A1Z^dftkR|A?P6sf1x_FNLT7CVRS_!U1wx`Vhqj$uTAyf23ep^gZwc2_T_f&1K%&1IK{tL-R^yCsQ% z&ka{gDe|oXd!d?CjWn2p}&#K)?u^ar} z9MzgP#lW=i{<;GvMJFU!KuEA;&Jqe^8u?hq0;ccql+HwM&d|Xr0#fll_;BTGR$G4= zE=uB7r8`; z^TO@ci{+!)?me=j_6<#I+oMYc8jywsEbthUJo0K~xW3h5^lPkfEVG|^mC}7{=R1x2 zt1UoSAWJJxZ_P4}_sF2W=P2sd|<}A?z(@jE41#j8;;k$qb}cF2b)oHC8DO9fe2fo-#~gXpWac3 zK1d2)^AZ^Cx3^P5Pe$Q$-Kf!qO)<}_KnG^=qJl(u%#`q-X^{q++TzqX z_ycU91=odGMf|jDIF5P|LWwBUul|Sww@ncJM?;M|j>x1!tglozKyJtr@p!A?b&&mL z;HsRr0aOjLu~bN)fQ!DcO#)heD_ep>b9eDCy$010$I~D3jHR?X&%H*K*2Z@;W1eZI zLIar^%!fm^6q01BtBJ+8S1D5sA(Z*k1}ALt2UEdw!b#Zy?&XJf}9{l$5-@+!EN z`;+A`ET4sNR>WAU4o6(}NV!Hz)Y*?oo-(7C!+m(zjOYUo9?;dlUn;O5X_fPdBz=Q~ zEN8wBX>lXc_#nLiXZ-SI`GN#Xe?Lbq=@AWXsR*xYzh}R+Cw?)l`q*$T-v| zM?cW=4hF!(FOZxDgYul^p=E3q&QxXv#`J{umx^kRb`sJ^$#`_bxdPznAbRGu&}QPH zV=gq3s#KDYf4sd>jT6ltBvm>j2YThS_)8U9(^05V-V)0B~o zMa|d4{z3?951HW2kBR>)98qVJ857wY+FGWch< z-wJVJAD+CVsEGLLYqc4FZY%ArHh18-o>fk53W}iCi z%vW{sKkf*>jRR@b@S7!sYKFIa4hK!0Dj&apC!&`t1TfMwlXIooq1E9;g+|jZKxHsq zixx{gz}ew7kuFZIbvpS)X&BM(E zttDya$Rl~y#t}=p)vHEgXD1P;l8%8H8a7;5T1w_L)IIkIdY6ROg}M6W*B|o(MH$9&tEhJjuMZX*68(&oMjWF;2j< zA3@+4iHRP2*v6x;F~S0m^;ND|Bg86a@g5A#kWAAtO`R9OuGlS}8Un|bQG!9YPCHo# zOxZ-M)YEI?QQjKi-=Wh*_kvhXWyOz;$!Hh1`SVrE<#l^{#wKJDTF5?=8d12GsQm%j z(QF*Epjg^iZ&=-zp33)HSXncc@VeQ+y&j~v+(gW^S+IC)bHIoX@NQE2{(udhY#@zE zYpp?a|H6I%O!>?Q1~A22D$1{P&E=n+&m9ZxhZHof&%3d06Bo6o{MJ`5XNg5Kx(^o-*?}2n0u4ffqI;SW@qIs!swE6)t0QvXWVhTm7Q*v5f0m)$oE3fF$2O#d zNopC1J`_$>htf&JD>PADZ<5V)7rNeSkMGhq-4+}4as?h(tS9^1&Jebui-?)7Mp2WQ!?@~%36s%S2iak7pB_Ar6 zc3YQ^pWfLw-eyF?VWiiscVLHqcvy=6bhNTsfM8hsqglwm<@KSUl{J#d$Bk~!!kf?i zDq&hL=;z43L$xMD|0T5OHxbV3L+KHBTM`9u zyT{GuA}k@jX3nlW>wF?lTFlGs`?n1T`ELaEO|F5S&R=KvY)Im6l5K;@ja~veGlP<* z@PI?yBtY)uW^;|`-_p(<+>5tU6*Mm()by-7y9VTX=PYaYcIT( zed`o?jQ!!KWAr*sE~R;}_1Tc*CiGd(^i;`td?vc&u+br|_w?cb0Bak)qVg!7yJCjG zXGhHNKTsUi(9aT31o1%!bMl~VitHA|RCdQmnT4UsQJKghACc4!70BrpA0)rZ?@vLWl; zX096uP`@f3?V{P2cNTM;SelDgR^TSjr(2Ps_@TqJ(I9(M{%oGDQ(Xl18&sUOVIt9i zkGh#b2*3K6)|V{Y_b>|Jzkjd;Fzyw~d8NNFb$jEJezjXxp4Yc0c|g|NFO6fvix|Sv z^hcgS`Q7$D!nxOW-72cMq-q=0w)PEKz-z2TTliy$$ptEd2W-2@@*feoV@9`_z2Nh& zN)Iw)4w32pmYXp_%k75^*XlW$Y<@@ct!u99Tq~Pk$*@N1bXY!bH{YjD zJupLB+u^UPiw5D3NHWpXDsN84I_N`DNbdnjGkOYUH%2y73{y#pnyL{L1Vu2 z3!d!4@Ng!m_MBIIsDx8#(*!bpdmKiEBAgxpgw-Yo= z?-UvnpV*45zWdf6>{Y8Q^L%=_sWtj2-4kV=-IBhRTF10Th@R1HF;f8_IwyvXjJ#*T z)MkQXah{!RJc=3o;R@xjEEqV#kDe!{zN=wY1L&^85ek zx-x4GWVv6mA5h8+q53jDRj9;rTcm`9IR}MCIxan}-tnfe+uLdW zg5|wwKqE(6D~dJW5~ZR-@+Q0l1Mm}!7-}vl!;hC7q#8IN?GZjUBVgwJ5O$!qq~AnY zw7et$1fr%Kl1iKrxf-+i6}IxB$=tmRJLnUj?PC#SHDp_7%dwIe5|{0Gzlz*zL&O8+m6#6P=ma*7+9 zo0>TS7@5D3&Let$U@-qS)hV{T>a=w@pS_^)3@Z)$rZn&f|^i zb%wYP?0}LyAqS{%=twZYk;54$CmlkhSOI9Ll2pdGg@pPeQ4q*IdX`%IsK7@Q@B3UBY9!MYI#gA(wJ!R|kzVlTsWhZ{4Q;%2rG-Nx- zTYW}P6_F&z)|}t>RZM(*&|f3Y?W-cqhoAxJoWw#p_V}_ym17_LpB3mOB0unRY~@Xz z8g;INAqMHXECS1lH?TPpz7QvQPVU;I#2X8~r=KwxvxSMapYjws3xB1RwSW%kwIocT zrEw}V)m&=~u<+sUX^S4SBb&Ym+}d#Q7UFR~z`3n63+o2SWt2{I+t>GV8D4 z;68Qcdb;=S+Xvi)aQ9Wq0X&eKFACHC5;}|t>dM{^MRnu`U{2V$v5L3-5p3u+N>t-!^t{sTY)cyf8I zzXNAXqk{;{&B3Nu8!D|LF}3|RDe*>($EZM4PoiX0UNn-)7*HtV7E~ybGtmSw2UJU* zFdnE$GsYCXg;Q#m6}Y&q_%Hp*qaip|N+19>;3lPJD69H_^alguj`u4eP`DRfDS2>}Lj)n0X2Fa>W~&3SQV6iXBDOnv~TEXStm&-|Y^s5Jd%61b6WN{G&JJ zQJ;4w`Oard5)nS>YSdK%3%$joTpdm8F#$miF>sUmpvk<3soHuo z!T&V+Cca&uP%dJ|mrFRJ`$}41&o&)VP6jK&2Mt3V@SXP7wAs+g|rImWas8{ z^nGo+5GV!uAPgE{+gWz4q$aXr@oIUWREii8Ssza8kYsFdTx45o4T$yqh5H#>Mn<;C zXX%q-!_`?385N}{X?vRXk*?I=D+JG@cH78Q^u|C3k;ZD5qtU}Q2oBU8c zE`mJ*+kT$DB<`>j0#xP6zkxY z!32zoKrSX7Szdee`K)Im%|L<$15l)4i2b;4JqyjhNq4-7e^o1){EoZNN3UhTaidh8WxY%?#62UTJ-pV9267gZI60-(T%d+#Uf-8D{;q|J({H zHUcXLD|nayneQWuJ_`qu0C`0QHa3o)lpMV!pJ>Y4nPXyDEorRS?t91is|e*_RDI>CPc6jblNgz#?I=> zyN+y%2`TRh)P8yS!QWzL!?1Z->!oI@xRW=4QS|1*8YwtFuYuI6^_x<% z5u=9sZ3zyZ-V)750JM~or6Qai<<7JqvfB}9M003d#FEkFvFs#8v>c>VR=cjkl9Q+> z-vF}sy%KW}oz-{mcUAe?H+gHyO6h?oje=lA)23Zx1r8{&NM~=Yv_4{rKS6cy)t(H< zf+iVuOYpA7rhE#|j=Owr0Mepk(t#$sh*=8#tEWa|R1)O;w ze0%ooH!W%QW5~N<^GW`oycteUFY`Q zf6(#HQ_8X1)5f6bxz}xT5zV`{=W@ryU?%$FrRJ!zul-zY4T2F0@Fs2uaEJrd zo<6CY*yn%XVI5AI^&Wq13dK z<^ljEtpH6lkm38BPaP*WF)qktcXS}#mnhK%&?osWo(Y<)?JaV9_O3yw?G}MmWiY3^ zBuirL?IV6ZS`80|&;SGVj&y&$3LgEyiYg zPNlDp9rT@v-K_iVS%rLT-?!a@y%M38Iepp$ zv#i3%y(`?4&qKqKE{p!+Kz~eRkX0_7@&*i`Ei@^>?37A9glEeF}1PYj~nqMb^AX5*lpGIHomGy=I$r2ypVjbLT+U^zDyv1O& z->8&>ud%LQ*oe4w%z~d5)e3XxjiR?b^XW*UN66Jf&kK+4B@$W{C&mcKcdNdYXa^D(Mn5h@J54-_V^AKm6O@gl@dqLQbm zf5*p8QkN%YpsuQ5M4OVdmy2Gp6+QX>&nZ-|l_8xyf&flb3oc9n8T;~7;e*U)d>lZ2 zhK}ZWC$N~pGJfR;XiIkKjMnm~)SMHrf+02}%Y;bY1bmGGVIpenAy@(Sm0Xs3C%;Gw zn`L6u-)BB>tZulG30PYvI2uY8xg0~MD+Z67zl5~}N)C-9J|J_u=Si*z4y__6nxev7 z{mr7~c2R|6egS^K7K1irfm%ReWT|NF_}iS)=c7cbG|w}kng+Yr+~($}<9L(U+?Kgw z4afrF%iJ9K0RG<^%-NV%)xRH8Prabp04HEt1_}!>1Y850ZI@g?hrSko1t1LkTSmLd zdy_mnV=3(?D(Ew#02ch^1Knb_oBy+JqdmzK0BgVz}@Rp^A2I zd!v~p391$^o6V7jh@-{{<$0iSG@8LU27a_638)U+vn&LDVnZErXCboQJl}Q_#VN0| zqU_F$=+)&rjAE$agqy)O)?Km`8@Un94JCvqm}k zl^m7)dFr&3nT*oj-Klc{nG$f-=9eYv#Q?``?iRnSvCPm0RfPEcG^&sJv;Ap;R{U1@ zu_d;7aXy1) z$EXo;&nM;VhKLF<-9fQ=vX!^iZk{LWzEf$#rNF?Hg-x=A%YC@QSFRE=kuTY`_c-_p z>4X#g9MH^m9{h}NywwCvTM!byF01ptY!G)`KH`{J?pwwutG~Kq0gZl{(hmhy_dV30 zv$~aC8X?%eK+plXDQ(J(I?22(MQD6W2FSK^(D+uM1`~QqrSLTyaBcKJ6dfM|h;m2) zCec=4E1!Em?qg-R0}oK&a|-s^s0cp5P4Sv1g_zMT+vpK$U02<>^ePqxqd4C1`DludC56H#oy%{rEPXPUSy45rp>hqyfM zo)hF(DWds$r*QgM1%(48C;J$EfQfHgNN&Lh2JlZ=++J4C>MP#jP9y4p6^{5kTv!Ez zF@;gd7W2-XUNvQiGh;`DE+f%1?7(5;PO0l3LPHFAytlx285MU!@>+xN#^doyJ?w1z zO4QF>;K^!svyn;Y3VV-yGL9k)v`x|##{oj#A6N3~RK$P-?gu&tz_9_t8vn|8wO~gG?L(j- z%eC4L%%BpOfeS!p{c5xg?oUUn5(#a#MYq0nxpN43We=Ly-EY#e%aUh@c)`a(AboEb zr%Qc4+F&wwF|Q_9BxqCcCT^hypH&A3+__{`w+CKz-p}LlUR#)@h!*q+NnrOl=m5$s z6VF{8>*BmgjcUk}-I(d;mjiWDKM6Y+tQ2>LGRX7?ouD6A_122RLZ>x0eK)I{zuOa_ z&@Er9bO#FhSEr%E&h}jlkYy)8%(RP_$hw2Gk}F@6sZ(KRegCmcKlPw95VWK9cy0g1 zNg`B9_0nFy*1Pw%(6S$t=Kgsx{h+=}Eq&5v2n9mB9(2Z*JZ&IaPV?i!ZF51yn6sKQWQ`BB5Ij`u3DXNkgqos^ zl;U6%CULo_=YZcz zwVx!BDY_#R#luVx*}hXuNu@D;9~*SvZ>3U>T)_qGKhrcqep&Z;rlg8B@92BOM_b0o zH~j9_BbgoVE2#bU8`A6^PLRXlM-iErWixK@6(q$ z68h>J^K(cVT#de8sV4f7SJlrE_s zz{-F-@FvaqQwm!@Y*4T50tbNGkZ6!$kM=OIxL?|rl-yPkqSyr5bza5y+)l&9fQTv%ufhUEnKb2_Q4?W*WzOa=D(C!aFX^ZGqXuf@do!GkZ>S- zvT9Dn9%2$!Duey+c~!p!#@KEv<@zGP)~AIk!>3u0uQwR`wUSzFl99wkgsYSQC|_dU zOx#}Pn?*$Po%;D$qYlwZ7LS8x6}Zpa3>dr|==}h(=+1AyJ0S(i%k3=#6h}o_5+-?djMu^TUhTeTq$%k8J zY+GvF{4-tWHjeu8Wv|B8+2{3sdP>zjl<7qr2YPf~>lwC)5KCr(hpyz)8bTDVQ^wXm zRu>346^5+&gsDwSpFCmQGlQsz1DxVuhwI+gPnvGY5Ry2_IuipQMGA`EG-VfmIVEmB zZMW4;;HnV2-`+hu;CI=878RU{xB$oZn;KdMk9t&wh&1bFA*uEurZP-X(e+NRqip`KmO5Wp)Tlie#t)MnWv%8x%QQ?o+Py8 z6qzbN@L~JEDo77$HAUqj>H74TC04ny*S5-X7WxX?y^{I&NtEMJNg0`;TxXWjVm|HG zQI3Ul=pkLgaKzG%1Jx-lB{DF;GDT5_A+TY>Qgyh@SRJ(A zkcw1FH9x4<4=kd}OaeX)JQPEPhkF^yaZqZ>@_0o*CP`9Tu|l;=GL3q-B21RPCdCV zR%L#5*8AXH@UyujX#2W&)}L3rRaI?YE(Z5=jHL;FKvbo7fcf9V?;$Z98+mlXYuQBt za|Q7IwwY;m&9`bdu-+A8WGXsuuePoZ3$vPXpT_CVc0n3+*B$ea36-mv8-u)*fTpkR ziQgEID;Po01R*yE8?U@{V*~iL|l@Fwgum(QWOD}51>QAheJ4@CS2R?@2A^Bi8WGa zxPa}><;fZY@edow2%%FJ-E^~fmg7p@!c1>1PuD(Ru=l;z)z8M|HQHiqcp6t68yfgP ztfH9h#Lp<%MuN>rJhm|70~)Q*{=8`7yiuB9gG!TFaVkTGJ-X)50AINAI?|?}iWG_4 zo!NJ%)y0F&Nh)wJ^1C^4Rps>of6;j&p^9)m&ZcK%re7MOu!b`m`8H^=W37ASF+`Vd zgg_zYop&mn$a1K>Lywj7-^n$RbEK{F_az8S=?QTx*Xj{hIGdvDkYyy<;}cJT<3F@f%A~nphhCO( zmyW#1j$;sga|_F=r34~*AQ2(ekI8V*nC`RQ1cfMCr0^2`OF&Y-sfA?KT2Z@$2b&cZ z>V&h8N%g%L?e~|Vh(DIP{4t*k*yR5roLGi7L=nVFTW}Px4b=ZD1x<>7 zbA#c?{RsYpAsRir!Dq18J8Uei5VO!40j3ckS8az#6t#<^&m^0C`8+<4pNsY(!a=f)?6H#?=~xowsE?WzhA^urkp94H!n1HTWJFYEv@u*=PCPcvERTEaapbEa zV4Vm%CzIUA2}1=Ec63<8kfJl}NWU7dLoC>l**Y-;LMSCy^lj2d%|0DyDvPU!xpP^x zs{$|$w@odjIM;wsct6t1$H?oC4huXtJ@;RSM^mE4vF{4V} zrRw&B*wBd;M%%2dyI8vU7M^oAoqih)SeYOiyo>pvd^_}^Oph7g*o={jRiDoUi546z zKI{=3aJ62j8E@@ay421&DR@fdYQ(n}6u)dxPvIn}mG1>LHHr-T#qU|^^Ssw+2Wmi^ z)UXi-iTY{a?inE|t0n}-`#2k=o-qMaH^tpuF&?n#Lgr_E4ErWjUtoP*0=|_^!Utj7 zebhfWQ9kpTqWIw&u!T~=Zk#6T(7d>`zGuS(Lmc({w)R(cbZO?zk=T7rKWUttKGxca zgEHx`BFvvnvd^EQvqtGT7rBWvmphLjcN?e)rnKfYd1u2hQIlzshb|bJ@vRjBp)Kj_ z@mg3JzD1a)Fi&{}uV%lF)oF2b=Pm!6=M|MY82+6hb0)-x3!6HZfF60MD`mNa+y35+ z%g`fk zV?6v)C=mF{kQxDYe0}+PQhY)P9zm_>Q3>4EgJFo&<%11=uW-4~F>2WDKD}_A_j|c! z;f1EaA$MH7l0QyITr6C&CmbeT2A&^jiQ3Ru|Gy5r^l5E4(xSjirhcXt{2k~Ip4ECy zufTk~FbSSRYh9r(6AwduZJ`}^N2|6gyhT~`}))@q_b~mFr{!ZQdqeOlywz$tk0RhZk&G65J=3Eka z@k1`Vk#7XrP$ValGksVTiw@#@T%98R^wgnow?BdE@d?@K&d)*6ZB?V!SnA9mE93`! z?^J`cW|baCN3C5bO9KTX_&VvRz;a&Sox1PW2GvJNt(bt7o@W>3PP!B<2isceofnxK z4WwV_fD110c`(ZYlAAt^uR9b&0Otyr<}8^GQZeqde*3vJTqha-5x-v^>sPzf!im@5 zA|I@258;Nxb5jTWE{WFXQ7kv*xx_Iaf&XmmBoU0xKhSA$*aqf&1(#SrLV=ECEdhaN zxBqLpj6IC0rJ#Bz1KTz&Gy!RAZbR^(o$SERg46~_bCjkBa))Vb@agIX)0XyGsWoa& zFBpvwsKM!%k+0%T5VaI%k}dpvvuX=RuNLkvGjGaA5`qc_65J4z)YxpT((-E3W6*hG zhrfT$TDGN7l~0~sxv#etb<_!t<6N29?I&$KiG<%TV)v8X9dTA~3Z#{3iQ2IZN z)>3}x4}no^<*!GIGvGRw()_uCs}T_kzC@zu$6xO*4dJCJ;lF>T++^$`3Z#eFBD3SWMz|i497Tvsf^?v-vk+VPxzP5%EJ6}Oet`w1*YGaa$O3=M2k5D#oM9YnpAn^27OI59kFelOPCQy2mpa&*8naS_;+boK-4%|k@pi`YgdnX=PA z)a`r3(C1^GoezXYBWM7xAu(Za0LKc5HICr^r$3*Cg7XK_E*^vd!+)VWMf#I|$)6;F zKgJ&}1OYT)L*9+Mk{+J%24w1^?az7>NnZ}Z`r6WL5OeUNgf_2Q8Ew zl8g0`>c|yRFvTAZayzfDi9vm) zHtI3o@nZ*;A#3r417Zpfk>0cdnDOD&<&HyBBFeCgx-z5ZxL0PdyAy z8bb?@Muk4Syx>STeV*ym-srsFKKF)*M>Ek5l@*{FybAM4&Vp{wIx3QL`Af*Gfwj82 zyzJAbTGo)RjqbNqZufg{$%yTBj~Qe*Jt(%j5~TiW{%98_HzpYJA`!=!j`)?!B#fE+B&O zFSWIcV({T~AfhZA*=S_nOPO@7m_7%z>fODz%32?jc+OKgqmBZ|ILPUel%D0&KSMQIPfu)AJFgXNg9r=!RpH!p1l3#{VPZ_i zKdD{OFWo9tLKq1kEJ&@~=NF1IJh|N-|A8OjA`!E9xW7%XS)v(AA9#Ph;q)gg1G9|I z?!9)2pbKB(e#c=FMUdk)KeRlryyA-UTnzH$mIrMJ^_@^D6iS5+8`*WDb zPYD>|Z+xn}+AKCET!&}9MRAe639v7l;}>~hWu3Y}Nk)U@Ey z`kNq1g)5PZ-pU*!$cgBr$3OdMYisB{;|8?5B1wnk@{aZoU=vzao2NA^mi=AB*Rw&v z`ubh#p5haPEywh2n<=hOD$!nJ@{^$v+~cYNi!aYtK*&D!dJr%)S3^s`=xIWoHJ76#kZqe|$aR+T3Whd4=%C8dfGS~TDYq~ZPs-DN6jU-CJD8H4=8zL-u_3$&9Xw8sWy<>YZQR<4oqAUdD z{TdL)|AO#*3-!ESZSm<_r6eUSUJW(KG~Or4Avck}50(^ExCjrWt?nQ?Y0jCkg8>Gm z!AU=@4fbDlK!8wpo_V*YUbpIAV-r##X-iOvR_hVJgN#dBNu48RtKDOKiIzr6o^*-zMqB*pTocp3!jx*I4D^X4dbY1qrgZy#O)Hbori; z4?s5~-3+LnTMe)dr#9FvS-=E*PGO z7pBt=Q*PIs{qpNFCTD+IhtskMj;_l$E`VwT50%3AJ+e*z3ndA`UG{$KW2!+pX!Ly)K4>rw)3^kbWBHAr## zb%DBA1P>c|ePeg(9vny$P-Fhj2P3oht*6bJ!Dx|jTV;2w;_VJLzUkqBWc2slbX}iE zo|y9RrV9b|eQ^VW#H0a=*%aLlIT-5`{zTj4NYYbeZE-bl9$bIYoIBYoJ-(9Pc|Y-P zM#{QkB?iAno$KRH2k38f!X%fD8hfW+f&! z@PZ=3?}8wNgeG|L2VZ$ts&B1esE?Y*FPxCV>uA2)fXnsjLqQKqMk*Er2!8u=?q>X| zDU0^v{OU1E`W?p9UgS=9C>rswm$E^SnTsQe?qH@OIDv#tMnTFUib-3z6PG)m3(CZg zz8ium4=gu}efNu3$tB^0uJNYN$N~N1Y00sF>Oinv;lB|++@Tssv>VwrGJPBu&}PXJ zY*6{^HISOoKVj!Kmd*pI&dG8KQ+{9lv?gew?a$k&C3XU*5qG?#{5^>Pf&Z38tn6dr zx0YaBTMr;(xS#0U6Pm1OFcx>t2^BK16XqEmprzE;_)Y?HXzl{VS^Bfs!OX!*8+&By zRR3|Kg%>gq{B+JG$*mp+)T=O=E*0}IucuCOTbYw3AaKhdS!qKgD7DdJjV2r|EV8SaxtwSg$M_J18#jiQOw;VeKj3%%}hT1*9qZWX}EDe~|dG@+a8? zIEFj%U9ql3#}SnEKo)OA+;> zzC4g)^gIZeuWu+Oc+~j>tm#VFMysSClol(S=T4&bmy%y+9Aq}TLj)-Hw8@n=e@N9L z%&kb;CC^IU&e0I5X76J0fi#b-iv#=c)?RlJWJ%3~X81@E$8%nQ_%lX%+(eekht?Q6 z!D}*k?n$3O;qZr(AMRUTKG`@Omcks*_r2-EekRthtz07NeH0@IFt3OSDBxW{FPBbA z3O0chyxh=M*Q{$l3BFIGt=}w*CW(B#7rc{oFu{#dknIN?5=FX)HNKinN^|CIBOKK3 zD?7piv@VbXCm+_a%vQMz$3?-{Aw49IFhEIDUfrXtlioWa|eCm+X^*9`=tPq60%kZ5Nqdn|owek)^sXk)r zkTD0yyENjk%9ozu1xSz!%4}rxo*Xv(@=I!60oU3BDmyL0ng8_@27=CYY%)7^&V|KC zs<25RXQJyrob!Ew5Q*`7;xNvCJZHe(R2r{>IXWfhz=9Bqph4h7w8?7#AA3~&zzqZj zND#Rfmv#ypmADvSsU2oUE4Wqz2U>r^24;g^6ksrL|F~J7M2b4Isl!vY`?q91T0QE0 zX{V#gn@?93U7ZZe@6&uX+OE5c0K2c9)=>Pbyl6TZ<$4qIFg9d(d`!HFeC?%hyj% z43)30-TeRIpSS=iaAEB<6u;1dfEo z;v1VQsE*S%7UgfNQ>OJQ#5nWTsc_s^uMG*&skE{HKZ%Bm>5pdrrx(*=mSp@sQY75M z{xB^$1#+gOm}_ndyZ_!;|HszK0>jQQSIb9kkN^}*85l8RA|)-2Z^To|0JMpckA^CL zgr=j<6vsHH&5Z3I=vC~a;=W17bXa`?`(Y{HZclvH*?DTl*|{Ntm}?L!!oF#E(_5BE zytlxcX6F9aabqW-vuknjiOc8=BibVN<3)aUisoDq&i9ztpOB@bt-jGC!&aiox=$l; zmUQ19pv;uGR{hv-^O>-MAt(Wa`gcV~DsPx(f00lH=7+`y)4vX;`ab@;M~%$NjY(${joSypvOV z(*dSY-->0$!Y0p$h2G)Rd*h*nA>_NV$yeKB_wygGSaM>TdrP8kaaN!;M6!BL0?TCh z5nKB$mTry{3jBPcNRi9L>2VkO@LxH-9FAR>d-KgYamu|QerV?(=Qq#dQUk}@%0CCy zig-IM8{>mGWJQ(e?0Y}T_lD8@^8HSUI2mH-a{9+m#BY7g^($5_#2_!YKGyYlfTn-J zt)He}wY;l#b5CYN^TbFaIp@=#EpOMqjx5J^6Lx4&;E@M6P|Lt%$I+RBK5;f4D!dG2A*i$|Tw zU5*j~ylnm7I>^^5I@lEd!#W{97Dwk!HuS>jzPo{D$O@Gc-_emyuO*+HqRbgsr6q~y zT{m*0XGW;=uSzqGg{92d+ja{BlwwevzW;fMRx`*b-zW?U^U}S{l>crjgYek?q45uRN~vfI<|XG z+{{7mPq?ud66&CMy7&FEyE8IF`h_*b-~&Slf#lJTXPqcb(dUl$m)y1md~}?;YL&P3 zZk|tOx&Mi8LkcT%?~Oi+tY%|0Ck#+>Zw%fi(CDg25P|>mq3@IR$E78czL4$6xw0dj z0>eT7*9by^dqUu8m{^A`o46Ul(zIO4yurs9Q&32hNEl@2^oFS4^NH zC(}*8=|+g%`lHK7=}UXuVnb%S@uoINY%_ul5=r|bXoPC~beQEVE_SsSm5oYw6mI7V zxKJ!p$w%Om{TE)*#Yj&GjNTpN{qZgguaYS&7jZKG8-O>Mk-}QFdkfrP)-%Azk)bKW zypFrZkti=-|Hc&-nC>_Isr}TPE8lNaV`Fs~kok~5E7laUP2VtP(T8CQWWYe0U~Vv` ze*au}vnymVME8XwP|RS@LzDm-xW;2TO@GkpvxQMK!r$DCIOo`yMyWXlt0eEG1u-nw zb4g{@r~BKsvDnx$g@nqgCWLwKh(uZn3rf8G;s4y;`CIC%upDAPQ}!9-@_&FnF06bK zjOi%Ya2gUJk@mO0cG4_JdHW1f@|&AAHLwa3&H=sVujPT@2%KTN`XJn&v^m2tE(r;O zoWW~PrhXd&Ej1fmW=xJHsWjVbwf#kMH$i_IKDC$Ynv35*14^wI@B^>OWDyLA!B7JE z0GK0GG=WJpY6=Y1T_6E0##~unD_w;718ysHd(;lG2J81hoRwEt{I4Pi=BN43u2}W2 zH2n#=4QOl9m{LQj^ac{TaLl2=>cfu-(fA5_k|kLDCRC~iL&*=SU||%X)?z{-RO9Qj z;;&^WF*E_wV$@2+VRE{ZT6Y5G2NHiY{ZCW^i6U?;Wieq##Qm?QGFX&iKRYFKr6#Hs zj>$>CRygkHy+~NLYO^&6V(TaRy@in5wrswz;L7ehDE3#6X-3PUD90bSsg$w=nDm3Z zS4D1!15(~Kt-rD6!Sq^39?omdK)*c)a%*e;$^K^wZ8I^2*~I|S`bfm0QbxhtR}f?H zJ}LnJ2V9>Lc>h9PmdwH6jv8A)sQvU0$89_gq1_0>R*HgrUD*tx%nNX=gMBgZ_DH@E zh?|HH;X%uJtW48<*Z+Fk*M?DXg9-2 z{*d8LfxN!?R+eeVM+-F!Y#Rb}Y@0p`zN872!b%HP_KMNR6n)$*8|+B@#23_x9~}!c z=S(%zlAoe2OfX>xtj56H^|7|cWLpcsW;d`qWOW|cs$IId4nRiZ?YvkSmrQ$oq*8wzfu?*}!i|nYT_Zash@v-OVNyJF>K%v`v#l|eL z(TAki^`7Y_I%-8khQDXCF}7)4tL=U#8%+W=T7;~+vaqi`db(QKpa=3^A|iY)pMWM2 z⁢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

      P!Nwrpd-qT`N? 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 delta 5149 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 CHP^cU delta 10634 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 z zlg}&NnKeILPdvbmO6l0RTz+@Q|jrcpeW2~M!+xwafuHyhZg~p3l;$9C(}?=YIy==>YjgRVT9Y zjh3Ps6M5M0AVmp7cz4UMZrfb?F#eG9UeWPtSv%SW!p*;-+bQ05hUE>rG8A%P)}>68 zRn119Bd>`O)+Y}PCastsjE1}jaBx1eGce$V>{(Qpn^Q*&?^$okn*{^7f8uaEODCzHjbRr}2* zXAayl*7B%oCOCq!;y<5;e1Bz2Z7pt~TTSWA6a?N|`n|^S8hEY$`=5_N0WjzRfLW;% z+7NK-0)ap@u2R~rX2!1O{HEW`|0^JNC_4u;8#^kAtvKa>yP=Yldqq5M#26LW~c z|0V1l&8#iF{vYwbV8BUe`#(VczY(t1_U10G#`aGCf0JNNEc~A#{~zPxXyNMdqZjY- F{{XOSDT4q2