Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions scripts/get-repo-workspace-metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/usr/bin/env python3
"""
Enter one line description here.

File:

Copyright 2025 Ankur Sinha
Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
"""

import requests
import datetime
from dateutil.relativedelta import relativedelta
import matplotlib.pyplot as plt
import matplotlib

# repositories_url = "https://v2.opensourcebrain.org/proxy/workspaces/api/osbrepository?page=1&per_page=4000"
repositories_url = "https://v2.opensourcebrain.org/proxy/workspaces/api/osbrepository?page=1&per_page=40"
workspaces_url = (
"https://v2.opensourcebrain.org/proxy/workspaces/api/workspace?page=1&per_page=1500"
)


def get_repo_metrics():
"""Get metrics on number of repositories on OSBv2"""
resp = requests.get(repositories_url)

resp.raise_for_status()
data = resp.json()
repositories = data["osbrepositories"]
total_number = data["pagination"]["total"]
assert total_number == len(repositories)
print(f">>> Number of repositories: {len(repositories)}")

model_or_data = {"models": 0, "data": 0}
repo_types = {"biomodels": 0, "dandi": 0, "github": 0, "figshare": 0}
creation_time_stamps = []

for repo in repositories:
repo_types[repo["repository_type"]] += 1
if repo["content_types_list"][0] == "modeling":
model_or_data["models"] += 1
else:
model_or_data["data"] += 1

creation_time_stamps.append(
datetime.datetime.fromisoformat(repo["timestamp_created"])
)

print(">>> Type break down:")
print(model_or_data)
print(repo_types)
print(creation_time_stamps[0:10])
print(creation_time_stamps[0:-10])
return creation_time_stamps


def get_workspace_metrics():
"""Get metrics on number of repositories on OSBv2"""
resp = requests.get(workspaces_url)

resp.raise_for_status()
data = resp.json()
workspaces = data["workspaces"]
total_number = data["pagination"]["total"]
assert total_number == len(workspaces)
print(f">>> Number of workspaces: {len(workspaces)}")

public_or_private = {"public": 0, "private": 0}

creation_time_stamps = []

for ws in workspaces:
if ws["publicable"]:
public_or_private["public"] += 1
else:
public_or_private["private"] += 1
creation_time_stamps.append(
datetime.datetime.fromisoformat(ws["timestamp_created"])
)

creation_time_stamps = sorted(creation_time_stamps)

print(">>> Type break down:")
print(public_or_private)
print(creation_time_stamps[0:10])
print(creation_time_stamps[0:-10])
return creation_time_stamps


def plot_trend(title, creation_time_stamps, timewindow=2):
time_now = datetime.datetime.now()
trend_start = datetime.datetime(2021, 1, 1, 0, 0, 0)

plot_data_x = []
plot_data_y = []

index = 0
trend_point = trend_start
for ts in creation_time_stamps:
index += 1
if ts.timestamp() > trend_point.timestamp():
plot_data_x.append(trend_point)
plot_data_y.append(index)
trend_point += relativedelta(months=timewindow)

assert index == len(creation_time_stamps)
plot_data_x.append(time_now)
plot_data_y.append(index)

fig, ax = plt.subplots()
ax.plot(
plot_data_x,
plot_data_y,
)
ax.set_xlabel("Date")
ax.set_ylabel(f"Number of {title}")
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.xaxis.set(
major_locator=matplotlib.dates.MonthLocator(interval=timewindow),
major_formatter=matplotlib.dates.DateFormatter("%Y-%m"),
)

plt.xticks(rotation=90)
plt.tight_layout()
plt.show()


if __name__ == "__main__":
repo_timestamps = get_repo_metrics()
plot_trend(title="Repositories", creation_time_stamps=repo_timestamps)
workspace_timestamps = get_workspace_metrics()
plot_trend(title="Workspaces", creation_time_stamps=workspace_timestamps)
138 changes: 138 additions & 0 deletions scripts/get-user-metrics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/env python3
"""
Script to get user metrics and plot them. This can only be used by admins,
since it requires access to the complete user list in KeyCloak.

File: get-user-metrics.py

Copyright 2025 Ankur Sinha
Author: Ankur Sinha <sanjay DOT ankur AT gmail DOT com>
"""

import sys
import datetime
import requests
import logging
import matplotlib
matplotlib.use('qtagg')
import matplotlib.pyplot as plt
import json
from dateutil.relativedelta import relativedelta

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)


def run(keycloak_user, keycloak_password, save_json=False):
"""Main runner method.

:param keycloak_user: keycloak username
:type keycloak_user: string
:param keycloak_password: keycloak password
:type keycloak_password: string
:param save_json: whether the downloaded user data should be saved to file
:type save_json: bool

"""
keycloak_url = "https://accounts.v2.opensourcebrain.org/auth"
keycloak_realm = "osb2"
logger.debug(f"Username: {keycloak_user} Password: {keycloak_password}")

resp = requests.post(
f"{keycloak_url}/realms/master/protocol/openid-connect/token",
data={
"client_id": "admin-cli",
"username": keycloak_user,
"password": keycloak_password,
"grant_type": "password",
},
)
resp.raise_for_status()
data = resp.json()
access_token = data["access_token"]
logger.debug(access_token)

auth_headers = {"Authorization": f"Bearer {access_token}"}

resp = requests.get(
f"{keycloak_url}/admin/realms/{keycloak_realm}/users/count",
headers=auth_headers,
)

resp.raise_for_status()
data = resp.json()
total_users = data
print(f">>> Number of users: {total_users}")

resp = requests.get(
f"{keycloak_url}/admin/realms/{keycloak_realm}/users",
headers=auth_headers,
params={"max": total_users},
)

resp.raise_for_status()
data = resp.json()

if save_json:
with open("osbv2-user-data.json", 'w') as f:
json.dump(data, f)

get_user_trends(data)


def get_user_trends(data, timewindow=2):
"""Get trends for user registration, and plot figure

:param data: user data from keycloak
:type data: list[dict]
:param timewindow: size of window/bin for plotting, in months
:type timewindow: int
"""
time_now = datetime.datetime.now()
trend_start = datetime.datetime(2021, 1, 1, 0, 0, 0)

# note, milliseconds
creation_time_stamps = sorted([au["createdTimestamp"] / 1000 for au in data])

plot_data_x = []
plot_data_y = []

index = 0
trend_point = trend_start
for ts in creation_time_stamps:
index += 1
if ts > trend_point.timestamp():
plot_data_x.append(trend_point)
plot_data_y.append(index)
trend_point += relativedelta(months=timewindow)

assert index == len(creation_time_stamps)
plot_data_x.append(time_now)
plot_data_y.append(index)

fig, ax = plt.subplots()
plt.title("OSBv2 users over time")
ax.plot(
plot_data_x,
plot_data_y,
)
ax.set_xlabel("Date")
ax.set_ylabel("Number of users")
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.xaxis.set(
major_locator=matplotlib.dates.MonthLocator(interval=timewindow),
major_formatter=matplotlib.dates.DateFormatter("%Y-%m"),
)

plt.xticks(rotation=90)
plt.tight_layout()
plt.show()


if __name__ == "__main__":
if len(sys.argv) < 3:
print("Error: two arguments required: username and password")
sys.exit(-1)
run(sys.argv[1], sys.argv[2])
3 changes: 3 additions & 0 deletions scripts/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
matplotlib
requests
pyqt6
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
(contribute)=
# Contribute
(contribute:osb)=
# Contribute to OSB development

There is a central repository for issues listing OSB projects looking for contributors. A number of other programming tasks (e.g. further development of NeuroML based tools) are also listed, which would be of benefit to OSB projects.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(general:contribute_docs)=
(contribute:docs)=
# Contribute to OSB documentation

This documentation is written using [Jupyter Book](https://jupyterbook.org).
Expand Down
Loading