From 234c6bdea4b58ed9267c991fdece994222462a24 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 11 Oct 2023 11:01:16 +0100 Subject: [PATCH 01/12] docs: move setup guide to wiki --- README.md | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/README.md b/README.md index ef634ed..1181c40 100644 --- a/README.md +++ b/README.md @@ -6,34 +6,3 @@ We make releasing music easier. We are DIY artists, label people, software developers and music consumers who are building tools to give indies superpowers in the age of big data. "Giving indies superpowers in the age of big data." - -Dev todo --------- - -Notes for Wednesday: the database speedup is immense. From 6 minutes to about 15 seconds. -But there's still more to do, and ideally it should all be done within 1 second. -So, here's how: - -- [x] When the file is uploaded, just store it in the Upload table. -- [x] Introduce a new field, Upload.processedUsages -- [x] In a background script, loop over all uploads that are not processed and extract their usages (then mark as processed) -- [ ] Introduce another new field, Usage.processed -- [ ] In another background script, loop over all usages that are unprocessed, finishing the job here. -- [ ] The usage processor needs to match products and artists - rather than doing this individually in a loop, lookup the unique artist/product first, to cache the IDs (or create new ones), then it's possible to insert UsageOfProduct rows on bulk! -- [ ] Then optimise further with a profiler. Ideally, a very large import should be completed before the page has chance to reload. -- [ ] If a spinner is necessary, it should be put onto the three-checkbox page. It's also possible to know how many usages are left to process, so an ACTUAL progress bar is possible. - -Setup guide ------------ - -TODO: From scratch for Linux, Windows and Mac. - -Running locally ---------------- - -TODO. - -Writing/running tests ---------------------- - -TODO. From a5e6030f28141ff8b66ca2237b0fb3691817b492 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 11 Oct 2023 15:47:53 +0100 Subject: [PATCH 02/12] fix: associate cost to user rather than product --- class/Cost/CostRepository.php | 1 + composer.json | 2 +- composer.lock | 207 ++++++++++++++--------------- query/Cost/create.sql | 2 + query/Cost/getAllForUser.sql | 19 +-- query/_migration/010-drop-cost.sql | 1 + query/_migration/011-cost-user.sql | 13 ++ test/behat/costs.feature | 20 ++- 8 files changed, 138 insertions(+), 127 deletions(-) create mode 100644 query/_migration/010-drop-cost.sql create mode 100644 query/_migration/011-cost-user.sql diff --git a/class/Cost/CostRepository.php b/class/Cost/CostRepository.php index 302d853..4aacdd3 100644 --- a/class/Cost/CostRepository.php +++ b/class/Cost/CostRepository.php @@ -28,6 +28,7 @@ public function create(Cost $cost, User $user):void { $this->db->insert("create", [ "id" => $cost->id, "productId" => $cost->product->id, + "userId" => $user->id, "description" => $cost->description, "amount" => $cost->amount->value, ]); diff --git a/composer.json b/composer.json index 4f341e0..7c1a29b 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "php": ">=8.2", "superhyperinstantfuturetime/spotify-api": "dev-master", "phpgt/webengine": "dev-master", - "phpgt/database": "dev-dynamic-sets as v1.5.0", + "phpgt/database": "^1.4", "league/commonmark": "^2.4" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 27040bd..0e9190b 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": "fb4cd5a2e3aaed7361c37b8f1d257cfb", + "content-hash": "ef74b7b8821c46eed8e091e04cbc03f3", "packages": [ { "name": "composer/semver", @@ -467,16 +467,16 @@ }, { "name": "nette/schema", - "version": "v1.2.4", + "version": "v1.2.5", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab" + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/c9ff517a53903b3d4e29ec547fb20feecb05b8ab", - "reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab", + "url": "https://api.github.com/repos/nette/schema/zipball/0462f0166e823aad657c9224d0f849ecac1ba10a", + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a", "shasum": "" }, "require": { @@ -523,9 +523,9 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.4" + "source": "https://github.com/nette/schema/tree/v1.2.5" }, - "time": "2023-08-05T18:56:25+00:00" + "time": "2023-10-05T20:37:59+00:00" }, { "name": "nette/utils", @@ -1089,30 +1089,28 @@ }, { "name": "phpgt/database", - "version": "dev-dynamic-sets", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/PhpGt/Database.git", - "reference": "4e48d3d390d0a54be969636535dfaa1e1def82b5" + "reference": "db929a74e7e57f8424027c8fe685b37c6459db86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Database/zipball/4e48d3d390d0a54be969636535dfaa1e1def82b5", - "reference": "4e48d3d390d0a54be969636535dfaa1e1def82b5", + "url": "https://api.github.com/repos/PhpGt/Database/zipball/db929a74e7e57f8424027c8fe685b37c6459db86", + "reference": "db929a74e7e57f8424027c8fe685b37c6459db86", "shasum": "" }, "require": { "ext-pdo": "*", - "php": ">=8.1", + "php": ">=7.4", "phpgt/cli": "^1.3", "phpgt/config": "^v1.1.0" }, "require-dev": { "ext-sqlite3": "*", - "phpmd/phpmd": "^2.13", - "phpstan/phpstan": "^v1.10", - "phpunit/phpunit": "^10.1", - "squizlabs/php_codesniffer": "^3.7" + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^9.4" }, "bin": [ "bin/migrate" @@ -1144,7 +1142,7 @@ "description": "Database API organisation.", "support": { "issues": "https://github.com/PhpGt/Database/issues", - "source": "https://github.com/PhpGt/Database/tree/dynamic-sets" + "source": "https://github.com/PhpGt/Database/tree/v1.4.0" }, "funding": [ { @@ -1152,7 +1150,7 @@ "type": "github" } ], - "time": "2023-08-18T11:21:10+00:00" + "time": "2021-09-23T09:07:04+00:00" }, { "name": "phpgt/dataobject", @@ -3353,16 +3351,16 @@ }, { "name": "composer/pcre", - "version": "3.1.0", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9", "shasum": "" }, "require": { @@ -3404,7 +3402,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.0" + "source": "https://github.com/composer/pcre/tree/3.1.1" }, "funding": [ { @@ -3420,7 +3418,7 @@ "type": "tidelift" } ], - "time": "2022-11-17T09:50:14+00:00" + "time": "2023-10-11T07:11:09+00:00" }, { "name": "composer/xdebug-handler", @@ -3547,16 +3545,16 @@ }, { "name": "friends-of-behat/mink-extension", - "version": "v2.7.3", + "version": "v2.7.4", "source": { "type": "git", "url": "https://github.com/FriendsOfBehat/MinkExtension.git", - "reference": "b1ccaef9136f62c8efaad88b966345dd68374fa9" + "reference": "18d5a53dff3e2c8934c53e2db8b02b7ea345fe85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfBehat/MinkExtension/zipball/b1ccaef9136f62c8efaad88b966345dd68374fa9", - "reference": "b1ccaef9136f62c8efaad88b966345dd68374fa9", + "url": "https://api.github.com/repos/FriendsOfBehat/MinkExtension/zipball/18d5a53dff3e2c8934c53e2db8b02b7ea345fe85", + "reference": "18d5a53dff3e2c8934c53e2db8b02b7ea345fe85", "shasum": "" }, "require": { @@ -3606,9 +3604,9 @@ "web" ], "support": { - "source": "https://github.com/FriendsOfBehat/MinkExtension/tree/v2.7.3" + "source": "https://github.com/FriendsOfBehat/MinkExtension/tree/v2.7.4" }, - "time": "2023-09-12T09:50:10+00:00" + "time": "2023-10-03T13:15:12+00:00" }, { "name": "masterminds/html5", @@ -3798,12 +3796,12 @@ "source": { "type": "git", "url": "https://github.com/pdepend/pdepend.git", - "reference": "8b513efa2390024ebc487d0feeea17b4b78fc186" + "reference": "d12f25bcdfb7754bea458a4a5cb159d55e9950d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pdepend/pdepend/zipball/8b513efa2390024ebc487d0feeea17b4b78fc186", - "reference": "8b513efa2390024ebc487d0feeea17b4b78fc186", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/d12f25bcdfb7754bea458a4a5cb159d55e9950d0", + "reference": "d12f25bcdfb7754bea458a4a5cb159d55e9950d0", "shasum": "" }, "require": { @@ -3846,7 +3844,7 @@ ], "support": { "issues": "https://github.com/pdepend/pdepend/issues", - "source": "https://github.com/pdepend/pdepend/tree/master" + "source": "https://github.com/pdepend/pdepend/tree/2.15.1" }, "funding": [ { @@ -3854,7 +3852,7 @@ "type": "tidelift" } ], - "time": "2023-09-07T10:27:45+00:00" + "time": "2023-09-28T12:00:56+00:00" }, { "name": "phar-io/manifest", @@ -3969,22 +3967,22 @@ }, { "name": "phpmd/phpmd", - "version": "2.13.0", + "version": "2.14.1", "source": { "type": "git", "url": "https://github.com/phpmd/phpmd.git", - "reference": "dad0228156856b3ad959992f9748514fa943f3e3" + "reference": "442fc2c34edcd5198b442d8647c7f0aec3afabe8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmd/phpmd/zipball/dad0228156856b3ad959992f9748514fa943f3e3", - "reference": "dad0228156856b3ad959992f9748514fa943f3e3", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/442fc2c34edcd5198b442d8647c7f0aec3afabe8", + "reference": "442fc2c34edcd5198b442d8647c7f0aec3afabe8", "shasum": "" }, "require": { "composer/xdebug-handler": "^1.0 || ^2.0 || ^3.0", "ext-xml": "*", - "pdepend/pdepend": "^2.12.1", + "pdepend/pdepend": "^2.15.1", "php": ">=5.3.9" }, "require-dev": { @@ -3994,7 +3992,7 @@ "gregwar/rst": "^1.0", "mikey179/vfsstream": "^1.6.8", "phpunit/phpunit": "^4.8.36 || ^5.7.27", - "squizlabs/php_codesniffer": "^2.0" + "squizlabs/php_codesniffer": "^2.9.2 || ^3.7.2" }, "bin": [ "src/bin/phpmd" @@ -4031,6 +4029,7 @@ "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", "homepage": "https://phpmd.org/", "keywords": [ + "dev", "mess detection", "mess detector", "pdepend", @@ -4040,7 +4039,7 @@ "support": { "irc": "irc://irc.freenode.org/phpmd", "issues": "https://github.com/phpmd/phpmd/issues", - "source": "https://github.com/phpmd/phpmd/tree/2.13.0" + "source": "https://github.com/phpmd/phpmd/tree/2.14.1" }, "funding": [ { @@ -4048,20 +4047,20 @@ "type": "tidelift" } ], - "time": "2022-09-10T08:44:15+00:00" + "time": "2023-09-28T13:07:44+00:00" }, { "name": "phpstan/phpstan", - "version": "1.10.35", + "version": "1.10.38", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "e730e5facb75ffe09dfb229795e8c01a459f26c3" + "reference": "5302bb402c57f00fb3c2c015bac86e0827e4b691" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e730e5facb75ffe09dfb229795e8c01a459f26c3", - "reference": "e730e5facb75ffe09dfb229795e8c01a459f26c3", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/5302bb402c57f00fb3c2c015bac86e0827e4b691", + "reference": "5302bb402c57f00fb3c2c015bac86e0827e4b691", "shasum": "" }, "require": { @@ -4110,20 +4109,20 @@ "type": "tidelift" } ], - "time": "2023-09-19T15:27:56+00:00" + "time": "2023-10-06T14:19:14+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "10.1.6", + "version": "10.1.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "56f33548fe522c8d82da7ff3824b42829d324364" + "reference": "355324ca4980b8916c18b9db29f3ef484078f26e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/56f33548fe522c8d82da7ff3824b42829d324364", - "reference": "56f33548fe522c8d82da7ff3824b42829d324364", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/355324ca4980b8916c18b9db29f3ef484078f26e", + "reference": "355324ca4980b8916c18b9db29f3ef484078f26e", "shasum": "" }, "require": { @@ -4180,7 +4179,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.6" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.7" }, "funding": [ { @@ -4188,7 +4187,7 @@ "type": "github" } ], - "time": "2023-09-19T04:59:03+00:00" + "time": "2023-10-04T15:34:17+00:00" }, { "name": "phpunit/php-file-iterator", @@ -4435,16 +4434,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.3.5", + "version": "10.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503" + "reference": "62bd7af13d282deeb95650077d28ba3600ca321c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/747c3b2038f1139e3dcd9886a3f5a948648b7503", - "reference": "747c3b2038f1139e3dcd9886a3f5a948648b7503", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/62bd7af13d282deeb95650077d28ba3600ca321c", + "reference": "62bd7af13d282deeb95650077d28ba3600ca321c", "shasum": "" }, "require": { @@ -4484,7 +4483,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.3-dev" + "dev-main": "10.4-dev" } }, "autoload": { @@ -4516,7 +4515,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.5" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.4.1" }, "funding": [ { @@ -4532,7 +4531,7 @@ "type": "tidelift" } ], - "time": "2023-09-19T05:42:37+00:00" + "time": "2023-10-08T05:01:11+00:00" }, { "name": "psr/log", @@ -4830,16 +4829,16 @@ }, { "name": "sebastian/complexity", - "version": "3.0.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "c70b73893e10757af9c6a48929fa6a333b56a97a" + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c70b73893e10757af9c6a48929fa6a333b56a97a", - "reference": "c70b73893e10757af9c6a48929fa6a333b56a97a", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", "shasum": "" }, "require": { @@ -4852,7 +4851,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.1-dev" } }, "autoload": { @@ -4876,7 +4875,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" }, "funding": [ { @@ -4884,7 +4883,7 @@ "type": "github" } ], - "time": "2023-08-31T09:55:53+00:00" + "time": "2023-09-28T11:50:59+00:00" }, { "name": "sebastian/diff", @@ -5019,16 +5018,16 @@ }, { "name": "sebastian/exporter", - "version": "5.1.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "c3fa8483f9539b190f7cd4bfc4a07631dd1df344" + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c3fa8483f9539b190f7cd4bfc4a07631dd1df344", - "reference": "c3fa8483f9539b190f7cd4bfc4a07631dd1df344", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc", + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc", "shasum": "" }, "require": { @@ -5042,7 +5041,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -5085,7 +5084,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.0" + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1" }, "funding": [ { @@ -5093,7 +5092,7 @@ "type": "github" } ], - "time": "2023-09-18T07:15:37+00:00" + "time": "2023-09-24T13:22:09+00:00" }, { "name": "sebastian/global-state", @@ -5856,16 +5855,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v6.3.4", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "68a5a9570806a087982f383f6109c5e925892a49" + "reference": "2ed62b3bf98346e1f45529a7b6be2196739bb993" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/68a5a9570806a087982f383f6109c5e925892a49", - "reference": "68a5a9570806a087982f383f6109c5e925892a49", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/2ed62b3bf98346e1f45529a7b6be2196739bb993", + "reference": "2ed62b3bf98346e1f45529a7b6be2196739bb993", "shasum": "" }, "require": { @@ -5917,7 +5916,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.3.4" + "source": "https://github.com/symfony/dependency-injection/tree/v6.3.5" }, "funding": [ { @@ -5933,7 +5932,7 @@ "type": "tidelift" } ], - "time": "2023-08-16T17:55:17+00:00" + "time": "2023-09-25T16:46:40+00:00" }, { "name": "symfony/dom-crawler", @@ -6223,16 +6222,16 @@ }, { "name": "symfony/http-client", - "version": "v6.3.2", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00" + "reference": "213e564da4cbf61acc9728d97e666bcdb868c10d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00", - "reference": "15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00", + "url": "https://api.github.com/repos/symfony/http-client/zipball/213e564da4cbf61acc9728d97e666bcdb868c10d", + "reference": "213e564da4cbf61acc9728d97e666bcdb868c10d", "shasum": "" }, "require": { @@ -6295,7 +6294,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.3.2" + "source": "https://github.com/symfony/http-client/tree/v6.3.5" }, "funding": [ { @@ -6311,7 +6310,7 @@ "type": "tidelift" } ], - "time": "2023-07-05T08:41:27+00:00" + "time": "2023-09-29T15:57:12+00:00" }, { "name": "symfony/http-client-contracts", @@ -6393,16 +6392,16 @@ }, { "name": "symfony/mime", - "version": "v6.3.3", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "9a0cbd52baa5ba5a5b1f0cacc59466f194730f98" + "reference": "d5179eedf1cb2946dbd760475ebf05c251ef6a6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/9a0cbd52baa5ba5a5b1f0cacc59466f194730f98", - "reference": "9a0cbd52baa5ba5a5b1f0cacc59466f194730f98", + "url": "https://api.github.com/repos/symfony/mime/zipball/d5179eedf1cb2946dbd760475ebf05c251ef6a6e", + "reference": "d5179eedf1cb2946dbd760475ebf05c251ef6a6e", "shasum": "" }, "require": { @@ -6457,7 +6456,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.3.3" + "source": "https://github.com/symfony/mime/tree/v6.3.5" }, "funding": [ { @@ -6473,7 +6472,7 @@ "type": "tidelift" } ], - "time": "2023-07-31T07:08:24+00:00" + "time": "2023-09-29T06:59:36+00:00" }, { "name": "symfony/polyfill-ctype", @@ -7052,16 +7051,16 @@ }, { "name": "symfony/string", - "version": "v6.3.2", + "version": "v6.3.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "53d1a83225002635bca3482fcbf963001313fb68" + "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/53d1a83225002635bca3482fcbf963001313fb68", - "reference": "53d1a83225002635bca3482fcbf963001313fb68", + "url": "https://api.github.com/repos/symfony/string/zipball/13d76d0fb049051ed12a04bef4f9de8715bea339", + "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339", "shasum": "" }, "require": { @@ -7118,7 +7117,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.3.2" + "source": "https://github.com/symfony/string/tree/v6.3.5" }, "funding": [ { @@ -7134,7 +7133,7 @@ "type": "tidelift" } ], - "time": "2023-07-05T08:41:27+00:00" + "time": "2023-09-18T10:38:32+00:00" }, { "name": "symfony/translation", @@ -7506,19 +7505,11 @@ "time": "2021-07-28T10:34:58+00:00" } ], - "aliases": [ - { - "package": "phpgt/database", - "version": "dev-dynamic-sets", - "alias": "v1.5.0", - "alias_normalized": "1.5.0.0" - } - ], + "aliases": [], "minimum-stability": "stable", "stability-flags": { "superhyperinstantfuturetime/spotify-api": 20, "phpgt/webengine": 20, - "phpgt/database": 20, "pdepend/pdepend": 20 }, "prefer-stable": false, diff --git a/query/Cost/create.sql b/query/Cost/create.sql index 45bb9f1..c306607 100644 --- a/query/Cost/create.sql +++ b/query/Cost/create.sql @@ -1,12 +1,14 @@ insert into Cost( id, productId, + userId, description, amount ) values ( :id, :productId, + :userId, :description, :amount ) diff --git a/query/Cost/getAllForUser.sql b/query/Cost/getAllForUser.sql index 8b7dce3..f4ba1d3 100644 --- a/query/Cost/getAllForUser.sql +++ b/query/Cost/getAllForUser.sql @@ -1,6 +1,7 @@ select distinct Cost.id, Cost.productId, + Cost.userId, Cost.description, Cost.amount @@ -17,22 +18,8 @@ inner join on Artist.id = Product.artistId -inner join - UsageOfProduct -on - UsageOfProduct.productId = Product.id - -inner join - Usage -on - Usage.id = UsageOfProduct.usageId - -inner join - Upload -on - Upload.id = Usage.uploadId -and - Upload.userId = ? +where + Cost.userId = ? order by Artist.name, Product.title diff --git a/query/_migration/010-drop-cost.sql b/query/_migration/010-drop-cost.sql new file mode 100644 index 0000000..8c3dc49 --- /dev/null +++ b/query/_migration/010-drop-cost.sql @@ -0,0 +1 @@ +drop table Cost; diff --git a/query/_migration/011-cost-user.sql b/query/_migration/011-cost-user.sql new file mode 100644 index 0000000..fe61a46 --- /dev/null +++ b/query/_migration/011-cost-user.sql @@ -0,0 +1,13 @@ +create table Cost +( + id text not null + primary key, + productId text not null + references Product + on update cascade on delete cascade, + userId text not null + constraint Cost_User_id_fk + references User, + description text, + amount decimal(10, 6) not null +); diff --git a/test/behat/costs.feature b/test/behat/costs.feature index 322344a..ca0b927 100644 --- a/test/behat/costs.feature +++ b/test/behat/costs.feature @@ -9,5 +9,21 @@ Feature: TrackShift should break down costs of products And I press "Upload" When I go to "/account/costs/" Then I should see 0 rows in the table - When I go to "/account/products/" - Then I should see "Add Costs" + + Scenario: A product can have a cost associated to it + Given I am on the homepage + And I attach the file "bandcamp-simple-3-songs.csv" to "upload[]" + And I press "Upload" + When I go to "/account/costs/" + And I follow "Add cost" + And I select "Person 1" from "artist" + And I press "Set artist" + And I select "BC 2" from "product" + And I fill in "description" with "Vinyl pressing" + And I fill in "amount" with "100.00" + And I press "Create" + Then I should be on "/account/costs/" + And I should see the following table data: + | Artist | Product | Cost | Description | + | Person 1 | BC 2 | £100.00 | Vinyl pressing | + From 879f74fb65f529f0628d2de987c904a7a68f64e3 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 11 Oct 2023 15:48:04 +0100 Subject: [PATCH 03/12] test: assert unknown uploads --- class/Upload/UnknownUpload.php | 4 ++-- class/Upload/UploadManager.php | 6 +----- test/phpunit/Upload/BandcampUploadTest.php | 2 +- test/phpunit/Upload/UnknownUploadTest.php | 25 ++++++++++++++++++++++ 4 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 test/phpunit/Upload/UnknownUploadTest.php diff --git a/class/Upload/UnknownUpload.php b/class/Upload/UnknownUpload.php index f921b3e..0b237cf 100644 --- a/class/Upload/UnknownUpload.php +++ b/class/Upload/UnknownUpload.php @@ -8,12 +8,12 @@ protected function processUsages():void {} // phpcs:ignore public function extractArtistName(array $row):string { - return ""; + return "Unknown"; } // phpcs:ignore public function extractProductTitle(array $row):string { - return ""; + return "Unknown"; } // phpcs:ignore diff --git a/class/Upload/UploadManager.php b/class/Upload/UploadManager.php index b11af09..6980106 100644 --- a/class/Upload/UploadManager.php +++ b/class/Upload/UploadManager.php @@ -22,9 +22,7 @@ use SHIFT\Trackshift\Usage\Usage; use SplFileObject; -/** - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) - */ +/** @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ readonly class UploadManager extends Repository { public function __construct( QueryCollection $db, @@ -405,6 +403,4 @@ private function rowToProduct(?Row $row, ?Artist $artist):?Product { $artist // ?? $this->getArtistById($row->getString("artistId") ); } - - } diff --git a/test/phpunit/Upload/BandcampUploadTest.php b/test/phpunit/Upload/BandcampUploadTest.php index 8bd9797..a247663 100644 --- a/test/phpunit/Upload/BandcampUploadTest.php +++ b/test/phpunit/Upload/BandcampUploadTest.php @@ -27,7 +27,7 @@ public function testExtractEarning():void { self::assertSame(6.22, $earning->value); } - public function testGetXYZ_bindGetters():void { + public function testGetCreatedUploadedDate_bindGetters():void { $filePath = "test/files/bandcamp-simple-3-songs.csv"; $sut = new BandcampUpload("test-id", $filePath); $actualCreationTime = new DateTime("@" . filectime($filePath)); diff --git a/test/phpunit/Upload/UnknownUploadTest.php b/test/phpunit/Upload/UnknownUploadTest.php new file mode 100644 index 0000000..c0bfe93 --- /dev/null +++ b/test/phpunit/Upload/UnknownUploadTest.php @@ -0,0 +1,25 @@ +generateDataRows()); + self::assertSame("Unknown", $sut->extractArtistName($dataRows)); + } + + public function testExtractProductTitle():void { + $sut = new UnknownUpload("test-id", "test/files/gubbins.txt"); + $dataRows = iterator_to_array($sut->generateDataRows()); + self::assertSame("Unknown", $sut->extractProductTitle($dataRows)); + } + + public function testExtractEarning():void { + $sut = new UnknownUpload("test-id", "test/files/gubbins.txt"); + $dataRows = iterator_to_array($sut->generateDataRows()); + self::assertSame(0.0, $sut->extractEarning($dataRows)->value); + } +} From ddbf1e95f5b09008ca9bcb23c0f0934526d47b67 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Wed, 11 Oct 2023 19:26:10 +0100 Subject: [PATCH 04/12] feature: notification when file is not understood for #62 --- behat.yml | 3 +- class/Audit/AuditItem.php | 3 +- class/Audit/AuditRepository.php | 45 +++++++++++++++++++++ class/Auth/UserRepository.php | 34 ++++++++++++++++ class/ServiceLoader.php | 1 + class/Upload/UploadManager.php | 8 +++- composer.json | 2 +- composer.lock | 32 ++++++++++----- page/_common.php | 8 ++++ page/_component/audit-list.html | 2 +- page/_component/file-uploader.html | 1 + page/_component/global-header.html | 2 +- page/account/audit.php | 1 + query/Audit/getLatest.sql | 13 ++++++ query/Audit/insertNotification.sql | 16 ++++++++ query/User/getById.sql | 9 +++++ query/User/getLastNotificationCheckTime.sql | 7 ++++ query/User/setNotificationCheckedAt.sql | 6 +++ style/component/audit-list.scss | 6 +++ style/component/global-header.scss | 6 ++- test/behat/Context/AnotherContext.php | 7 ---- test/behat/Context/DebugContext.php | 13 ++++++ test/behat/Context/FeatureContext.php | 6 --- test/behat/Context/NotificationContext.php | 21 ++++++++++ test/behat/Context/ServerContext.php | 25 ++++++++---- test/behat/Context/UploadContext.php | 1 - test/behat/upload-unknown.feature | 11 +++++ 27 files changed, 249 insertions(+), 40 deletions(-) create mode 100644 query/Audit/getLatest.sql create mode 100644 query/Audit/insertNotification.sql create mode 100644 query/User/getById.sql create mode 100644 query/User/getLastNotificationCheckTime.sql create mode 100644 query/User/setNotificationCheckedAt.sql delete mode 100644 test/behat/Context/AnotherContext.php create mode 100644 test/behat/Context/DebugContext.php create mode 100644 test/behat/Context/NotificationContext.php create mode 100644 test/behat/upload-unknown.feature diff --git a/behat.yml b/behat.yml index 17717b5..3479a82 100644 --- a/behat.yml +++ b/behat.yml @@ -14,7 +14,8 @@ default: - \SHIFT\Trackshift\BehatContext\AuthContext: - \SHIFT\Trackshift\BehatContext\PageContext: - \SHIFT\Trackshift\BehatContext\UploadContext: - - \SHIFT\Trackshift\BehatContext\AnotherContext: + - \SHIFT\Trackshift\BehatContext\NotificationContext: + - \SHIFT\Trackshift\BehatContext\DebugContext: extensions: Behat\MinkExtension: diff --git a/class/Audit/AuditItem.php b/class/Audit/AuditItem.php index 5b99956..f739b89 100644 --- a/class/Audit/AuditItem.php +++ b/class/Audit/AuditItem.php @@ -31,7 +31,8 @@ public function getHtml():string { "create" => "Created new $typeName ($descriptionOrId)", "update" => "Updated $typeName ($descriptionOrId)", "delete" => "Deleted $typeName ($descriptionOrId)", - default => "Something happened...", + "notification" => $descriptionOrId, + default => "Unhandled audit type ($this->type)", }; } diff --git a/class/Audit/AuditRepository.php b/class/Audit/AuditRepository.php index d9fecdc..fead111 100644 --- a/class/Audit/AuditRepository.php +++ b/class/Audit/AuditRepository.php @@ -1,12 +1,22 @@ db->insert("insertCreation", [ "id" => new Ulid("audit"), @@ -16,6 +26,16 @@ public function create(User $user, string $newId, ?string $description = null):v ]); } + public function notify(User $user, string $description, ?string $idInQuestion = null):void { + $this->db->insert("insertNotification", [ + "id" => new Ulid("audit"), + "userId" => $user->id, + "description" => $description, + "valueId" => $idInQuestion, + ]); + } + + public function delete(User $user, string $deletedId, ?string $description = null):void { $this->db->insert("insertDeletion", [ "id" => new Ulid("audit"), @@ -39,11 +59,20 @@ public function getAll(User $user):array { return $auditItemArray; } + public function getLatest(User $user):null|AuditItem|NotificationItem { + $row = $this->db->fetch("getLatest", $user->id); + return $this->rowToAuditItem($row, $user); + } + private function rowToAuditItem(?Row $row, ?User $user = null):?AuditItem { if(!$row) { return null; } + if(!$user) { + $user = $this->userRepository->getById($row->getString("userId")); + } + return new AuditItem( $row->getString("id"), $user, @@ -85,6 +114,21 @@ public function update( } } + public function checkNotifications(User $user):void { + $this->userRepository->setNotificationCheckTime($user); + } + + public function isNewNotification(User $user):bool { + $timeLatest = new DateTime(); + $timeChecked = $this->userRepository->getLatestNotificationCheckTime($user); + + if($latestNotification = $this->getLatest($user)) { + $timeLatest->setTimestamp((new Ulid(init: $latestNotification->id))->getTimestamp() / 1000); + } + + return $timeLatest > $timeChecked; + } + /** @return array key = property name, value = "$oldValue -> $newValue" */ private function getDiff(object $from, object $to):array { $fromVars = get_object_vars($from); @@ -102,4 +146,5 @@ private function getDiff(object $from, object $to):array { } + } diff --git a/class/Auth/UserRepository.php b/class/Auth/UserRepository.php index 01ecb84..95be832 100644 --- a/class/Auth/UserRepository.php +++ b/class/Auth/UserRepository.php @@ -1,7 +1,10 @@ session->getInstance(self::SESSION_USER, User::class); } + public function getById(string $id):?User { + $row = $this->db->fetch("getById", $id); + return $this->rowToUser($row); + } + public function createNewUser():User { $user = new User(new Ulid("user")); $this->db->insert("create", $user->id); @@ -30,4 +38,30 @@ public function createNewUser():User { public function persistUser(User $user):void { $this->session->set(self::SESSION_USER, $user); } + + public function setNotificationCheckTime(User $user, DateTime $when = null):void { + if(is_null($when)) { + $when = new DateTime(); + } + + $this->db->update("setNotificationCheckedAt", [ + "userId" => $user->id, + "checkedAt" => $when->getTimestamp(), + ]); + } + + public function getLatestNotificationCheckTime(User $user):?DateTimeInterface { + return $this->db->fetchDateTime("getLastNotificationCheckTime", $user->id); + } + + private function rowToUser(?Row $row):?User { + if(!$row) { + return null; + } + + return new User($row->getString("id")); + } + + + } diff --git a/class/ServiceLoader.php b/class/ServiceLoader.php index e85822b..fc4122c 100644 --- a/class/ServiceLoader.php +++ b/class/ServiceLoader.php @@ -19,6 +19,7 @@ public function loadAuditRepo():AuditRepository { $database = $this->container->get(Database::class); return new AuditRepository( $database->queryCollection("Audit"), + $this->container->get(UserRepository::class), ); } diff --git a/class/Upload/UploadManager.php b/class/Upload/UploadManager.php index 6980106..e33d8b5 100644 --- a/class/Upload/UploadManager.php +++ b/class/Upload/UploadManager.php @@ -65,7 +65,13 @@ public function upload(User $user, FileUpload...$uploadList):array { "type" => $upload::class, ]); - $this->auditRepository->create($user, $upload->id, $upload->filename); + if($upload instanceof UnknownUpload) { + $this->auditRepository->notify($user, "Your latest upload was not processed ($upload->filename)", $upload->id); + } + else { + $this->auditRepository->create($user, $upload->id, $upload->filename); + } + array_push($completedUploadList, $upload); } diff --git a/composer.json b/composer.json index 7c1a29b..915ccaa 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "php": ">=8.2", "superhyperinstantfuturetime/spotify-api": "dev-master", "phpgt/webengine": "dev-master", - "phpgt/database": "^1.4", + "phpgt/database": "dev-datetime-from-timestamp as v1.4.0", "league/commonmark": "^2.4" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 0e9190b..b393b94 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": "ef74b7b8821c46eed8e091e04cbc03f3", + "content-hash": "10c3717ed54750783a8e0eb20a717a4b", "packages": [ { "name": "composer/semver", @@ -1089,28 +1089,30 @@ }, { "name": "phpgt/database", - "version": "v1.4.0", + "version": "dev-datetime-from-timestamp", "source": { "type": "git", "url": "https://github.com/PhpGt/Database.git", - "reference": "db929a74e7e57f8424027c8fe685b37c6459db86" + "reference": "9b38262a359a2154d15c1bfb859126600e2a2402" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PhpGt/Database/zipball/db929a74e7e57f8424027c8fe685b37c6459db86", - "reference": "db929a74e7e57f8424027c8fe685b37c6459db86", + "url": "https://api.github.com/repos/PhpGt/Database/zipball/9b38262a359a2154d15c1bfb859126600e2a2402", + "reference": "9b38262a359a2154d15c1bfb859126600e2a2402", "shasum": "" }, "require": { "ext-pdo": "*", - "php": ">=7.4", + "php": ">=8.1", "phpgt/cli": "^1.3", "phpgt/config": "^v1.1.0" }, "require-dev": { "ext-sqlite3": "*", - "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^9.4" + "phpmd/phpmd": "^2.13", + "phpstan/phpstan": "^v1.10", + "phpunit/phpunit": "^10.1", + "squizlabs/php_codesniffer": "^3.7" }, "bin": [ "bin/migrate" @@ -1142,7 +1144,7 @@ "description": "Database API organisation.", "support": { "issues": "https://github.com/PhpGt/Database/issues", - "source": "https://github.com/PhpGt/Database/tree/v1.4.0" + "source": "https://github.com/PhpGt/Database/tree/datetime-from-timestamp" }, "funding": [ { @@ -1150,7 +1152,7 @@ "type": "github" } ], - "time": "2021-09-23T09:07:04+00:00" + "time": "2023-10-11T18:08:12+00:00" }, { "name": "phpgt/dataobject", @@ -7505,11 +7507,19 @@ "time": "2021-07-28T10:34:58+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "phpgt/database", + "version": "dev-datetime-from-timestamp", + "alias": "v1.4.0", + "alias_normalized": "1.4.0.0" + } + ], "minimum-stability": "stable", "stability-flags": { "superhyperinstantfuturetime/spotify-api": 20, "phpgt/webengine": 20, + "phpgt/database": 20, "pdepend/pdepend": 20 }, "prefer-stable": false, diff --git a/page/_common.php b/page/_common.php index 441b832..35fc1b2 100644 --- a/page/_common.php +++ b/page/_common.php @@ -3,7 +3,9 @@ use Gt\Dom\Element; use Gt\Dom\HTMLDocument; use Gt\DomTemplate\DocumentBinder; +use SHIFT\Trackshift\Audit\AuditRepository; use SHIFT\Trackshift\Auth\User; +use SHIFT\Trackshift\Auth\UserRepository; use SHIFT\Trackshift\Content\ContentRepository; use SHIFT\Trackshift\Egg\UploadMessageList; use SHIFT\Trackshift\Upload\UploadManager; @@ -30,3 +32,9 @@ function go( $binder->bindList(new UploadMessageList(3), $fileUploader); } } + +function go_after(User $user, AuditRepository $auditRepository, HTMLDocument $document):void { + if($user && $auditRepository->isNewNotification($user)) { + $document->querySelector("global-header .bell")->classList->add("notify"); + } +} diff --git a/page/_component/audit-list.html b/page/_component/audit-list.html index 07496b1..d9a6bd4 100644 --- a/page/_component/audit-list.html +++ b/page/_component/audit-list.html @@ -1,5 +1,5 @@
    -
  • +
  • Something happened
  • diff --git a/page/_component/file-uploader.html b/page/_component/file-uploader.html index d7cd6e1..ae66a8a 100644 --- a/page/_component/file-uploader.html +++ b/page/_component/file-uploader.html @@ -8,6 +8,7 @@ Upload sales report

    + diff --git a/page/_component/global-header.html b/page/_component/global-header.html index 460dba2..8e9792f 100644 --- a/page/_component/global-header.html +++ b/page/_component/global-header.html @@ -3,7 +3,7 @@

    TrackShift