Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-27 07:41:42

0001 """
0002 SQL builders and row helpers for PanDA database queries.
0003 
0004 Pure functions that construct SQL strings and transform database rows.
0005 No database I/O — callers execute the returned (sql, params) tuples.
0006 """
0007 
0008 from .constants import PANDA_SCHEMA, ERROR_COMPONENTS
0009 
0010 
0011 # ── SQL builders ─────────────────────────────────────────────────────────────
0012 
0013 def build_union_query(fields, where_clauses, params, order_by, limit):
0014     """Build a UNION ALL query across jobsactive4 and jobsarchived4."""
0015     field_list = ', '.join(f'"{f}"' for f in fields)
0016     where_sql = ''
0017     if where_clauses:
0018         where_sql = ' WHERE ' + ' AND '.join(where_clauses)
0019 
0020     sql = f"""
0021         SELECT * FROM (
0022             SELECT {field_list} FROM "{PANDA_SCHEMA}"."jobsactive4"{where_sql}
0023             UNION ALL
0024             SELECT {field_list} FROM "{PANDA_SCHEMA}"."jobsarchived4"{where_sql}
0025         ) combined
0026         ORDER BY {order_by}
0027         LIMIT {limit}
0028     """
0029     full_params = list(params) + list(params)
0030     return sql, full_params
0031 
0032 
0033 def build_count_query(where_clauses, params):
0034     """Build a count-by-status query across both job tables."""
0035     where_sql = ''
0036     if where_clauses:
0037         where_sql = ' WHERE ' + ' AND '.join(where_clauses)
0038 
0039     sql = f"""
0040         SELECT "jobstatus", COUNT(*) FROM (
0041             SELECT "jobstatus" FROM "{PANDA_SCHEMA}"."jobsactive4"{where_sql}
0042             UNION ALL
0043             SELECT "jobstatus" FROM "{PANDA_SCHEMA}"."jobsarchived4"{where_sql}
0044         ) combined
0045         GROUP BY "jobstatus"
0046         ORDER BY COUNT(*) DESC
0047     """
0048     full_params = list(params) + list(params)
0049     return sql, full_params
0050 
0051 
0052 def build_task_query(fields, where_clauses, params, order_by, limit):
0053     """Build a query against the jedi_tasks table."""
0054     field_list = ', '.join(f'"{f}"' for f in fields)
0055     where_sql = ''
0056     if where_clauses:
0057         where_sql = ' WHERE ' + ' AND '.join(where_clauses)
0058     sql = f"""
0059         SELECT {field_list}
0060         FROM "{PANDA_SCHEMA}"."jedi_tasks"{where_sql}
0061         ORDER BY {order_by}
0062         LIMIT {limit}
0063     """
0064     return sql, list(params)
0065 
0066 
0067 def build_task_count_query(where_clauses, params):
0068     """Build a count-by-status query for jedi_tasks."""
0069     where_sql = ''
0070     if where_clauses:
0071         where_sql = ' WHERE ' + ' AND '.join(where_clauses)
0072     sql = f"""
0073         SELECT "status", COUNT(*)
0074         FROM "{PANDA_SCHEMA}"."jedi_tasks"{where_sql}
0075         GROUP BY "status"
0076         ORDER BY COUNT(*) DESC
0077     """
0078     return sql, list(params)
0079 
0080 
0081 # ── Row helpers ──────────────────────────────────────────────────────────────
0082 
0083 def row_to_dict(row, fields):
0084     """Convert a database row to a dict, formatting datetimes."""
0085     result = {}
0086     for i, field in enumerate(fields):
0087         val = row[i]
0088         if val is not None and hasattr(val, 'isoformat'):
0089             val = val.isoformat()
0090         result[field] = val
0091     return result
0092 
0093 
0094 def extract_errors(job_dict):
0095     """Extract non-zero error components from a job dict."""
0096     errors = []
0097     for comp in ERROR_COMPONENTS:
0098         code = job_dict.get(comp['code'])
0099         if code and int(code) != 0:
0100             errors.append({
0101                 'component': comp['name'],
0102                 'code': int(code),
0103                 'diag': job_dict.get(comp['diag'], ''),
0104             })
0105     transexitcode = job_dict.get('transexitcode')
0106     if transexitcode and str(transexitcode).strip() not in ('', '0'):
0107         errors.append({
0108             'component': 'transformation',
0109             'code': transexitcode,
0110             'diag': '',
0111         })
0112     return errors
0113 
0114 
0115 def like_or_eq(field, value):
0116     """Return (where_clause, param) using LIKE if value contains %, else =."""
0117     if '%' in value:
0118         return f'"{field}" LIKE %s', value
0119     return f'"{field}" = %s', value
0120 
0121 
0122 # ── DataTables SQL builders ──────────────────────────────────────────────────
0123 
0124 def build_union_query_dt(fields, where_clauses, params, order_by, limit, offset):
0125     """Build a UNION ALL query with OFFSET for DataTables pagination."""
0126     field_list = ', '.join(f'"{f}"' for f in fields)
0127     where_sql = ''
0128     if where_clauses:
0129         where_sql = ' WHERE ' + ' AND '.join(where_clauses)
0130 
0131     sql = f"""
0132         SELECT * FROM (
0133             SELECT {field_list} FROM "{PANDA_SCHEMA}"."jobsactive4"{where_sql}
0134             UNION ALL
0135             SELECT {field_list} FROM "{PANDA_SCHEMA}"."jobsarchived4"{where_sql}
0136         ) combined
0137         ORDER BY {order_by}
0138         LIMIT {limit} OFFSET {offset}
0139     """
0140     full_params = list(params) + list(params)
0141     return sql, full_params
0142 
0143 
0144 def build_union_count(where_clauses, params):
0145     """Build a total count query across both job tables."""
0146     where_sql = ''
0147     if where_clauses:
0148         where_sql = ' WHERE ' + ' AND '.join(where_clauses)
0149 
0150     sql = f"""
0151         SELECT COUNT(*) FROM (
0152             SELECT 1 FROM "{PANDA_SCHEMA}"."jobsactive4"{where_sql}
0153             UNION ALL
0154             SELECT 1 FROM "{PANDA_SCHEMA}"."jobsarchived4"{where_sql}
0155         ) combined
0156     """
0157     full_params = list(params) + list(params)
0158     return sql, full_params
0159 
0160 
0161 def build_union_count_by_field(field, where_clauses, params):
0162     """Build a GROUP BY count for a single field across both job tables."""
0163     where_sql = ''
0164     if where_clauses:
0165         where_sql = ' WHERE ' + ' AND '.join(where_clauses)
0166 
0167     sql = f"""
0168         SELECT "{field}", COUNT(*) FROM (
0169             SELECT "{field}" FROM "{PANDA_SCHEMA}"."jobsactive4"{where_sql}
0170             UNION ALL
0171             SELECT "{field}" FROM "{PANDA_SCHEMA}"."jobsarchived4"{where_sql}
0172         ) combined
0173         WHERE "{field}" IS NOT NULL
0174         GROUP BY "{field}"
0175         ORDER BY COUNT(*) DESC
0176     """
0177     full_params = list(params) + list(params)
0178     return sql, full_params
0179 
0180 
0181 def build_task_query_dt(fields, where_clauses, params, order_by, limit, offset):
0182     """Build a task query with OFFSET for DataTables pagination."""
0183     field_list = ', '.join(f'"{f}"' for f in fields)
0184     where_sql = ''
0185     if where_clauses:
0186         where_sql = ' WHERE ' + ' AND '.join(where_clauses)
0187     sql = f"""
0188         SELECT {field_list}
0189         FROM "{PANDA_SCHEMA}"."jedi_tasks"{where_sql}
0190         ORDER BY {order_by}
0191         LIMIT {limit} OFFSET {offset}
0192     """
0193     return sql, list(params)
0194 
0195 
0196 def build_task_count(where_clauses, params):
0197     """Build a total count query for jedi_tasks."""
0198     where_sql = ''
0199     if where_clauses:
0200         where_sql = ' WHERE ' + ' AND '.join(where_clauses)
0201     sql = f"""
0202         SELECT COUNT(*)
0203         FROM "{PANDA_SCHEMA}"."jedi_tasks"{where_sql}
0204     """
0205     return sql, list(params)
0206 
0207 
0208 def build_task_count_by_field(field, where_clauses, params):
0209     """Build a GROUP BY count for a single field in jedi_tasks."""
0210     all_clauses = list(where_clauses) + [f'"{field}" IS NOT NULL']
0211     where_sql = ' WHERE ' + ' AND '.join(all_clauses)
0212     sql = f"""
0213         SELECT "{field}", COUNT(*)
0214         FROM "{PANDA_SCHEMA}"."jedi_tasks"{where_sql}
0215         GROUP BY "{field}"
0216         ORDER BY COUNT(*) DESC
0217     """
0218     return sql, list(params)
0219 
0220 
0221 def build_search_clauses(fields, search_value):
0222     """Build ILIKE search clauses across multiple fields."""
0223     clauses = []
0224     params = []
0225     for f in fields:
0226         clauses.append(f'CAST("{f}" AS TEXT) ILIKE %s')
0227         params.append(f'%{search_value}%')
0228     return f"({' OR '.join(clauses)})", params