Skip to content

Commit 64a1dcc

Browse files
authored
add some organization stuff to the dashboard (#17896)
1 parent 072fd89 commit 64a1dcc

File tree

5 files changed

+167
-21
lines changed

5 files changed

+167
-21
lines changed

tests/unit/admin/views/test_core.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,40 @@
1414

1515
from warehouse.admin.views import core as views
1616

17+
from ....common.db.organizations import (
18+
OrganizationApplicationFactory,
19+
OrganizationFactory,
20+
OrganizationRoleFactory,
21+
OrganizationStripeSubscriptionFactory,
22+
)
1723
from ....common.db.packaging import ProjectObservationFactory
1824

1925

2026
class TestDashboard:
21-
def test_dashboard(self, pyramid_request):
22-
pyramid_request.has_permission = pretend.call_recorder(lambda perm: False)
27+
def test_dashboard(self, db_request):
28+
company_orgs = OrganizationFactory.create_batch(7, orgtype="Company")
29+
OrganizationFactory.create_batch(11, orgtype="Community")
30+
OrganizationApplicationFactory.create_batch(5, orgtype="Company")
31+
OrganizationApplicationFactory.create_batch(3, orgtype="Community")
2332

24-
assert views.dashboard(pyramid_request) == {
33+
for organization in company_orgs:
34+
OrganizationRoleFactory.create_batch(2, organization=organization)
35+
36+
for organization in company_orgs[:3]:
37+
OrganizationStripeSubscriptionFactory.create(organization=organization)
38+
39+
db_request.user = pretend.stub()
40+
db_request.has_permission = pretend.call_recorder(lambda perm: False)
41+
42+
assert views.dashboard(db_request) == {
2543
"malware_reports_count": None,
44+
"organizations_count": {"Total": 18, "Community": 11, "Company": 7},
45+
"organization_applications_count": {"Total": 8, "submitted": 8},
46+
"active_company_organizations": 3,
47+
"active_company_organization_users": 6,
2648
}
2749

28-
assert pyramid_request.has_permission.calls == [
50+
assert db_request.has_permission.calls == [
2951
pretend.call(views.Permissions.AdminObservationsRead),
3052
]
3153

@@ -41,6 +63,10 @@ def test_dashboard_with_permission_and_observation(self, db_request):
4163

4264
assert views.dashboard(db_request) == {
4365
"malware_reports_count": 1,
66+
"organizations_count": {"Total": 0},
67+
"organization_applications_count": {"Total": 0},
68+
"active_company_organizations": 0,
69+
"active_company_organization_users": 0,
4470
}
4571
assert db_request.has_permission.calls == [
4672
pretend.call(views.Permissions.AdminObservationsRead),

warehouse/admin/templates/admin/base.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@
223223
<div class="container-fluid">
224224
<div class="row mb-2">
225225
<div class="col-sm-6">
226-
<h1 class="m-0">{{ self.title()|default('Dashboard', true) }}</h1>
226+
{% block title_header %}<h1 class="m-0">{{ self.title()|default('Dashboard', true) }}</h1>{% endblock %}
227227
</div><!-- /.col -->
228228
<div class="col-sm-6">
229229
<ol class="breadcrumb float-sm-right">

warehouse/admin/templates/admin/dashboard.html

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,59 @@
1414

1515
{% extends "base.html" %}
1616

17+
{% block title_header %}{% endblock %}
18+
1719
{% block content %}
20+
<h2>Organizations</h2>
21+
<div class="row">
22+
<div class="col-lg-4 col-6">
23+
<div class="small-box bg-gradient-info">
24+
<div class="inner">
25+
<h3>{{ organizations_count.Total }} Approved</h3>
26+
<h4>Community: {{ organizations_count.Community }}</h4>
27+
<h4>Company : {{ organizations_count.Company }}</h4>
28+
</div>
29+
<div class="icon">
30+
<i class="fa-solid fa-people-group"></i>
31+
</div>
32+
<a href="{{ request.route_path("admin.organization.list") }}"
33+
class="small-box-footer">
34+
View All <i class="fas fa-arrow-circle-right"></i>
35+
</a>
36+
</div>
37+
</div>
38+
<div class="col-lg-4 col-6">
39+
<div class="small-box bg-gradient-purple">
40+
<div class="inner">
41+
<h3>{{ organization_applications_count.submitted }} To Review</h3>
42+
<h4>Awaiting Response: {{ organization_applications_count.moreinformationneeded }}</h4>
43+
<h4>Deferred: {{ organization_applications_count.deferred }}</h4>
44+
</div>
45+
<div class="icon">
46+
<i class="fa-solid fa-inbox"></i>
47+
</div>
48+
<a href="{{ request.route_path("admin.organization_application.list") }}"
49+
class="small-box-footer">
50+
View All Reviewable <i class="fas fa-arrow-circle-right"></i>
51+
</a>
52+
</div>
53+
</div>
54+
</div>
55+
<h3>Company Orgs</h3>
56+
<div class="row">
57+
<div class="col-lg-4 col-6">
58+
<div class="small-box bg-gradient-success">
59+
<div class="inner">
60+
<h3>{{ active_company_organizations }} Subscribed</h3>
61+
<h4>With {{ active_company_organization_users }} Users</h4>
62+
<h4>${{ active_company_organization_users * 5 }} / mo</h4>
63+
</div>
64+
<div class="icon">
65+
<i class="fa-solid fa-money-bill-trend-up"></i>
66+
</div>
67+
</div>
68+
</div>
69+
</div>
1870
<div class="row">
1971
{% if malware_reports_count %}
2072
<div class="col-lg-4 col-6">

warehouse/admin/templates/admin/organization_applications/detail.html

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,6 @@ <h6 class="widget-user-username text-center">{{ organization_application.display
5959
<h2 class="card-title">Actions</h2>
6060
</div>
6161
<div class="card-body">
62-
<div class="form-group">
63-
<button
64-
type="button"
65-
class="btn btn-warning btn-block"
66-
id="editModalButton"
67-
data-toggle="modal"
68-
data-target="#editModal" {{ 'disabled' if not request.has_permission(Permissions.AdminOrganizationsWrite) or organization_application.status.value in ['approved', 'declined'] }}
69-
title="Edit organization request"
70-
>
71-
<i class="fa-solid fa-pen-to-square"></i> Edit
72-
</button>
73-
</div>
74-
7562
<div class="form-group">
7663
<button
7764
type="button"
@@ -136,8 +123,8 @@ <h4 class="modal-title" id="editModalLabel">
136123
</div>
137124
<div class="modal-footer justify-content-between">
138125
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
139-
<button type="submit" class="btn btn-primary">
140-
<i class="icon fa fa-edit"></i> Approve organization request
126+
<button type="submit" class="btn btn-warning">
127+
<i class="icon fa fa-edit"></i> Update Application
141128
</button>
142129
</div>
143130
</div>
@@ -335,6 +322,20 @@ <h4 class="modal-title" id="declineModalLabel">
335322
<label class="custom-control-label" for="organizationApplicationTurboMode">Turbo Mode</label>
336323
</div>
337324
</div>
325+
{% if request.user.is_superuser %}
326+
<div class="form-group">
327+
<button
328+
type="button"
329+
class="btn btn-warning btn-block"
330+
id="editModalButton"
331+
data-toggle="modal"
332+
data-target="#editModal" {{ 'disabled' if not request.has_permission(Permissions.AdminOrganizationsWrite) or organization_application.status.value in ['approved', 'declined'] }}
333+
title="Edit organization request"
334+
>
335+
<i class="fa-solid fa-pen-to-square"></i> Edit
336+
</button>
337+
</div>
338+
{% endif %}
338339
</div>
339340
</div>
340341
</div>

warehouse/admin/views/core.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515

1616
from warehouse.authnz import Permissions
1717
from warehouse.observations.models import Observation, ObservationKind
18+
from warehouse.organizations.models import (
19+
Organization,
20+
OrganizationApplication,
21+
OrganizationRole,
22+
OrganizationType,
23+
)
24+
from warehouse.subscriptions.models import StripeSubscription, StripeSubscriptionStatus
1825

1926

2027
@view_config(
@@ -36,4 +43,64 @@ def dashboard(request):
3643
else:
3744
malware_reports_count = None
3845

39-
return {"malware_reports_count": malware_reports_count}
46+
organizations_count = (
47+
request.db.query(Organization.orgtype, func.count(Organization.id))
48+
.group_by(Organization.orgtype)
49+
.all()
50+
)
51+
organizations_count = {k.value: v for k, v in organizations_count}
52+
organizations_count["Total"] = sum([v for k, v in organizations_count.items()])
53+
54+
organization_applications_count = (
55+
request.db.query(
56+
OrganizationApplication.status, func.count(OrganizationApplication.id)
57+
)
58+
.group_by(OrganizationApplication.status)
59+
.all()
60+
)
61+
organization_applications_count = {
62+
k.value: v for k, v in organization_applications_count
63+
}
64+
organization_applications_count["Total"] = sum(
65+
[v for k, v in organization_applications_count.items()]
66+
)
67+
68+
active_company_organizations = (
69+
request.db.query(func.count(Organization.id))
70+
.filter(Organization.orgtype == OrganizationType.Company)
71+
.filter(
72+
Organization.subscriptions.any(
73+
StripeSubscription.status.in_(
74+
(
75+
StripeSubscriptionStatus.Active.value,
76+
StripeSubscriptionStatus.Trialing.value,
77+
)
78+
)
79+
)
80+
)
81+
.scalar()
82+
)
83+
active_company_organization_users = (
84+
request.db.query(func.count(OrganizationRole.id))
85+
.join(Organization)
86+
.filter(Organization.orgtype == OrganizationType.Company)
87+
.filter(
88+
Organization.subscriptions.any(
89+
StripeSubscription.status.in_(
90+
(
91+
StripeSubscriptionStatus.Active.value,
92+
StripeSubscriptionStatus.Trialing.value,
93+
)
94+
)
95+
)
96+
)
97+
.scalar()
98+
)
99+
100+
return {
101+
"malware_reports_count": malware_reports_count,
102+
"organizations_count": organizations_count,
103+
"organization_applications_count": organization_applications_count,
104+
"active_company_organizations": active_company_organizations,
105+
"active_company_organization_users": active_company_organization_users,
106+
}

0 commit comments

Comments
 (0)