Skip to content

Commit

Permalink
Merge pull request #1038 from thunderstore-io/Analysis
Browse files Browse the repository at this point in the history
Necessary changes for better analysis tools
  • Loading branch information
MythicManiac authored Jul 11, 2024
2 parents fdd82cc + 48fbbf2 commit f0b063c
Show file tree
Hide file tree
Showing 17 changed files with 142 additions and 18 deletions.
19 changes: 18 additions & 1 deletion django/thunderstore/community/admin/package_listing.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from html import escape
from typing import Optional

from django.contrib import admin
from django.db import transaction
from django.db.models import QuerySet
from django.utils.safestring import mark_safe

from ..consts import PackageListingReviewStatus
from ..forms import PackageListingAdminForm
Expand Down Expand Up @@ -62,6 +64,7 @@ class PackageListingAdmin(admin.ModelAdmin):
"package__namespace__name",
"package__owner__name",
"package__name",
"package__versions__file_tree__entries__blob__checksum_sha256",
)
list_select_related = (
"package",
Expand All @@ -70,18 +73,32 @@ class PackageListingAdmin(admin.ModelAdmin):
"community",
)
readonly_fields = (
"package",
"package_link",
"community",
"datetime_created",
"datetime_updated",
)
exclude = ("package",)

def package_link(self, obj):
return mark_safe(
f'<a href="{obj.package.get_admin_url()}">{escape(str(obj.package))}</a>'
)

package_link.short_description = "Package"

def get_readonly_fields(self, request, obj=None):
if obj:
return self.readonly_fields
else:
return []

def get_exclude(self, request, obj=None):
if obj:
return self.exclude
else:
return []

def get_view_on_site_url(
self, obj: Optional[PackageListing] = None
) -> Optional[str]:
Expand Down
4 changes: 2 additions & 2 deletions django/thunderstore/community/models/package_listing.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from thunderstore.cache.enums import CacheBustCondition
from thunderstore.cache.tasks import invalidate_cache_on_commit_async
from thunderstore.community.consts import PackageListingReviewStatus
from thunderstore.core.mixins import TimestampMixin
from thunderstore.core.mixins import AdminLinkMixin, TimestampMixin
from thunderstore.core.types import UserType
from thunderstore.core.utils import check_validity
from thunderstore.frontend.url_reverse import get_community_url_reverse_args
Expand Down Expand Up @@ -46,7 +46,7 @@ def filter_by_community_approval_rule(self):
# TODO: Add a db constraint that ensures a package listing and it's categories
# belong to the same community. This might require actually specifying
# the intermediate model in code rather than letting Django handle it
class PackageListing(TimestampMixin, models.Model):
class PackageListing(TimestampMixin, AdminLinkMixin, models.Model):
"""
Represents a package's relation to how it's displayed on the site and APIs
"""
Expand Down
12 changes: 12 additions & 0 deletions django/thunderstore/core/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.db import DEFAULT_DB_ALIAS, connections, models
from django.db.models import Q
from django.shortcuts import redirect
from django.urls import reverse

from thunderstore.cache.storage import CACHE_STORAGE

Expand Down Expand Up @@ -106,3 +107,14 @@ class Meta:
indexes = [
models.Index(fields=["last_modified"]),
]


class AdminLinkMixin(models.Model):
def get_admin_url(self):
return reverse(
f"admin:{self._meta.app_label}_{self._meta.model_name}_change",
args=[self.pk],
)

class Meta:
abstract = True
30 changes: 27 additions & 3 deletions django/thunderstore/repository/admin/package.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from html import escape
from typing import Optional

from django.contrib import admin
from django.db import transaction
from django.db.models import QuerySet
from django.http import HttpRequest
from django.utils.safestring import mark_safe

from thunderstore.repository.admin.actions import activate, deactivate
from thunderstore.repository.models import Package, PackageVersion
Expand All @@ -12,19 +14,26 @@
class PackageVersionInline(admin.StackedInline):
model = PackageVersion
readonly_fields = (
"version_link",
"date_created",
"description",
"downloads",
"file",
"file_size",
"format_spec",
"icon",
"version_number",
"file_tree_link",
"visibility",
"website_url",
)
exclude = (
"version_number",
"file_tree",
"visibility",
"dependencies",
"name",
"readme",
"changelog",
)
exclude = ("readme", "changelog", "dependencies", "name")
extra = 0

def has_add_permission(self, request: HttpRequest, obj) -> bool:
Expand All @@ -33,6 +42,20 @@ def has_add_permission(self, request: HttpRequest, obj) -> bool:
def has_delete_permission(self, request: HttpRequest, obj=None) -> bool:
return False

def version_link(self, obj):
return mark_safe(f'<a href="{obj.get_admin_url()}">{escape(str(obj))}</a>')

version_link.short_description = "Version"

def file_tree_link(self, obj):
if not obj.file_tree:
return None
return mark_safe(
f'<a href="{obj.file_tree.get_admin_url()}">{escape(str(obj.file_tree))}</a>'
)

file_tree_link.short_description = "File Tree"


@transaction.atomic
def deprecate_package(modeladmin, request, queryset: QuerySet[Package]):
Expand Down Expand Up @@ -93,6 +116,7 @@ class PackageAdmin(admin.ModelAdmin):
"name",
"namespace__name",
"owner__name",
"versions__file_tree__entries__blob__checksum_sha256",
)
list_select_related = (
"latest",
Expand Down
19 changes: 19 additions & 0 deletions django/thunderstore/repository/admin/package_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.urls import reverse
from django.utils.safestring import mark_safe

from thunderstore.community.models import PackageListing
from thunderstore.repository.models import PackageVersion
from thunderstore.repository.tasks.files import extract_package_version_file_tree

Expand Down Expand Up @@ -41,10 +42,15 @@ class PackageVersionAdmin(admin.ModelAdmin):
"package__owner__name",
"package__namespace__name",
"version_number",
"file_tree__entries__blob__checksum_sha256",
)
date_hierarchy = "date_created"
readonly_fields = [x.name for x in PackageVersion._meta.fields] + [
"file_tree_link",
"listings",
]
exclude = [
"file_tree",
]

def get_queryset(self, request: HttpRequest) -> QuerySet:
Expand All @@ -58,6 +64,9 @@ def get_queryset(self, request: HttpRequest) -> QuerySet:
)
)

def get_readonly_fields(self, request, obj=None):
return [x for x in self.readonly_fields if x not in self.exclude]

def has_file_tree(self, obj):
return obj.has_file_tree

Expand All @@ -73,6 +82,16 @@ def file_tree_link(self, obj):
)
return mark_safe(f'<a href="{url}">{obj.file_tree}</a>')

file_tree_link.short_description = "File tree"

def listings(self, obj):
url = reverse(
f"admin:{PackageListing._meta.app_label}_{PackageListing._meta.model_name}_changelist",
)
return mark_safe(
f'<a href="{url}?package__exact={obj.package.pk}">View package listings</a>'
)

def has_add_permission(self, request: HttpRequest) -> bool:
return False

Expand Down
3 changes: 2 additions & 1 deletion django/thunderstore/repository/models/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from thunderstore.cache.enums import CacheBustCondition
from thunderstore.cache.tasks import invalidate_cache_on_commit_async
from thunderstore.core.enums import OptionalBoolChoice
from thunderstore.core.mixins import AdminLinkMixin
from thunderstore.core.types import UserType
from thunderstore.core.utils import check_validity
from thunderstore.permissions.utils import validate_user
Expand Down Expand Up @@ -43,7 +44,7 @@ def get_package_dependants_list(package_pk: int):
return list(get_package_dependants(package_pk))


class Package(models.Model):
class Package(AdminLinkMixin, models.Model):
objects = PackageQueryset.as_manager()
wiki: Optional["PackageWiki"]

Expand Down
3 changes: 2 additions & 1 deletion django/thunderstore/repository/models/package_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from django.utils import timezone
from django.utils.functional import cached_property

from thunderstore.core.mixins import AdminLinkMixin
from thunderstore.permissions.mixins import VisibilityMixin, VisibilityQuerySet
from thunderstore.repository.consts import PACKAGE_NAME_REGEX
from thunderstore.repository.models import Package
Expand Down Expand Up @@ -66,7 +67,7 @@ def listed_in(self, community_identifier: str):
)


class PackageVersion(VisibilityMixin):
class PackageVersion(VisibilityMixin, AdminLinkMixin):
installers: "Manager[PackageInstaller]"
installer_declarations: "Manager[PackageInstallerDeclaration]"
objects: "Manager[PackageVersion]" = PackageVersionQuerySet.as_manager()
Expand Down
25 changes: 25 additions & 0 deletions django/thunderstore/repository/tests/test_tabs_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,28 @@ def assert_disabled(user: UserType, expected: bool) -> None:
active_package_listing.package.latest.changelog = "# Foo bar"
active_package_listing.package.latest.save()
assert_disabled(user, False)


@pytest.mark.django_db
def test_active_tabs_are_visible(
user: UserType,
active_package_listing: PackageListing,
) -> None:
tabs_mixin = PackageTabsMixin()

tabs1 = tabs_mixin.get_tab_context(
user,
active_package_listing,
"details",
)["tabs"]

for tab1 in tabs1:
tabs2 = tabs_mixin.get_tab_context(
user,
active_package_listing,
tab1.name,
)["tabs"]

for tab2 in tabs2:
if tab2.is_active:
assert tab2.is_visible
4 changes: 4 additions & 0 deletions django/thunderstore/repository/views/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class PartialTab:
url: str
title: str
is_disabled: bool = False
is_visible: bool = True


@dataclasses.dataclass
Expand All @@ -24,6 +25,7 @@ class Tab:
name: str
url: str
is_disabled: bool
is_visible: bool
is_active: bool


Expand Down Expand Up @@ -67,9 +69,11 @@ def get_tab_context(
name=k,
url=v.url,
is_disabled=v.is_disabled and k != active_tab,
is_visible=v.is_visible or k == active_tab,
is_active=k == active_tab,
)
for k, v in tabs.items()
if (v.is_visible or k == active_tab)
],
}

Expand Down
12 changes: 10 additions & 2 deletions django/thunderstore/storage/admin/group.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from html import escape

from django.contrib import admin
from django.http import HttpRequest
from django.utils.safestring import mark_safe
Expand All @@ -9,17 +11,23 @@
class DataBlobReferenceInline(ReadOnlyInline, admin.TabularInline):
model = DataBlobReference
fields = (
"name",
"link",
"data_size",
"file",
)
readonly_fields = (
"link",
"data_size",
"file",
)

def file(self, obj: DataBlobReference):
return mark_safe(f'<a href="{obj.data_url}">{obj.blob.data}</a>')
return mark_safe(f'<a href="{obj.data_url}">{escape(str(obj.blob.data))}</a>')

def link(self, obj: DataBlobReference):
return mark_safe(f'<a href="{obj.get_admin_url()}">{escape(str(obj))}</a>')

link.short_description = "Name"


@admin.register(DataBlobGroup)
Expand Down
13 changes: 11 additions & 2 deletions django/thunderstore/storage/admin/reference.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from html import escape

from django.contrib import admin
from django.http import HttpRequest
from django.utils.safestring import mark_safe
Expand All @@ -9,7 +11,7 @@
class DataBlobReferenceAdmin(admin.ModelAdmin):
list_display = (
"name",
"group",
"linked_group",
"data_size",
"content_type",
"content_encoding",
Expand All @@ -32,7 +34,14 @@ class DataBlobReferenceAdmin(admin.ModelAdmin):
date_hierarchy = "datetime_created"

def file(self, obj: DataBlobReference):
return mark_safe(f'<a href="{obj.data_url}">{obj.blob.data}</a>')
return mark_safe(f'<a href="{obj.data_url}">{escape(str(obj.blob.data))}</a>')

def linked_group(self, obj: DataBlobReference):
return mark_safe(
f'<a href="{obj.group.get_admin_url()}">{escape(str(obj.group))}</a>'
)

linked_group.short_description = "group"

def has_add_permission(self, request: HttpRequest) -> bool:
return False
Expand Down
4 changes: 2 additions & 2 deletions django/thunderstore/storage/models/blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
from django.core.files.storage import get_storage_class
from django.db import models

from thunderstore.core.mixins import SafeDeleteMixin
from thunderstore.core.mixins import AdminLinkMixin, SafeDeleteMixin


def get_object_file_path(_, filename: str) -> str:
return f"blob-storage/sha256/{filename}.sha256.blob"


class DataBlob(SafeDeleteMixin):
class DataBlob(SafeDeleteMixin, AdminLinkMixin):
"""
The DataBlob class is responsible for storing arbitrary blobs of data with
automatic deduplication by blob sha256 checksum. It is not interested in
Expand Down
Loading

0 comments on commit f0b063c

Please sign in to comment.