Skip to content

Commit

Permalink
add functionality with queryset modifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
PetrDlouhy committed Oct 12, 2023
1 parent 9898d16 commit de50d3e
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 10 deletions.
2 changes: 1 addition & 1 deletion admin_tools_stats/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class DashboardStatsAdmin(admin.ModelAdmin):
"fields": (
"graph_key",
"graph_title",
("model_app_name", "model_name", "date_field_name"),
("model_app_name", "model_name", "date_field_name", "queryset_modifiers"),
("operation_field_name", "distinct"),
("user_field_name", "show_to_users"),
("allowed_type_operation_field_name", "type_operation_field_name"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 4.2.3 on 2023-10-12 11:26

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("admin_tools_stats", "0021_auto_20230210_1102"),
]

operations = [
migrations.AddField(
model_name="dashboardstats",
name="queryset_modifiers",
field=models.JSONField(
blank=True,
help_text="Additional queryset modifiers in JSON format, ex. <code>[{'filter': {'status': 'active'}}, {'exclude': {'status': 'deleted'}}]</code>.<br>Ensure the format is a valid JSON array of objects.",
null=True,
verbose_name="Queryset modifiers",
),
),
]
43 changes: 34 additions & 9 deletions admin_tools_stats/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,21 @@ class DashboardStats(models.Model):
"Can contain multiple fields divided by comma.",
),
)
queryset_modifiers = JSONField(
verbose_name=_("Queryset modifiers"),
null=True,
blank=True,
help_text=mark_safe(
"Additional queryset modifiers in JSON format:<br>"
"<pre>"
"[<br>"
" {\"filter\": {\"status\": \"active\"}},<br>"
" {\"exclude\": {\"status\": \"deleted\"}}<br>"
"]"
"</pre>"
"Ensure the format is a valid JSON array of objects."
),
)
distinct = models.BooleanField(
default=False,
null=False,
Expand Down Expand Up @@ -437,12 +452,24 @@ def allowed_chart_types_choices(self):
def get_model(self):
return apps.get_model(self.model_app_name, self.model_name)

def get_queryset(self):
qs = self.get_model().objects
if self.queryset_modifiers:
for modifier in self.queryset_modifiers:
method_name = list(modifier.keys())[0]
method_args = modifier[method_name]
if isinstance(method_args, dict):
qs = getattr(qs, method_name)(**method_args)
else:
qs = getattr(qs, method_name)(*method_args)
return qs

def get_operation_field(self, operation):
query = self.get_model().objects.all().query
query = self.get_queryset().all().query
return query.resolve_ref(operation).field

def get_date_field(self):
query = self.get_model().objects.all().query
query = self.get_queryset().all().query
return query.resolve_ref(self.date_field_name).field

def clean(self, *args, **kwargs):
Expand Down Expand Up @@ -526,7 +553,6 @@ def get_time_series(
interval: Interval,
):
"""Get the stats time series"""
model_name = apps.get_model(self.model_app_name, self.model_name)
kwargs = {}
dynamic_kwargs: List[Optional[Q]] = []
if not user.has_perm("admin_tools_stats.view_dashboardstats") and self.user_field_name:
Expand Down Expand Up @@ -596,7 +622,7 @@ def get_time_series(

# TODO: maybe backport values_list support back to django-qsstats-magic and use it again for the query
time_range = {"%s__range" % self.date_field_name: (time_since, time_until)}
qs = model_name.objects
qs = self.get_queryset()
qs = qs.filter(**time_range)
qs = qs.filter(**kwargs)
if isinstance(self.get_date_field(), DateTimeField):
Expand Down Expand Up @@ -881,9 +907,9 @@ def get_dynamic_criteria_field_name(self):
return self.prefix + self.criteria.dynamic_criteria_field_name
return self.criteria.dynamic_criteria_field_name

def get_dynamic_field(self, model):
def get_dynamic_field(self):
field_name = self.get_dynamic_criteria_field_name()
query = model.objects.all().query
query = self.stats.get_queryset().all().query
return query.resolve_ref(field_name).field

@memoize(60 * 60 * 24 * 7)
Expand All @@ -896,7 +922,6 @@ def _get_dynamic_choices(
operation_field_choice=None,
user=None,
) -> "Optional[OrderedDict[str, Tuple[Union[str, bool, List[str]], str]]]":
model = self.stats.get_model()
field_name = self.get_dynamic_criteria_field_name()
if self.criteria.criteria_dynamic_mapping:
return OrderedDict(self.criteria.criteria_dynamic_mapping)
Expand All @@ -909,7 +934,7 @@ def _get_dynamic_choices(
("False", (False, "Non blank")),
)
)
field = self.get_dynamic_field(model)
field = self.get_dynamic_field()
if field.__class__ == models.BooleanField:
return OrderedDict(
(
Expand Down Expand Up @@ -940,7 +965,7 @@ def _get_dynamic_choices(
)
end_time = time_until
date_filters["%s__lte" % self.stats.date_field_name] = end_time
choices_queryset = model.objects.filter(
choices_queryset = self.stats.get_queryset().filter(
**date_filters,
)
if user and not user.has_perm("admin_tools_stats.view_dashboardstats"):
Expand Down

0 comments on commit de50d3e

Please sign in to comment.