Repository: readthedocs/readthedocs.org

Issue:
Update build list to include an alert state
As raised in #4018, a better UI for a queue backup would be to show the affected builds in an alert state:

* Show an alert state on each hanging build in the build list if build.state == triggered and build.date > 5m ago. No need for redis here
* Alert state should be very minimal, a FA icon -- ie https://fontawesome.com/icons/exclamation-triangle?style=solid
* Perhaps mouse over on icon shows a brief message "This build is still waiting to be built"
* Link still goes to detail page
* There should be a warning box (using the standard notification pattern!) that says something along the lines of "This build has not started yet. This can be due to heavy build volume or to a problem with Read the Docs. If the problem persists, contact us"


Discussion:
Where should the warning box be on the list page or the detail page?

Resolving patch:
diff --git a/readthedocs/builds/models.py b/readthedocs/builds/models.py
--- a/readthedocs/builds/models.py
+++ b/readthedocs/builds/models.py
@@ -2,6 +2,7 @@
 
 """Models for the builds app."""
 
+import datetime
 import logging
 import os.path
 import re
@@ -32,6 +33,7 @@
     BRANCH,
     BUILD_STATE,
     BUILD_STATE_FINISHED,
+    BUILD_STATE_TRIGGERED,
     BUILD_TYPES,
     LATEST,
     NON_REPOSITORY_VERSIONS,
@@ -630,6 +632,12 @@ def finished(self):
         """Return if build has a finished state."""
         return self.state == BUILD_STATE_FINISHED
 
+    @property
+    def is_stale(self):
+        """Return if build state is triggered & date more than 5m ago."""
+        mins_ago = timezone.now() - datetime.timedelta(minutes=5)
+        return self.state == BUILD_STATE_TRIGGERED and self.date < mins_ago
+
 
 class BuildCommandResultMixin:
 


Test patch:
diff --git a/readthedocs/rtd_tests/tests/test_builds.py b/readthedocs/rtd_tests/tests/test_builds.py
--- a/readthedocs/rtd_tests/tests/test_builds.py
+++ b/readthedocs/rtd_tests/tests/test_builds.py
@@ -1,9 +1,11 @@
 # -*- coding: utf-8 -*-
+import datetime
 import os
 
 import mock
 from django.test import TestCase
 from django_dynamic_fixture import fixture, get
+from django.utils import timezone
 
 from readthedocs.builds.models import Build, Version
 from readthedocs.doc_builder.config import load_yaml_config
@@ -531,3 +533,32 @@ def test_do_not_reference_empty_configs(self):
         build_two.save()
         self.assertEqual(build_two._config, {})
         self.assertEqual(build_two.config, {})
+
+    def test_build_is_stale(self):
+        now = timezone.now()
+
+        build_one = get(
+            Build,
+            project=self.project,
+            version=self.version,
+            date=now - datetime.timedelta(minutes=8),
+            state='finished'
+        )
+        build_two = get(
+            Build,
+            project=self.project,
+            version=self.version,
+            date=now - datetime.timedelta(minutes=6),
+            state='triggered'
+        )
+        build_three = get(
+            Build,
+            project=self.project,
+            version=self.version,
+            date=now - datetime.timedelta(minutes=2),
+            state='triggered'
+        )
+
+        self.assertFalse(build_one.is_stale)
+        self.assertTrue(build_two.is_stale)
+        self.assertFalse(build_three.is_stale)