Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add average round duration #17

Merged
merged 33 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2104dde
Only parse categories when needed
nicoSWD Apr 10, 2024
a458547
Remove redundant PHP action
nicoSWD Apr 10, 2024
b4f5fd0
Rename classes and add tests
nicoSWD Apr 10, 2024
bbf5187
Remove poster property as they're no longer a thing
nicoSWD Apr 10, 2024
0827108
Use YouTube video duration to calculate end time
nicoSWD Apr 10, 2024
71a2688
Cleanup
nicoSWD Apr 10, 2024
be140ba
Add YouTube video duration
nicoSWD Apr 10, 2024
96d3d8e
Add provisional status
nicoSWD Apr 10, 2024
85563ae
Add tests
nicoSWD Apr 10, 2024
4b44059
Rename class
nicoSWD Apr 10, 2024
11212b0
Add source dir
nicoSWD Apr 10, 2024
c460752
Add calendar diff to PR
nicoSWD Apr 11, 2024
a5b0108
Add calendar diff to PR
nicoSWD Apr 11, 2024
3b09bda
Add calendar diff to PR
nicoSWD Apr 11, 2024
b034a8a
Fix event start date
nicoSWD Apr 11, 2024
e97f789
Add 5 minutes to YouTube scheduled start time
nicoSWD Apr 11, 2024
35e499d
Cleanup
nicoSWD Apr 11, 2024
b94ba26
Add scrutinizer config
nicoSWD Apr 11, 2024
469d6a1
Fix spelling
nicoSWD Apr 11, 2024
69bddcf
Improve diff
nicoSWD Apr 11, 2024
f18d6d2
Improve diff
nicoSWD Apr 11, 2024
beac99d
Remove redundant clone
nicoSWD Apr 11, 2024
2419631
Fix alarms, add speed qualifications to iCal calendar
nicoSWD Apr 12, 2024
0b28af1
Add proper average round duration
nicoSWD Apr 12, 2024
69a739d
Merge branch 'main' into avg_round_duration
nicoSWD Apr 12, 2024
2785df7
Fix tests
nicoSWD Apr 12, 2024
b7cc1b8
Removed unused constant
nicoSWD Apr 12, 2024
bdd2cae
Always use YT duration
nicoSWD Apr 12, 2024
648546d
Fix start time issue and add tests
nicoSWD Apr 12, 2024
51f11fa
Add missing category tags and improve matching
nicoSWD Apr 13, 2024
238575b
Guess end time base on YouTube's start time if times mismatch
nicoSWD Apr 13, 2024
1c585c1
Fix name
nicoSWD Apr 13, 2024
2d99596
Small refactor
nicoSWD Apr 13, 2024
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
54 changes: 54 additions & 0 deletions bin/avg-round-duration
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env php
<?php declare(strict_types=1);

use nicoSWD\IfscCalendar\Domain\Event\IFSCEventTagsRegex as Tag;
use nicoSWD\IfscCalendar\Domain\Tags\IFSCTagsParser;

require __DIR__ . '/../vendor/autoload.php';

$videos = json_decode(
file_get_contents(__DIR__ . '/../vendor/sportclimbing/ifsc-youtube-videos/data/videos.json')
);

$tagsParser = new IFSCTagsParser();

function cache_key(array $tags): string
{
$key = [];

foreach ($tags as $tag) {
$key[] = $tag->name;
}

sort($key);

return strtolower(implode('_', $key));
}

$videoDurations = [];

foreach ($videos as $video) {
$tags = $tagsParser->fromString($video->title);

if (!$tags->hasTag(Tag::WOMEN) && !$tags->hasTag(Tag::MEN)) {
$videoTags = [Tag::WOMEN, Tag::MEN, ...$tags->allTags()];
} else {
$videoTags = $tags->allTags();
}

$videoDurations[cache_key($videoTags)][] = $video->duration;
}

$results = [];

foreach ($videoDurations as $key => $minutes) {
$averageDuration = (int) floor(array_sum($minutes) / count($minutes));
$averageDuration -= ($averageDuration % 30);

if ($averageDuration > 30) {
$results[$key] = $averageDuration;
}
}

ksort($results);
var_export($results);
4 changes: 4 additions & 0 deletions config/services/domain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,15 @@ services:
- '@nicoSWD\IfscCalendar\Domain\Round\IFSCRoundFactory'
- '@nicoSWD\IfscCalendar\Infrastructure\Round\InfoSheetRoundProvider'

nicoSWD\IfscCalendar\Domain\Round\IFSCAverageRoundDuration:
class: nicoSWD\IfscCalendar\Domain\Round\IFSCAverageRoundDuration

nicoSWD\IfscCalendar\Domain\Round\IFSCRoundFactory:
class: nicoSWD\IfscCalendar\Domain\Round\IFSCRoundFactory
arguments:
- '@nicoSWD\IfscCalendar\Domain\Tags\IFSCTagsParser'
- '@nicoSWD\IfscCalendar\Domain\YouTube\YouTubeLiveStreamFinder'
- '@nicoSWD\IfscCalendar\Domain\Round\IFSCAverageRoundDuration'

nicoSWD\IfscCalendar\Domain\Round\IFSCRoundNameNormalizer:
class: nicoSWD\IfscCalendar\Domain\Round\IFSCRoundNameNormalizer
Expand Down
77 changes: 77 additions & 0 deletions src/Domain/Round/IFSCAverageRoundDuration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php declare(strict_types=1);

/**
* @license http://opensource.org/licenses/mit-license.php MIT
* @link https://github.com/nicoSWD
* @author Nicolas Oelgart <nico@oelgart.com>
*/
namespace nicoSWD\IfscCalendar\Domain\Round;

use nicoSWD\IfscCalendar\Domain\Event\IFSCEventTagsRegex as Tag;

final readonly class IFSCAverageRoundDuration
{
private const int DEFAULT_ROUND_DURATION = 90;

// Run `bin/avg-round-duration` to generate an updated list
private const array AVERAGE_DURATIONS = [
'boulder_combined_men_qualification' => 120,
'boulder_combined_men_qualification_women' => 120,
'boulder_combined_qualification_women' => 120,
'boulder_final_lead_men' => 180,
'boulder_final_lead_men_semi_final_women' => 360,
'boulder_final_lead_men_women' => 240,
'boulder_final_lead_women' => 180,
'boulder_final_men' => 120,
'boulder_final_men_paraclimbing' => 150,
'boulder_final_men_women' => 180,
'boulder_final_women' => 120,
'boulder_lead_men_semi_final' => 180,
'boulder_lead_men_semi_final_women' => 120,
'boulder_lead_semi_final_women' => 90,
'boulder_men_semi_final' => 120,
'boulder_men_semi_final_women' => 120,
'boulder_semi_final_women' => 120,
'combined_final_men' => 240,
'combined_final_men_women' => 420,
'combined_final_women' => 240,
'combined_lead_men_qualification' => 90,
'combined_lead_men_qualification_women' => 90,
'combined_lead_qualification_women' => 120,
'combined_men_qualification_speed_women' => 60,
'final_lead_men' => 60,
'final_lead_men_women' => 120,
'final_lead_women' => 120,
'final_men_paraclimbing_women' => 210,
'final_men_speed_women' => 90,
'final_speed_women' => 60,
'lead_men_semi_final' => 120,
'lead_men_semi_final_women' => 150,
'lead_semi_final_women' => 210,
'men_paraclimbing_women' => 120,
'men_qualification_speed' => 60,
'men_qualification_speed_women' => 90,
];

/** @param Tag[] $tags */
public function fromTags(array $tags): int
{
return self::AVERAGE_DURATIONS[$this->lookupKey($tags)]
?? self::DEFAULT_ROUND_DURATION;
}

/** @param Tag[] $tags */
private function lookupKey(array $tags): string
{
$tags = array_map(
static fn (Tag $tag): string => $tag->name,
$tags,
);

sort($tags);

return strtolower(
implode('_', $tags),
);
}
}
52 changes: 38 additions & 14 deletions src/Domain/Round/IFSCRoundFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,50 @@
use nicoSWD\IfscCalendar\Domain\Stream\LiveStream;
use nicoSWD\IfscCalendar\Domain\Tags\IFSCParsedTags;
use nicoSWD\IfscCalendar\Domain\Tags\IFSCTagsParser;
use nicoSWD\IfscCalendar\Domain\YouTube\YouTubeLiveStreamFinder;
use nicoSWD\IfscCalendar\Domain\YouTube\YouTubeLiveStreamFinderInterface;

final readonly class IFSCRoundFactory
{
private const int DEFAULT_ROUND_DURATION = 90;

public function __construct(
private IFSCTagsParser $tagsParser,
private YouTubeLiveStreamFinder $liveStreamFinder,
private YouTubeLiveStreamFinderInterface $liveStreamFinder,
private IFSCAverageRoundDuration $averageRoundDuration,
) {
}

public function create(
IFSCEventInfo $event,
string $roundName,
DateTimeImmutable $startTime,
DateTimeImmutable $endTime,
?DateTimeImmutable $endTime,
IFSCRoundStatus $status,
): IFSCRound {
$tags = $this->getTags($roundName);
$liveStream = $this->findLiveStream($event, $roundName);

if ($liveStream->scheduledStartTime) {
$startTime = $this->buildStartTime($liveStream, $event);
$endTime = $this->buildEndTime($startTime, $liveStream);
$youTubeStartTime = $this->buildStartTime($liveStream, $event);

if ($endTime && !$this->startTimeMatchesYouTubes($startTime, $liveStream)) {
$diff = $startTime->diff($endTime);
$endTime = $youTubeStartTime->add($diff);
}

$startTime = $youTubeStartTime;

if ($liveStream->duration > 0) {
$endTime = $startTime->modify(
sprintf('+%d minutes', $liveStream->duration),
);
}

$status = IFSCRoundStatus::CONFIRMED;
}

if (!$endTime) {
$endTime = $this->calcEndTime($startTime, $tags);
}

return new IFSCRound(
name: $roundName,
categories: $tags->getCategories(),
Expand Down Expand Up @@ -80,16 +96,24 @@ private function findLiveStream(IFSCEventInfo $event, string $roundName): LiveSt
return $this->liveStreamFinder->findLiveStream($event, $roundName);
}

private function buildEndTime(DateTimeImmutable $startTime, LiveStream $liveStream): DateTimeImmutable
private function averageRoundDuration(IFSCParsedTags $tags): int
{
if ($liveStream->duration > 0) {
$duration = $liveStream->duration;
} else {
$duration = self::DEFAULT_ROUND_DURATION;
}
return $this->averageRoundDuration->fromTags($tags->allTags());
}

private function startTimeMatchesYouTubes(DateTimeImmutable $startTime, LiveStream $liveStream): bool
{
$diff = $startTime->diff($liveStream->scheduledStartTime);

return
$diff->i <= 5 &&
($diff->y + $diff->m + $diff->d + $diff->h) === 0;
}

private function calcEndTime(DateTimeImmutable $startTime, IFSCParsedTags $tags): DateTimeImmutable
{
return $startTime->modify(
sprintf('+%d minutes', $duration)
sprintf('+%d minutes', $this->averageRoundDuration($tags)),
);
}
}
2 changes: 1 addition & 1 deletion src/Domain/Schedule/IFSCSchedule.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
public function __construct(
public string $name,
public DateTimeImmutable $startsAt,
public DateTimeImmutable $endsAt,
public ?DateTimeImmutable $endsAt,
public bool $isPreRound,
) {
}
Expand Down
2 changes: 1 addition & 1 deletion src/Domain/Schedule/IFSCScheduleFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function __construct(
public function create(
string $name,
DateTimeImmutable $startsAt,
DateTimeImmutable $endsAt,
?DateTimeImmutable $endsAt,
): IFSCSchedule {
$tags = $this->tagsParser->fromString($name);

Expand Down
2 changes: 1 addition & 1 deletion src/Domain/Tags/IFSCParsedTags.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public function isPreRound(): bool
return $this->hasTag(Tag::PRE_ROUND);
}

private function hasTag(Tag $tag): bool
public function hasTag(Tag $tag): bool
{
return in_array($tag, $this->tags, strict: true);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Domain/YouTube/YouTubeLiveStreamFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use nicoSWD\IfscCalendar\Domain\Event\Info\IFSCEventInfo;
use nicoSWD\IfscCalendar\Domain\Stream\LiveStream;

final readonly class YouTubeLiveStreamFinder
final readonly class YouTubeLiveStreamFinder implements YouTubeLiveStreamFinderInterface
{
public function __construct(
private YouTubeLinkMatcher $linkMatcher,
Expand Down
16 changes: 16 additions & 0 deletions src/Domain/YouTube/YouTubeLiveStreamFinderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

/**
* @license http://opensource.org/licenses/mit-license.php MIT
* @link https://github.com/nicoSWD
* @author Nicolas Oelgart <nico@oelgart.com>
*/
namespace nicoSWD\IfscCalendar\Domain\YouTube;

use nicoSWD\IfscCalendar\Domain\Event\Info\IFSCEventInfo;
use nicoSWD\IfscCalendar\Domain\Stream\LiveStream;

interface YouTubeLiveStreamFinderInterface
{
public function findLiveStream(IFSCEventInfo $event, string $roundName): LiveStream;
}
6 changes: 3 additions & 3 deletions src/Infrastructure/Schedule/InfoSheetScheduleProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ private function parseDaySchedule(string $schedule, DateTimeZone $timeZone): Ite
$startsAt = $this->createStartDate($dayName, $match['start_time'][$key], $timeZone);
}

$endsAt = $this->createEndDate($dayName, $match['end_time'][$key] ?? null, $startsAt, $timeZone);
$endsAt = $this->createEndDate($dayName, $match['end_time'][$key] ?? null, $timeZone);
$lastStart = $startsAt;

$schedule = $this->scheduleFactory->create(
Expand Down Expand Up @@ -113,13 +113,13 @@ private function createStartDate(string $day, string $time, DateTimeZone $timeZo
);
}

private function createEndDate(string $dayName, ?string $time, DateTimeImmutable $startsAt, DateTimeZone $timeZone): DateTimeImmutable
private function createEndDate(string $dayName, ?string $time, DateTimeZone $timeZone): ?DateTimeImmutable
{
if ($time !== null && trim($time) !== '') {
return $this->createStartDate($dayName, $time, $timeZone);
}

return $startsAt->modify('+2 hours');
return null;
}

/** @return string[] */
Expand Down
Loading