04/24 Fixed running report issues
This commit is contained in:
@@ -953,15 +953,26 @@ def get_unchecked_report(target_date=None, user_id=None):
|
||||
# One user_id param slot for the user_filter inside the main query
|
||||
user_filter_params = [user_id] if user_id else []
|
||||
|
||||
# The subquery needs: dow_params, (optional user_id for shift_users join)
|
||||
# Outer query needs: date_params, user_filter_params, date_params
|
||||
# EXISTS(shift) subquery: dow_params + (user_id if filtering by user)
|
||||
# We build params carefully to match the f-string placeholders below.
|
||||
# Fix for Bug #1: MySQL does not allow a derived table (subquery in FROM/JOIN)
|
||||
# to reference outer-query aliases (e.g. u.id). The previous approach used
|
||||
# "AND su.user_id = u.id" inside a derived table when user_id=None, which
|
||||
# MySQL rejects with "Unknown column 'u.id' in 'where clause'".
|
||||
#
|
||||
# Solution: replace the derived-table JOIN with an EXISTS correlated subquery
|
||||
# in the WHERE clause. Correlated subqueries CAN reference outer aliases,
|
||||
# so u.id is always in scope. This works identically for both the
|
||||
# single-user and all-users cases.
|
||||
#
|
||||
# Param order:
|
||||
# date_params → {date_expr} in SELECT
|
||||
# user_filter_params → {user_filter} AND u.id = %s
|
||||
# dow_params → DAYOFWEEK(%s) in EXISTS
|
||||
# user_filter_params → su.user_id = u.id or %s in EXISTS (always u.id now)
|
||||
# date_params → DATE(sc.checked_at) = {date_expr}
|
||||
params = (
|
||||
date_params # {date_expr} in SELECT
|
||||
+ user_filter_params # {user_filter} AND u.id = %s
|
||||
+ dow_params # DAYOFWEEK(%s) in shift EXISTS
|
||||
+ (user_id and [user_id] or []) # su.user_id=%s in shift EXISTS
|
||||
+ date_params # DATE(sc.checked_at) = {date_expr}
|
||||
)
|
||||
|
||||
@@ -975,29 +986,27 @@ def get_unchecked_report(target_date=None, user_id=None):
|
||||
w.url,
|
||||
'Not Checked' AS status
|
||||
FROM users u
|
||||
-- Only websites the user was expected to check on this date
|
||||
JOIN (
|
||||
SELECT DISTINCT sw.website_id
|
||||
FROM shift_websites sw
|
||||
JOIN shifts s ON s.id = sw.shift_id
|
||||
JOIN shift_users su ON su.shift_id = s.id
|
||||
JOIN websites w2 ON w2.id = sw.website_id
|
||||
WHERE s.is_active = 1
|
||||
AND w2.is_active = 1
|
||||
AND LOCATE(CAST({dow_expr} AS CHAR), s.days_of_week) > 0
|
||||
{"AND su.user_id = %s" if user_id else "AND su.user_id = u.id"}
|
||||
AND (
|
||||
w2.visibility = 'all'
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM website_users wu
|
||||
WHERE wu.website_id = w2.id AND wu.user_id = su.user_id
|
||||
)
|
||||
)
|
||||
) expected ON 1=1
|
||||
JOIN websites w ON w.id = expected.website_id
|
||||
JOIN websites w ON w.is_active = 1
|
||||
WHERE u.is_active = 1
|
||||
AND u.role = 'user'
|
||||
{user_filter}
|
||||
-- Only include websites the user was expected to check via their shifts
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM shift_websites sw
|
||||
JOIN shifts s ON s.id = sw.shift_id
|
||||
JOIN shift_users su ON su.shift_id = s.id AND su.user_id = u.id
|
||||
WHERE sw.website_id = w.id
|
||||
AND s.is_active = 1
|
||||
AND LOCATE(CAST({dow_expr} AS CHAR), s.days_of_week) > 0
|
||||
AND (
|
||||
w.visibility = 'all'
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM website_users wu
|
||||
WHERE wu.website_id = w.id AND wu.user_id = u.id
|
||||
)
|
||||
)
|
||||
)
|
||||
-- Exclude sites the user DID check on the target date
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM shift_checks sc
|
||||
@@ -1049,46 +1058,63 @@ def get_summary_report(date_from=None, date_to=None):
|
||||
|
||||
where_clause = ("WHERE " + " AND ".join(conditions)) if conditions else ""
|
||||
|
||||
# Fix for Bug #2: MySQL only_full_group_by rejects referencing sc.checked_at
|
||||
# (the full timestamp) inside correlated subqueries when only DATE(sc.checked_at)
|
||||
# appears in the GROUP BY clause. Even though sc.checked_at is functionally
|
||||
# determined by DATE(sc.checked_at) in intent, MySQL strict mode does not
|
||||
# infer that relationship automatically.
|
||||
#
|
||||
# Solution: pre-aggregate in a CTE (agg) that produces a single, unambiguous
|
||||
# check_date (DATE) and user_id per group. The outer SELECT then references
|
||||
# agg.check_date — a fully grouped column — inside the correlated subqueries,
|
||||
# satisfying only_full_group_by completely.
|
||||
cur.execute(
|
||||
f"""
|
||||
WITH agg AS (
|
||||
SELECT
|
||||
DATE(sc.checked_at) AS check_date,
|
||||
sc.user_id,
|
||||
COUNT(DISTINCT sc.website_id) AS checked_count
|
||||
FROM shift_checks sc
|
||||
{where_clause}
|
||||
GROUP BY DATE(sc.checked_at), sc.user_id
|
||||
)
|
||||
SELECT
|
||||
DATE(sc.checked_at) AS check_date,
|
||||
agg.check_date,
|
||||
u.username,
|
||||
COALESCE(u.full_name, u.username) AS full_name,
|
||||
COUNT(DISTINCT sc.website_id) AS checked_count,
|
||||
COALESCE(u.full_name, u.username) AS full_name,
|
||||
agg.checked_count,
|
||||
(
|
||||
SELECT COUNT(DISTINCT sw2.website_id)
|
||||
FROM shift_websites sw2
|
||||
JOIN shifts s2 ON s2.id = sw2.shift_id
|
||||
JOIN shifts s2 ON s2.id = sw2.shift_id
|
||||
JOIN shift_users su2 ON su2.shift_id = s2.id
|
||||
AND su2.user_id = u.id
|
||||
AND su2.user_id = u.id
|
||||
WHERE s2.is_active = 1
|
||||
AND LOCATE(
|
||||
CAST(DAYOFWEEK(DATE(sc.checked_at)) AS CHAR),
|
||||
CAST(DAYOFWEEK(agg.check_date) AS CHAR),
|
||||
s2.days_of_week
|
||||
) > 0
|
||||
) AS total_sites,
|
||||
ROUND(
|
||||
COUNT(DISTINCT sc.website_id) * 100.0 /
|
||||
agg.checked_count * 100.0 /
|
||||
NULLIF((
|
||||
SELECT COUNT(DISTINCT sw2.website_id)
|
||||
FROM shift_websites sw2
|
||||
JOIN shifts s2 ON s2.id = sw2.shift_id
|
||||
JOIN shifts s2 ON s2.id = sw2.shift_id
|
||||
JOIN shift_users su2 ON su2.shift_id = s2.id
|
||||
AND su2.user_id = u.id
|
||||
AND su2.user_id = u.id
|
||||
WHERE s2.is_active = 1
|
||||
AND LOCATE(
|
||||
CAST(DAYOFWEEK(DATE(sc.checked_at)) AS CHAR),
|
||||
CAST(DAYOFWEEK(agg.check_date) AS CHAR),
|
||||
s2.days_of_week
|
||||
) > 0
|
||||
), 0),
|
||||
1
|
||||
) AS pct_complete
|
||||
FROM shift_checks sc
|
||||
JOIN users u ON u.id = sc.user_id
|
||||
{where_clause}
|
||||
GROUP BY DATE(sc.checked_at), sc.user_id
|
||||
ORDER BY check_date DESC, u.username
|
||||
FROM agg
|
||||
JOIN users u ON u.id = agg.user_id
|
||||
ORDER BY agg.check_date DESC, u.username
|
||||
""",
|
||||
params
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user