This is an automated email from the ASF dual-hosted git repository.

smarru pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/airavata-custos-portal.git

commit ac2a89410fd6d3b2b81567a5ade60c800b2bd6fe
Author: Shivam Rastogi <shivam_r...@yahoo.com>
AuthorDate: Sun Mar 22 21:10:07 2020 -0400

    Added navigation buttons to the application
---
 .../custos_admin_portal/app_config.py              | 101 +++++++++++++++++++++
 .../custos_admin_portal/apps/admin/apps.py         |  19 +++-
 .../custos_admin_portal/apps/admin/urls.py         |   2 +-
 .../custos_admin_portal/apps/admin/views.py        |   3 +-
 .../custos_admin_portal/apps/workspace/apps.py     |  28 +++++-
 .../custos_admin_portal/apps/workspace/urls.py     |   2 +-
 .../custos_admin_portal/apps/workspace/views.py    |   6 +-
 .../custos_admin_portal/context_processors.py      |  87 ++++++++++++++++++
 .../custos_admin_portal/settings.py                |   3 +-
 .../static/common/dist/webpack-stats.json          |   2 +-
 .../static/common/package-lock.json                |   7 +-
 .../static/common/vue.config.js                    |   1 +
 .../custos_admin_portal/templates/base.html        |  68 +++++++++++++-
 13 files changed, 310 insertions(+), 19 deletions(-)

diff --git a/custos_admin_portal/custos_admin_portal/app_config.py 
b/custos_admin_portal/custos_admin_portal/app_config.py
new file mode 100644
index 0000000..b99cf99
--- /dev/null
+++ b/custos_admin_portal/custos_admin_portal/app_config.py
@@ -0,0 +1,101 @@
+
+import logging
+from abc import ABC, abstractmethod
+from importlib import import_module
+
+from django.apps import AppConfig
+
+logger = logging.getLogger(__name__)
+
+
+class CustosAppConfig(AppConfig, ABC):
+    """Custom AppConfig for Django Airavata apps."""
+
+    @property
+    def url_app_name(self):
+        """Return the urls application namespace."""
+        return get_url_app_name(self)
+
+    @property
+    @abstractmethod
+    def app_order(self):
+        """Return positive int order of app in listings, lowest sorts first."""
+        pass
+
+    @property
+    @abstractmethod
+    def url_home(self):
+        """Named route of home page for this application."""
+        pass
+
+    @property
+    @abstractmethod
+    def fa_icon_class(self):
+        """Font Awesome icon class name."""
+        pass
+
+    @property
+    @abstractmethod
+    def app_description(self):
+        """Some user friendly text to briefly describe the application."""
+        pass
+
+
+def enhance_custom_app_config(app):
+    """As necessary add default values for properties to custom AppConfigs."""
+    app.url_app_name = get_url_app_name(app)
+    app.url_home = get_url_home(app)
+    app.fa_icon_class = get_fa_icon_class(app)
+    app.app_description = get_app_description(app)
+    return app
+
+
+def get_url_app_name(app_config):
+    """Return the urls namespace for the given AppConfig instance."""
+    urls = get_app_urls(app_config)
+    return getattr(urls, 'app_name', None)
+
+
+def get_url_home(app_config):
+    """Get named URL of home page of app."""
+    if hasattr(app_config, 'url_home'):
+        return app_config.url_home
+    else:
+        return get_default_url_home(app_config)
+
+
+def get_default_url_home(app_config):
+    """Return first url pattern as a default."""
+    urls = get_app_urls(app_config)
+    app_name = get_url_app_name(app_config)
+    logger.warning("Custom Django app {} has no URL namespace "
+                   "defined".format(app_config.label))
+    first_named_url = None
+    for urlpattern in urls.urlpatterns:
+        if hasattr(urlpattern, 'name'):
+            first_named_url = urlpattern.name
+            break
+    if not first_named_url:
+        raise Exception("{} has no named urls, "
+                        "can't figure out default home URL")
+    if app_name:
+        return app_name + ":" + first_named_url
+    else:
+        return first_named_url
+
+
+def get_fa_icon_class(app_config):
+    """Return Font Awesome icon class to use for app."""
+    if hasattr(app_config, "fa_icon_class"):
+        return app_config.fa_icon_class
+    else:
+        return 'fa-circle'
+
+
+def get_app_description(app_config):
+    """Return brief description of app."""
+    return getattr(app_config, 'app_description', None)
+
+
+def get_app_urls(app_config):
+    return import_module(".urls", app_config.name)
diff --git a/custos_admin_portal/custos_admin_portal/apps/admin/apps.py 
b/custos_admin_portal/custos_admin_portal/apps/admin/apps.py
index 6b3d0d6..2385180 100644
--- a/custos_admin_portal/custos_admin_portal/apps/admin/apps.py
+++ b/custos_admin_portal/custos_admin_portal/apps/admin/apps.py
@@ -1,6 +1,21 @@
-from django.apps import AppConfig
+from custos_admin_portal.app_config import CustosAppConfig
 
 
-class AdminConfig(AppConfig):
+class AdminConfig(CustosAppConfig):
     name = 'custos_admin_portal.apps.admin'
     label = 'custos_admin_portal_admin'
+    verbose_name = 'Admin'
+    app_order = 100
+    url_home = 'custos_admin_portal_admin:list_requests'
+    fa_icon_class = 'fa-cog'
+    app_description = """
+        Configure and share resources with other users.
+    """
+    nav = [
+        {
+            'label': 'Application Catalog',
+            'icon': 'fa fa-list',
+            'url': 'custos_admin_portal_admin:list_requests',
+            'active_prefixes': ['applications', 'list-requests']
+        }
+    ]
\ No newline at end of file
diff --git a/custos_admin_portal/custos_admin_portal/apps/admin/urls.py 
b/custos_admin_portal/custos_admin_portal/apps/admin/urls.py
index dd0828d..dc77d80 100644
--- a/custos_admin_portal/custos_admin_portal/apps/admin/urls.py
+++ b/custos_admin_portal/custos_admin_portal/apps/admin/urls.py
@@ -5,7 +5,7 @@ from . import views
 
 app_name = 'custos_admin_portal_admin'
 urlpatterns = [
-    url(r'^list-requests', views.list_new_tenant_requests, 
name='list_request'),
+    url(r'^list-requests', views.list_new_tenant_requests, 
name='list_requests'),
     url(r'^request/(?P<tenant_request_id>[^/]+)/$', views.view_tenant_request, 
name="view_tenant_request"),
     url(r'^edit-tenant-request/(?P<tenant_request_id>[^/]+)/$', 
views.edit_tenant_request, name="edit_tenant_request")
 ]
diff --git a/custos_admin_portal/custos_admin_portal/apps/admin/views.py 
b/custos_admin_portal/custos_admin_portal/apps/admin/views.py
index 4b4428f..6be7603 100644
--- a/custos_admin_portal/custos_admin_portal/apps/admin/views.py
+++ b/custos_admin_portal/custos_admin_portal/apps/admin/views.py
@@ -2,7 +2,8 @@ from django.shortcuts import render
 
 
 def list_new_tenant_requests(request):
-    print("New tenant requests list is called")
+    request.active_nav_item = 'list-requests'
+
     # TODO fetch all the tenant requests from airavata here.
     return render(request, 'workspace/list_requests.html', {
         'bundle_name': 'admin-list-requests',
diff --git a/custos_admin_portal/custos_admin_portal/apps/workspace/apps.py 
b/custos_admin_portal/custos_admin_portal/apps/workspace/apps.py
index 7be3c80..4cf73f2 100644
--- a/custos_admin_portal/custos_admin_portal/apps/workspace/apps.py
+++ b/custos_admin_portal/custos_admin_portal/apps/workspace/apps.py
@@ -1,5 +1,27 @@
-from django.apps import AppConfig
+from custos_admin_portal.app_config import CustosAppConfig
 
 
-class WorkspaceConfig(AppConfig):
-    name = 'workspace'
+class WorkspaceConfig(CustosAppConfig):
+    name = 'custos_admin_portal.apps.workspace'
+    label = 'custos_admin_portal_workspace'
+    verbose_name = 'Workspace'
+    app_order = 0
+    url_home = 'custos_admin_portal_workspace:request_new_tenant'
+    fa_icon_class = 'fa-flask'
+    app_description = """
+        Launch applications and manage your experiments and projects.
+    """
+    nav = [
+        {
+            'label': 'Create new tenant request',
+            'icon': 'fa fa-plus-square',
+            'url': 'custos_admin_portal_workspace:request_new_tenant',
+            'active_prefixes': ['applications', 'request-new-tenant']
+        },
+        {
+            'label': 'List of all existing tenant requests',
+            'icon': 'fa fa-list',
+            'url': 'custos_admin_portal_workspace:list_requests',
+            'active_prefixes': ['applications', 'list-requests']
+        }
+    ]
\ No newline at end of file
diff --git a/custos_admin_portal/custos_admin_portal/apps/workspace/urls.py 
b/custos_admin_portal/custos_admin_portal/apps/workspace/urls.py
index f41aec0..29c5c34 100644
--- a/custos_admin_portal/custos_admin_portal/apps/workspace/urls.py
+++ b/custos_admin_portal/custos_admin_portal/apps/workspace/urls.py
@@ -5,6 +5,6 @@ from . import views
 app_name = 'custos_admin_portal_workspace'
 urlpatterns = [
     url(r'^request-new-tenant', views.request_new_tenant, 
name='request_new_tenant'),
-    url(r'^list-requests', views.list_new_tenant_requests, 
name='list_request'),
+    url(r'^list-requests', views.list_new_tenant_requests, 
name='list_requests'),
     url(r'request/(?P<tenant_request_id>[^/]+)/$', views.view_tenant_request, 
name="view_tenant_request")
 ]
diff --git a/custos_admin_portal/custos_admin_portal/apps/workspace/views.py 
b/custos_admin_portal/custos_admin_portal/apps/workspace/views.py
index 9c4d624..8a89243 100644
--- a/custos_admin_portal/custos_admin_portal/apps/workspace/views.py
+++ b/custos_admin_portal/custos_admin_portal/apps/workspace/views.py
@@ -2,7 +2,7 @@ from django.shortcuts import render
 
 
 def request_new_tenant(request):
-    print("request new tenant is called")
+    request.active_nav_item = 'request-new-tenant'
 
     return render(request, 'workspace/request_new_tenant.html', {
         'bundle_name': 'request-new-tenant',
@@ -10,7 +10,7 @@ def request_new_tenant(request):
 
 
 def list_new_tenant_requests(request):
-    print("New tenant requests list is called")
+    request.active_nav_item = 'list-requests'
 
     return render(request, 'workspace/list_requests.html', {
         'bundle_name': 'list-requests',
@@ -18,7 +18,7 @@ def list_new_tenant_requests(request):
 
 
 def view_tenant_request(request, tenant_request_id):
-    print("Tenant request Id: {}".format(tenant_request_id)),
+
     return render(request, 'workspace/view_tenant_request.html', {
         'bundle_name': 'view-request',
         'tenant_request_id': tenant_request_id
diff --git a/custos_admin_portal/custos_admin_portal/context_processors.py 
b/custos_admin_portal/custos_admin_portal/context_processors.py
new file mode 100644
index 0000000..824ec95
--- /dev/null
+++ b/custos_admin_portal/custos_admin_portal/context_processors.py
@@ -0,0 +1,87 @@
+import copy
+import logging
+import re
+
+from django.apps import apps
+from django.conf import settings
+
+from custos_admin_portal.app_config import CustosAppConfig
+
+logger = logging.getLogger(__name__)
+
+
+def airavata_app_registry(request):
+    """Put airavata django apps into the context."""
+    print([app for app in apps.app_configs])
+    print([app for app in apps.get_app_configs()])
+
+    airavata_apps = [app for app in apps.get_app_configs()
+                     if isinstance(app, CustosAppConfig)]
+    print("Custos apps", airavata_apps)
+    # Sort by app_order then by verbose_name (case-insensitive)
+    airavata_apps.sort(
+        key=lambda app: "{:09}-{}".format(app.app_order,
+                                          app.verbose_name.lower()))
+    current_app = _get_current_app(request, airavata_apps)
+
+    return {
+        'airavata_apps': airavata_apps,
+        'current_airavata_app': current_app,
+        'airavata_app_nav': (_get_app_nav(request, current_app)
+                             if current_app else None)
+    }
+
+
+def custom_app_registry(request):
+    """Put custom Django apps into the context."""
+    custom_apps = settings.CUSTOM_DJANGO_APPS.copy()
+    custom_apps.sort(key=lambda app: app.verbose_name.lower())
+    current_custom_app = _get_current_app(request, custom_apps)
+    return {
+        # 'custom_apps': list(map(_app_to_dict, custom_apps)),
+        'custom_apps': custom_apps,
+        'current_custom_app': current_custom_app,
+        'custom_app_nav': (_get_app_nav(request, current_custom_app)
+                           if current_custom_app else None)
+    }
+
+
+def _get_current_app(request, apps):
+    current_app = [
+        app for app in apps
+        if request.resolver_match and
+        app.url_app_name == request.resolver_match.app_name]
+    return current_app[0] if len(current_app) > 0 else None
+
+
+def _get_app_nav(request, current_app):
+    if hasattr(current_app, 'nav'):
+        nav = copy.copy(current_app.nav)
+        # convert "/djangoapp/path/in/app" to "path/in/app"
+        app_path = "/".join(request.path.split("/")[2:])
+        print(app_path)
+        for nav_item in nav:
+            if 'active_prefixes' in nav_item:
+                if re.match("|".join(nav_item['active_prefixes']), app_path):
+                    nav_item['active'] = True
+                else:
+                    nav_item['active'] = False
+            else:
+                # 'active_prefixes' is optional, and if not specified, assume
+                # current item is active
+                nav_item['active'] = True
+    else:
+        # Default to the home view in the app
+        nav = [
+            {
+                'label': current_app.verbose_name,
+                'icon': 'fa ' + current_app.fa_icon_class,
+                'url': current_app.url_home
+            }
+        ]
+    return nav
+
+
+def resolver_match(request):
+    """Put resolver_match (ResolverMatch instance) into the context."""
+    return {'resolver_match': request.resolver_match}
diff --git a/custos_admin_portal/custos_admin_portal/settings.py 
b/custos_admin_portal/custos_admin_portal/settings.py
index 8247c61..d248ab9 100644
--- a/custos_admin_portal/custos_admin_portal/settings.py
+++ b/custos_admin_portal/custos_admin_portal/settings.py
@@ -38,7 +38,7 @@ INSTALLED_APPS = [
     'webpack_loader',
 
     'custos_admin_portal.apps.auth.apps.AuthConfig',
-    'custos_admin_portal.apps.workspace',
+    'custos_admin_portal.apps.workspace.apps.WorkspaceConfig',
     'custos_admin_portal.apps.admin.apps.AdminConfig'
 ]
 
@@ -65,6 +65,7 @@ TEMPLATES = [
                 'django.template.context_processors.request',
                 'django.contrib.auth.context_processors.auth',
                 'django.contrib.messages.context_processors.messages',
+                'custos_admin_portal.context_processors.airavata_app_registry',
             ],
         },
     },
diff --git 
a/custos_admin_portal/custos_admin_portal/static/common/dist/webpack-stats.json 
b/custos_admin_portal/custos_admin_portal/static/common/dist/webpack-stats.json
index 4e733dd..111ba4b 100644
--- 
a/custos_admin_portal/custos_admin_portal/static/common/dist/webpack-stats.json
+++ 
b/custos_admin_portal/custos_admin_portal/static/common/dist/webpack-stats.json
@@ -1 +1 @@
-{"status":"done","publicPath":"http://localhost:9000/static/common/dist/","chunks":{"admin-edit-request":[{"name":"admin-edit-request.js","publicPath":"http://localhost:9000/static/common/dist/admin-edit-request.js","path":"/home/shivam/Programming/custos_UI/custos_admin_portal/custos_admin_portal/static/common/dist/admin-edit-request.js"},{"name":"admin-edit-request.e3e36cbf5bfd0814904f.hot-update.js","publicPath":"http://localhost:9000/static/common/dist/admin-edit-request.e3e36cbf5bfd
 [...]
\ No newline at end of file
+{"status":"done","publicPath":"http://localhost:9000/static/common/dist/","chunks":{"admin-edit-request":[{"name":"admin-edit-request.js","publicPath":"http://localhost:9000/static/common/dist/admin-edit-request.js","path":"/home/shivam/Programming/custos_UI/custos_admin_portal/custos_admin_portal/static/common/dist/admin-edit-request.js"},{"name":"admin-edit-request.js.map","publicPath":"http://localhost:9000/static/common/dist/admin-edit-request.js.map","path":"/home/shivam/Programming
 [...]
\ No newline at end of file
diff --git 
a/custos_admin_portal/custos_admin_portal/static/common/package-lock.json 
b/custos_admin_portal/custos_admin_portal/static/common/package-lock.json
index 81b4457..fc49d8d 100755
--- a/custos_admin_portal/custos_admin_portal/static/common/package-lock.json
+++ b/custos_admin_portal/custos_admin_portal/static/common/package-lock.json
@@ -1,5 +1,5 @@
 {
-  "name": "django-airavata-common-ui",
+  "name": "custos-admin-portal-ui",
   "version": "1.0.0",
   "lockfileVersion": 1,
   "requires": true,
@@ -11968,6 +11968,11 @@
       "integrity": 
"sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
       "dev": true
     },
+    "vuelidate": {
+      "version": "0.7.5",
+      "resolved": "https://registry.npmjs.org/vuelidate/-/vuelidate-0.7.5.tgz";,
+      "integrity": 
"sha512-GAAG8QAFVp7BFeQlNaThpTbimq3+HypBPNwdkCkHZZeVaD5zmXXfhp357dcUJXHXTZjSln0PvP6wiwLZXkFTwg=="
+    },
     "watchpack": {
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz";,
diff --git 
a/custos_admin_portal/custos_admin_portal/static/common/vue.config.js 
b/custos_admin_portal/custos_admin_portal/static/common/vue.config.js
index 6edcf61..bbe120e 100755
--- a/custos_admin_portal/custos_admin_portal/static/common/vue.config.js
+++ b/custos_admin_portal/custos_admin_portal/static/common/vue.config.js
@@ -39,6 +39,7 @@ module.exports = {
        * See also: 
https://bitbucket.org/calidae/dejavu/src/d63d10b0030a951c3cafa6b574dad25b3bef3fe9/%7B%7Bcookiecutter.project_slug%7D%7D/frontend/vue.config.js?at=master&fileviewer=file-view-default#vue.config.js-27
        */
       splitChunks: {
+        chunks: 'all',
         cacheGroups: {
           vendors: {
             name: 'chunk-vendors',
diff --git a/custos_admin_portal/custos_admin_portal/templates/base.html 
b/custos_admin_portal/custos_admin_portal/templates/base.html
index 9a4a419..07e2425 100644
--- a/custos_admin_portal/custos_admin_portal/templates/base.html
+++ b/custos_admin_portal/custos_admin_portal/templates/base.html
@@ -149,6 +149,69 @@
     {% endblock %}
     <div class=c-header__title><a href="/">{% block title %}Custos Portal{% 
endblock %}</a>
     </div>
+    {% if not user.is_authenticated %}
+    <div class=c-header__controls>
+      <div class="btn-group">
+        <div class=dropdown>
+          <a href=# class="dropdown-toggle text-dark" id=appDropdownMenuButton 
data-toggle=dropdown aria-haspopup=true
+            aria-expanded=false>
+            {% if current_airavata_app %}
+            <i class="fa {{ current_airavata_app.fa_icon_class }} mr-2"></i>
+            {{ current_airavata_app.verbose_name }}
+            {% elif current_custom_app %}
+            <i class="fa {{ current_custom_app.fa_icon_class }} mr-2"></i>
+            {{ current_custom_app.verbose_name }}
+            {% else %}
+            Menu
+            {% endif %}
+          </a>
+          <div class=dropdown-menu aria-labelledby=appDropdownMenuButton>
+            {% for app in airavata_apps %}
+            {% if app == current_airavata_app %}
+            <a class="dropdown-item active" href="{% url app.url_home %}">
+              <i class="fa {{ app.fa_icon_class }} mr-2"></i>{{ 
app.verbose_name }}
+            </a>
+            {% else %}
+            <a class="dropdown-item" href="{% url app.url_home %}">
+              <i class="fa {{ app.fa_icon_class }} mr-2"></i>{{ 
app.verbose_name }}
+            </a>
+            {% endif %}
+            {% endfor %}
+            {% if custom_apps|length > 0 %}
+              <div class="dropdown-divider"></div>
+              {% for app in custom_apps %}
+                {% if current_custom_app and app.label == 
current_custom_app.label %}
+                <a class="dropdown-item active" href="{% url app.url_home %}">
+                  <i class="fa {{ app.fa_icon_class }} mr-2"></i>{{ 
app.verbose_name }}
+                </a>
+                {% else %}
+                <a class="dropdown-item" href="{% url app.url_home %}">
+                  <i class="fa {{ app.fa_icon_class }} mr-2"></i>{{ 
app.verbose_name }}
+                </a>
+                {% endif %}
+              {% endfor %}
+            {% endif %}
+          </div>
+        </div>
+      </div>
+      <div class="btn-group ml-3">
+        <div class=dropdown>
+          <a href=# class="dropdown-toggle text-dark" id=dropdownMenuButton 
data-toggle=dropdown aria-haspopup=true
+            aria-expanded=false>
+            <i class="fa fa-user mr-2"></i>
+            {{ request.session.USERINFO.given_name }}
+            {{ request.session.USERINFO.family_name }}
+          </a>
+          <div class=dropdown-menu aria-labelledby=dropdownMenuButton>
+            <a class=dropdown-item href=#>User settings</a>
+            <a class=dropdown-item href="#">
+              Logout <i class="fa fa-sign-out-alt"></i>
+            </a>
+          </div>
+        </div>
+      </div>
+    </div>
+    {% endif %}
   </header>
 
   <div class=stage>
@@ -167,11 +230,6 @@
               <i class="{{ nav_item.icon }}"></i> <span class=sr-only>{{ 
nav_item.label }}</span>
           </a>
         {% endfor %}
-        {% for nav_item in custom_app_nav %}
-          <a href="{% url nav_item.url %}" class="c-nav__item {% if 
nav_item.active %}is-active{% endif %}" data-toggle=tooltip 
data-placement=right title="{{ nav_item.label }}">
-              <i class="{{ nav_item.icon }}"></i> <span class=sr-only>{{ 
nav_item.label }}</span>
-          </a>
-        {% endfor %}
       {% endblock %}
     </nav>
     {% comment %}

Reply via email to