diff --git a/etc/default_data.json b/etc/default_data.json
index f5c7e0b7f..594fbf625 100644
--- a/etc/default_data.json
+++ b/etc/default_data.json
@@ -551,7 +551,7 @@
"launchpad_id": "anteaya",
"companies": [
{
- "company_name": "independent",
+ "company_name": "*independent",
"end_date": "2013-Apr-02"
},
{
diff --git a/stackalytics/dashboard/parameters.py b/stackalytics/dashboard/parameters.py
index 5b59bf98a..b5e92bf37 100644
--- a/stackalytics/dashboard/parameters.py
+++ b/stackalytics/dashboard/parameters.py
@@ -69,7 +69,7 @@ def get_default(param_name):
if 'release' not in DEFAULTS:
release = cfg.CONF.default_release
if not release:
- runtime_storage_inst = vault.get_vault()['runtime_storage']
+ runtime_storage_inst = vault.get_runtime_storage()
releases = runtime_storage_inst.get_by_key('releases')
if releases:
release = releases[-1]['release_name']
diff --git a/stackalytics/dashboard/reports.py b/stackalytics/dashboard/reports.py
index 28a6a2c4f..a2200faf9 100644
--- a/stackalytics/dashboard/reports.py
+++ b/stackalytics/dashboard/reports.py
@@ -159,6 +159,22 @@ def members():
}
+@blueprint.route('/affiliation_changes')
+@decorators.exception_handler()
+@decorators.templated()
+def affiliation_changes():
+ start_days = str(flask.request.args.get('start_days') or
+ utils.timestamp_to_date(int(time.time()) -
+ 365 * 24 * 60 * 60))
+ end_days = str(flask.request.args.get('end_days') or
+ utils.timestamp_to_date(int(time.time())))
+
+ return {
+ 'start_days': start_days,
+ 'end_days': end_days,
+ }
+
+
@blueprint.route('/cores')
@decorators.exception_handler()
@decorators.templated()
diff --git a/stackalytics/dashboard/templates/reports/affiliation_changes.html b/stackalytics/dashboard/templates/reports/affiliation_changes.html
new file mode 100644
index 000000000..cbb8234c3
--- /dev/null
+++ b/stackalytics/dashboard/templates/reports/affiliation_changes.html
@@ -0,0 +1,162 @@
+{% extends "reports/base_report.html" %}
+
+{% block title %}
+Company Affiliation Changes
+{% endblock %}
+
+{% block scripts %}
+
+
+{% endblock %}
+
+{% block content %}
+
Company Affiliation Changes
+
+ Start of the period: End of the period:
+
+
+
+
+
+ From |
+ To |
+ Count |
+ Users |
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/stackalytics/dashboard/vault.py b/stackalytics/dashboard/vault.py
index 7ac30831e..15a2bccee 100644
--- a/stackalytics/dashboard/vault.py
+++ b/stackalytics/dashboard/vault.py
@@ -68,7 +68,7 @@ def compact_records(records):
def extend_record(record):
- runtime_storage_inst = get_vault()['runtime_storage']
+ runtime_storage_inst = get_runtime_storage()
return runtime_storage_inst.get_by_key(
runtime_storage_inst._get_record_name(record.record_id))
@@ -121,6 +121,10 @@ def get_memory_storage():
return get_vault()['memory_storage']
+def get_runtime_storage():
+ return get_vault()['runtime_storage']
+
+
def _init_releases(vault):
runtime_storage_inst = vault['runtime_storage']
releases = runtime_storage_inst.get_by_key('releases')
@@ -170,7 +174,7 @@ def get_project_types():
def get_release_options():
- runtime_storage_inst = get_vault()['runtime_storage']
+ runtime_storage_inst = get_runtime_storage()
releases = (runtime_storage_inst.get_by_key('releases') or [None])[1:]
releases.append({'release_name': 'all'})
releases.reverse()
@@ -195,7 +199,7 @@ def get_project_type(project_type_id):
def get_user_from_runtime_storage(user_id):
- runtime_storage_inst = get_vault()['runtime_storage']
+ runtime_storage_inst = get_runtime_storage()
user_index = get_vault()['user_index']
if user_id not in user_index:
user_index[user_id] = user_processor.load_user(
diff --git a/stackalytics/dashboard/web.py b/stackalytics/dashboard/web.py
index ba0a0d76d..66fde1110 100644
--- a/stackalytics/dashboard/web.py
+++ b/stackalytics/dashboard/web.py
@@ -490,6 +490,52 @@ def get_project_types_json(**kwargs):
parameters.get_default('project_type'))
+@app.route('/api/1.0/affiliation_changes')
+@decorators.exception_handler()
+@decorators.response()
+@decorators.jsonify('affiliation_changes')
+def get_company_changes(**kwargs):
+
+ start_days = str(flask.request.args.get('start_days') or
+ utils.timestamp_to_date(int(time.time()) -
+ 365 * 24 * 60 * 60))
+ end_days = str(flask.request.args.get('end_days') or
+ utils.timestamp_to_date(int(time.time())))
+
+ start_date = utils.date_to_timestamp_ext(start_days)
+ end_date = utils.date_to_timestamp_ext(end_days)
+
+ runtime_storage = vault.get_runtime_storage()
+ result = []
+
+ for user in runtime_storage.get_all_users():
+ companies = user.get('companies') or []
+ if len(companies) < 2:
+ continue
+
+ companies_iter = iter(companies)
+ company = companies_iter.next()
+ old_company_name = company['company_name']
+ date = company['end_date']
+
+ for company in companies_iter:
+ new_company_name = company['company_name']
+
+ if start_date <= date <= end_date:
+ result.append({
+ 'user_id': user['user_id'],
+ 'user_name': user['user_name'],
+ 'old_company_name': old_company_name,
+ 'new_company_name': new_company_name,
+ 'date': date,
+ })
+
+ old_company_name = new_company_name
+ date = company['end_date']
+
+ return result
+
+
def _get_week(kwargs, param_name):
date_param = parameters.get_single_parameter(kwargs, param_name)
if date_param:
diff --git a/stackalytics/processor/utils.py b/stackalytics/processor/utils.py
index c9a0884b6..692751120 100644
--- a/stackalytics/processor/utils.py
+++ b/stackalytics/processor/utils.py
@@ -57,6 +57,11 @@ def iso8601_to_timestamp(s):
return int(time.mktime(iso8601.parse_date(s).timetuple()))
+def timestamp_to_date(timestamp):
+ return (datetime.datetime.fromtimestamp(timestamp).
+ strftime('%Y-%b-%d'))
+
+
def timestamp_to_week(timestamp):
# Jan 4th 1970 is the first Sunday in the Epoch
return (timestamp - 3 * 24 * 3600) // (7 * 24 * 3600)