Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/vendor/
/data/
data/cache
data/upload
/ts.db
/config.*.ini
/www/
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.
Binary file modified asset/.DS_Store
Binary file not shown.
3 changes: 2 additions & 1 deletion behat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
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
51 changes: 51 additions & 0 deletions 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 @@ -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
9 changes: 9 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,11 @@ 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"),
);
}
}
9 changes: 9 additions & 0 deletions class/Split/EmptySplitPercentage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php
namespace SHIFT\Trackshift\Split;

use SHIFT\Trackshift\Split\SplitPercentage;

readonly class EmptySplitPercentage extends SplitPercentage {
/** @noinspection PhpMissingParentConstructorInspection */
public function __construct() {}
}
10 changes: 10 additions & 0 deletions class/Split/RemainderSplitPercentage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
namespace SHIFT\Trackshift\Split;

use SHIFT\Trackshift\Split\EmptySplitPercentage;

readonly class RemainderSplitPercentage extends EmptySplitPercentage {
public function __construct() {

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

use SHIFT\Trackshift\Auth\User;
use SHIFT\Trackshift\Product\Product;
use SHIFT\Trackshift\Repository\Entity;

readonly class Split extends Entity {
public function __construct(
public string $id,
public User $user,
public Product $product,
) {}
}
14 changes: 14 additions & 0 deletions class/Split/SplitPercentage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
namespace SHIFT\Trackshift\Split;

use SHIFT\Trackshift\Repository\Entity;

readonly class SplitPercentage extends Entity {
public function __construct(
public string $id,
public Split $split,
public string $owner,
public float $percentage,
public string $email,
) {}
}
13 changes: 13 additions & 0 deletions class/Split/SplitRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
namespace SHIFT\Trackshift\Split;

use SHIFT\Trackshift\Auth\User;
use SHIFT\Trackshift\Repository\Repository;

readonly class SplitRepository extends Repository {
/** @return array<SplitPercentage> */
public function getPercentageList(User $user, string $productId):array {
return [];
}

}
4 changes: 2 additions & 2 deletions class/Upload/UnknownUpload.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 8 additions & 6 deletions class/Upload/UploadManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -67,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);
}

Expand Down Expand Up @@ -405,6 +409,4 @@ private function rowToProduct(?Row $row, ?Artist $artist):?Product {
$artist // ?? $this->getArtistById($row->getString("artistId")
);
}


}
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "dev-318-semicolon as v1.4.0",
"league/commonmark": "^2.4"
},
"require-dev": {
Expand Down
Loading