Skip to content

Commit

Permalink
Bug 1862684: Add relative weeks/days for train dates
Browse files Browse the repository at this point in the history
- Add Duration class to calculate date intervals and working days (exclude week-ends)
  - We ignore hours for all interval calculations
- Add tooltips on hover on dates of future releases view to indicate working days left
- Show text with the mention of working days left only for expanded items
- Clicking on the date now opens toggles the details tag of the row to display the working days
  • Loading branch information
pascalchevrel committed Nov 8, 2023
1 parent 9c6ee63 commit 4e25de7
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 20 deletions.
87 changes: 87 additions & 0 deletions app/classes/ReleaseInsights/Duration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

namespace ReleaseInsights;

use DateInterval, DatePeriod, DateTime;

/**
* Calculate durations in days and working days to indicate working days left in schedules
*/
class Duration
{
public function __construct(public readonly Datetime $start, public readonly Datetime $end)
{
}

/**
* Get The number of days between 2 dates
*/
public function days(): int
{
return $this->start->diff($this->end)->days;
}

/**
* Get The number of weeks between 2 dates
* We round down to 0.5
*/
public function weeks(): float
{
// We are limiting to 1 decimal for the week
$weeks = number_format($this->days()/7, 1);

// We want some logic to round down only the decimal to 0.5
[$main, $minor] = explode('.', (string) $weeks);
$minor = $minor >= 5 ? 5 : 0;

return (float) ($main . '.' . $minor);
}

/**
* Check if a day is a workday
*/
public function isWorkDay(DateTime $day): bool
{
/**
* We only substract week-ends, we may add more logic to
* also remove wellness days in the future.
*/
return ! in_array($day->format('l'), ['Saturday','Sunday']);
}

/**
* Get The number of working days between 2 dates
*/
public function workDays(): int
{
$count = 0;

// P1D is short for 'Period: 1 Day'
$range = new DatePeriod(start: $this->start, end: $this->end, interval: new DateInterval('P1D'));

foreach($range as $date){
if ($this->isWorkDay($date)) {
$count++;
}
}

// We substract 1 because we don't count the first day of the period
return $count - 1;
}

/**
* Return all the data in an array for template use
*
* @return array<string, float|int>
*/
public function report(): array
{
return [
'days' => $this->days(),
'workdays' => $this->workDays(),
'weeks' => $this->weeks(),
];
}
}
51 changes: 51 additions & 0 deletions app/classes/ReleaseInsights/Release.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,55 @@ public static function getNiceLabel(string $version, string $label, bool $short=

return $labels[$label];
}

/**
* Get The list of existing milestones per sub-cycle
*
* @return array<string, array<string>>
*/
public static function getMilestonesNames(): array
{
$nightly_milestones = [
'nightly_start',
'qa_request_deadline',
'qa_feature_done_1',
'qa_feature_done_2',
'soft_code_freeze',
'qa_pre_merge_done',
'string_freeze',
];
$beta_milestones = [
'merge_day',
'beta_1',
'beta_2',
'beta_3',
'sumo_1',
'beta_4',
'beta_5',
'beta_6',
'beta_7',
'sumo_2',
'beta_8',
'qa_pre_rc_signoff',
'beta_9',
'beta_10',
'beta_11',
'beta_12',
'beta_13',
'beta_14',
'beta_15',
];
$release_milestones = [
'rc_gtb',
'rc',
'release',
'planned_dot_release',
];

return [
'nightly' => $nightly_milestones,
'beta' => $beta_milestones,
'release' => $release_milestones,
];
}
}
31 changes: 17 additions & 14 deletions app/controllers/release.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use ReleaseInsights\{Data, ESR, Template, Utils, Version};

$requested_version = Version::get();
$requested_version_int = (int) Version::get();

if ($requested_version == '0.0') {
header('Location: /404/');
Expand All @@ -17,14 +18,14 @@

$template_data = [
'css_page_id' => 'release',
'page_title' => 'Milestones and key data for Firefox ' . (int) $requested_version,
'release' => (int) $requested_version,
'page_title' => 'Milestones and key data for Firefox ' . $requested_version_int,
'release' => $requested_version_int,
'release_owner' => $owners[$requested_version] ?? 'TBD',
'fallback_content' => '',
];

// Releases before version 4 were handled completely differently
if ((int) $requested_version < 4) {
if ($requested_version_int < 4) {
[$dot_release_count, $release_date] = require MODELS . 'pre4_release.php';
$template_data += ['dot_release_count' => $dot_release_count];
$template_data += ['release_date' => $release_date];
Expand All @@ -35,8 +36,8 @@
$template_data = array_merge(
$template_data,
[
'ESR' => ESR::getVersion((int) $requested_version),
'OLDER_ESR' => ESR::getOlderSupportedVersion((int) $requested_version),
'ESR' => ESR::getVersion($requested_version_int),
'OLDER_ESR' => ESR::getOlderSupportedVersion($requested_version_int),
]
);

Expand Down Expand Up @@ -64,7 +65,7 @@
}

// If this is a release we already shipped, display stats for the release
if ((int) $requested_version <= RELEASE) {
if ($requested_version_int <= RELEASE) {
[
$last_release_date,
$previous_release_date,
Expand Down Expand Up @@ -111,7 +112,7 @@
'firefox_releases' => $firefox_releases,
'no_planned_dot_releases' => $no_planned_dot_releases,
]);
} elseif ((int) $requested_version > RELEASE
} elseif ($requested_version_int > RELEASE
&& array_key_exists($requested_version, $upcoming_releases)) {
[
$release_date,
Expand All @@ -121,16 +122,18 @@
$nightly_updates,
$nightly_emergency,
$cycle_dates,
$deadlines,
] = require_once MODELS . 'future_release.php';
$template_file = 'future_release.html.twig';
$template_data = array_merge($template_data, [
'release_date' => $release_date,
'beta_cycle_length' => $beta_cycle_length,
'nightly_cycle_length' => $nightly_cycle_length,
'nightly_fixes' => $nightly_fixes,
'nightly_updates' => $nightly_updates,
'nightly_emergency' => $nightly_emergency,
'cycle_dates' => $cycle_dates,
'release_date' => $release_date,
'beta_cycle_length' => $beta_cycle_length,
'nightly_cycle_length' => $nightly_cycle_length,
'nightly_fixes' => $nightly_fixes,
'nightly_updates' => $nightly_updates,
'nightly_emergency' => $nightly_emergency,
'cycle_dates' => $cycle_dates,
'deadlines' => $deadlines,
]);
} else {
$template_file = 'future_release.html.twig';
Expand Down
41 changes: 40 additions & 1 deletion app/models/future_release.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

use ReleaseInsights\{Bugzilla, Data, Nightly, Release, Version};
use ReleaseInsights\{Bugzilla, Data, Duration, Nightly, Release, Utils, Version};

$requested_version = Version::get();

Expand Down Expand Up @@ -58,6 +58,44 @@
$nightly_emergency = Bugzilla::linkify($nightly_state->emergency_message);
}

$deadlines = [];
foreach ($cycle_dates as $k => $date) {
// Not a real milestone
if ($k === 'version') {
continue;
}

// let's ignore hours and only consider full days for simplicity
$reference_date = (new DateTime($date))->setTime(0, 0);
$time = new Duration(new DateTime(), $reference_date);
$deadlines[$k] = $time->report();

if ($reference_date>= new DateTime()) {
// Future
if ($time->report()['weeks'] >= 2) {
$deadlines[$k]['message'] = 'In ' . $time->report()['weeks'] . ' weeks';
} else {
$deadlines[$k]['message'] = match($time->report()['workdays']) {
0 => 'No working day before milestone',
1 => '1 working day',
default => 'In ' . $time->report()['workdays'] . ' working days',
};
}
} else {
// Past
if ($time->report()['weeks'] >= 2) {
$deadlines[$k]['message'] = $time->report()['weeks'] . ' weeks ago';
} else {
$deadlines[$k]['message'] = match($time->report()['days']) {
-1 => 'yesterday',
0 => 'Today',
1 => 'Yesterday',
default => $time->report()['days'] . ' days ago',
};
}
}
}

return [
$release_date,
$beta_cycle_length,
Expand All @@ -66,4 +104,5 @@
$nightly_updates,
$nightly_emergency,
$cycle_dates,
$deadlines,
];
2 changes: 1 addition & 1 deletion app/views/templates/base.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css" media="screen">
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap-icons.css" media="screen">
<link rel="stylesheet" href="/style/base.css?version=22" type="text/css" media="all" />
<link rel="stylesheet" href="/style/base.css?version=23" type="text/css" media="all" />
<link rel="icon" type="image/svg+xml" href="/assets/img/site_icon.svg" />
<link rel="apple-touch-icon" type="image/png" href="/assets/img/site_icon.png" />
<link rel="author" href="humans.txt" />
Expand Down
35 changes: 31 additions & 4 deletions app/views/templates/future_release.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,17 @@
{% set next_milestone = false %}
{% endif %}

<td title="{{ value|format_date('relative_full', locale='en') }}" {{ date(value) < date() ? ' class="text-muted fw-light"' }}>
{{ value|format_date(pattern='MMMM d', locale='en') }}
<td {{ date(value) < date() ? ' class="text-muted fw-light"' }}>
<span
class="schedule_date"
data-bs-toggle="tooltip"
data-bs-placement="left"
data-bs-html="true"
title="{{ value|format_date('relative_full', locale='en')}} <br><b> {{ deadlines[key]['message'] ?? '' }}"
>
{{ value|format_date(pattern='MMMM d', locale='en') }}</span>
<br><small class="fst-italic text-end">{{ deadlines[key]['message']}}</small>

</td>
</tr>
{% endif %}
Expand All @@ -296,9 +305,27 @@

{% endif %}
{{ fallback_content|raw }}

<script src="/assets/jquery/jquery.slim.min.js"></script>
<script src="/assets/bootstrap/js/bootstrap.bundle.min.js"></script>
{% endblock %}

{% block footer %}
{% include 'footer_UTC_warning.html.twig' %}
{% include 'footer_UTC_warning.html.twig' %}
<footer class="d-flex justify-content-center text-white-50 mt-0">Wording days exclude week-ends and the current day</footer>

<script nonce="4AEemGb0xJptoIGFP3Nd">
$(function () {
$('[data-bs-toggle="tooltip"]').tooltip()
})
$('span.schedule_date').click(function (){
var current_row = $(this).closest('tr');
var details_in_row = current_row.find('details');
if (details_in_row.attr('open')) {
details_in_row.removeAttr('open');
} else {
details_in_row.attr('open', 'open')
};
});
</script>
{% endblock %}
12 changes: 12 additions & 0 deletions public/style/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ h3 {
#release_esr .table td {
text-align: right;
white-space: nowrap;
width: 9em;
}

#release .table th details p {
Expand All @@ -132,6 +133,17 @@ h3 {
font-weight: normal;
}

/* hide working days when <details> tag not popen */
#release th:has(details) + td small {
display:none;
}

#release th:has(details[open]) + td small {
display: inline;
}




#release .table th summary::marker {
color: lightgray;
Expand Down
Loading

0 comments on commit 4e25de7

Please sign in to comment.