Skip to content

Commit

Permalink
Merge pull request #557 from dandi/dashboard
Browse files Browse the repository at this point in the history
Add rudimentary admin-only dashboard
  • Loading branch information
dchiquito authored Oct 8, 2021
2 parents 406847a + 66fcb54 commit 9416105
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 0 deletions.
54 changes: 54 additions & 0 deletions dandiapi/api/dashboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from django.core.exceptions import PermissionDenied
from django.db.models import Exists, OuterRef, Q
from django.views.generic.base import TemplateView

from dandiapi.api.models import Asset, AssetBlob, Upload, Version


class DashboardView(TemplateView):
template_name = 'dashboard/index.html'

def get_context_data(self, **kwargs):
if not self.request.user.is_superuser:
raise PermissionDenied()
context = super().get_context_data(**kwargs)
context['orphaned_asset_count'] = self._orphaned_asset_count()
context['orphaned_asset_blob_count'] = self._orphaned_asset_blob_count()
non_valid_assets = self._non_valid_assets()
context['non_valid_asset_count'] = non_valid_assets.count()
context['non_valid_assets'] = non_valid_assets[:10]
uploads = self._uploads()
context['upload_count'] = uploads.count()
context['uploads'] = uploads[:10]

return context

def _orphaned_asset_count(self):
return (
Asset.objects.annotate(
has_version=Exists(Version.objects.filter(assets=OuterRef('id')))
)
.filter(has_version=False)
.count()
)

def _orphaned_asset_blob_count(self):
return (
AssetBlob.objects.annotate(
has_asset=Exists(Asset.objects.filter(blob_id=OuterRef('id')))
)
.filter(has_asset=False)
.count()
)

def _non_valid_assets(self):
return (
Asset.objects.annotate(
has_version=Exists(Version.objects.filter(assets=OuterRef('id')))
)
.filter(has_version=True)
.filter(~Q(status=Asset.Status.VALID))
)

def _uploads(self):
return Upload.objects.annotate()
115 changes: 115 additions & 0 deletions dandiapi/api/templates/dashboard/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
{% extends "admin/base.html" %}

{% block title %} Data Dashboard {% endblock %}

{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></h1>
{% endblock %}

{% block nav-global %}{% endblock %}

{% block content %}
<div id="content-main">
<div>
<h1>Orphaned Assets</h1>
<p>There are currently {{ orphaned_asset_count }} Assets with no associated Versions.</p>
{% if orphaned_asset_count %}
<p>They can be cleaned up with the collect_garbage script.</p>
{% endif %}
</div>

<div>
<h1>Orphaned AssetBlobs</h1>
<p>There are currently {{ orphaned_asset_blob_count }} AssetBlobs with no associated Assets.</p>
{% if orphaned_asset_blob_count %}
<p>They can be cleaned up with the collect_garbage script.</p>
{% endif %}
</div>

<div>
<h1>Orphaned Uploads</h1>
<p>There are currently {{ upload_count }} Uploads.</p>
{% if uploads %}
<p>They may simply be very recent and currently uploading, or they might be abandoned.</p>
<table>
<caption>
Some Uploads
</caption>
<thead>
<tr>
<td>ID</td>
<td>Upload ID</td>
<td>Size (reported)</td>
<td>Size (actual)</td>
<td>ETag (reported)</td>
<td>ETag (actual)</td>
<td>Created</td>
<td>Modified</td>
</tr>
</thead>
{% for upload in uploads %}
<tr>
<td>{{ upload.id }}</td>
<td>{{ upload.upload_id }}</td>
<td>{{ upload.size }}</td>
<td>{% if upload.object_key_exists %} {{ upload.actual_size }} {% else %} Doesn't exist {% endif %}</td>
<td>{{ upload.etag }}</td>
<td>{% if upload.object_key_exists %} {{ upload.actual_etag }} {% else %} Doesn't exist {% endif %}</td>
<td>{{ upload.created }}</td>
<td>{{ upload.modified }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
</div>

<div>
<h1>Non-valid Assets</h1>
<p>There are currently {{ non_valid_asset_count }} Assets that are not valid.</p>
{% if non_valid_assets %}
<table>
<caption>
Some Assets that are not valid
</caption>
<thead>
<tr>
<td>ID</td>
<td>Asset ID</td>
<td>Path</td>
<td>Status</td>
<td>Validation Errors</td>
<td>Metadata</td>
<td>Blob ID</td>
<td>Size</td>
<td>SHA256</td>
<td>Versions</td>
<td>Created</td>
<td>Modified</td>
</tr>
</thead>
{% for asset in non_valid_assets %}
<tr>
<td>{{ asset.id }}</td>
<td>{{ asset.asset_id }}</td>
<td>{{ asset.path }}</td>
<td>{{ asset.status }}</td>
<td>{{ asset.validation_errors }}</td>
<td>{{ asset.metadata }}</td>
<td>{{ asset.blob.blob_id }}</td>
<td>{{ asset.size }}</td>
<td>{{ asset.blob.sha256 }}</td>
<td>
{% for version in asset.versions.all %}
{{ version.dandiset.identifier }}/{{ version.version }},
{% endfor %}
</td>
<td>{{ asset.created }}</td>
<td>{{ asset.modified }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
</div>

</div>
{% endblock %}
2 changes: 2 additions & 0 deletions dandiapi/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from rest_framework import permissions
from rest_framework_extensions.routers import ExtendedSimpleRouter

from dandiapi.api.dashboard import DashboardView
from dandiapi.api.views import (
AssetViewSet,
DandisetViewSet,
Expand Down Expand Up @@ -98,6 +99,7 @@ def to_url(self, value):
path('api/users/search/', users_search_view),
path('accounts/', include('allauth.urls')),
path('admin/', admin.site.urls),
path('dashboard/', DashboardView.as_view()),
path('oauth/', include('oauth2_provider.urls', namespace='oauth2_provider')),
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
Expand Down

0 comments on commit 9416105

Please sign in to comment.