- Add copy button to the build logs
- Add support for timestamps in the build logs - Other small UI improvements to the build view
This commit is contained in:
parent
e227d7e526
commit
ed46d37ea7
12 changed files with 189 additions and 18 deletions
|
@ -4,7 +4,13 @@
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
box-shadow: inset 10px 10px 5px -9px rgba(0,0,0,0.75);
|
box-shadow: inset 10px 10px 5px -9px rgba(0,0,0,0.75);
|
||||||
background: linear-gradient(30deg, #263945, #061C2A) no-repeat left top fixed;
|
background-color: #263945;
|
||||||
|
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-logs-view .no-logs {
|
||||||
|
color: #8C8C8C;
|
||||||
}
|
}
|
||||||
|
|
||||||
.build-logs-view .container-header {
|
.build-logs-view .container-header {
|
||||||
|
@ -109,6 +115,22 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.build-logs-view .log-entry:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-logs-view .log-entry:hover .timestamp {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-logs-view .log-entry .timestamp {
|
||||||
|
float: right;
|
||||||
|
visibility: hidden;
|
||||||
|
color: #aaa;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.build-logs-view .log-entry .message {
|
.build-logs-view .log-entry .message {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 46px;
|
margin-left: 46px;
|
||||||
|
@ -117,12 +139,41 @@
|
||||||
.build-logs-view .log-entry .id {
|
.build-logs-view .log-entry .id {
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
padding-right: 6px;
|
padding-right: 6px;
|
||||||
margin-right: 6px;
|
margin-right: 10px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 4px;
|
top: 1px;
|
||||||
left: 4px;
|
left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.build-logs-view .copy-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
right: 6px;
|
||||||
|
z-index: 2;
|
||||||
|
transition: all 0.15s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-logs-view .copy-button:not(.zeroclipboard-is-hover) {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-logs-view .copy-button i.fa {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-logs-view-element.with-timestamps .log-entry .message {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 146px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.build-logs-view-element.with-timestamps .log-entry .id {
|
||||||
|
width: 140px;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
|
@ -3,9 +3,16 @@
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.triggered-build-description-element .commit-message a {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
.triggered-build-description-element .commit-who img {
|
.triggered-build-description-element .commit-who img {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: 2px;
|
||||||
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.triggered-build-description-element .fa-github {
|
.triggered-build-description-element .fa-github {
|
||||||
|
|
|
@ -36,6 +36,11 @@
|
||||||
color: #2fcc66;
|
color: #2fcc66;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.build-view .build-status-header .cor-options-menu {
|
||||||
|
float: right;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.build-view .build-status-header .timing {
|
.build-view .build-status-header .timing {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
<div class="build-logs-view-element">
|
<div class="build-logs-view-element" ng-class="useTimestamps ? 'with-timestamps' : ''">
|
||||||
|
<button id="copyButton" class="btn btn-primary copy-button" data-clipboard-text="{{ buildLogsText }}">
|
||||||
|
<i class="fa fa-clipboard"></i>Copy Logs
|
||||||
|
</button>
|
||||||
|
|
||||||
<span class="cor-loader" ng-if="!logEntries"></span>
|
<span class="cor-loader" ng-if="!logEntries"></span>
|
||||||
|
|
||||||
<span ng-if="!logEntries.length">(No Logs)</span>
|
<span class="no-logs" ng-if="!logEntries.length">(Waiting for build to start)</span>
|
||||||
|
|
||||||
<div class="log-container" ng-class="container.type" ng-repeat="container in logEntries">
|
<div class="log-container" ng-class="container.type" ng-repeat="container in logEntries">
|
||||||
<div class="container-header" ng-class="container.type == 'phase' ? container.message : ''"
|
<div class="container-header" ng-class="container.type == 'phase' ? container.message : ''"
|
||||||
ng-switch on="container.type" ng-click="container.logs.toggle()">
|
ng-switch on="container.type" ng-click="container.logs.toggle()">
|
||||||
<i class="fa chevron"
|
<i class="fa chevron"
|
||||||
ng-class="container.logs.isVisible ? 'fa-chevron-down' : 'fa-chevron-right'" ng-show="hasLogs(container)"></i>
|
ng-class="container.logs.isVisible ? 'fa-chevron-down' : 'fa-chevron-right'"
|
||||||
|
ng-show="hasLogs(container)"></i>
|
||||||
<div ng-switch-when="phase">
|
<div ng-switch-when="phase">
|
||||||
<span class="container-content build-log-phase" phase="container"></span>
|
<span class="container-content build-log-phase" phase="container"></span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -22,8 +27,10 @@
|
||||||
<!-- Display the entries for the container -->
|
<!-- Display the entries for the container -->
|
||||||
<div class="container-logs" ng-show="container.logs.isVisible">
|
<div class="container-logs" ng-show="container.logs.isVisible">
|
||||||
<div class="log-entry" bindonce ng-repeat="entry in container.logs.visibleEntries">
|
<div class="log-entry" bindonce ng-repeat="entry in container.logs.visibleEntries">
|
||||||
<span class="id" bo-text="$index + container.index + 1"></span>
|
<span class="id" bo-text="$index + container.index + 1" ng-if="!useTimestamps"></span>
|
||||||
|
<span class="id" bo-text="formatDatetime(entry.datetime)" ng-if="useTimestamps"></span>
|
||||||
<span class="message" bo-html="processANSI(entry.message, container)"></span>
|
<span class="message" bo-html="processANSI(entry.message, container)"></span>
|
||||||
|
<span class="timestamp" bo-text="formatDatetime(entry.datetime)" ng-if="!useTimestamps"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<span class="source-commit-link-elememt">
|
<span class="source-commit-link-elememt">
|
||||||
<i class="fa fa-dot-circle-o"></i>
|
<i class="fa fa-dot-circle-o" data-title="Commit" data-container="body" bs-tooltip></i>
|
||||||
<a ng-href="{{ getUrl(commitSha, urlTemplate) }}" target="_blank">{{ commitSha.substring(0, 8) }}</a>
|
<a ng-href="{{ getUrl(commitSha, urlTemplate) }}" target="_blank">{{ commitSha.substring(0, 8) }}</a>
|
||||||
</span>
|
</span>
|
|
@ -2,13 +2,13 @@
|
||||||
<span ng-switch on="getKind(ref)">
|
<span ng-switch on="getKind(ref)">
|
||||||
<!-- Branch -->
|
<!-- Branch -->
|
||||||
<span ng-switch-when="heads">
|
<span ng-switch-when="heads">
|
||||||
<i class="fa fa-code-fork" data-title="Branch" bs-tooltip></i>
|
<i class="fa fa-code-fork" data-container="body" data-title="Branch" bs-tooltip></i>
|
||||||
<a ng-href="{{ getUrl(ref, branchTemplate, 'branch') }}" target="_blank">{{ getTitle(ref) }}</a>
|
<a ng-href="{{ getUrl(ref, branchTemplate, 'branch') }}" target="_blank">{{ getTitle(ref) }}</a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- Tag -->
|
<!-- Tag -->
|
||||||
<span ng-switch-when="tags">
|
<span ng-switch-when="tags">
|
||||||
<i class="fa fa-tag" data-title="Tag" bs-tooltip></i>
|
<i class="fa fa-tag" data-container="body" data-title="Tag" bs-tooltip></i>
|
||||||
<a ng-href="{{ getUrl(ref, tagTemplate, 'tag') }}" target="_blank">{{ getTitle(ref) }}</a>
|
<a ng-href="{{ getUrl(ref, tagTemplate, 'tag') }}" target="_blank">{{ getTitle(ref) }}</a>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -4,9 +4,15 @@
|
||||||
<span ng-switch-when="github">
|
<span ng-switch-when="github">
|
||||||
<!-- Full Commit Information -->
|
<!-- Full Commit Information -->
|
||||||
<span ng-if="build.job_config.trigger_metadata.commit_info">
|
<span ng-if="build.job_config.trigger_metadata.commit_info">
|
||||||
<div class="commit-message">{{ build.job_config.trigger_metadata.commit_info.message }}</div>
|
<div class="commit-message">
|
||||||
|
<a ng-href="{{ getGitHubRepoURL(build) }}/commit/{{ build.job_config.trigger_metadata.commit_sha }}"
|
||||||
|
target="_blank">
|
||||||
|
{{ build.job_config.trigger_metadata.commit_info.message }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
<div class="commit-information">
|
<div class="commit-information">
|
||||||
<span class="commit-who-when">
|
<span class="commit-who-when">
|
||||||
|
Authored
|
||||||
<span am-time-ago="build.job_config.trigger_metadata.commit_info.date"></span>
|
<span am-time-ago="build.job_config.trigger_metadata.commit_info.date"></span>
|
||||||
<span class="commit-who">
|
<span class="commit-who">
|
||||||
<img ng-src="{{ build.job_config.trigger_metadata.commit_info.author.avatar_url }}">
|
<img ng-src="{{ build.job_config.trigger_metadata.commit_info.author.avatar_url }}">
|
||||||
|
@ -27,7 +33,7 @@
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- Just commit SHA -->
|
<!-- Just commit SHA -->
|
||||||
<span ng-if="!build.job_config.trigger_metadata.commit_info">
|
<span ng-if="build.job_config.trigger_metadata && !build.job_config.trigger_metadata.commit_info">
|
||||||
Triggered by commit
|
Triggered by commit
|
||||||
<span class="source-commit-link"
|
<span class="source-commit-link"
|
||||||
commit-sha="build.job_config.trigger_metadata.commit_sha"
|
commit-sha="build.job_config.trigger_metadata.commit_sha"
|
||||||
|
|
|
@ -10,18 +10,35 @@ angular.module('quay').directive('buildLogsView', function () {
|
||||||
restrict: 'C',
|
restrict: 'C',
|
||||||
scope: {
|
scope: {
|
||||||
'build': '=build',
|
'build': '=build',
|
||||||
|
'useTimestamps': '=useTimestamps',
|
||||||
'buildUpdated': '&buildUpdated'
|
'buildUpdated': '&buildUpdated'
|
||||||
},
|
},
|
||||||
controller: function($scope, $element, $interval, $sanitize, ansi2html, AngularViewArray,
|
controller: function($scope, $element, $interval, $sanitize, ansi2html, AngularViewArray,
|
||||||
AngularPollChannel, ApiService, Restangular) {
|
AngularPollChannel, ApiService, Restangular) {
|
||||||
|
|
||||||
|
var result = $element.find('#copyButton').clipboardCopy();
|
||||||
|
if (!result) {
|
||||||
|
$element.find('#copyButton').hide();
|
||||||
|
}
|
||||||
|
|
||||||
$scope.logEntries = null;
|
$scope.logEntries = null;
|
||||||
$scope.currentParentEntry = null;
|
$scope.currentParentEntry = null;
|
||||||
$scope.logStartIndex = 0;
|
$scope.logStartIndex = 0;
|
||||||
|
$scope.buildLogsText = '';
|
||||||
|
|
||||||
var pollChannel = null;
|
var pollChannel = null;
|
||||||
var currentBuild = null;
|
var currentBuild = null;
|
||||||
|
|
||||||
|
var appendToTextLog = function(type, message) {
|
||||||
|
if (type == 'phase') {
|
||||||
|
text = 'Starting phase: ' + message + '\n';
|
||||||
|
} else {
|
||||||
|
text = message + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.buildLogsText += text.replace(new RegExp("\\033\\[[^m]+m"), '');
|
||||||
|
};
|
||||||
|
|
||||||
var processLogs = function(logs, startIndex, endIndex) {
|
var processLogs = function(logs, startIndex, endIndex) {
|
||||||
if (!$scope.logEntries) { $scope.logEntries = []; }
|
if (!$scope.logEntries) { $scope.logEntries = []; }
|
||||||
|
|
||||||
|
@ -43,6 +60,8 @@ angular.module('quay').directive('buildLogsView', function () {
|
||||||
} else if ($scope.currentParentEntry) {
|
} else if ($scope.currentParentEntry) {
|
||||||
$scope.currentParentEntry['logs'].push(entry);
|
$scope.currentParentEntry['logs'].push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appendToTextLog(type, entry['message']);
|
||||||
}
|
}
|
||||||
|
|
||||||
return endIndex;
|
return endIndex;
|
||||||
|
@ -112,6 +131,11 @@ angular.module('quay').directive('buildLogsView', function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.$watch('useTimestamps', function() {
|
||||||
|
if (!$scope.logEntries) { return; }
|
||||||
|
$scope.logEntries = $scope.logEntries.slice();
|
||||||
|
});
|
||||||
|
|
||||||
$scope.$watch('build', function(build) {
|
$scope.$watch('build', function(build) {
|
||||||
if (build) {
|
if (build) {
|
||||||
startWatching(build);
|
startWatching(build);
|
||||||
|
@ -124,6 +148,11 @@ angular.module('quay').directive('buildLogsView', function () {
|
||||||
return container.logs.hasEntries;
|
return container.logs.hasEntries;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.formatDatetime = function(datetimeString) {
|
||||||
|
var dt = new Date(datetimeString);
|
||||||
|
return dt.toLocaleString();
|
||||||
|
}
|
||||||
|
|
||||||
$scope.processANSI = function(message, container) {
|
$scope.processANSI = function(message, container) {
|
||||||
var filter = container.logs._filter = (container.logs._filter || ansi2html.create());
|
var filter = container.logs._filter = (container.logs._filter || ansi2html.create());
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,9 @@ $.fn.clipboardCopy = function() {
|
||||||
ZeroClipboard.on('aftercopy', function(e) {
|
ZeroClipboard.on('aftercopy', function(e) {
|
||||||
var container = e.target.parentNode.parentNode.parentNode;
|
var container = e.target.parentNode.parentNode.parentNode;
|
||||||
var message = $(container).find('.clipboard-copied-message')[0];
|
var message = $(container).find('.clipboard-copied-message')[0];
|
||||||
|
if (!message) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Resets the animation.
|
// Resets the animation.
|
||||||
var elem = message;
|
var elem = message;
|
||||||
|
|
|
@ -8,11 +8,13 @@
|
||||||
});
|
});
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
function BuildViewCtrl($scope, ApiService, $routeParams, AngularPollChannel) {
|
function BuildViewCtrl($scope, ApiService, $routeParams, AngularPollChannel, CookieService) {
|
||||||
$scope.namespace = $routeParams.namespace;
|
$scope.namespace = $routeParams.namespace;
|
||||||
$scope.name = $routeParams.name;
|
$scope.name = $routeParams.name;
|
||||||
$scope.build_uuid = $routeParams.buildid;
|
$scope.build_uuid = $routeParams.buildid;
|
||||||
|
|
||||||
|
$scope.showLogTimestamps = CookieService.get('quay.showBuildLogTimestamps') == 'true';
|
||||||
|
|
||||||
var loadBuild = function() {
|
var loadBuild = function() {
|
||||||
var params = {
|
var params = {
|
||||||
'repository': $scope.namespace + '/' + $scope.name,
|
'repository': $scope.namespace + '/' + $scope.name,
|
||||||
|
@ -39,6 +41,26 @@
|
||||||
loadRepository();
|
loadRepository();
|
||||||
loadBuild();
|
loadBuild();
|
||||||
|
|
||||||
|
$scope.askCancelBuild = function(build) {
|
||||||
|
bootbox.confirm('Are you sure you want to cancel this build?', function(r) {
|
||||||
|
if (r) {
|
||||||
|
var params = {
|
||||||
|
'repository': $scope.namespace + '/' + $scope.name,
|
||||||
|
'build_uuid': build.id
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.cancelRepoBuild(null, params).then(function() {
|
||||||
|
document.location = '/repository/' + $scope.namespace + '/' + $scope.name;
|
||||||
|
}, ApiService.errorDisplay('Cannot cancel build'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.toggleTimestamps = function() {
|
||||||
|
$scope.showLogTimestamps = !$scope.showLogTimestamps;
|
||||||
|
CookieService.putPermanent('quay.showBuildLogTimestamps', $scope.showLogTimestamps);
|
||||||
|
};
|
||||||
|
|
||||||
$scope.setUpdatedBuild = function(build) {
|
$scope.setUpdatedBuild = function(build) {
|
||||||
$scope.build = build;
|
$scope.build = build;
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,6 +31,21 @@
|
||||||
<span class="build-message" phase="build.phase"></span>
|
<span class="build-message" phase="build.phase"></span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span class="cor-options-menu">
|
||||||
|
<span class="cor-option" option-click="toggleTimestamps()">
|
||||||
|
<span ng-if="showLogTimestamps">
|
||||||
|
<i class="fa fa-clock-o"></i> Hide Timestamps
|
||||||
|
</span>
|
||||||
|
<span ng-if="!showLogTimestamps">
|
||||||
|
<i class="fa fa-clock-o"></i> Show Timestamps
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="cor-option" option-click="askCancelBuild(build)"
|
||||||
|
ng-if="build.phase == 'waiting'">
|
||||||
|
<i class="fa fa-times"></i> Cancel Build
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
<div class="timing">
|
<div class="timing">
|
||||||
<i class="fa fa-clock-o"></i>
|
<i class="fa fa-clock-o"></i>
|
||||||
Build started
|
Build started
|
||||||
|
@ -39,7 +54,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Build Logs -->
|
<!-- Build Logs -->
|
||||||
<div class="build-logs-view" build="originalBuild"
|
<div class="build-logs-view"
|
||||||
|
build="originalBuild"
|
||||||
|
use-timestamps="showLogTimestamps"
|
||||||
build-updated="setUpdatedBuild(build)"></div>
|
build-updated="setUpdatedBuild(build)"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
import datetime
|
||||||
|
|
||||||
from random import SystemRandom
|
from random import SystemRandom
|
||||||
from functools import wraps, partial
|
from functools import wraps, partial
|
||||||
|
@ -108,7 +109,13 @@ class TestBuildLogs(RedisBuildLogs):
|
||||||
return script
|
return script
|
||||||
|
|
||||||
def _generate_phase(self, start_weight, phase_name):
|
def _generate_phase(self, start_weight, phase_name):
|
||||||
return (start_weight, {'message': phase_name, 'type': self.PHASE},
|
message = {
|
||||||
|
'message': phase_name,
|
||||||
|
'type': self.PHASE,
|
||||||
|
'datetime': str(datetime.datetime.now())
|
||||||
|
}
|
||||||
|
|
||||||
|
return (start_weight, message,
|
||||||
(phase_name, deepcopy(self.STATUS_TEMPLATE)))
|
(phase_name, deepcopy(self.STATUS_TEMPLATE)))
|
||||||
|
|
||||||
def _generate_command(self, command_num, total_commands, command_weight):
|
def _generate_command(self, command_num, total_commands, command_weight):
|
||||||
|
@ -123,6 +130,7 @@ class TestBuildLogs(RedisBuildLogs):
|
||||||
msg = {
|
msg = {
|
||||||
'message': 'Step %s: %s %s' % (command_num, command, sentence),
|
'message': 'Step %s: %s %s' % (command_num, command, sentence),
|
||||||
'type': self.COMMAND,
|
'type': self.COMMAND,
|
||||||
|
'datetime': str(datetime.datetime.now())
|
||||||
}
|
}
|
||||||
status = deepcopy(self.STATUS_TEMPLATE)
|
status = deepcopy(self.STATUS_TEMPLATE)
|
||||||
status['total_commands'] = total_commands
|
status['total_commands'] = total_commands
|
||||||
|
@ -133,10 +141,26 @@ class TestBuildLogs(RedisBuildLogs):
|
||||||
def _generate_logs(count):
|
def _generate_logs(count):
|
||||||
others = []
|
others = []
|
||||||
if random.randint(0, 10) <= 8:
|
if random.randint(0, 10) <= 8:
|
||||||
count = count - 2
|
premessage = {
|
||||||
others = [(1, {'message': '\x1b[91m' + get_sentence()}, None), (1, {'message': '\x1b[0m'}, None)]
|
'message': '\x1b[91m' + get_sentence(),
|
||||||
|
'datetime': str(datetime.datetime.now())
|
||||||
|
}
|
||||||
|
|
||||||
return others + [(1, {'message': get_sentence()}, None) for _ in range(count)]
|
postmessage = {
|
||||||
|
'message': '\x1b[0m',
|
||||||
|
'datetime': str(datetime.datetime.now())
|
||||||
|
}
|
||||||
|
|
||||||
|
count = count - 2
|
||||||
|
others = [(1, premessage, None), (1, postmessage, None)]
|
||||||
|
|
||||||
|
def get_message():
|
||||||
|
return {
|
||||||
|
'message': get_sentence(),
|
||||||
|
'datetime': str(datetime.datetime.now())
|
||||||
|
}
|
||||||
|
|
||||||
|
return others + [(1, get_message(), None) for _ in range(count)]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _compute_total_completion(statuses, total_images):
|
def _compute_total_completion(statuses, total_images):
|
||||||
|
|
Reference in a new issue