Skip to content

Commit

Permalink
WIP: SLC 3 (#115)
Browse files Browse the repository at this point in the history
* docs: move setup guide to wiki

* fix: associate cost to user rather than product

* test: assert unknown uploads

* feature: notification when file is not understood
for #62

* tweak: don't notify for new audits

* wip: split editor UI

* ui: split editor ui

* wip: split classes

* wip: split percentage ui complete

* wip: split percentage ui complete

* test: update qa

* asset: plus minus icons

* feature: splits and profits in product table
closes #105
closes #101

* style: splits list + editor

* bug fix: profit sum

* style: splits list border and margin

* test: assertions for profit/balance

* test: test profit calculations

* ci: wip behat on github actions

---------

Co-authored-by: rjbirkin <[email protected]>
  • Loading branch information
g105b and richardbirkin authored Nov 10, 2023
1 parent 25e8cef commit 1eac6ba
Show file tree
Hide file tree
Showing 86 changed files with 1,610 additions and 215 deletions.
24 changes: 23 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,29 @@ jobs:
path: class/
standard: phpcs.xml

# dev-deploy:
# behat:
# runs-on: ubuntu-latest
# needs: [ composer ]
# strategy:
# matrix:
# php: [ 8.2 ]
#
# steps:
# - uses: actions/download-artifact@v3
# with:
# name: build-artifact
# path: /tmp/github-actions
#
# - name: Extract build archive
# run: tar -xvf /tmp/github-actions/build.tar ./
#
# - name: Behat
# uses: php-actions/behat@v1
# with:
# php_version: ${{ matrix.php }}


# dev-deploy:
# runs-on: ubuntu-latest
# needs: [ composer, phpunit, phpstan, phpcs, phpmd ]
#
Expand Down
31 changes: 0 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions asset/icon/minus-solid.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions asset/icon/plus-solid.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion behat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ default:
- \SHIFT\Trackshift\BehatContext\AuthContext:
- \SHIFT\Trackshift\BehatContext\PageContext:
- \SHIFT\Trackshift\BehatContext\UploadContext:
- \SHIFT\Trackshift\BehatContext\AnotherContext:
- \SHIFT\Trackshift\BehatContext\ProductContext:
- \SHIFT\Trackshift\BehatContext\SplitContext:
- \SHIFT\Trackshift\BehatContext\NotificationContext:
- \SHIFT\Trackshift\BehatContext\DebugContext:

extensions:
Behat\MinkExtension:
Expand Down
2 changes: 1 addition & 1 deletion class/Artist/ArtistRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use SHIFT\Trackshift\Repository\Repository;

readonly class ArtistRepository extends Repository {
/** return array<Artist> */
/** @return array<Artist> */
public function getAll(User $user):array {
$artistArray = [];

Expand Down
3 changes: 2 additions & 1 deletion class/Audit/AuditItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)",
};
}

Expand Down
53 changes: 52 additions & 1 deletion class/Audit/AuditRepository.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
<?php
namespace SHIFT\Trackshift\Audit;

use DateTime;
use Gt\Database\Query\QueryCollection;
use Gt\Database\Result\Row;
use Gt\Ulid\Ulid;
use SHIFT\Trackshift\Auth\User;
use SHIFT\Trackshift\Auth\UserRepository;
use SHIFT\Trackshift\Repository\Repository;

readonly class AuditRepository extends Repository {
public function __construct(
QueryCollection $db,
private UserRepository $userRepository,
) {
parent::__construct($db);
}

public function create(User $user, string $newId, ?string $description = null):void {
$this->db->insert("insertCreation", [
"id" => new Ulid("audit"),
Expand All @@ -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"),
Expand All @@ -25,7 +45,7 @@ public function delete(User $user, string $deletedId, ?string $description = nul
]);
}

/** @return array<AuditItem,NotificationItem> */
/** @return array<AuditItem|NotificationItem> */
public function getAll(User $user):array {
$auditItemArray = [];

Expand All @@ -39,11 +59,25 @@ public function getAll(User $user):array {
return $auditItemArray;
}

public function getLatest(User $user, bool $notificationOnly = false):null|AuditItem|NotificationItem {
if($notificationOnly) {
$row = $this->db->fetch("getLatestNotification", $user->id);
}
else {
$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,
Expand Down Expand Up @@ -85,6 +119,22 @@ public function update(
}
}

public function checkNotifications(User $user):void {
$this->userRepository->setNotificationCheckTime($user);
}

public function isNewNotification(User $user):bool {
$timeLatestNotification = null;
$timeLastChecked = $this->userRepository->getLatestNotificationCheckTime($user);

if($latestNotification = $this->getLatest($user, true)) {
$timeLatestNotification = new DateTime();
$timeLatestNotification->setTimestamp((new Ulid(init: $latestNotification->id))->getTimestamp() / 1000);
}

return $timeLatestNotification && $timeLatestNotification > $timeLastChecked;
}

/** @return array<string, string> key = property name, value = "$oldValue -> $newValue" */
private function getDiff(object $from, object $to):array {
$fromVars = get_object_vars($from);
Expand All @@ -102,4 +152,5 @@ private function getDiff(object $from, object $to):array {
}



}
34 changes: 34 additions & 0 deletions class/Auth/UserRepository.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<?php
namespace SHIFT\Trackshift\Auth;

use DateTime;
use DateTimeInterface;
use Gt\Database\Query\QueryCollection;
use Gt\Database\Result\Row;
use Gt\Session\SessionStore;
use Gt\Ulid\Ulid;
use SHIFT\Trackshift\Repository\Repository;
Expand All @@ -21,6 +24,11 @@ public function getLoggedInUser():?User {
return $this->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);
Expand All @@ -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"));
}



}
1 change: 1 addition & 0 deletions class/Cost/CostRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
]);
Expand Down
11 changes: 11 additions & 0 deletions class/Product/ProductEarning.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public function __construct(
public Product $product,
public Money $earning,
public Money $cost,
private Money $outgoing,
public Money $profit,
) {}

public function getArtistName():string {
Expand All @@ -30,4 +32,13 @@ public function getBalance():?string {

return null;
}

#[BindGetter]
public function getOutgoing():?string {
if($this->outgoing->value) {
return $this->outgoing;
}

return null;
}
}
15 changes: 14 additions & 1 deletion class/Product/ProductRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,30 @@ public function getProductEarnings(User $user):array {
$artist = new Artist($row->getString("artistId"), $row->getString("artistName"));
$product = new Product($row->getString("productId"), $row->getString("title"), $artist);
$earning = new Money($row->getFloat("totalEarning"));
$cost = new Money(0);
$cost = new Money();
if($costValue = $row->getFloat("totalCost")) {
$cost = new Money($costValue);
}

$balance = $earning->withSubtraction($cost);

$outgoing = new Money();
if($outgoingPercentage = $row->getFloat("percentageOutgoing")) {
$outgoingValue = ($outgoingPercentage / 100) * $balance->value;
$outgoing = new Money(round($outgoingValue, 2));
}

$profit = $balance->withSubtraction($outgoing);

array_push(
$earningList,
new ProductEarning(
$user,
$product,
$earning,
$cost,
$outgoing,
$profit,
)
);
}
Expand All @@ -54,6 +66,7 @@ public function getById(string $id):Product {
);
}

/** @return array<Product> */
public function getForArtist(string|Artist $artist):array {
$artist = is_string($artist) ? $this->artistRepository->getById($artist) : $artist;

Expand Down
3 changes: 3 additions & 0 deletions class/Repository/Entity.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<?php
namespace SHIFT\Trackshift\Repository;

/**
* @property string $id
*/
abstract readonly class Entity {
}
12 changes: 12 additions & 0 deletions class/ServiceLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
use SHIFT\Trackshift\Content\ContentRepository;
use SHIFT\Trackshift\Cost\CostRepository;
use SHIFT\Trackshift\Product\ProductRepository;
use SHIFT\Trackshift\Split\SplitRepository;
use SHIFT\Trackshift\Upload\UploadManager;

class ServiceLoader extends DefaultServiceLoader {
public function loadAuditRepo():AuditRepository {
$database = $this->container->get(Database::class);
return new AuditRepository(
$database->queryCollection("Audit"),
$this->container->get(UserRepository::class),
);
}

Expand Down Expand Up @@ -89,4 +91,14 @@ public function loadCostRepository():CostRepository {
$this->container->get(AuditRepository::class),
);
}

public function loadSplitRepository():SplitRepository {
$database = $this->container->get(Database::class);

return new SplitRepository(
$database->queryCollection("Split"),
$this->container->get(UserRepository::class),
$this->container->get(ProductRepository::class),
);
}
}
11 changes: 11 additions & 0 deletions class/Split/EmptySplitPercentage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php
namespace SHIFT\Trackshift\Split;

use Gt\DomTemplate\Bind;
use SHIFT\Trackshift\Repository\Entity;

readonly class EmptySplitPercentage extends Entity {
public function __construct(
public string $productId,
) {}
}
Loading

0 comments on commit 1eac6ba

Please sign in to comment.