Skip to content

Commit

Permalink
ADD registered users in last year visual
Browse files Browse the repository at this point in the history
  • Loading branch information
wabscale committed Feb 15, 2022
1 parent e45359f commit 9748b1a
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 55 deletions.
9 changes: 8 additions & 1 deletion api/anubis/utils/usage/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from sqlalchemy.sql import distinct

from anubis.models import db, Submission, TheiaSession
from anubis.models import db, Submission, TheiaSession, User
from anubis.utils.cache import cache
from anubis.utils.data import is_debug, is_job

Expand Down Expand Up @@ -43,3 +43,10 @@ def get_active_submission_users(day: datetime = None, end_day: datetime = None)
day_start, day_end = _get_day_start_end(day, end_day)
active_owner_ids = _get_active_ids(Submission, day_start, day_end)
return set(active_owner_ids)


@cache.memoize(timeout=60, source_check=True, unless=is_debug, forced_update=is_job)
def get_platform_users(day: datetime = None) -> int:
return User.query.filter(
User.created < day,
).count()
111 changes: 57 additions & 54 deletions api/anubis/utils/visuals/usage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from datetime import datetime, timedelta
from typing import Optional
from typing import Optional, List

from anubis.models import Assignment, Course, TheiaImage
from anubis.utils.cache import cache
Expand All @@ -9,14 +9,7 @@
from anubis.utils.usage.theia import get_theia_sessions
from anubis.utils.visuals.files import convert_fig_bytes
from anubis.utils.usage.users import get_active_submission_users, get_active_theia_users


def add_watermark(ax, utcnow):
ax.text(
0.97, 0.9, f"Generated {utcnow} UTC",
transform=ax.transAxes, fontsize=12, color="gray", alpha=0.5,
ha="right", va="center",
)
from anubis.utils.visuals.watermark import add_watermark


@cache.memoize(timeout=-1, forced_update=is_job, unless=is_debug)
Expand All @@ -31,67 +24,80 @@ def get_usage_plot(course_id: Optional[str]) -> Optional[bytes]:
if course is None:
return None

assignments = Assignment.query.filter(
assignments: List[Assignment] = Assignment.query.filter(
Assignment.hidden == False,
Assignment.release_date <= datetime.now(),
Assignment.course_id == course_id,
).all()
).order_by(Assignment.release_date.desc()).all()
submissions = get_submissions(course_id)
theia_sessions = get_theia_sessions(course_id)

fig, axs = plt.subplots(2, 1, figsize=(12, 10))

legend_handles0 = []
legend_handles1 = []

# submissions over hour line
submissions.groupby(["assignment_id", "created"])["id"].count().reset_index().rename(
ss = submissions.groupby(["assignment_id", "created"])["id"].count().reset_index().rename(
columns={"id": "count"}
).groupby("assignment_id").plot(x="created", label=None, ax=axs[0])
).groupby("assignment_id")

# ides over hour line
theia_sessions.groupby(["assignment_id", "created"])["id"].count().reset_index().rename(
tt = theia_sessions.groupby(["assignment_id", "created"])["id"].count().reset_index().rename(
columns={"id": "count"}
).groupby("assignment_id").plot(x="created", label=None, ax=axs[1])

# assignment release line
for color, assignment in zip(mcolors.TABLEAU_COLORS, assignments):
legend_handles0.append(
axs[0].axvline(
x=assignment.due_date,
color=color,
linestyle="dotted",
label=f"{assignment.name}",
)
)
legend_handles1.append(
axs[1].axvline(
x=assignment.due_date,
color=color,
linestyle="dotted",
label=f"{assignment.name}",
)
)

utcnow = datetime.utcnow().replace(microsecond=0)

add_watermark(axs[0], utcnow)
axs[0].legend(handles=legend_handles0, loc="upper left")
).groupby("assignment_id")

assignment_colors = {
assignment.id: color
for assignment, color in zip(assignments, mcolors.TABLEAU_COLORS)
}

for key, group in ss:
for assignment in assignments:
if assignment.id == key:
logger.info(f'PLOTTING ASSIGNMENT SUBMISSIONS = {assignment.id}')
color = assignment_colors[assignment.id]
axs[0].plot(group['created'], group['count'], color=color, label=None)
axs[0].axvline(
x=assignment.due_date,
color=color,
linestyle="dotted",
label=f"{assignment.name}",
)

for key, group in tt:
for assignment in assignments:
if assignment.id == key:
logger.info(f'PLOTTING ASSIGNMENT IDES = {assignment.id}')
color = assignment_colors[assignment.id]
axs[1].plot(group['created'], group['count'], color=color, label=None)
axs[1].axvline(
x=assignment.due_date,
color=color,
linestyle="dotted",
label=f"{assignment.name}",
)

# Watermarks
add_watermark(axs[0])
add_watermark(axs[1])

# Legends
axs[0].legend(loc="upper left")
axs[1].legend(loc="upper left")

# Grids
axs[0].grid(True)
axs[1].grid(True)

# Labels
axs[0].set(
title=f"{course.course_code} - Submissions over time",
xlabel="time",
ylabel="count",
)
axs[0].grid(True)

add_watermark(axs[1], utcnow)
axs[1].legend(handles=legend_handles1, loc="upper left")
axs[1].set(
title=f"{course.course_code} - Cloud IDEs over time",
xlabel="time",
ylabel="count",
)
axs[1].grid(True)

return convert_fig_bytes(plt, fig)

Expand All @@ -100,8 +106,6 @@ def get_usage_plot(course_id: Optional[str]) -> Optional[bytes]:
def get_usage_plot_playgrounds():
import matplotlib.pyplot as plt

now = datetime.now().replace(microsecond=0)

fig, ax = plt.subplots(figsize=(12, 10))

images = TheiaImage.query.filter().order_by(TheiaImage.id.desc()).all()
Expand All @@ -114,15 +118,15 @@ def get_usage_plot_playgrounds():
for image in images:
if image.id == key and image.public:
ax.plot(group["created"], group["count"], label=image.title)
ax.legend()

add_watermark(ax, datetime.utcnow())
add_watermark(ax)
ax.legend()
ax.grid()
ax.set(
title=f"Anubis Playgrounds - IDEs spawned per hour",
xlabel="time",
ylabel="IDEs spawned",
)
ax.grid(True)

return convert_fig_bytes(plt, fig)

Expand Down Expand Up @@ -155,7 +159,7 @@ def get_usage_plot_active(days: int = 14, step: int = 1):
ax.plot(xx, autograde_y, 'g--', label='Total users that used Anubis Autograder')
ax.legend()

add_watermark(ax, datetime.utcnow())
add_watermark(ax)
ax.set(
title=f"Anubis LMS - Active users in the last {days} days - step {step} days",
xlabel="time",
Expand All @@ -164,4 +168,3 @@ def get_usage_plot_active(days: int = 14, step: int = 1):
ax.grid(True)

return convert_fig_bytes(plt, fig)

43 changes: 43 additions & 0 deletions api/anubis/utils/visuals/users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from anubis.utils.usage.users import get_platform_users
from anubis.utils.cache import cache
from anubis.utils.data import is_debug, is_job
from datetime import datetime, timedelta
from anubis.utils.visuals.files import convert_fig_bytes
from anubis.utils.visuals.watermark import add_watermark


@cache.memoize(timeout=-1, forced_update=is_job, unless=is_debug)
def get_platform_users_plot(days: int, step: int = 1):
import matplotlib.pyplot as plt

now = datetime.now().replace(hour=0, second=0, microsecond=0)
start_datetime = now - timedelta(days=days - 1)

xx = []
yy = []

fig, ax = plt.subplots(figsize=(12, 10))

for n in range(0, days, step):
day = start_datetime + timedelta(days=n)
y = get_platform_users(day)

xx.append(day)
yy.append(y)

ax.plot(xx, yy, 'b--', label='Total users registered on platform')

ax.legend(loc='upper left')
ax.grid()
ax.set(
title='Total users registered on Anubis LMS over time',
xlabel='Time',
ylabel='Registered Users',
)
# Set y max to the nearest hundred
ax.set_ylim([0, ((yy[-1] // 100) + 1) * 100])
add_watermark(ax)

return convert_fig_bytes(plt, fig)


9 changes: 9 additions & 0 deletions api/anubis/utils/visuals/watermark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from datetime import datetime


def add_watermark(ax):
ax.text(
0.97, 0.9, f"Generated {datetime.now()}",
transform=ax.transAxes, fontsize=12, color="gray", alpha=0.5,
ha="right", va="center",
)
10 changes: 10 additions & 0 deletions api/anubis/views/public/visuals.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from anubis.utils.http import req_assert
from anubis.utils.http.files import make_png_response
from anubis.utils.visuals.usage import get_usage_plot, get_usage_plot_playgrounds, get_usage_plot_active
from anubis.utils.visuals.users import get_platform_users_plot

visuals_ = Blueprint("public-visuals", __name__, url_prefix="/public/visuals")

Expand Down Expand Up @@ -68,3 +69,12 @@ def public_visuals_usage_active_month():
blob = get_usage_plot_active(90, 7)

return make_png_response(blob)


@visuals_.route("/users/last-year")
def public_visuals_users_last_year():
# Get the png blob of the usage graph.
# The get_usage_plot is itself a cached function.
blob = get_platform_users_plot(365, 7)

return make_png_response(blob)
5 changes: 5 additions & 0 deletions web/src/pages/core/public/Visuals/Visuals.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ import standardErrorHandler from '../../../../utils/standardErrorHandler';


const visuals = [
{
title: 'Anubis LMS Registered Users Last Year',
url: `/api/public/visuals/users/last-year`,
filename: 'anubis-registered-users-last-year.png',
},
{
title: 'Anubis LMS Active Users Per Day In Last 2 Weeks',
url: `/api/public/visuals/usage/active`,
Expand Down

0 comments on commit 9748b1a

Please sign in to comment.