We have a pure SQL in postgres that groups the instance visits registered in that table grouped by country, what I need is to pass this pure query to django queryset to get the same result
Here is the pure SQL query:
<code>SELECT
country,
SUM(difference) AS total_difference
FROM (
SELECT
id,
entry_or_exit_datetime AS entry_time,
LEAD(entry_or_exit_datetime) OVER (ORDER BY entry_or_exit_datetime) AS exit_time,
EXTRACT(EPOCH FROM (LEAD(entry_or_exit_datetime) OVER (ORDER BY entry_or_exit_datetime) - entry_or_exit_datetime)) AS difference,
country
FROM (
SELECT
id,
entry_or_exit_datetime,
country,
LAG(is_joining) OVER (ORDER BY entry_or_exit_datetime) AS prev_is_joining
FROM
public.access_sectionslog
WHERE
date(entry_or_exit_datetime) >= '2024-05-17'
AND date(entry_or_exit_datetime) <= '2024-05-23'
AND section_name = 'landing-page'
AND country IN ('VE', 'CO')
) AS subquery
) AS subquery_with_difference
GROUP BY country;
</code>
<code>SELECT
country,
SUM(difference) AS total_difference
FROM (
SELECT
id,
entry_or_exit_datetime AS entry_time,
LEAD(entry_or_exit_datetime) OVER (ORDER BY entry_or_exit_datetime) AS exit_time,
EXTRACT(EPOCH FROM (LEAD(entry_or_exit_datetime) OVER (ORDER BY entry_or_exit_datetime) - entry_or_exit_datetime)) AS difference,
country
FROM (
SELECT
id,
entry_or_exit_datetime,
country,
LAG(is_joining) OVER (ORDER BY entry_or_exit_datetime) AS prev_is_joining
FROM
public.access_sectionslog
WHERE
date(entry_or_exit_datetime) >= '2024-05-17'
AND date(entry_or_exit_datetime) <= '2024-05-23'
AND section_name = 'landing-page'
AND country IN ('VE', 'CO')
) AS subquery
) AS subquery_with_difference
GROUP BY country;
</code>
SELECT
country,
SUM(difference) AS total_difference
FROM (
SELECT
id,
entry_or_exit_datetime AS entry_time,
LEAD(entry_or_exit_datetime) OVER (ORDER BY entry_or_exit_datetime) AS exit_time,
EXTRACT(EPOCH FROM (LEAD(entry_or_exit_datetime) OVER (ORDER BY entry_or_exit_datetime) - entry_or_exit_datetime)) AS difference,
country
FROM (
SELECT
id,
entry_or_exit_datetime,
country,
LAG(is_joining) OVER (ORDER BY entry_or_exit_datetime) AS prev_is_joining
FROM
public.access_sectionslog
WHERE
date(entry_or_exit_datetime) >= '2024-05-17'
AND date(entry_or_exit_datetime) <= '2024-05-23'
AND section_name = 'landing-page'
AND country IN ('VE', 'CO')
) AS subquery
) AS subquery_with_difference
GROUP BY country;
This is the model in django:
<code>class SectionsLog(BaseModel):
class SectionNameChoice(models.TextChoices):
GROUPS = "section-groups", _("Groups")
FEED = "section-feed", _("Feed")
NEWS = "section-news", _("News")
LANDING_PAGE = "landing-page", _("Landing Page")
EXTERNALS_SHARING = "external-sharing", _("External Sharing")
LIVESCORE = "section-livescore", _("Livescore")
SALES_PAGE = "sales-page", _("Sales page")
ON_BOARDING = "onboarding", _("On boarding")
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
blank=True,
null=True,
related_name="sections_log",
)
section_name = models.CharField(
max_length=20,
choices=SectionNameChoice.choices,
null=True,
blank=True,
)
is_joining = models.BooleanField(
blank=True,
null=True,
help_text="Enter the Section(True)/Leave the Section(False)",
)
is_anonymous = models.BooleanField(
default=False,
blank=True,
null=True,
help_text="Is True to anybody who enter to the landing page without an user access token.",
)
entry_or_exit_datetime = models.DateTimeField(null=True, blank=True)
user_ip = models.CharField(
max_length=350,
null=True,
blank=True,
)
city = models.CharField(
max_length=220,
null=True,
blank=True,
)
country = models.CharField(
max_length=220,
null=True,
blank=True,
)
latitude = models.CharField(
max_length=90,
null=True,
blank=True,
)
region = models.CharField(
max_length=90,
null=True,
blank=True,
)
timezone = models.CharField(
max_length=90,
null=True,
blank=True,
)
def __str__(self):
direction = "unknow"
user = self.user
if not self.user and self.is_anonymous:
user = f"Anonymous User (IP:{self.user_ip})"
if self.is_joining:
direction = "enter"
else:
direction = "leave"
return f"{user} {direction} {self.section_name} section"
</code>
<code>class SectionsLog(BaseModel):
class SectionNameChoice(models.TextChoices):
GROUPS = "section-groups", _("Groups")
FEED = "section-feed", _("Feed")
NEWS = "section-news", _("News")
LANDING_PAGE = "landing-page", _("Landing Page")
EXTERNALS_SHARING = "external-sharing", _("External Sharing")
LIVESCORE = "section-livescore", _("Livescore")
SALES_PAGE = "sales-page", _("Sales page")
ON_BOARDING = "onboarding", _("On boarding")
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
blank=True,
null=True,
related_name="sections_log",
)
section_name = models.CharField(
max_length=20,
choices=SectionNameChoice.choices,
null=True,
blank=True,
)
is_joining = models.BooleanField(
blank=True,
null=True,
help_text="Enter the Section(True)/Leave the Section(False)",
)
is_anonymous = models.BooleanField(
default=False,
blank=True,
null=True,
help_text="Is True to anybody who enter to the landing page without an user access token.",
)
entry_or_exit_datetime = models.DateTimeField(null=True, blank=True)
user_ip = models.CharField(
max_length=350,
null=True,
blank=True,
)
city = models.CharField(
max_length=220,
null=True,
blank=True,
)
country = models.CharField(
max_length=220,
null=True,
blank=True,
)
latitude = models.CharField(
max_length=90,
null=True,
blank=True,
)
region = models.CharField(
max_length=90,
null=True,
blank=True,
)
timezone = models.CharField(
max_length=90,
null=True,
blank=True,
)
def __str__(self):
direction = "unknow"
user = self.user
if not self.user and self.is_anonymous:
user = f"Anonymous User (IP:{self.user_ip})"
if self.is_joining:
direction = "enter"
else:
direction = "leave"
return f"{user} {direction} {self.section_name} section"
</code>
class SectionsLog(BaseModel):
class SectionNameChoice(models.TextChoices):
GROUPS = "section-groups", _("Groups")
FEED = "section-feed", _("Feed")
NEWS = "section-news", _("News")
LANDING_PAGE = "landing-page", _("Landing Page")
EXTERNALS_SHARING = "external-sharing", _("External Sharing")
LIVESCORE = "section-livescore", _("Livescore")
SALES_PAGE = "sales-page", _("Sales page")
ON_BOARDING = "onboarding", _("On boarding")
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
blank=True,
null=True,
related_name="sections_log",
)
section_name = models.CharField(
max_length=20,
choices=SectionNameChoice.choices,
null=True,
blank=True,
)
is_joining = models.BooleanField(
blank=True,
null=True,
help_text="Enter the Section(True)/Leave the Section(False)",
)
is_anonymous = models.BooleanField(
default=False,
blank=True,
null=True,
help_text="Is True to anybody who enter to the landing page without an user access token.",
)
entry_or_exit_datetime = models.DateTimeField(null=True, blank=True)
user_ip = models.CharField(
max_length=350,
null=True,
blank=True,
)
city = models.CharField(
max_length=220,
null=True,
blank=True,
)
country = models.CharField(
max_length=220,
null=True,
blank=True,
)
latitude = models.CharField(
max_length=90,
null=True,
blank=True,
)
region = models.CharField(
max_length=90,
null=True,
blank=True,
)
timezone = models.CharField(
max_length=90,
null=True,
blank=True,
)
def __str__(self):
direction = "unknow"
user = self.user
if not self.user and self.is_anonymous:
user = f"Anonymous User (IP:{self.user_ip})"
if self.is_joining:
direction = "enter"
else:
direction = "leave"
return f"{user} {direction} {self.section_name} section"
I have tried to do it this way but it generates an error.
<code> subquery = SectionsLog.objects.filter(
entry_or_exit_datetime__date__range=(start_date, end_date),
section_name=SectionsLog.SectionNameChoice.LANDING_PAGE,
country__in=countries
).annotate(
prev_is_joining=Window(
expression=Lead('is_joining'),
order_by=F('entry_or_exit_datetime').asc()
)
)
subquery_with_difference = subquery.annotate(
entry_time=F('entry_or_exit_datetime'),
exit_time=Window(
expression=Lead('entry_or_exit_datetime'),
order_by=F('entry_or_exit_datetime').asc()
),
difference=ExpressionWrapper(
RawSQL("EXTRACT(EPOCH FROM (?? - ??))", ['exit_time', 'entry_time']),
output_field=FloatField()
)
)
# Define the final query
final_query = subquery_with_difference.values('country').annotate(
total_difference=Sum('difference')
)
</code>
<code> subquery = SectionsLog.objects.filter(
entry_or_exit_datetime__date__range=(start_date, end_date),
section_name=SectionsLog.SectionNameChoice.LANDING_PAGE,
country__in=countries
).annotate(
prev_is_joining=Window(
expression=Lead('is_joining'),
order_by=F('entry_or_exit_datetime').asc()
)
)
subquery_with_difference = subquery.annotate(
entry_time=F('entry_or_exit_datetime'),
exit_time=Window(
expression=Lead('entry_or_exit_datetime'),
order_by=F('entry_or_exit_datetime').asc()
),
difference=ExpressionWrapper(
RawSQL("EXTRACT(EPOCH FROM (?? - ??))", ['exit_time', 'entry_time']),
output_field=FloatField()
)
)
# Define the final query
final_query = subquery_with_difference.values('country').annotate(
total_difference=Sum('difference')
)
</code>
subquery = SectionsLog.objects.filter(
entry_or_exit_datetime__date__range=(start_date, end_date),
section_name=SectionsLog.SectionNameChoice.LANDING_PAGE,
country__in=countries
).annotate(
prev_is_joining=Window(
expression=Lead('is_joining'),
order_by=F('entry_or_exit_datetime').asc()
)
)
subquery_with_difference = subquery.annotate(
entry_time=F('entry_or_exit_datetime'),
exit_time=Window(
expression=Lead('entry_or_exit_datetime'),
order_by=F('entry_or_exit_datetime').asc()
),
difference=ExpressionWrapper(
RawSQL("EXTRACT(EPOCH FROM (?? - ??))", ['exit_time', 'entry_time']),
output_field=FloatField()
)
)
# Define the final query
final_query = subquery_with_difference.values('country').annotate(
total_difference=Sum('difference')
)