From 66daa80c0735bac0dcfb1f49e98862174de5124b Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Sun, 19 Feb 2023 22:26:46 +0530 Subject: [PATCH 01/20] feat: upgrade hamster script --- src/Appwrite/Platform/Tasks/Hamster.php | 33 ++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index 723f3e0e47..581a3230bb 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -19,12 +19,17 @@ use Utopia\Registry\Registry; class Hamster extends Action { private array $columns = [ + 'Timestamp', 'Project ID', 'Project Name', 'Functions', 'Deployments', 'Members', 'Domains', + 'Platforms - Web', + 'Platforms - Android', + 'Platforms - iOS', + 'Platforms - Flutter', 'Files', 'Buckets', 'Databases', @@ -79,6 +84,9 @@ class Hamster extends Action { $stats = []; + /** Set the timestamp in ISO 8601 format */ + $stats['Timestamp'] = \date('c'); + /** Get Project ID */ $stats['Project ID'] = $project->getId(); @@ -104,6 +112,28 @@ class Hamster extends Action /** Get Domains */ $stats['Domains'] = $dbForProject->count('domains', [], APP_LIMIT_COUNT); + /** Get Platforms */ + $platforms = $dbForConsole->find('platforms', [ + Query::equal('projectInternalId', [$project->getInternalId()]), + Query::limit(100) + ]); + + $stats['Platforms - Web'] = array_count_values(array_filter($platforms, function ($platform) { + return $platform['platform'] === 'web'; + })); + + $stats['Platforms - Android'] = array_count_values(array_filter($platforms, function ($platform) { + return $platform['platform'] === 'android'; + })); + + $stats['Platforms - iOS'] = array_count_values(array_filter($platforms, function ($platform) { + return str_contains($platform['platform'], 'apple'); + })); + + $stats['Platforms - Flutter'] = array_count_values(array_filter($platforms, function ($platform) { + return str_contains($platform['platform'],'flutter'); + })); + /** Get Usage stats */ $range = '90d'; $periods = [ @@ -223,7 +253,8 @@ class Hamster extends Action Console::log('Iterated through ' . $count . '/' . $totalProjects . ' projects...'); } - $this->sendEmail($register); + // var_dump($csv->toString()); + // $this->sendEmail($register); $pools ->get('console') From ac08640bc446afb595e04c6e036b15eadaee0f40 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 17 Mar 2023 11:26:16 +0530 Subject: [PATCH 02/20] feat: update hamster script --- app/console | 2 +- composer.json | 2 +- composer.lock | 253 +++++++++++------------- src/Appwrite/Platform/Tasks/Hamster.php | 6 + 4 files changed, 123 insertions(+), 140 deletions(-) diff --git a/app/console b/app/console index 43891a526e..a0dfd89382 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit 43891a526e061454617cbb13def3c4901d99a7f1 +Subproject commit a0dfd89382ef55a5c8457bb965d1cc8c28d05746 diff --git a/composer.json b/composer.json index f505291643..88abe79aaa 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "appwrite/php-clamav": "1.1.*", "appwrite/php-runtimes": "0.11.*", "utopia-php/abuse": "0.16.*", - "utopia-php/analytics": "0.2.*", + "utopia-php/analytics": "0.10.0", "utopia-php/audit": "0.17.*", "utopia-php/cache": "0.8.*", "utopia-php/cli": "0.14.*", diff --git a/composer.lock b/composer.lock index 1324412b95..fa78e435f0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "55eaba9ed1fd51ed74a18ee488c3fdd1", + "content-hash": "f7dec197f1580c0b63141d397d1ffeb4", "packages": [ { "name": "adhocore/jwt", @@ -620,16 +620,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.4.3", + "version": "2.4.4", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "67c26b443f348a51926030c83481b85718457d3d" + "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d", - "reference": "67c26b443f348a51926030c83481b85718457d3d", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf", + "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf", "shasum": "" }, "require": { @@ -719,7 +719,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.3" + "source": "https://github.com/guzzle/psr7/tree/2.4.4" }, "funding": [ { @@ -735,7 +735,7 @@ "type": "tidelift" } ], - "time": "2022-10-26T14:07:24+00:00" + "time": "2023-03-09T13:19:02+00:00" }, { "name": "influxdb/influxdb-php", @@ -872,34 +872,37 @@ }, { "name": "league/csv", - "version": "9.8.0", + "version": "9.9.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "9d2e0265c5d90f5dd601bc65ff717e05cec19b47" + "reference": "b4418ede47fbd88facc34e40a16c8ce9153b961b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/9d2e0265c5d90f5dd601bc65ff717e05cec19b47", - "reference": "9d2e0265c5d90f5dd601bc65ff717e05cec19b47", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/b4418ede47fbd88facc34e40a16c8ce9153b961b", + "reference": "b4418ede47fbd88facc34e40a16c8ce9153b961b", "shasum": "" }, "require": { "ext-json": "*", "ext-mbstring": "*", - "php": "^7.4 || ^8.0" + "php": "^8.1.2" }, "require-dev": { - "ext-curl": "*", + "doctrine/collections": "^2.1.2", "ext-dom": "*", - "friendsofphp/php-cs-fixer": "^v3.4.0", - "phpstan/phpstan": "^1.3.0", - "phpstan/phpstan-phpunit": "^1.0.0", - "phpstan/phpstan-strict-rules": "^1.1.0", - "phpunit/phpunit": "^9.5.11" + "ext-xdebug": "*", + "friendsofphp/php-cs-fixer": "^v3.14.3", + "phpbench/phpbench": "^1.2.8", + "phpstan/phpstan": "^1.10.4", + "phpstan/phpstan-deprecation-rules": "^1.1.2", + "phpstan/phpstan-phpunit": "^1.3.10", + "phpstan/phpstan-strict-rules": "^1.5.0", + "phpunit/phpunit": "^10.0.14" }, "suggest": { - "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes", + "ext-dom": "Required to use the XMLConverter and the HTMLConverter classes", "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" }, "type": "library", @@ -952,7 +955,7 @@ "type": "github" } ], - "time": "2022-01-04T00:13:07+00:00" + "time": "2023-03-11T15:57:12+00:00" }, { "name": "matomo/device-detector", @@ -1546,16 +1549,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.2.0", + "version": "v3.2.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" + "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", - "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", "shasum": "" }, "require": { @@ -1593,7 +1596,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" }, "funding": [ { @@ -1609,7 +1612,7 @@ "type": "tidelift" } ], - "time": "2022-11-25T10:21:52+00:00" + "time": "2023-03-01T10:25:55+00:00" }, { "name": "utopia-php/abuse", @@ -1667,24 +1670,25 @@ }, { "name": "utopia-php/analytics", - "version": "0.2.0", + "version": "0.10.0", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "adfc2d057a7f6ab618a77c8a20ed3e35485ff416" + "reference": "c367bbc87e88779a33decb37483fef8172a0fda4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/adfc2d057a7f6ab618a77c8a20ed3e35485ff416", - "reference": "adfc2d057a7f6ab618a77c8a20ed3e35485ff416", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/c367bbc87e88779a33decb37483fef8172a0fda4", + "reference": "c367bbc87e88779a33decb37483fef8172a0fda4", "shasum": "" }, "require": { - "php": ">=7.4" + "php": ">=8.0", + "utopia-php/cli": "^0.14.0" }, "require-dev": { - "phpunit/phpunit": "^9.3", - "vimeo/psalm": "4.0.1" + "laravel/pint": "dev-main", + "phpunit/phpunit": "^9.3" }, "type": "library", "autoload": { @@ -1696,16 +1700,6 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Torsten Dittmann", - "email": "torsten@appwrite.io" - } - ], "description": "A simple library to track events & users.", "keywords": [ "analytics", @@ -1716,9 +1710,9 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.2.0" + "source": "https://github.com/utopia-php/analytics/tree/0.10.0" }, - "time": "2021-03-23T21:33:07+00:00" + "time": "2023-03-16T20:42:19+00:00" }, { "name": "utopia-php/audit", @@ -2224,22 +2218,23 @@ }, { "name": "utopia-php/logger", - "version": "0.3.0", + "version": "0.3.1", "source": { "type": "git", "url": "https://github.com/utopia-php/logger.git", - "reference": "079656cb5169ca9600861eda0b6819199e3d4a57" + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/079656cb5169ca9600861eda0b6819199e3d4a57", - "reference": "079656cb5169ca9600861eda0b6819199e3d4a57", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/de623f1ec1c672c795d113dd25c5bf212f7ef4fc", + "reference": "de623f1ec1c672c795d113dd25c5bf212f7ef4fc", "shasum": "" }, "require": { "php": ">=8.0" }, "require-dev": { + "phpstan/phpstan": "1.9.x-dev", "phpunit/phpunit": "^9.3", "vimeo/psalm": "4.0.1" }, @@ -2253,20 +2248,6 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - }, - { - "name": "Matej Bačo", - "email": "matej@appwrite.io" - }, - { - "name": "Christy Jacob", - "email": "christy@appwrite.io" - } - ], "description": "Utopia Logger library is simple and lite library for logging information, such as errors or warnings. This library is aiming to be as simple and easy to learn and use.", "keywords": [ "appsignal", @@ -2284,22 +2265,22 @@ ], "support": { "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.3.0" + "source": "https://github.com/utopia-php/logger/tree/0.3.1" }, - "time": "2022-03-18T10:56:57+00:00" + "time": "2023-02-10T15:52:50+00:00" }, { "name": "utopia-php/messaging", - "version": "0.1.0", + "version": "0.1.1", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "501272fad666f06bec8f130076862e7981a73f8c" + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/501272fad666f06bec8f130076862e7981a73f8c", - "reference": "501272fad666f06bec8f130076862e7981a73f8c", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/a75d66ddd59b834ab500a4878a2c084e6572604a", + "reference": "a75d66ddd59b834ab500a4878a2c084e6572604a", "shasum": "" }, "require": { @@ -2307,9 +2288,9 @@ "php": ">=8.0.0" }, "require-dev": { + "laravel/pint": "^1.2", "phpmailer/phpmailer": "6.6.*", - "phpunit/phpunit": "9.5.*", - "squizlabs/php_codesniffer": "^3.6" + "phpunit/phpunit": "9.5.*" }, "type": "library", "autoload": { @@ -2321,12 +2302,6 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Jake Barnby", - "email": "jake@appwrite.io" - } - ], "description": "A simple, light and advanced PHP messaging library", "keywords": [ "library", @@ -2338,9 +2313,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.1.0" + "source": "https://github.com/utopia-php/messaging/tree/0.1.1" }, - "time": "2022-09-29T11:22:48+00:00" + "time": "2023-02-07T05:42:46+00:00" }, { "name": "utopia-php/orchestration", @@ -3184,16 +3159,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.11.0", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "shasum": "" }, "require": { @@ -3231,7 +3206,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" }, "funding": [ { @@ -3239,20 +3214,20 @@ "type": "tidelift" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2023-03-08T13:26:56+00:00" }, { "name": "nikic/php-parser", - "version": "v4.15.3", + "version": "v4.15.4", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", "shasum": "" }, "require": { @@ -3293,9 +3268,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" }, - "time": "2023-01-16T22:05:37+00:00" + "time": "2023-03-05T19:49:14+00:00" }, { "name": "phar-io/manifest", @@ -3575,20 +3550,20 @@ }, { "name": "phpspec/prophecy", - "version": "v1.16.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "be8cac52a0827776ff9ccda8c381ac5b71aeb359" + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/be8cac52a0827776ff9ccda8c381ac5b71aeb359", - "reference": "be8cac52a0827776ff9ccda8c381ac5b71aeb359", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/15873c65b207b07765dbc3c95d20fdf4a320cbe2", + "reference": "15873c65b207b07765dbc3c95d20fdf4a320cbe2", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.2", + "doctrine/instantiator": "^1.2 || ^2.0", "php": "^7.2 || 8.0.* || 8.1.* || 8.2.*", "phpdocumentor/reflection-docblock": "^5.2", "sebastian/comparator": "^3.0 || ^4.0", @@ -3596,6 +3571,7 @@ }, "require-dev": { "phpspec/phpspec": "^6.0 || ^7.0", + "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", @@ -3636,29 +3612,29 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.16.0" + "source": "https://github.com/phpspec/prophecy/tree/v1.17.0" }, - "time": "2022-11-29T15:06:56+00:00" + "time": "2023-02-02T15:41:36+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.23", + "version": "9.2.26", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c" + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c", - "reference": "9f1f0f9a2fbb680b26d1cf9b61b6eac43a6e4e9c", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.14", + "nikic/php-parser": "^4.15", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -3673,8 +3649,8 @@ "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { @@ -3707,7 +3683,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.23" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" }, "funding": [ { @@ -3715,7 +3691,7 @@ "type": "github" } ], - "time": "2022-12-28T12:41:10+00:00" + "time": "2023-03-06T12:58:08+00:00" }, { "name": "phpunit/php-file-iterator", @@ -4427,16 +4403,16 @@ }, { "name": "sebastian/environment", - "version": "5.1.4", + "version": "5.1.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7" + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7", - "reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", "shasum": "" }, "require": { @@ -4478,7 +4454,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.4" + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" }, "funding": [ { @@ -4486,7 +4462,7 @@ "type": "github" } ], - "time": "2022-04-03T09:37:03+00:00" + "time": "2023-02-03T06:03:51+00:00" }, { "name": "sebastian/exporter", @@ -4800,16 +4776,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", - "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", "shasum": "" }, "require": { @@ -4848,10 +4824,10 @@ } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" }, "funding": [ { @@ -4859,7 +4835,7 @@ "type": "github" } ], - "time": "2020-10-26T13:17:30+00:00" + "time": "2023-02-03T06:07:39+00:00" }, { "name": "sebastian/resource-operations", @@ -4918,16 +4894,16 @@ }, { "name": "sebastian/type", - "version": "3.2.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", - "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", "shasum": "" }, "require": { @@ -4962,7 +4938,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" }, "funding": [ { @@ -4970,7 +4946,7 @@ "type": "github" } ], - "time": "2022-09-12T14:47:03+00:00" + "time": "2023-02-03T06:13:03+00:00" }, { "name": "sebastian/version", @@ -5027,16 +5003,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.7.1", + "version": "3.7.2", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619" + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", - "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", "shasum": "" }, "require": { @@ -5072,14 +5048,15 @@ "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", "keywords": [ "phpcs", - "standards" + "standards", + "static analysis" ], "support": { "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", "source": "https://github.com/squizlabs/PHP_CodeSniffer", "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" }, - "time": "2022-06-18T07:21:10+00:00" + "time": "2023-02-22T23:07:41+00:00" }, { "name": "swoole/ide-helper", @@ -5389,16 +5366,16 @@ }, { "name": "twig/twig", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "3ffcf4b7d890770466da3b2666f82ac054e7ec72" + "reference": "a6e0510cc793912b451fd40ab983a1d28f611c15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/3ffcf4b7d890770466da3b2666f82ac054e7ec72", - "reference": "3ffcf4b7d890770466da3b2666f82ac054e7ec72", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/a6e0510cc793912b451fd40ab983a1d28f611c15", + "reference": "a6e0510cc793912b451fd40ab983a1d28f611c15", "shasum": "" }, "require": { @@ -5449,7 +5426,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.5.0" + "source": "https://github.com/twigphp/Twig/tree/v3.5.1" }, "funding": [ { @@ -5461,7 +5438,7 @@ "type": "tidelift" } ], - "time": "2022-12-27T12:28:18+00:00" + "time": "2023-02-08T07:49:20+00:00" } ], "aliases": [], diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index 581a3230bb..a078612001 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -12,6 +12,7 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use League\Csv\Writer; use PHPMailer\PHPMailer\PHPMailer; +use Utopia\Analytics\Adapter\Mixpanel; use Utopia\Database\Document; use Utopia\Pools\Group; use Utopia\Registry\Registry; @@ -58,10 +59,13 @@ 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'; @@ -69,6 +73,8 @@ class Hamster extends Action public function __construct() { + $this->mixpanel = new Mixpanel('bce512333a58ec62f44541328607f53c'); + $this ->desc('Get stats for projects') ->inject('register') From 42ebaf6541de95842cbd2dc6667ec8b42af66edd Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 17 Mar 2023 21:37:29 +0530 Subject: [PATCH 03/20] feat: store token in env var --- .env | 1 + composer.json | 6 +- composer.lock | 99 ++++++------ docker-compose.yml | 1 + src/Appwrite/Platform/Tasks/Hamster.php | 192 +++++++++++------------- 5 files changed, 142 insertions(+), 157 deletions(-) diff --git a/.env b/.env index 096bde5c52..b9bc40f0e2 100644 --- a/.env +++ b/.env @@ -47,6 +47,7 @@ _APP_SMTP_USERNAME= _APP_SMTP_PASSWORD= _APP_HAMSTER_RECIPIENTS= _APP_HAMSTER_INTERVAL=86400 +_APP_MIXPANEL_TOKEN=bce512333a58ec62f44541328607f53c _APP_SMS_PROVIDER=sms://username:password@mock _APP_SMS_FROM=+123456789 _APP_STORAGE_LIMIT=30000000 diff --git a/composer.json b/composer.json index 88abe79aaa..4954f033fb 100644 --- a/composer.json +++ b/composer.json @@ -44,10 +44,10 @@ "appwrite/php-clamav": "1.1.*", "appwrite/php-runtimes": "0.11.*", "utopia-php/abuse": "0.16.*", - "utopia-php/analytics": "0.10.0", + "utopia-php/analytics": "0.10.1", "utopia-php/audit": "0.17.*", "utopia-php/cache": "0.8.*", - "utopia-php/cli": "0.14.*", + "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", "utopia-php/database": "0.28.*", "utopia-php/queue": "0.5.*", @@ -56,7 +56,7 @@ "utopia-php/pools": "0.4.*", "utopia-php/preloader": "0.2.*", "utopia-php/domains": "1.1.*", - "utopia-php/framework": "0.25.*", + "utopia-php/framework": "0.26.*", "utopia-php/image": "0.5.*", "utopia-php/dsn": "0.1.*", "utopia-php/locale": "0.4.*", diff --git a/composer.lock b/composer.lock index fa78e435f0..46ce3b7f34 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f7dec197f1580c0b63141d397d1ffeb4", + "content-hash": "8ef68cd9203018cf26fa24bd522e1d1a", "packages": [ { "name": "adhocore/jwt", @@ -1670,21 +1670,21 @@ }, { "name": "utopia-php/analytics", - "version": "0.10.0", + "version": "0.10.1", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "c367bbc87e88779a33decb37483fef8172a0fda4" + "reference": "70ada5e6b192ae27e6d5467899a4cdd886003835" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/c367bbc87e88779a33decb37483fef8172a0fda4", - "reference": "c367bbc87e88779a33decb37483fef8172a0fda4", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/70ada5e6b192ae27e6d5467899a4cdd886003835", + "reference": "70ada5e6b192ae27e6d5467899a4cdd886003835", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "^0.14.0" + "utopia-php/cli": "^0.15.0" }, "require-dev": { "laravel/pint": "dev-main", @@ -1710,9 +1710,9 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.0" + "source": "https://github.com/utopia-php/analytics/tree/0.10.1" }, - "time": "2023-03-16T20:42:19+00:00" + "time": "2023-03-17T14:42:35+00:00" }, { "name": "utopia-php/audit", @@ -1812,16 +1812,16 @@ }, { "name": "utopia-php/cli", - "version": "0.14.0", + "version": "0.15.0", "source": { "type": "git", "url": "https://github.com/utopia-php/cli.git", - "reference": "c30ef985a4e739758a0d95eb0706b357b6d8c086" + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/cli/zipball/c30ef985a4e739758a0d95eb0706b357b6d8c086", - "reference": "c30ef985a4e739758a0d95eb0706b357b6d8c086", + "url": "https://api.github.com/repos/utopia-php/cli/zipball/ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", + "reference": "ccb7c8125ffe0254fef8f25744bfa376eb7bd0ea", "shasum": "" }, "require": { @@ -1829,8 +1829,10 @@ "utopia-php/framework": "0.*.*" }, "require-dev": { + "laravel/pint": "1.2.*", "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6" + "squizlabs/php_codesniffer": "^3.6", + "vimeo/psalm": "4.0.1" }, "type": "library", "autoload": { @@ -1842,12 +1844,6 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], "description": "A simple CLI library to manage command line applications", "keywords": [ "cli", @@ -1859,9 +1855,9 @@ ], "support": { "issues": "https://github.com/utopia-php/cli/issues", - "source": "https://github.com/utopia-php/cli/tree/0.14.0" + "source": "https://github.com/utopia-php/cli/tree/0.15.0" }, - "time": "2022-10-09T10:19:07+00:00" + "time": "2023-03-01T05:55:14+00:00" }, { "name": "utopia-php/config", @@ -2071,16 +2067,16 @@ }, { "name": "utopia-php/framework", - "version": "0.25.1", + "version": "0.26.0", "source": { "type": "git", "url": "https://github.com/utopia-php/framework.git", - "reference": "2391b397135586b2100d39e338827bef8d2f4ad0" + "reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/framework/zipball/2391b397135586b2100d39e338827bef8d2f4ad0", - "reference": "2391b397135586b2100d39e338827bef8d2f4ad0", + "url": "https://api.github.com/repos/utopia-php/framework/zipball/e8da5576370366d3bf9c574ec855f8c96fe4f34e", + "reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e", "shasum": "" }, "require": { @@ -2109,9 +2105,9 @@ ], "support": { "issues": "https://github.com/utopia-php/framework/issues", - "source": "https://github.com/utopia-php/framework/tree/0.25.1" + "source": "https://github.com/utopia-php/framework/tree/0.26.0" }, - "time": "2022-11-23T18:22:23+00:00" + "time": "2023-01-13T08:14:43+00:00" }, { "name": "utopia-php/image", @@ -2319,23 +2315,24 @@ }, { "name": "utopia-php/orchestration", - "version": "0.9.0", + "version": "0.9.1", "source": { "type": "git", "url": "https://github.com/utopia-php/orchestration.git", - "reference": "1d4f66684b8c4927f31b695817eae6d84aafd172" + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/1d4f66684b8c4927f31b695817eae6d84aafd172", - "reference": "1d4f66684b8c4927f31b695817eae6d84aafd172", + "url": "https://api.github.com/repos/utopia-php/orchestration/zipball/55f43513b3f940a3f4f9c2cde7682d0c2581beb0", + "reference": "55f43513b3f940a3f4f9c2cde7682d0c2581beb0", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.14.*" + "utopia-php/cli": "0.15.*" }, "require-dev": { + "laravel/pint": "^1.2", "phpunit/phpunit": "^9.3", "vimeo/psalm": "4.0.1" }, @@ -2362,34 +2359,34 @@ ], "support": { "issues": "https://github.com/utopia-php/orchestration/issues", - "source": "https://github.com/utopia-php/orchestration/tree/0.9.0" + "source": "https://github.com/utopia-php/orchestration/tree/0.9.1" }, - "time": "2022-11-09T17:38:00+00:00" + "time": "2023-03-17T15:05:06+00:00" }, { "name": "utopia-php/platform", - "version": "0.3.1", + "version": "0.3.3", "source": { "type": "git", "url": "https://github.com/utopia-php/platform.git", - "reference": "fe9f64420957dc8fb6201d22b499572f021411e4" + "reference": "a9e7a501f33e0da59779782359a747cb8d34cf6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/platform/zipball/fe9f64420957dc8fb6201d22b499572f021411e4", - "reference": "fe9f64420957dc8fb6201d22b499572f021411e4", + "url": "https://api.github.com/repos/utopia-php/platform/zipball/a9e7a501f33e0da59779782359a747cb8d34cf6f", + "reference": "a9e7a501f33e0da59779782359a747cb8d34cf6f", "shasum": "" }, "require": { "ext-json": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/cli": "0.14.*", - "utopia-php/framework": "0.25.*" + "utopia-php/cli": "0.15.*", + "utopia-php/framework": "0.26.*" }, "require-dev": { - "phpunit/phpunit": "^9.3", - "squizlabs/php_codesniffer": "^3.6" + "laravel/pint": "1.2.*", + "phpunit/phpunit": "^9.3" }, "type": "library", "autoload": { @@ -2411,9 +2408,9 @@ ], "support": { "issues": "https://github.com/utopia-php/platform/issues", - "source": "https://github.com/utopia-php/platform/tree/0.3.1" + "source": "https://github.com/utopia-php/platform/tree/0.3.3" }, - "time": "2022-11-10T07:04:24+00:00" + "time": "2023-03-07T08:52:22+00:00" }, { "name": "utopia-php/pools", @@ -2521,21 +2518,21 @@ }, { "name": "utopia-php/queue", - "version": "0.5.1", + "version": "0.5.2", "source": { "type": "git", "url": "https://github.com/utopia-php/queue.git", - "reference": "4b39e1f232b2e619b0d7fb4004f2356df334ddc1" + "reference": "310271c5cd477541208d7fa74a4dea64df8e04a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/queue/zipball/4b39e1f232b2e619b0d7fb4004f2356df334ddc1", - "reference": "4b39e1f232b2e619b0d7fb4004f2356df334ddc1", + "url": "https://api.github.com/repos/utopia-php/queue/zipball/310271c5cd477541208d7fa74a4dea64df8e04a0", + "reference": "310271c5cd477541208d7fa74a4dea64df8e04a0", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/cli": "0.14.*", + "utopia-php/cli": "0.15.*", "utopia-php/framework": "0.*.*" }, "require-dev": { @@ -2576,9 +2573,9 @@ ], "support": { "issues": "https://github.com/utopia-php/queue/issues", - "source": "https://github.com/utopia-php/queue/tree/0.5.1" + "source": "https://github.com/utopia-php/queue/tree/0.5.2" }, - "time": "2022-11-16T19:47:26+00:00" + "time": "2023-03-07T08:54:10+00:00" }, { "name": "utopia-php/registry", diff --git a/docker-compose.yml b/docker-compose.yml index a6b8af4d74..e238f79983 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -576,6 +576,7 @@ services: - _APP_CONNECTIONS_CACHE - _APP_HAMSTER_RECIPIENTS - _APP_HAMSTER_INTERVAL + - _APP_MIXPANEL_TOKEN appwrite-maintenance: entrypoint: maintenance diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index a078612001..a78f8d3779 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -13,53 +13,29 @@ use Utopia\Database\Validator\Authorization; use League\Csv\Writer; use PHPMailer\PHPMailer\PHPMailer; use Utopia\Analytics\Adapter\Mixpanel; +use Utopia\Analytics\Event; use Utopia\Database\Document; use Utopia\Pools\Group; use Utopia\Registry\Registry; class Hamster extends Action { - private array $columns = [ - 'Timestamp', - 'Project ID', - 'Project Name', - 'Functions', - 'Deployments', - 'Members', - 'Domains', - 'Platforms - Web', - 'Platforms - Android', - 'Platforms - iOS', - 'Platforms - Flutter', - 'Files', - 'Buckets', - 'Databases', - 'Documents', - 'Collections', - 'Storage', - 'Requests', - 'Bandwidth', - 'Users', - 'Sessions', - 'Executions' - ]; - private array $usageStats = [ - 'Files' => 'files.$all.count.total', - 'Buckets' => 'buckets.$all.count.total', - 'Databases' => 'databases.$all.count.total', - 'Documents' => 'documents.$all.count.total', - 'Collections' => 'collections.$all.count.total', - 'Storage' => 'project.$all.storage.size', - 'Requests' => 'project.$all.network.requests', - 'Bandwidth' => 'project.$all.network.bandwidth', - 'Users' => 'users.$all.count.total', - 'Sessions' => 'sessions.$all.requests.create', - 'Executions' => 'executions.$all.compute.total', + 'files' => 'files.$all.count.total', + 'buckets' => 'buckets.$all.count.total', + 'databases' => 'databases.$all.count.total', + 'documents' => 'documents.$all.count.total', + 'collections' => 'collections.$all.count.total', + 'storage' => 'project.$all.storage.size', + 'requests' => 'project.$all.network.requests', + 'bandwidth' => 'project.$all.network.bandwidth', + 'users' => 'users.$all.count.total', + 'sessions' => 'sessions.email.requests.create', + 'executions' => 'executions.$all.compute.total', ]; protected string $directory = '/usr/local'; - + protected string $path; protected string $date; @@ -73,7 +49,7 @@ class Hamster extends Action public function __construct() { - $this->mixpanel = new Mixpanel('bce512333a58ec62f44541328607f53c'); + $this->mixpanel = new Mixpanel(App::getEnv('_APP_MIXPANEL_TOKEN', '')); $this ->desc('Get stats for projects') @@ -90,54 +66,70 @@ class Hamster extends Action { $stats = []; - /** Set the timestamp in ISO 8601 format */ - $stats['Timestamp'] = \date('c'); + $stats['time'] = microtime(true); /** Get Project ID */ - $stats['Project ID'] = $project->getId(); + $stats['projectId'] = $project->getId(); /** Get Project Name */ - $stats['Project Name'] = $project->getAttribute('name'); + $stats['projectName'] = $project->getAttribute('name'); /** Get Total Functions */ - $stats['Functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT); + $stats['functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT); /** Get Total Deployments */ - $stats['Deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT); + $stats['deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT); /** Get Total Members */ $teamInternalId = $project->getAttribute('teamInternalId', null); if ($teamInternalId) { - $stats['Members'] = $dbForConsole->count('memberships', [ + $stats['members'] = $dbForConsole->count('memberships', [ Query::equal('teamInternalId', [$teamInternalId]) ], APP_LIMIT_COUNT); } else { - $stats['Members'] = 0; + $stats['members'] = 0; + } + + /** Get Email and Name of the project owner */ + if ($teamInternalId) { + $membership = $dbForConsole->findOne('memberships', [ + Query::equal('teamInternalId', [$teamInternalId]), + ]); + + $userInternalId = $membership->getAttribute('userInternalId', null); + if ($userInternalId) { + $user = $dbForConsole->findOne('users', [ + Query::equal('_id', [$userInternalId]), + ]); + + $stats['email'] = $user->getAttribute('email', null); + $stats['name'] = $user->getAttribute('name', null); + } } /** Get Domains */ - $stats['Domains'] = $dbForProject->count('domains', [], APP_LIMIT_COUNT); + $stats['domains'] = $dbForProject->count('domains', [], APP_LIMIT_COUNT); /** Get Platforms */ $platforms = $dbForConsole->find('platforms', [ Query::equal('projectInternalId', [$project->getInternalId()]), - Query::limit(100) + Query::limit(APP_LIMIT_COUNT) ]); - $stats['Platforms - Web'] = array_count_values(array_filter($platforms, function ($platform) { - return $platform['platform'] === 'web'; + $stats['platforms_web'] = sizeof(array_filter($platforms, function ($platform) { + return $platform['type'] === 'web'; })); - $stats['Platforms - Android'] = array_count_values(array_filter($platforms, function ($platform) { - return $platform['platform'] === 'android'; + $stats['platforms_android'] = sizeof(array_filter($platforms, function ($platform) { + return $platform['type'] === 'android'; })); - $stats['Platforms - iOS'] = array_count_values(array_filter($platforms, function ($platform) { - return str_contains($platform['platform'], 'apple'); + $stats['platforms_iOS'] = sizeof(array_filter($platforms, function ($platform) { + return str_contains($platform['type'], 'apple'); })); - $stats['Platforms - Flutter'] = array_count_values(array_filter($platforms, function ($platform) { - return str_contains($platform['platform'],'flutter'); + $stats['platforms_flutter'] = sizeof(array_filter($platforms, function ($platform) { + return str_contains($platform['type'],'flutter'); })); /** Get Usage stats */ @@ -183,26 +175,36 @@ class Hamster extends Action { Console::title('Cloud Hamster V1'); - Console::success(APP_NAME . ' cloud hamster process v1 has started'); + Console::success(APP_NAME . ' cloud hamster process has started'); - $interval = (int) App::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default) + $sleep = (int) App::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default) - Console::loop(function () use ($register, $pools, $cache, $dbForConsole, $interval) { + $jobInitTime = '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(); + /** + * If time passed for the target day. + */ + if ($delay <= 0) { + $next->add(\DateInterval::createFromDateString('1 days')); + $delay = $next->getTimestamp() - $now->getTimestamp(); + } + + 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 ($register, $pools, $cache, $dbForConsole, $sleep) { $now = date('d-m-Y H:i:s', time()); - Console::info("[{$now}] Getting Cloud Usage Stats every {$interval} seconds"); + Console::info("[{$now}] Getting Cloud Usage Stats every {$sleep} seconds"); $loopStart = microtime(true); /* Initialise new Utopia app */ $app = new App('UTC'); $console = $app->getResource('console'); - /** CSV stuff */ - $this->date = date('Y-m-d'); - $this->path = "{$this->directory}/stats_{$this->date}.csv"; - $csv = Writer::createFromPath($this->path, 'w'); - $csv->insertOne($this->columns); - /** Database connections */ $totalProjects = $dbForConsole->count('projects') + 1; Console::success("Found a total of: {$totalProjects} projects"); @@ -235,7 +237,26 @@ class Hamster extends Action $dbForProject->setNamespace('_' . $project->getInternalId()); $statsPerProject = $this->getStats($dbForConsole, $dbForProject, $project); - $csv->insertOne(array_values($statsPerProject)); + + /** 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('Appwrite Cloud Project Stats') + ->setProps($statsPerProject); + $res = $this->mixpanel->createEvent($event); + if (!$res) { + Console::error('Failed to create event for project: ' . $project->getId()); + } + } catch (\Throwable $th) { throw $th; Console::error('Failed to update project ("' . $project->getId() . '") version with error: ' . $th->getMessage()); @@ -259,8 +280,6 @@ class Hamster extends Action Console::log('Iterated through ' . $count . '/' . $totalProjects . ' projects...'); } - // var_dump($csv->toString()); - // $this->sendEmail($register); $pools ->get('console') @@ -269,40 +288,7 @@ class Hamster extends Action $loopTook = microtime(true) - $loopStart; $now = date('d-m-Y H:i:s', time()); Console::info("[{$now}] Cloud Stats took {$loopTook} seconds"); - }, $interval); - } - private function sendEmail(Registry $register) - { - /** @var \PHPMailer\PHPMailer\PHPMailer $mail */ - $mail = $register->get('smtp'); - - $mail->clearAddresses(); - $mail->clearAllRecipients(); - $mail->clearReplyTos(); - $mail->clearAttachments(); - $mail->clearBCCs(); - $mail->clearCCs(); - - try { - /** Addresses */ - $mail->setFrom(App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), 'Appwrite Cloud Hamster'); - $recipients = explode(',', App::getEnv('_APP_HAMSTER_RECIPIENTS', '')); - foreach ($recipients as $recipient) { - $mail->addAddress($recipient); - } - - /** Attachments */ - $mail->addAttachment($this->path); - - /** Content */ - $mail->Subject = "Cloud Report for {$this->date}"; - $mail->Body = "Please find the daily cloud report atttached"; - - $mail->send(); - Console::success('Email has been sent!'); - } catch (Exception $e) { - Console::error("Message could not be sent. Mailer Error: {$mail->ErrorInfo}"); - } + }, $sleep, $delay); } } From 5fe65ab99144518eeb6095396296f421f37ce255 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 17 Mar 2023 21:38:23 +0530 Subject: [PATCH 04/20] feat: store token in env var --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index b9bc40f0e2..73990bc91c 100644 --- a/.env +++ b/.env @@ -47,7 +47,7 @@ _APP_SMTP_USERNAME= _APP_SMTP_PASSWORD= _APP_HAMSTER_RECIPIENTS= _APP_HAMSTER_INTERVAL=86400 -_APP_MIXPANEL_TOKEN=bce512333a58ec62f44541328607f53c +_APP_MIXPANEL_TOKEN= _APP_SMS_PROVIDER=sms://username:password@mock _APP_SMS_FROM=+123456789 _APP_STORAGE_LIMIT=30000000 From a164991c75cde43c73baef1174a3a34bccc01452 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Fri, 17 Mar 2023 21:44:29 +0530 Subject: [PATCH 05/20] feat: linter --- src/Appwrite/Platform/Tasks/Hamster.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index a78f8d3779..623e7484ab 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -129,7 +129,7 @@ class Hamster extends Action })); $stats['platforms_flutter'] = sizeof(array_filter($platforms, function ($platform) { - return str_contains($platform['type'],'flutter'); + return str_contains($platform['type'], 'flutter'); })); /** Get Usage stats */ @@ -243,7 +243,7 @@ class Hamster extends Action 'name' => $statsPerProject['name'], 'email' => $statsPerProject['email'] ]); - + if (!$res) { Console::error('Failed to create user profile for project: ' . $project->getId()); } @@ -256,7 +256,6 @@ class Hamster extends Action if (!$res) { Console::error('Failed to create event for project: ' . $project->getId()); } - } catch (\Throwable $th) { throw $th; Console::error('Failed to update project ("' . $project->getId() . '") version with error: ' . $th->getMessage()); @@ -288,7 +287,6 @@ class Hamster extends Action $loopTook = microtime(true) - $loopStart; $now = date('d-m-Y H:i:s', time()); Console::info("[{$now}] Cloud Stats took {$loopTook} seconds"); - }, $sleep, $delay); } } From e8b11b2d14c25c8486a87ffd2196846b2a15feaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 19 Mar 2023 10:43:57 +0100 Subject: [PATCH 06/20] Fix scheduler --- app/config/collections.php | 11 ---- app/controllers/api/functions.php | 71 ++++++++++++-------------- app/workers/builds.php | 4 +- src/Appwrite/Migration/Version/V17.php | 11 ++++ 4 files changed, 45 insertions(+), 52 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index afde813fa1..4c71a404a0 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -2278,17 +2278,6 @@ $collections = [ 'array' => false, 'filters' => [], ], - [ - '$id' => ID::custom('scheduleUpdatedAt'), - 'type' => Database::VAR_DATETIME, - 'format' => '', - 'size' => 0, - 'signed' => false, - 'required' => false, - 'default' => null, - 'array' => false, - 'filters' => ['datetime'], - ], [ '$id' => ID::custom('timeout'), 'type' => Database::VAR_INTEGER, diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 6fe7c67e69..67674e3bb5 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -84,7 +84,6 @@ App::post('/v1/functions') 'deployment' => '', 'events' => $events, 'schedule' => $schedule, - 'scheduleUpdatedAt' => DateTime::now(), 'timeout' => $timeout, 'search' => implode(' ', [$functionId, $name, $runtime]) ])); @@ -463,7 +462,6 @@ App::put('/v1/functions/:functionId') 'name' => $name, 'events' => $events, 'schedule' => $schedule, - 'scheduleUpdatedAt' => DateTime::now(), 'timeout' => $timeout, 'enabled' => $enabled, 'search' => implode(' ', [$functionId, $name, $function->getAttribute('runtime')]), @@ -471,18 +469,11 @@ App::put('/v1/functions/:functionId') $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - /** - * In case we want to clear the schedule - */ - if (!empty($function->getAttribute('deployment'))) { - $schedule->setAttribute('resourceUpdatedAt', $function->getAttribute('scheduleUpdatedAt')); - } - $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) ->setAttribute('schedule', $function->getAttribute('schedule')) ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $eventsInstance->setParam('functionId', $function->getId()); @@ -538,15 +529,10 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId') ]))); $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - - $active = !empty($function->getAttribute('schedule')); - - if ($active) { - $schedule->setAttribute('resourceUpdatedAt', datetime::now()); - } - - $schedule->setAttribute('active', $active); - + $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); $events @@ -786,22 +772,6 @@ App::post('/v1/functions/:functionId/deployments') } } - /** - * TODO Should we update also the function collection with the scheduleUpdatedAt attr? - */ - - $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - - $active = !empty($function->getAttribute('schedule')); - - if ($active) { - $schedule->setAttribute('resourceUpdatedAt', datetime::now()); - } - - $schedule->setAttribute('active', $active); - - Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); - $metadata = null; $events @@ -1381,7 +1351,8 @@ App::post('/v1/functions/:functionId/variables') ->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', false) ->inject('response') ->inject('dbForProject') - ->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject) { + ->inject('dbForConsole') + ->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -1410,6 +1381,13 @@ App::post('/v1/functions/:functionId/variables') throw new Exception(Exception::VARIABLE_ALREADY_EXISTS); } + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); + $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $dbForProject->deleteCachedDocument('functions', $function->getId()); $response @@ -1497,7 +1475,8 @@ App::put('/v1/functions/:functionId/variables/:variableId') ->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', true) ->inject('response') ->inject('dbForProject') - ->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject) { + ->inject('dbForConsole') + ->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); @@ -1526,6 +1505,13 @@ App::put('/v1/functions/:functionId/variables/:variableId') throw new Exception(Exception::VARIABLE_ALREADY_EXISTS); } + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); + $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $dbForProject->deleteCachedDocument('functions', $function->getId()); $response->dynamic($variable, Response::MODEL_VARIABLE); @@ -1547,7 +1533,8 @@ App::delete('/v1/functions/:functionId/variables/:variableId') ->param('variableId', '', new UID(), 'Variable unique ID.', false) ->inject('response') ->inject('dbForProject') - ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject) { + ->inject('dbForConsole') + ->action(function (string $functionId, string $variableId, Response $response, Database $dbForProject, Database $dbForConsole) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -1564,6 +1551,14 @@ App::delete('/v1/functions/:functionId/variables/:variableId') } $dbForProject->deleteDocument('variables', $variable->getId()); + + $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); + $schedule + ->setAttribute('resourceUpdatedAt', DateTime::now()) + ->setAttribute('schedule', $function->getAttribute('schedule')) + ->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment'))); + Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule)); + $dbForProject->deleteCachedDocument('functions', $function->getId()); $response->noContent(); diff --git a/app/workers/builds.php b/app/workers/builds.php index d26f07ab75..9dfd69b872 100644 --- a/app/workers/builds.php +++ b/app/workers/builds.php @@ -202,8 +202,6 @@ class BuildsV1 extends Worker Console::success("Build id: $buildId created"); - $function->setAttribute('scheduleUpdatedAt', DateTime::now()); - /** Set auto deploy */ if ($deployment->getAttribute('activate') === true) { $function->setAttribute('deployment', $deployment->getId()); @@ -213,7 +211,7 @@ class BuildsV1 extends Worker /** Update function schedule */ $dbForConsole = $this->getConsoleDB(); $schedule = $dbForConsole->getDocument('schedules', $function->getAttribute('scheduleId')); - $schedule->setAttribute('resourceUpdatedAt', $function->getAttribute('scheduleUpdatedAt')); + $schedule->setAttribute('resourceUpdatedAt', DateTime::now()); $schedule ->setAttribute('schedule', $function->getAttribute('schedule')) diff --git a/src/Appwrite/Migration/Version/V17.php b/src/Appwrite/Migration/Version/V17.php index 66a02662d1..5ee897ae19 100644 --- a/src/Appwrite/Migration/Version/V17.php +++ b/src/Appwrite/Migration/Version/V17.php @@ -58,6 +58,17 @@ class V17 extends Migration } break; + case 'functions': + try { + /** + * Delete 'scheduleUpdatedAt' attribute + */ + $this->projectDB->deleteAttribute($id, 'scheduleUpdatedAt'); + $this->projectDB->deleteCachedCollection($id); + } catch (\Throwable $th) { + Console::warning("'scheduleUpdatedAt' from {$id}: {$th->getMessage()}"); + } + break; default: break; From c3ff315668ca485edaffa2fa02a47595157d6618 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Mon, 20 Mar 2023 11:44:13 +0530 Subject: [PATCH 07/20] feat: remove unused csv library --- app/console | 2 +- composer.json | 3 +- composer.lock | 194 ++++++++++++++++++++++++++------------------------ 3 files changed, 101 insertions(+), 98 deletions(-) diff --git a/app/console b/app/console index a0dfd89382..cad6f3b1bf 160000 --- a/app/console +++ b/app/console @@ -1 +1 @@ -Subproject commit a0dfd89382ef55a5c8457bb965d1cc8c28d05746 +Subproject commit cad6f3b1bfdae4d423ba6f0735ba2a5cd5a58551 diff --git a/composer.json b/composer.json index 4954f033fb..f947592f9e 100644 --- a/composer.json +++ b/composer.json @@ -73,8 +73,7 @@ "phpmailer/phpmailer": "6.6.0", "chillerlan/php-qrcode": "4.3.3", "adhocore/jwt": "1.1.2", - "slickdeals/statsd": "3.1.0", - "league/csv": "^9.0.0" + "slickdeals/statsd": "3.1.0" }, "repositories": [ { diff --git a/composer.lock b/composer.lock index 46ce3b7f34..a54754a036 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8ef68cd9203018cf26fa24bd522e1d1a", + "content-hash": "f409ee69f8040b1928cbc618ff3e8a43", "packages": [ { "name": "adhocore/jwt", @@ -870,93 +870,6 @@ }, "time": "2022-11-29T16:25:20+00:00" }, - { - "name": "league/csv", - "version": "9.9.0", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/csv.git", - "reference": "b4418ede47fbd88facc34e40a16c8ce9153b961b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/b4418ede47fbd88facc34e40a16c8ce9153b961b", - "reference": "b4418ede47fbd88facc34e40a16c8ce9153b961b", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^8.1.2" - }, - "require-dev": { - "doctrine/collections": "^2.1.2", - "ext-dom": "*", - "ext-xdebug": "*", - "friendsofphp/php-cs-fixer": "^v3.14.3", - "phpbench/phpbench": "^1.2.8", - "phpstan/phpstan": "^1.10.4", - "phpstan/phpstan-deprecation-rules": "^1.1.2", - "phpstan/phpstan-phpunit": "^1.3.10", - "phpstan/phpstan-strict-rules": "^1.5.0", - "phpunit/phpunit": "^10.0.14" - }, - "suggest": { - "ext-dom": "Required to use the XMLConverter and the HTMLConverter classes", - "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "9.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "League\\Csv\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ignace Nyamagana Butera", - "email": "nyamsprod@gmail.com", - "homepage": "https://github.com/nyamsprod/", - "role": "Developer" - } - ], - "description": "CSV data manipulation made easy in PHP", - "homepage": "https://csv.thephpleague.com", - "keywords": [ - "convert", - "csv", - "export", - "filter", - "import", - "read", - "transform", - "write" - ], - "support": { - "docs": "https://csv.thephpleague.com", - "issues": "https://github.com/thephpleague/csv/issues", - "rss": "https://github.com/thephpleague/csv/releases.atom", - "source": "https://github.com/thephpleague/csv" - }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2023-03-11T15:57:12+00:00" - }, { "name": "matomo/device-detector", "version": "6.0.0", @@ -2960,6 +2873,49 @@ }, "time": "2022-09-22T09:15:54+00:00" }, + { + "name": "doctrine/deprecations", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "shasum": "" + }, + "require": { + "php": "^7.1|^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + }, + "time": "2022-05-02T15:47:09+00:00" + }, { "name": "doctrine/instantiator", "version": "1.5.0", @@ -3492,24 +3448,27 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.6.2", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d" + "reference": "1534aea9bde19a5c85c5d1e1f834ab63f4c5dcf5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d", - "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/1534aea9bde19a5c85c5d1e1f834ab63f4c5dcf5", + "reference": "1534aea9bde19a5c85c5d1e1f834ab63f4c5dcf5", "shasum": "" }, "require": { + "doctrine/deprecations": "^1.0", "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" }, "require-dev": { "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.8", "phpstan/phpstan-phpunit": "^1.1", @@ -3541,9 +3500,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.0" }, - "time": "2022-10-14T12:47:21+00:00" + "time": "2023-03-12T10:13:29+00:00" }, { "name": "phpspec/prophecy", @@ -3613,6 +3572,51 @@ }, "time": "2023-02-02T15:41:36+00:00" }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.16.1", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e27e92d939e2e3636f0a1f0afaba59692c0bf571", + "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.16.1" + }, + "time": "2023-02-07T18:11:17+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "9.2.26", From 7e061156864ba63fbd50d694b0b1fdc953fbae03 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Mon, 20 Mar 2023 11:44:43 +0530 Subject: [PATCH 08/20] feat: remove unused csv library --- 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 623e7484ab..7097cd731a 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -10,8 +10,6 @@ use Utopia\CLI\Console; use Utopia\Database\Database; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; -use League\Csv\Writer; -use PHPMailer\PHPMailer\PHPMailer; use Utopia\Analytics\Adapter\Mixpanel; use Utopia\Analytics\Event; use Utopia\Database\Document; From 39388e9b92bddc5a673c0138d16ba3b3d4cc8af5 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Mon, 20 Mar 2023 12:58:11 +0530 Subject: [PATCH 09/20] feat: use env vars for the hamster time --- .env | 2 +- docker-compose.yml | 3 +-- src/Appwrite/Platform/Tasks/Hamster.php | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.env b/.env index 73990bc91c..804b8c999e 100644 --- a/.env +++ b/.env @@ -45,8 +45,8 @@ _APP_SMTP_PORT=1025 _APP_SMTP_SECURE= _APP_SMTP_USERNAME= _APP_SMTP_PASSWORD= -_APP_HAMSTER_RECIPIENTS= _APP_HAMSTER_INTERVAL=86400 +_APP_HAMSTER_TIME=21:00 _APP_MIXPANEL_TOKEN= _APP_SMS_PROVIDER=sms://username:password@mock _APP_SMS_FROM=+123456789 diff --git a/docker-compose.yml b/docker-compose.yml index e238f79983..a2117adc5a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -135,7 +135,6 @@ services: - _APP_SMTP_SECURE - _APP_SMTP_USERNAME - _APP_SMTP_PASSWORD - - _APP_HAMSTER_RECIPIENTS - _APP_USAGE_STATS - _APP_INFLUXDB_HOST - _APP_INFLUXDB_PORT @@ -574,8 +573,8 @@ services: - _APP_CONNECTIONS_DB_CONSOLE - _APP_CONNECTIONS_DB_PROJECT - _APP_CONNECTIONS_CACHE - - _APP_HAMSTER_RECIPIENTS - _APP_HAMSTER_INTERVAL + - _APP_HAMSTER_TIME - _APP_MIXPANEL_TOKEN appwrite-maintenance: diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index 7097cd731a..e695d94289 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -177,7 +177,7 @@ class Hamster extends Action $sleep = (int) App::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default) - $jobInitTime = '22:00'; // (hour:minutes) + $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")); From f951da2f558645874427ff4e2042468cccac11ef Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Mon, 20 Mar 2023 14:30:24 +0530 Subject: [PATCH 10/20] feat: update hamster scrpipt --- src/Appwrite/Platform/Tasks/Hamster.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index e695d94289..85f751ec49 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -94,14 +94,16 @@ class Hamster extends Action Query::equal('teamInternalId', [$teamInternalId]), ]); - $userInternalId = $membership->getAttribute('userInternalId', null); - if ($userInternalId) { - $user = $dbForConsole->findOne('users', [ - Query::equal('_id', [$userInternalId]), - ]); + if($membership && !$membership->isEmpty()) { + $userInternalId = $membership->getAttribute('userInternalId', null); + if ($userInternalId) { + $user = $dbForConsole->findOne('users', [ + Query::equal('_id', [$userInternalId]), + ]); - $stats['email'] = $user->getAttribute('email', null); - $stats['name'] = $user->getAttribute('name', null); + $stats['email'] = $user->getAttribute('email', null); + $stats['name'] = $user->getAttribute('name', null); + } } } @@ -255,8 +257,7 @@ class Hamster extends Action Console::error('Failed to create event for project: ' . $project->getId()); } } catch (\Throwable $th) { - throw $th; - Console::error('Failed to update project ("' . $project->getId() . '") version with error: ' . $th->getMessage()); + Console::error('Failed to get stats for project ("' . $project->getId() . '") with error: ' . $th->getMessage()); } finally { $pools ->get($db) From deab7caed978527dbe0cd4a55f021058713869d0 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Mon, 20 Mar 2023 15:57:14 +0530 Subject: [PATCH 11/20] feat: update docker image --- src/Appwrite/Platform/Tasks/Hamster.php | 52 +++++++++++++------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index 85f751ec49..cf484f2703 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -94,16 +94,18 @@ class Hamster extends Action Query::equal('teamInternalId', [$teamInternalId]), ]); - if($membership && !$membership->isEmpty()) { - $userInternalId = $membership->getAttribute('userInternalId', null); - if ($userInternalId) { - $user = $dbForConsole->findOne('users', [ - Query::equal('_id', [$userInternalId]), - ]); + if (!$membership || $membership->isEmpty()) { + throw new Exception('Membership not found. Skipping project : ' . $project->getId()); + } - $stats['email'] = $user->getAttribute('email', null); - $stats['name'] = $user->getAttribute('name', null); - } + $userInternalId = $membership->getAttribute('userInternalId', null); + if ($userInternalId) { + $user = $dbForConsole->findOne('users', [ + Query::equal('_id', [$userInternalId]), + ]); + + $stats['email'] = $user->getAttribute('email', null); + $stats['name'] = $user->getAttribute('name', null); } } @@ -238,23 +240,25 @@ class Hamster extends Action $statsPerProject = $this->getStats($dbForConsole, $dbForProject, $project); - /** Send data to mixpanel */ - $res = $this->mixpanel->createProfile($statsPerProject['email'], '', [ - 'name' => $statsPerProject['name'], - 'email' => $statsPerProject['email'] - ]); + 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()); - } + if (!$res) { + Console::error('Failed to create user profile for project: ' . $project->getId()); + } - $event = new Event(); - $event - ->setName('Appwrite Cloud Project Stats') - ->setProps($statsPerProject); - $res = $this->mixpanel->createEvent($event); - if (!$res) { - Console::error('Failed to create event for project: ' . $project->getId()); + $event = new Event(); + $event + ->setName('Appwrite Cloud Project Stats') + ->setProps($statsPerProject); + $res = $this->mixpanel->createEvent($event); + if (!$res) { + Console::error('Failed to create event for project: ' . $project->getId()); + } } } catch (\Throwable $th) { Console::error('Failed to get stats for project ("' . $project->getId() . '") with error: ' . $th->getMessage()); From 6c15326f8f3448dae7e511e106f70b2d60b1cb1a Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 22 Mar 2023 20:18:23 +0400 Subject: [PATCH 12/20] feat: add new stats --- .env | 4 +- composer.json | 2 +- composer.lock | 14 +- src/Appwrite/Platform/Tasks/Hamster.php | 487 +++++++++++++++--------- 4 files changed, 312 insertions(+), 195 deletions(-) diff --git a/.env b/.env index 804b8c999e..14738d1a3b 100644 --- a/.env +++ b/.env @@ -46,8 +46,8 @@ _APP_SMTP_SECURE= _APP_SMTP_USERNAME= _APP_SMTP_PASSWORD= _APP_HAMSTER_INTERVAL=86400 -_APP_HAMSTER_TIME=21:00 -_APP_MIXPANEL_TOKEN= +_APP_HAMSTER_TIME=12:31 +_APP_MIXPANEL_TOKEN=bce512333a58ec62f44541328607f53c _APP_SMS_PROVIDER=sms://username:password@mock _APP_SMS_FROM=+123456789 _APP_STORAGE_LIMIT=30000000 diff --git a/composer.json b/composer.json index f947592f9e..1eec45940f 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "appwrite/php-clamav": "1.1.*", "appwrite/php-runtimes": "0.11.*", "utopia-php/abuse": "0.16.*", - "utopia-php/analytics": "0.10.1", + "utopia-php/analytics": "0.10.2", "utopia-php/audit": "0.17.*", "utopia-php/cache": "0.8.*", "utopia-php/cli": "0.15.*", diff --git a/composer.lock b/composer.lock index a54754a036..d86ee62b7f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f409ee69f8040b1928cbc618ff3e8a43", + "content-hash": "ca2a083ff1c0d0c77942674400137793", "packages": [ { "name": "adhocore/jwt", @@ -1583,16 +1583,16 @@ }, { "name": "utopia-php/analytics", - "version": "0.10.1", + "version": "0.10.2", "source": { "type": "git", "url": "https://github.com/utopia-php/analytics.git", - "reference": "70ada5e6b192ae27e6d5467899a4cdd886003835" + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/analytics/zipball/70ada5e6b192ae27e6d5467899a4cdd886003835", - "reference": "70ada5e6b192ae27e6d5467899a4cdd886003835", + "url": "https://api.github.com/repos/utopia-php/analytics/zipball/14c805114736f44c26d6d24b176a2f8b93d86a1f", + "reference": "14c805114736f44c26d6d24b176a2f8b93d86a1f", "shasum": "" }, "require": { @@ -1623,9 +1623,9 @@ ], "support": { "issues": "https://github.com/utopia-php/analytics/issues", - "source": "https://github.com/utopia-php/analytics/tree/0.10.1" + "source": "https://github.com/utopia-php/analytics/tree/0.10.2" }, - "time": "2023-03-17T14:42:35+00:00" + "time": "2023-03-22T12:01:09+00:00" }, { "name": "utopia-php/audit", diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index cf484f2703..cc89321704 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -3,6 +3,7 @@ namespace Appwrite\Platform\Tasks; use Exception; +use finfo; use Utopia\App; use Utopia\Platform\Action; use Utopia\Cache\Cache; @@ -14,22 +15,23 @@ use Utopia\Analytics\Adapter\Mixpanel; use Utopia\Analytics\Event; use Utopia\Database\Document; use Utopia\Pools\Group; +use Utopia\Pools\Pool; use Utopia\Registry\Registry; class Hamster extends Action { - private array $usageStats = [ - 'files' => 'files.$all.count.total', - 'buckets' => 'buckets.$all.count.total', - 'databases' => 'databases.$all.count.total', - 'documents' => 'documents.$all.count.total', - 'collections' => 'collections.$all.count.total', - 'storage' => 'project.$all.storage.size', - 'requests' => 'project.$all.network.requests', - 'bandwidth' => 'project.$all.network.bandwidth', - 'users' => 'users.$all.count.total', - 'sessions' => 'sessions.email.requests.create', - 'executions' => 'executions.$all.compute.total', + 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'; @@ -51,129 +53,188 @@ class Hamster extends Action $this ->desc('Get stats for projects') - ->inject('register') ->inject('pools') ->inject('cache') ->inject('dbForConsole') - ->callback(function (Registry $register, Group $pools, Cache $cache, Database $dbForConsole) { - $this->action($register, $pools, $cache, $dbForConsole); + ->callback(function (Group $pools, Cache $cache, Database $dbForConsole) { + $this->action($pools, $cache, $dbForConsole); }); } - private function getStats(Database $dbForConsole, Database $dbForProject, Document $project): array + private function getStatsPerProject(Group $pools, Cache $cache, Database $dbForConsole) { - $stats = []; - - $stats['time'] = microtime(true); - - /** Get Project ID */ - $stats['projectId'] = $project->getId(); - - /** Get Project Name */ - $stats['projectName'] = $project->getAttribute('name'); - - /** Get Total Functions */ - $stats['functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT); - - /** Get Total Deployments */ - $stats['deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT); - - /** Get Total Members */ - $teamInternalId = $project->getAttribute('teamInternalId', null); - if ($teamInternalId) { - $stats['members'] = $dbForConsole->count('memberships', [ - Query::equal('teamInternalId', [$teamInternalId]) - ], APP_LIMIT_COUNT); - } else { - $stats['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()); + $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; } - $userInternalId = $membership->getAttribute('userInternalId', null); - if ($userInternalId) { - $user = $dbForConsole->findOne('users', [ - Query::equal('_id', [$userInternalId]), - ]); + Console::log("Getting stats for {$project->getId()}"); - $stats['email'] = $user->getAttribute('email', null); - $stats['name'] = $user->getAttribute('name', null); - } - } + try { + $db = $project->getAttribute('database'); + $adapter = $pools + ->get($db) + ->pop() + ->getResource(); - /** Get Domains */ - $stats['domains'] = $dbForProject->count('domains', [], APP_LIMIT_COUNT); + $dbForProject = new Database($adapter, $cache); + $dbForProject->setDefaultDatabase('appwrite'); + $dbForProject->setNamespace('_' . $project->getInternalId()); - /** Get Platforms */ - $platforms = $dbForConsole->find('platforms', [ - Query::equal('projectInternalId', [$project->getInternalId()]), - Query::limit(APP_LIMIT_COUNT) - ]); + $statsPerProject = []; - $stats['platforms_web'] = sizeof(array_filter($platforms, function ($platform) { - return $platform['type'] === 'web'; - })); + $statsPerProject['time'] = microtime(true); - $stats['platforms_android'] = sizeof(array_filter($platforms, function ($platform) { - return $platform['type'] === 'android'; - })); + /** Get Project ID */ + $statsPerProject['project_id'] = $project->getId(); - $stats['platforms_iOS'] = sizeof(array_filter($platforms, function ($platform) { - return str_contains($platform['type'], 'apple'); - })); + /** Get project created time */ + $statsPerProject['project_created'] = $project->getAttribute('$createdAt'); - $stats['platforms_flutter'] = sizeof(array_filter($platforms, function ($platform) { - return str_contains($platform['type'], 'flutter'); - })); + /** Get Project Name */ + $statsPerProject['project_name'] = $project->getAttribute('name'); - /** Get Usage stats */ - $range = '90d'; - $periods = [ - '90d' => [ - 'period' => '1d', - 'limit' => 90, - ], - ]; + /** Get Total Functions */ + $statsPerProject['custom_functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT); - $metrics = array_values($this->usageStats); - Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) { - foreach ($metrics as $metric) { - $limit = $periods[$range]['limit']; - $period = $periods[$range]['period']; + /** Get Total Deployments */ + $statsPerProject['custom_deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT); - $requestDocs = $dbForProject->find('stats', [ - Query::equal('period', [$period]), - Query::equal('metric', [$metric]), - Query::limit($limit), - Query::orderDesc('time'), - ]); + /** Get Total Teams */ + $statsPerProject['custom_teams'] = $dbForProject->count('teams', [], APP_LIMIT_COUNT); - $stats[$metric] = []; - foreach ($requestDocs as $requestDoc) { - $stats[$metric][] = [ - 'value' => $requestDoc->getAttribute('value'), - 'date' => $requestDoc->getAttribute('time'), - ]; + /** 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; } - $stats[$metric] = array_reverse($stats[$metric]); - // Calculate aggregate of each metric - $stats[$metric] = array_sum(array_column($stats[$metric], 'value')); + /** 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()); + } + + $userInternalId = $membership->getAttribute('userInternalId', null); + if ($userInternalId) { + $user = $dbForConsole->findOne('users', [ + Query::equal('_id', [$userInternalId]), + ]); + + $statsPerProject['email'] = $user->getAttribute('email', null); + $statsPerProject['name'] = $user->getAttribute('name', null); + } + } + + /** Get Domains */ + $statsPerProject['custom_domains'] = $dbForProject->count('domains', [], 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_iOS'] = 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'); + })); + + /** 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(); } }); - - return $stats; } - public function action(Registry $register, Group $pools, Cache $cache, Database $dbForConsole): void + public function action(Group $pools, Cache $cache, Database $dbForConsole): void { Console::title('Cloud Hamster V1'); @@ -181,7 +242,7 @@ class Hamster extends Action $sleep = (int) App::getEnv('_APP_HAMSTER_INTERVAL', '30'); // 30 seconds (by default) - $jobInitTime = App::getEnv('_APP_HAMSTER_TIME', '22:00');// (hour:minutes) + $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")); @@ -198,90 +259,25 @@ 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 ($register, $pools, $cache, $dbForConsole, $sleep) { + Console::loop(function () use ($pools, $cache, $dbForConsole, $sleep) { $now = date('d-m-Y H:i:s', time()); Console::info("[{$now}] Getting Cloud Usage Stats every {$sleep} seconds"); $loopStart = microtime(true); /* Initialise new Utopia app */ $app = new App('UTC'); - $console = $app->getResource('console'); - /** Database connections */ - $totalProjects = $dbForConsole->count('projects') + 1; - Console::success("Found a total of: {$totalProjects} projects"); + Console::info('Getting stats for all projects'); + $this->getStatsPerProject($pools, $cache, $dbForConsole); + Console::success('Completed getting stats for all projects'); - $projects = [$console]; - $count = 0; - $limit = 30; - $sum = 30; - $offset = 0; - while (!empty($projects)) { - foreach ($projects as $project) { - /** - * Skip user projects with id 'console' - */ - if ($project->getId() === 'console') { - continue; - } - - Console::info("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 = $this->getStats($dbForConsole, $dbForProject, $project); - - 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('Appwrite Cloud Project Stats') - ->setProps($statsPerProject); - $res = $this->mixpanel->createEvent($event); - if (!$res) { - Console::error('Failed to create event for project: ' . $project->getId()); - } - } - } catch (\Throwable $th) { - Console::error('Failed to get stats for project ("' . $project->getId() . '") with error: ' . $th->getMessage()); - } finally { - $pools - ->get($db) - ->reclaim(); - } - } - - $sum = \count($projects); - - $projects = $dbForConsole->find('projects', [ - Query::limit($limit), - Query::offset($offset), - ]); - - $offset = $offset + $limit; - $count = $count + $sum; - - Console::log('Iterated through ' . $count . '/' . $totalProjects . ' projects...'); - } + Console::info('Getting stats for all organizations'); + $this->getStatsPerOrganization($dbForConsole); + Console::success('Completed getting stats for all organizations'); + Console::info('Getting stats for all users'); + $this->getStatsPerUser($dbForConsole); + Console::success('Completed getting stats for all users'); $pools ->get('console') @@ -292,4 +288,125 @@ class Hamster extends Action Console::info("[{$now}] Cloud Stats took {$loopTook} seconds"); }, $sleep, $delay); } + + protected function calculateByGroup(string $collection, Database $dbForConsole, callable $callback) + { + $count = 0; + $chunk = 0; + $limit = 50; + $results = []; + $sum = $limit; + + $executionStart = \microtime(true); + + while ($sum === $limit) { + $chunk++; + + $results = $dbForConsole->find($collection, \array_merge([Query::limit($limit)])); + + $sum = count($results); + + Console::log('Processing chunk #' . $chunk . '. Found ' . $sum . ' documents'); + + foreach ($results as $document) { + call_user_func($callback, $dbForConsole, $document); + $count++; + } + } + + $executionEnd = \microtime(true); + + Console::log("Processed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); + } + + protected function getStatsPerOrganization(Database $dbForConsole) + { + + $this->calculateByGroup('teams', $dbForConsole, function (Database $dbForConsole, Document $document) { + $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()); + } + + $userInternalId = $membership->getAttribute('userInternalId', null); + if ($userInternalId) { + $user = $dbForConsole->findOne('users', [ + Query::equal('_id', [$userInternalId]), + ]); + + $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()); + } + }); + } + + protected function getStatsPerUser(Database $dbForConsole) + { + $this->calculateByGroup('users', $dbForConsole, function (Database $dbForConsole, Document $document) { + $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()); + } + }); + } } From 07e3a29b632cf0d3504c79e1d3f6c6e2484f1bd3 Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 22 Mar 2023 20:19:28 +0400 Subject: [PATCH 13/20] feat: add new stats --- .env | 4 ++-- src/Appwrite/Platform/Tasks/Hamster.php | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 14738d1a3b..804b8c999e 100644 --- a/.env +++ b/.env @@ -46,8 +46,8 @@ _APP_SMTP_SECURE= _APP_SMTP_USERNAME= _APP_SMTP_PASSWORD= _APP_HAMSTER_INTERVAL=86400 -_APP_HAMSTER_TIME=12:31 -_APP_MIXPANEL_TOKEN=bce512333a58ec62f44541328607f53c +_APP_HAMSTER_TIME=21:00 +_APP_MIXPANEL_TOKEN= _APP_SMS_PROVIDER=sms://username:password@mock _APP_SMS_FROM=+123456789 _APP_STORAGE_LIMIT=30000000 diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index cc89321704..d33e26c980 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -3,7 +3,6 @@ namespace Appwrite\Platform\Tasks; use Exception; -use finfo; use Utopia\App; use Utopia\Platform\Action; use Utopia\Cache\Cache; From 9723792a29d2ed959ed4c0402865a9fc7780ab2c Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Wed, 22 Mar 2023 20:22:56 +0400 Subject: [PATCH 14/20] feat: add new stats --- src/Appwrite/Platform/Tasks/Hamster.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index d33e26c980..6024dec6af 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -62,7 +62,7 @@ class Hamster extends Action private function getStatsPerProject(Group $pools, Cache $cache, Database $dbForConsole) { - $this->calculateByGroup('projects', $dbForConsole, function (Database $dbForConsole, Document $project) use ($pools, $cache){ + $this->calculateByGroup('projects', $dbForConsole, function (Database $dbForConsole, Document $project) use ($pools, $cache) { /** * Skip user projects with id 'console' */ From cf39092c98e16ae67335003777e213fb82f3ffbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 22 Mar 2023 19:52:43 +0100 Subject: [PATCH 15/20] Add patch task for scheduleUpdatedAt --- src/Appwrite/Migration/Version/V17.php | 11 --- src/Appwrite/Platform/Services/Tasks.php | 2 + .../PatchDeleteScheduleUpdatedAtAttribute.php | 70 +++++++++++++++++++ 3 files changed, 72 insertions(+), 11 deletions(-) create mode 100644 src/Appwrite/Platform/Tasks/PatchDeleteScheduleUpdatedAtAttribute.php diff --git a/src/Appwrite/Migration/Version/V17.php b/src/Appwrite/Migration/Version/V17.php index 5ee897ae19..66a02662d1 100644 --- a/src/Appwrite/Migration/Version/V17.php +++ b/src/Appwrite/Migration/Version/V17.php @@ -58,17 +58,6 @@ class V17 extends Migration } break; - case 'functions': - try { - /** - * Delete 'scheduleUpdatedAt' attribute - */ - $this->projectDB->deleteAttribute($id, 'scheduleUpdatedAt'); - $this->projectDB->deleteCachedCollection($id); - } catch (\Throwable $th) { - Console::warning("'scheduleUpdatedAt' from {$id}: {$th->getMessage()}"); - } - break; default: break; diff --git a/src/Appwrite/Platform/Services/Tasks.php b/src/Appwrite/Platform/Services/Tasks.php index 00cf3f89c5..a0c5d3a547 100644 --- a/src/Appwrite/Platform/Services/Tasks.php +++ b/src/Appwrite/Platform/Services/Tasks.php @@ -13,6 +13,7 @@ use Appwrite\Platform\Tasks\SDKs; use Appwrite\Platform\Tasks\Specs; use Appwrite\Platform\Tasks\SSL; use Appwrite\Platform\Tasks\Hamster; +use Appwrite\Platform\Tasks\PatchDeleteScheduleUpdatedAtAttribute; use Appwrite\Platform\Tasks\Usage; use Appwrite\Platform\Tasks\Vars; use Appwrite\Platform\Tasks\Version; @@ -33,6 +34,7 @@ class Tasks extends Service ->addAction(Install::getName(), new Install()) ->addAction(Maintenance::getName(), new Maintenance()) ->addAction(PatchCreateMissingSchedules::getName(), new PatchCreateMissingSchedules()) + ->addAction(PatchDeleteScheduleUpdatedAtAttribute::getName(), new PatchDeleteScheduleUpdatedAtAttribute()) ->addAction(Schedule::getName(), new Schedule()) ->addAction(Migrate::getName(), new Migrate()) ->addAction(SDKs::getName(), new SDKs()) diff --git a/src/Appwrite/Platform/Tasks/PatchDeleteScheduleUpdatedAtAttribute.php b/src/Appwrite/Platform/Tasks/PatchDeleteScheduleUpdatedAtAttribute.php new file mode 100644 index 0000000000..4ee213303c --- /dev/null +++ b/src/Appwrite/Platform/Tasks/PatchDeleteScheduleUpdatedAtAttribute.php @@ -0,0 +1,70 @@ +desc('Ensure function collections do not have scheduleUpdatedAt attribute') + ->inject('dbForConsole') + ->inject('getProjectDB') + ->callback(fn (Database $dbForConsole, callable $getProjectDB) => $this->action($dbForConsole, $getProjectDB)); + } + + /** + * Iterate over every function on every project to make sure there is a schedule. If not, recreate the schedule. + */ + public function action(Database $dbForConsole, callable $getProjectDB): void + { + Authorization::disable(); + Authorization::setDefaultStatus(false); + + Console::title('PatchDeleteScheduleUpdatedAtAttribute V1'); + Console::success(APP_NAME . ' PatchDeleteScheduleUpdatedAtAttribute v1 has started'); + + $limit = 100; + $projectCursor = null; + while (true) { + $projectsQueries = [Query::limit($limit)]; + if ($projectCursor !== null) { + $projectsQueries[] = Query::cursorAfter($projectCursor); + } + $projects = $dbForConsole->find('projects', $projectsQueries); + + if (count($projects) === 0) { + break; + } + + foreach ($projects as $project) { + Console::log("Checking Project " . $project->getAttribute('name') . " (" . $project->getId() . ")"); + $dbForProject = $getProjectDB($project); + + try { + /** + * Delete 'scheduleUpdatedAt' attribute + */ + $dbForProject->deleteAttribute('functions', 'scheduleUpdatedAt'); + $dbForProject->deleteCachedCollection('functions'); + Console::success("'scheduleUpdatedAt' deleted."); + } catch (\Throwable $th) { + Console::warning("'scheduleUpdatedAt' errored: {$th->getMessage()}"); + } + } + + $projectCursor = $projects[array_key_last($projects)]; + } + } +} From b558a284f3e6a224c0ed5273ea172875a9db3802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 22 Mar 2023 20:15:01 +0100 Subject: [PATCH 16/20] Add bin files --- Dockerfile | 1 + bin/patch-delete-schedule-updated-at-attribute | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 bin/patch-delete-schedule-updated-at-attribute diff --git a/Dockerfile b/Dockerfile index abe9b7128f..a29afa2baf 100755 --- a/Dockerfile +++ b/Dockerfile @@ -117,6 +117,7 @@ RUN mkdir -p /storage/uploads && \ # Executables RUN chmod +x /usr/local/bin/doctor && \ chmod +x /usr/local/bin/patch-create-missing-schedules && \ + chmod +x /usr/local/bin/patch-delete-schedule-updated-at-attribute && \ chmod +x /usr/local/bin/maintenance && \ chmod +x /usr/local/bin/volume-sync && \ chmod +x /usr/local/bin/usage && \ diff --git a/bin/patch-delete-schedule-updated-at-attribute b/bin/patch-delete-schedule-updated-at-attribute new file mode 100644 index 0000000000..3e28289cbe --- /dev/null +++ b/bin/patch-delete-schedule-updated-at-attribute @@ -0,0 +1,3 @@ +#!/bin/sh + +php /usr/src/code/app/cli.php patch-delete-schedule-updated-at-attribute $@ \ No newline at end of file From 567cb01b9940948575abb08a1654b92cc61fe6d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 22 Mar 2023 20:35:04 +0100 Subject: [PATCH 17/20] add reclaim logic --- .../Tasks/PatchDeleteScheduleUpdatedAtAttribute.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/PatchDeleteScheduleUpdatedAtAttribute.php b/src/Appwrite/Platform/Tasks/PatchDeleteScheduleUpdatedAtAttribute.php index 4ee213303c..95a7c4ffe1 100644 --- a/src/Appwrite/Platform/Tasks/PatchDeleteScheduleUpdatedAtAttribute.php +++ b/src/Appwrite/Platform/Tasks/PatchDeleteScheduleUpdatedAtAttribute.php @@ -7,6 +7,7 @@ use Utopia\CLI\Console; use Utopia\Database\Query; use Utopia\Database\Database; use Utopia\Database\Validator\Authorization; +use Utopia\Pools\Group; class PatchDeleteScheduleUpdatedAtAttribute extends Action { @@ -19,15 +20,16 @@ class PatchDeleteScheduleUpdatedAtAttribute extends Action { $this ->desc('Ensure function collections do not have scheduleUpdatedAt attribute') + ->inject('pools') ->inject('dbForConsole') ->inject('getProjectDB') - ->callback(fn (Database $dbForConsole, callable $getProjectDB) => $this->action($dbForConsole, $getProjectDB)); + ->callback(fn (Group $pools, Database $dbForConsole, callable $getProjectDB) => $this->action($pools, $dbForConsole, $getProjectDB)); } /** * Iterate over every function on every project to make sure there is a schedule. If not, recreate the schedule. */ - public function action(Database $dbForConsole, callable $getProjectDB): void + public function action(Group $pools, Database $dbForConsole, callable $getProjectDB): void { Authorization::disable(); Authorization::setDefaultStatus(false); @@ -62,6 +64,8 @@ class PatchDeleteScheduleUpdatedAtAttribute extends Action } catch (\Throwable $th) { Console::warning("'scheduleUpdatedAt' errored: {$th->getMessage()}"); } + + $pools->reclaim(); } $projectCursor = $projects[array_key_last($projects)]; From bd620cc9240015a9182ca648bee17676ff0b6bf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 22 Mar 2023 20:39:56 +0100 Subject: [PATCH 18/20] Remove old patch bin file --- Dockerfile | 1 - bin/patch-create-missing-schedules | 3 --- 2 files changed, 4 deletions(-) delete mode 100644 bin/patch-create-missing-schedules diff --git a/Dockerfile b/Dockerfile index a29afa2baf..eaf8b73e1b 100755 --- a/Dockerfile +++ b/Dockerfile @@ -116,7 +116,6 @@ RUN mkdir -p /storage/uploads && \ # Executables RUN chmod +x /usr/local/bin/doctor && \ - chmod +x /usr/local/bin/patch-create-missing-schedules && \ chmod +x /usr/local/bin/patch-delete-schedule-updated-at-attribute && \ chmod +x /usr/local/bin/maintenance && \ chmod +x /usr/local/bin/volume-sync && \ diff --git a/bin/patch-create-missing-schedules b/bin/patch-create-missing-schedules deleted file mode 100644 index e38d3e9a6f..0000000000 --- a/bin/patch-create-missing-schedules +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -php /usr/src/code/app/cli.php patch-create-missing-schedules $@ \ No newline at end of file From 65c6123ccda26e99b55041c03b028897f6ea0c6b Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 23 Mar 2023 12:22:38 +0400 Subject: [PATCH 19/20] feat: fix hamster script --- src/Appwrite/Platform/Tasks/Hamster.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index 6024dec6af..414050c413 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -301,7 +301,10 @@ class Hamster extends Action while ($sum === $limit) { $chunk++; - $results = $dbForConsole->find($collection, \array_merge([Query::limit($limit)])); + $results = $dbForConsole->find($collection, \array_merge([ + Query::limit($limit), + Query::offset($count) + ])); $sum = count($results); From f6ae33aebaa3b74c069d05a5a44be1e88cfc0ffe Mon Sep 17 00:00:00 2001 From: Christy Jacob Date: Thu, 23 Mar 2023 14:23:49 +0400 Subject: [PATCH 20/20] feat: update appwrite image --- src/Appwrite/Platform/Tasks/Hamster.php | 130 +++++++++++++----------- 1 file changed, 68 insertions(+), 62 deletions(-) diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php index 414050c413..3e3cdd9d73 100644 --- a/src/Appwrite/Platform/Tasks/Hamster.php +++ b/src/Appwrite/Platform/Tasks/Hamster.php @@ -14,8 +14,6 @@ use Utopia\Analytics\Adapter\Mixpanel; use Utopia\Analytics\Event; use Utopia\Database\Document; use Utopia\Pools\Group; -use Utopia\Pools\Pool; -use Utopia\Registry\Registry; class Hamster extends Action { @@ -325,52 +323,56 @@ class Hamster extends Action { $this->calculateByGroup('teams', $dbForConsole, function (Database $dbForConsole, Document $document) { - $statsPerOrganization = []; + try { + $statsPerOrganization = []; - /** Organization name */ - $statsPerOrganization['name'] = $document->getAttribute('name'); + /** 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()); - } - - $userInternalId = $membership->getAttribute('userInternalId', null); - if ($userInternalId) { - $user = $dbForConsole->findOne('users', [ - Query::equal('_id', [$userInternalId]), + /** Get Email and of the organization owner */ + $membership = $dbForConsole->findOne('memberships', [ + Query::equal('teamInternalId', [$document->getInternalId()]), ]); - $statsPerOrganization['email'] = $user->getAttribute('email', null); - } + if (!$membership || $membership->isEmpty()) { + throw new Exception('Membership not found. Skipping organization : ' . $document->getId()); + } - /** Organization Creation Date */ - $statsPerOrganization['created'] = $document->getAttribute('$createdAt'); + $userInternalId = $membership->getAttribute('userInternalId', null); + if ($userInternalId) { + $user = $dbForConsole->findOne('users', [ + Query::equal('_id', [$userInternalId]), + ]); - /** Number of team members */ - $statsPerOrganization['members'] = $document->getAttribute('total'); + $statsPerOrganization['email'] = $user->getAttribute('email', null); + } - /** Number of projects in this organization */ - $statsPerOrganization['projects'] = $dbForConsole->count('projects', [ - Query::equal('teamId', [$document->getId()]), - Query::limit(APP_LIMIT_COUNT) - ]); + /** Organization Creation Date */ + $statsPerOrganization['created'] = $document->getAttribute('$createdAt'); - if (!isset($statsPerOrganization['email'])) { - throw new Exception('Email not found. Skipping organization : ' . $document->getId()); - } + /** Number of team members */ + $statsPerOrganization['members'] = $document->getAttribute('total'); - $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()); + /** 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()); + } + } catch (Exception $e) { + Console::error($e->getMessage()); } }); } @@ -378,36 +380,40 @@ class Hamster extends Action protected function getStatsPerUser(Database $dbForConsole) { $this->calculateByGroup('users', $dbForConsole, function (Database $dbForConsole, Document $document) { - $statsPerUser = []; + try { + $statsPerUser = []; - /** Organization name */ - $statsPerUser['name'] = $document->getAttribute('name'); + /** 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 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'); + /** 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) - ]); + /** 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()); - } + 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); + /** 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()); + if (!$res) { + throw new Exception('Failed to create user profile for user: ' . $document->getId()); + } + } catch (Exception $e) { + Console::error($e->getMessage()); } }); }