Repository: learningequality/kolibri

Issue:
The search icon is duplicated in the ePUB viewer after enabling the search feature
### Actual result:
The search icon is duplicated in the ePUB viewer after enabling the search feature
### Expected result:
The search icon shouldn't be duplicated in the ePUB viewer after enabling the search feature

### Steps to reproduce:

1. Given I am signed in to Kolibri
2. Open ePUB file from the Kolibri QA channel
3. Click on the search icon
4. Pay attention to the search icon which is duplicated

### Video:
http://e.pc.cd/VYbotalK

![image](https://user-images.githubusercontent.com/10248067/97351564-29efda80-184f-11eb-92c9-2665889123ae.png)

### Context:
Kolibri version: 0.14.3
Device: Raspberry Pi 3B+
OS: tested on Mac mini (Mid 2011) macOS Sierra v 10.12.6
Browser: Google Chrome latest


Discussion:

Resolving patch:
diff --git a/kolibri/core/utils/cache.py b/kolibri/core/utils/cache.py
--- a/kolibri/core/utils/cache.py
+++ b/kolibri/core/utils/cache.py
@@ -26,12 +26,12 @@ class ProcessLock(object):
     def __init__(self, key, expire=None):
         """
         :param key: The lock key
-        :param expire: The cache key expiration in seconds
+        :param expire: The cache key expiration in seconds (defaults to the CACHE_LOCK_TTL option if not set)
         :type key: str
         :type expire: int
         """
         self.key = key
-        self.expire = expire
+        self.expire = expire if expire else OPTIONS["Cache"]["CACHE_LOCK_TTL"]
 
         self._lock_object = None
 
@@ -39,7 +39,7 @@ def __init__(self, key, expire=None):
     def _lock(self):
         if self._lock_object is None:
             if OPTIONS["Cache"]["CACHE_BACKEND"] == "redis":
-                expire = self.expire * 1000 if self.expire is not None else None
+                expire = self.expire * 1000
                 # if we're using Redis, be sure we use Redis' locking mechanism which uses
                 # `SET NX` under the hood. See redis.lock.Lock
                 # The Django RedisCache backend provide the lock method to proxy this
diff --git a/kolibri/core/views.py b/kolibri/core/views.py
--- a/kolibri/core/views.py
+++ b/kolibri/core/views.py
@@ -149,7 +149,7 @@ def get(self, request):
             )
             if user_kinds.ADMIN in roles:
                 url = url or get_url_by_role(user_kinds.ADMIN)
-            if user_kinds.COACH in roles:
+            if user_kinds.COACH in roles or user_kinds.ASSIGNABLE_COACH in roles:
                 url = url or get_url_by_role(user_kinds.COACH)
             url = url or get_url_by_role(user_kinds.LEARNER)
         else:
diff --git a/kolibri/utils/options.py b/kolibri/utils/options.py
--- a/kolibri/utils/options.py
+++ b/kolibri/utils/options.py
@@ -11,6 +11,7 @@
 CACHE_MAX_ENTRIES
 CACHE_PASSWORD
 CACHE_LOCATION
+CACHE_LOCK_TTL
 CACHE_REDIS_MIN_DB
 CACHE_REDIS_MAX_POOL_SIZE
 CACHE_REDIS_POOL_TIMEOUT
@@ -215,6 +216,11 @@ def path_list(value):
             "default": "localhost:6379",
             "envvars": ("KOLIBRI_CACHE_LOCATION",),
         },
+        "CACHE_LOCK_TTL": {
+            "type": "integer",
+            "default": 30,
+            "envvars": ("KOLIBRI_CACHE_LOCK_TTL",),
+        },
         "CACHE_REDIS_MIN_DB": {
             "type": "integer",
             "default": 0,


Test patch:
diff --git a/integration_testing/features/coach/coach-create-lesson.feature b/integration_testing/features/coach/coach-create-lesson.feature
--- a/integration_testing/features/coach/coach-create-lesson.feature
+++ b/integration_testing/features/coach/coach-create-lesson.feature
@@ -157,6 +157,7 @@ Feature: Coach creates lessons
   Scenario: View metadata on topic cards in search results
     Given I am on the search results page
       And there are topics in the search results
+      And one of those topics has content for coaches
     When I see a topic card
     Then I see its title
       And I see its description
diff --git a/kolibri/core/test/test_key_urls.py b/kolibri/core/test/test_key_urls.py
--- a/kolibri/core/test/test_key_urls.py
+++ b/kolibri/core/test/test_key_urls.py
@@ -19,45 +19,58 @@
 from kolibri.deployment.default.urls import urlpatterns
 
 
-class KolibriTagNavigationTestCase(APITestCase):
+class BeforeDeviceProvisionTests(APITestCase):
     def test_redirect_to_setup_wizard(self):
-        with patch("kolibri.core.views.is_provisioned", return_value=False):
-            response = self.client.get(reverse("kolibri:core:root_redirect"))
-            self.assertEqual(response.status_code, 302)
-            self.assertEqual(
-                response.get("location"),
-                reverse("kolibri:kolibri.plugins.setup_wizard:setupwizard"),
-            )
-
-    def test_redirect_root_to_user_if_not_logged_in(self):
-        provision_device()
         response = self.client.get(reverse("kolibri:core:root_redirect"))
         self.assertEqual(response.status_code, 302)
         self.assertEqual(
-            response.get("location"), reverse("kolibri:kolibri.plugins.user:user")
+            response.get("location"),
+            reverse("kolibri:kolibri.plugins.setup_wizard:setupwizard"),
         )
 
-    def test_redirect_root_to_device_if_superuser_logged_in(self):
-        facility = FacilityFactory.create()
-        do = create_superuser(facility)
+
+class KolibriTagNavigationTestCase(APITestCase):
+    @classmethod
+    def setUpTestData(cls):
         provision_device()
-        self.client.login(username=do.username, password=DUMMY_PASSWORD)
+        facility = cls.facility = FacilityFactory.create()
+        cls.learner = FacilityUserFactory.create(facility=facility)
+        cls.facility_coach = FacilityUserFactory.create(facility=facility)
+        facility.add_role(cls.facility_coach, "coach")
+        cls.class_coach = FacilityUserFactory.create(facility=facility)
+        facility.add_role(cls.class_coach, "classroom assignable coach")
+        cls.superuser = create_superuser(cls.facility)
+
+    def tearDown(self):
+        self.client.logout()
+
+    def _assert_location_reverse_url(self, url_name):
         response = self.client.get(reverse("kolibri:core:root_redirect"))
         self.assertEqual(response.status_code, 302)
-        self.assertEqual(
-            response.get("location"),
-            reverse("kolibri:kolibri.plugins.device:device_management"),
+        self.assertEqual(response.get("location"), reverse(url_name))
+
+    def test_anonymous_user_is_redirected_to_user_plugin(self):
+        self._assert_location_reverse_url("kolibri:kolibri.plugins.user:user")
+
+    def test_superuser_is_redirected_to_device_plugin(self):
+        self.client.login(username=self.superuser.username, password=DUMMY_PASSWORD)
+        self._assert_location_reverse_url(
+            "kolibri:kolibri.plugins.device:device_management"
         )
 
-    def test_redirect_root_to_learn_if_logged_in(self):
-        do = FacilityUserFactory.create()
-        provision_device()
-        self.client.login(username=do.username, password=DUMMY_PASSWORD)
-        response = self.client.get(reverse("kolibri:core:root_redirect"))
-        self.assertEqual(response.status_code, 302)
-        self.assertEqual(
-            response.get("location"), reverse("kolibri:kolibri.plugins.learn:learn")
+    def test_learner_is_redirected_to_learn_plugin(self):
+        self.client.login(username=self.learner.username, password=DUMMY_PASSWORD)
+        self._assert_location_reverse_url("kolibri:kolibri.plugins.learn:learn")
+
+    def test_facility_coach_is_redirected_to_coach_plugin(self):
+        self.client.login(
+            username=self.facility_coach.username, password=DUMMY_PASSWORD
         )
+        self._assert_location_reverse_url("kolibri:kolibri.plugins.coach:coach")
+
+    def test_class_coach_is_redirected_to_coach_plugin(self):
+        self.client.login(username=self.class_coach.username, password=DUMMY_PASSWORD)
+        self._assert_location_reverse_url("kolibri:kolibri.plugins.coach:coach")
 
 
 class AllUrlsTest(APITransactionTestCase):