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
26 changes: 25 additions & 1 deletion src/olympia/amo/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
from django.utils.html import format_html, format_html_join

from django_vite.templatetags.django_vite import vite_hmr_client
from rangefilter.filters import DateRangeFilter as DateRangeFilterBase
from rangefilter.filters import (
DateRangeFilter as DateRangeFilterBase,
NumericRangeFilter as NumericRangeFilterBase,
)

from olympia.activity.models import IPLog
from olympia.amo.models import GroupConcat, Inet6Ntoa
Expand Down Expand Up @@ -642,6 +645,27 @@ def choices(self, changelist):
yield all_choice


class NumericRangeFilter(FakeChoicesMixin, NumericRangeFilterBase):
"""
Custom rangefilter.filters.NumericRangeFilter class without the need for
inline CSS/JavaScript.
Needs FakeChoicesMixin for the fake choices the template will be using (the
upstream implementation depends on inline JavaScript for this, which we
want to avoid).
"""

template = 'admin/amo/numeric_range_filter.html'

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When testing the label tags were empty (the label tag existed, but there was no text value). Date range filter fields get "To" and "From" labels; numeric range filter fields did not - though the placeholder texts did say "To" and "From".

Do you need to override _get_form_fields too?

At that point, you probably want to inherit from DateRangeFilter, or a common ancestor - it's worth considering anyway as your choices seems to be a direct copy and paste.

def choices(self, changelist):
# We want a fake 'All' choice as per FakeChoicesMixin, but as of 0.3.15
# rangefilter's implementation doesn't bother setting the selected
# property, and our mixin calls super(), so we have to do it here.
all_choice = next(super().choices(changelist))
all_choice['selected'] = not any(self.used_parameters)
yield all_choice


class MultipleRelatedListFilter(admin.SimpleListFilter):
template = 'admin/amo/multiple_filter.html'

Expand Down
4 changes: 3 additions & 1 deletion src/olympia/amo/templates/admin/amo/date_range_filter.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<h3>By {{ title }}</h3>
<details data-filter-title="{{ title }}" open>
<summary>By {{ title }}</summary>
{% with choices.0 as all_choice %}
<form method="GET" action=".">
<div>
Expand All @@ -24,3 +25,4 @@ <h3>By {{ title }}</h3>
</ul>
</form>
{% endwith %}
</details>
29 changes: 29 additions & 0 deletions src/olympia/amo/templates/admin/amo/numeric_range_filter.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<details data-filter-title="{{ title }}" open>
<summary>By {{ title }}</summary>
{% with choices.0 as all_choice %}
<form method="GET" action=".">
<div>
{# Use .params.lists to properly support multiple values - so that if a filter using this template is present multiple times on the page, its current values are all passed to the new page when submitting this form #}
{% for key, values in all_choice.params.lists %}
{% for value in values %}
<input type="hidden" name="{{ key }}" value="{{ value }}" />
{% endfor %}
{% endfor %}
</div>
<ul>
<li {% if all_choice.selected %}class="selected"{% endif %}>
<a href="{{ all_choice.query_string }}">All</a>
</li>
<li>
{% for field in spec.form %}
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% endfor %}
</li>
<li>
<input type="submit" value="Submit" />
</li>
</ul>
</form>
{% endwith %}
</details>
2 changes: 1 addition & 1 deletion src/olympia/amo/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ def addon_factory(status=amo.STATUS_APPROVED, version_kw=None, file_kw=None, **k

# version_changed task will be triggered and will update last_updated in
# database for this add-on depending on the state of the version / files.
# We're calling the function it uses to compute the value ourselves and=
# We're calling the function it uses to compute the value ourselves and
# sticking that into the attribute ourselves so that we already have the
# correct value in the instance we are going to return.
# Note: the aim is to have the instance consistent with what will be in the
Expand Down
19 changes: 19 additions & 0 deletions src/olympia/files/migrations/0036_alter_file_datestatuschanged.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.24 on 2025-10-03 17:51

from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

dependencies = [
('files', '0035_remove_file_reviewed'),
]

operations = [
migrations.AlterField(
model_name='file',
name='datestatuschanged',
field=models.DateTimeField(default=django.utils.timezone.now, null=True),
),
]
3 changes: 2 additions & 1 deletion src/olympia/files/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.db import models
from django.dispatch import receiver
from django.urls import reverse
from django.utils import timezone
from django.utils.crypto import get_random_string
from django.utils.encoding import force_str
from django.utils.functional import cached_property
Expand Down Expand Up @@ -112,7 +113,7 @@ class File(OnChangeMixin, ModelBase):
status = models.PositiveSmallIntegerField(
choices=STATUS_CHOICES.items(), default=amo.STATUS_AWAITING_REVIEW
)
datestatuschanged = models.DateTimeField(null=True, auto_now_add=True)
datestatuschanged = models.DateTimeField(null=True, default=timezone.now)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not saying this is a bad change to make ... but we could just special case datestatuschanged field in file_factory - pop the value and set it after the create, and so avoid any database/model change to prod.

strict_compatibility = models.BooleanField(default=False)
approval_date = models.DateTimeField(null=True)
# Serial number of the certificate use for the signature.
Expand Down
40 changes: 38 additions & 2 deletions src/olympia/scanners/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@
from olympia import amo
from olympia.access import acl
from olympia.addons.models import Addon
from olympia.amo.admin import AMOModelAdmin, MultipleRelatedListFilter
from olympia.amo.admin import (
AMOModelAdmin,
DateRangeFilter,
MultipleRelatedListFilter,
NumericRangeFilter,
)
from olympia.amo.templatetags.jinja_helpers import vite_asset
from olympia.amo.utils import is_safe_url
from olympia.constants import scanners
Expand Down Expand Up @@ -202,6 +207,24 @@ def __init__(self, *args, **kwargs):
self.title = 'version channel'


class VersionCreatedFilter(DateRangeFilter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title = 'version creation date'


class AddonCreatedFilter(DateRangeFilter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title = 'add-on creation date'


class AddonLastUpdatedFilter(DateRangeFilter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title = 'add-on last updated date'


class AddonStatusFilter(admin.ChoicesFieldListFilter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -230,6 +253,12 @@ def choices(self, changelist):
}


class AddonAverageDailyUsers(NumericRangeFilter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title = 'add-on ADU'


class FileStatusFilter(admin.ChoicesFieldListFilter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -735,8 +764,12 @@ class ScannerQueryResultAdmin(AbstractScannerResultAdminMixin, AMOModelAdmin):
list_filter = (
('matched_rule', ScannerRuleListFilter),
('version__channel', VersionChannelFilter),
('version__created', VersionCreatedFilter),
('version__addon__status', AddonStatusFilter),
('version__addon__created', AddonCreatedFilter),
('version__addon__last_updated', AddonLastUpdatedFilter),
('version__addon__disabled_by_user', AddonVisibilityFilter),
('version__addon__average_daily_users', AddonAverageDailyUsers),
('version__file__status', FileStatusFilter),
('version__file__is_signed', FileIsSignedFilter),
('was_blocked', admin.BooleanFieldListFilter),
Expand Down Expand Up @@ -848,7 +881,10 @@ class ScannerQueryRuleAdmin(AbstractScannerRuleAdminMixin, AMOModelAdmin):
'completion_rate',
'matched_results_link',
)
list_filter = ('state',)
list_filter = (
'scanner',
'state',
)
fields = (
'scanner',
'run_on_disabled_addons',
Expand Down
Loading
Loading