|
11 | 11 | from guardian.shortcuts import assign
|
12 | 12 | from taggit.managers import TaggableManager
|
13 | 13 |
|
14 |
| -from readthedocs.privacy.loader import VersionManager, RelatedProjectManager |
| 14 | +from readthedocs.privacy.loader import (VersionManager, RelatedProjectManager, |
| 15 | + RelatedBuildManager) |
15 | 16 | from readthedocs.projects.models import Project
|
16 |
| -from readthedocs.projects import constants |
17 |
| -from .constants import (BUILD_STATE, BUILD_TYPES, VERSION_TYPES, |
18 |
| - LATEST, NON_REPOSITORY_VERSIONS, STABLE |
19 |
| - ) |
| 17 | +from readthedocs.projects.constants import (PRIVACY_CHOICES, REPO_TYPE_GIT, |
| 18 | + REPO_TYPE_HG) |
20 | 19 |
|
| 20 | +from .constants import (BUILD_STATE, BUILD_TYPES, VERSION_TYPES, |
| 21 | + LATEST, NON_REPOSITORY_VERSIONS, STABLE, |
| 22 | + BUILD_STATE_FINISHED) |
21 | 23 | from .version_slug import VersionSlugField
|
22 | 24 |
|
23 | 25 |
|
@@ -70,7 +72,7 @@ class Version(models.Model):
|
70 | 72 | built = models.BooleanField(_('Built'), default=False)
|
71 | 73 | uploaded = models.BooleanField(_('Uploaded'), default=False)
|
72 | 74 | privacy_level = models.CharField(
|
73 |
| - _('Privacy Level'), max_length=20, choices=constants.PRIVACY_CHOICES, |
| 75 | + _('Privacy Level'), max_length=20, choices=PRIVACY_CHOICES, |
74 | 76 | default=DEFAULT_VERSION_PRIVACY_LEVEL, help_text=_("Level of privacy for this Version.")
|
75 | 77 | )
|
76 | 78 | tags = TaggableManager(blank=True)
|
@@ -368,4 +370,54 @@ def get_absolute_url(self):
|
368 | 370 | @property
|
369 | 371 | def finished(self):
|
370 | 372 | '''Return if build has a finished state'''
|
371 |
| - return self.state == 'finished' |
| 373 | + return self.state == BUILD_STATE_FINISHED |
| 374 | + |
| 375 | + |
| 376 | +class BuildCommandResultMixin(object): |
| 377 | + '''Mixin for common command result methods/properties |
| 378 | +
|
| 379 | + Shared methods between the database model :py:cls:`BuildCommandResult` and |
| 380 | + non-model respresentations of build command results from the API |
| 381 | + ''' |
| 382 | + |
| 383 | + @property |
| 384 | + def successful(self): |
| 385 | + '''Did the command exit with a successful exit code''' |
| 386 | + return self.exit_code == 0 |
| 387 | + |
| 388 | + @property |
| 389 | + def failed(self): |
| 390 | + '''Did the command exit with a failing exit code |
| 391 | +
|
| 392 | + Helper for inverse of :py:meth:`successful`''' |
| 393 | + return not self.successful |
| 394 | + |
| 395 | + |
| 396 | +class BuildCommandResult(BuildCommandResultMixin, models.Model): |
| 397 | + build = models.ForeignKey(Build, verbose_name=_('Build'), |
| 398 | + related_name='commands') |
| 399 | + |
| 400 | + command = models.TextField(_('Command')) |
| 401 | + description = models.TextField(_('Description'), blank=True) |
| 402 | + output = models.TextField(_('Command output'), blank=True) |
| 403 | + exit_code = models.IntegerField(_('Command exit code')) |
| 404 | + |
| 405 | + start_time = models.DateTimeField(_('Start time')) |
| 406 | + end_time = models.DateTimeField(_('End time')) |
| 407 | + |
| 408 | + class Meta: |
| 409 | + ordering = ['start_time'] |
| 410 | + get_latest_by = 'start_time' |
| 411 | + |
| 412 | + objects = RelatedBuildManager() |
| 413 | + |
| 414 | + def __unicode__(self): |
| 415 | + return (ugettext(u'Build command {pk} for build {build}') |
| 416 | + .format(pk=self.pk, build=self.build)) |
| 417 | + |
| 418 | + @property |
| 419 | + def run_time(self): |
| 420 | + """Total command runtime in seconds""" |
| 421 | + if self.start_time is not None and self.end_time is not None: |
| 422 | + diff = self.end_time - self.start_time |
| 423 | + return diff.seconds |
0 commit comments