diff --git a/stackalytics/dashboard/decorators.py b/stackalytics/dashboard/decorators.py
index 1322219ee..ad5706f52 100644
--- a/stackalytics/dashboard/decorators.py
+++ b/stackalytics/dashboard/decorators.py
@@ -404,15 +404,17 @@ def templated(template=None, return_code=200):
module = parameters.get_single_parameter(kwargs, 'module')
ctx['module'] = module
+ module_name = None
if module and module in vault_inst['module_id_index']:
ctx['module_inst'] = vault_inst['module_id_index'][module]
+ module_name = ctx['module_inst']['module_group_name']
ctx['user_id'] = parameters.get_single_parameter(kwargs, 'user_id')
if ctx['user_id']:
ctx['user_inst'] = vault.get_user_from_runtime_storage(
ctx['user_id'])
ctx['page_title'] = helpers.make_page_title(
- ctx['company'], ctx['user_id'], ctx['module'], ctx['release'])
+ ctx['company'], ctx['user_id'], module_name, ctx['release'])
ctx['stackalytics_version'] = (
stackalytics_version.version_info.version_string())
ctx['stackalytics_release'] = (
diff --git a/stackalytics/dashboard/helpers.py b/stackalytics/dashboard/helpers.py
index 6c0dc0951..568512fa4 100644
--- a/stackalytics/dashboard/helpers.py
+++ b/stackalytics/dashboard/helpers.py
@@ -111,6 +111,35 @@ def extend_user(user):
return user
+def extend_module(module_id):
+ module_id_index = vault.get_vault()['module_id_index']
+ module_id = module_id.lower()
+
+ if module_id not in module_id_index:
+ return None
+
+ repos_index = vault.get_vault()['repos_index']
+
+ module = module_id_index[module_id]
+ name = module['module_group_name']
+ if name[0].islower():
+ name = name.capitalize()
+
+ child_modules = []
+ for m in module['modules']:
+ child = {'module_name': m}
+ if m in repos_index:
+ child['repo_uri'] = repos_index[m]['uri']
+ child_modules.append(child)
+
+ return {
+ 'id': module_id,
+ 'name': name,
+ 'tag': module['tag'],
+ 'modules': child_modules,
+ }
+
+
def get_activity(records, start_record, page_size, query_message=None):
if query_message:
# note that all records are now dicts!
diff --git a/stackalytics/dashboard/templates/_macros/module_details.html b/stackalytics/dashboard/templates/_macros/module_details.html
new file mode 100644
index 000000000..dcf76c6a4
--- /dev/null
+++ b/stackalytics/dashboard/templates/_macros/module_details.html
@@ -0,0 +1,54 @@
+{% macro show_module_details(module) -%}
+
+
+
+
+
+
+
+{%- endmacro %}
diff --git a/stackalytics/dashboard/templates/overview.html b/stackalytics/dashboard/templates/overview.html
index 91a358232..ad12189e6 100644
--- a/stackalytics/dashboard/templates/overview.html
+++ b/stackalytics/dashboard/templates/overview.html
@@ -2,6 +2,7 @@
{% import '_macros/activity_log.html' as activity_log %}
{% import '_macros/contribution_summary.html' as contribution_summary %}
{% import '_macros/user_profile.html' as user_profile %}
+{% import '_macros/module_details.html' as module_details %}
{% set show_company_breakdown = (not company) and (not user_id) %}
{% set show_engineer_breakdown = (not user_id) %}
@@ -13,6 +14,7 @@
{% set show_contribution_on_left = (not user_id) and (module) %}
{% set show_contribution_on_right = (user_id) or (company and not module) %}
{% set show_user_profile = (user_id) %}
+{% set show_module_details = (module) %}
{% set show_top_mentors_options = (metric == 'tm_marks') %}
{% set show_review_ratio = (metric in ['marks', 'tm_marks']) %}
@@ -184,6 +186,10 @@
{% endif %}
+ {% if show_module_details %}
+ {{ module_details.show_module_details(module=module) }}
+ {% endif %}
+
{% if show_bp_breakdown %}
Blueprint popularity
diff --git a/stackalytics/dashboard/vault.py b/stackalytics/dashboard/vault.py
index 8eabc1570..5f0383fba 100644
--- a/stackalytics/dashboard/vault.py
+++ b/stackalytics/dashboard/vault.py
@@ -89,6 +89,7 @@ def get_vault():
_init_releases(vault)
_init_module_groups(vault)
_init_project_types(vault)
+ _init_repos(vault)
_init_user_index(vault)
return vault
@@ -128,8 +129,14 @@ def _init_project_types(vault):
project_types = runtime_storage_inst.get_by_key('project_types') or {}
vault['project_types'] = project_types
- vault['project_types_index'] = dict([(pt['id'], pt)
- for pt in project_types])
+ vault['project_types_index'] = dict((pt['id'], pt) for pt in project_types)
+
+
+def _init_repos(vault):
+ runtime_storage_inst = vault['runtime_storage']
+ repos = runtime_storage_inst.get_by_key('repos') or {}
+
+ vault['repos_index'] = dict((r['module'], r) for r in repos)
def _init_user_index(vault):
@@ -173,7 +180,7 @@ def get_user_from_runtime_storage(user_id):
return user_index[user_id]
-def resolve_modules_for_releases(module_ids, releases):
+def _resolve_modules_for_releases(module_ids, releases):
module_id_index = get_vault().get('module_id_index') or {}
for module_id in module_ids:
@@ -201,7 +208,7 @@ def resolve_modules_for_releases(module_ids, releases):
def resolve_modules(module_ids, releases):
all_releases = get_vault()['releases'].keys()
- for module, release in resolve_modules_for_releases(module_ids, releases):
+ for module, release in _resolve_modules_for_releases(module_ids, releases):
if release is None:
for r in all_releases:
yield (module, r)
diff --git a/stackalytics/dashboard/web.py b/stackalytics/dashboard/web.py
index 6bad82c65..2ce824cd2 100644
--- a/stackalytics/dashboard/web.py
+++ b/stackalytics/dashboard/web.py
@@ -343,18 +343,15 @@ def get_company(company_name, **kwargs):
flask.abort(404)
-@app.route('/api/1.0/modules/')
+@app.route('/api/1.0/modules/')
@decorators.response()
@decorators.cached()
@decorators.jsonify('module')
-def get_module(module, **kwargs):
- module_id_index = vault.get_vault()['module_id_index']
- module = module.lower()
- if module in module_id_index:
- return {'id': module_id_index[module]['id'],
- 'text': module_id_index[module]['module_group_name'],
- 'tag': module_id_index[module]['tag']}
- flask.abort(404)
+def get_module(module_id, **kwargs):
+ module = helpers.extend_module(module_id)
+ if not module:
+ flask.abort(404)
+ return module
@app.route('/api/1.0/members')
diff --git a/tests/api/test_modules.py b/tests/api/test_modules.py
index 6d747ede7..a47c68637 100644
--- a/tests/api/test_modules.py
+++ b/tests/api/test_modules.py
@@ -79,7 +79,7 @@ class TestAPIModules(test_api.TestAPI):
'module_groups': {
'nova-group': {'id': 'nova-group',
'module_group_name': 'nova-group',
- 'modules': ['nova', 'nova-cli'],
+ 'modules': ['nova-cli', 'nova'],
'tag': 'group'},
'nova': test_api.make_module('nova'),
'nova-cli': test_api.make_module('nova-cli'),
@@ -89,10 +89,20 @@ class TestAPIModules(test_api.TestAPI):
response = self.app.get('/api/1.0/modules/nova')
module = test_api.load_json(response)['module']
self.assertEqual(
- {'id': 'nova', 'text': 'nova', 'tag': 'module'}, module)
+ {'id': 'nova',
+ 'modules': [
+ {'module_name': 'nova',
+ 'repo_uri': 'git://git.openstack.org/openstack/nova.git'}
+ ],
+ 'name': 'Nova', 'tag': 'module'}, module)
response = self.app.get('/api/1.0/modules/nova-group')
module = test_api.load_json(response)['module']
self.assertEqual(
- {'tag': 'group', 'id': 'nova-group', 'text': 'nova-group'},
- module)
+ {'id': 'nova-group',
+ 'modules': [
+ {'module_name': 'nova-cli'},
+ {'module_name': 'nova',
+ 'repo_uri': 'git://git.openstack.org/openstack/nova.git'}
+ ],
+ 'name': 'Nova-group', 'tag': 'group'}, module)