Changes between Initial Version and Version 1 of AdvancedReports


Ignore:
Timestamp:
09/19/26 01:44:15 (12 days ago)
Author:
183164
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • AdvancedReports

    v1 v1  
     1= Напредни извештаи =
     2
     3Извештаите се извршуваат со SET search_path TO project; Двата извештаи се во прикачената датотека [attachment:advanced_reports.sql].
     4
     5Во релационата алгебра се користи проширената нотација: σ (селекција), π (генерализирана проекција, со пресметани атрибути), ⋈ (спојување), ⟕ (лево надворешно спојување), ρ (преименување), γ (групирање и агрегатни функции), τ (подредување) и ← (доделување на привремена релација).
     6
     7== Квартален учинок во решавањето на пријавите по категорија ==
     8
     9Општината сака да знае колку ефикасно ги решава различните видови комунални проблеми и дали се подобрува со текот на времето. Извештајот за секој квартал и за секоја категорија прикажува: вкупен број на пријави, број на решени и одбиени пријави, процент на решени, просечно време до првиот одговор (од поднесување до статус „примена“, во часови), просечно време до решавање (во денови), промена на времето до решавање во однос на претходниот квартал, и рангирање на категориите во кварталот од најбавната кон најбрзата. Со извештајот се откриваат категориите каде е потребно повеќе ресурси, и се следи долгорочниот тренд на квартално, полугодишно и годишно ниво.
     10
     11=== Решение во SQL ===
     12
     13{{{#!sql
     14WITH report_times AS (
     15    SELECT r.report_id, r.category_id, r.status, r.created_at,
     16           date_trunc('quarter', r.created_at) AS quarter,
     17           MIN(l.changed_at) FILTER (WHERE l.status = 'received') AS received_at,
     18           MIN(l.changed_at) FILTER (WHERE l.status = 'resolved') AS resolved_at
     19    FROM reports r
     20    JOIN status_logs l ON l.report_id = r.report_id
     21    GROUP BY r.report_id
     22),
     23per_quarter AS (
     24    SELECT quarter, category_id,
     25           COUNT(*)                                     AS total,
     26           COUNT(*) FILTER (WHERE status = 'resolved')  AS resolved,
     27           COUNT(*) FILTER (WHERE status = 'rejected')  AS rejected,
     28           AVG(EXTRACT(EPOCH FROM received_at - created_at) / 3600)  AS avg_response_hours,
     29           AVG(EXTRACT(EPOCH FROM resolved_at - created_at) / 86400) AS avg_resolution_days
     30    FROM report_times
     31    GROUP BY quarter, category_id
     32)
     33SELECT to_char(pq.quarter, 'YYYY-"Q"Q')                         AS quarter,
     34       c.name                                                   AS category,
     35       pq.total, pq.resolved, pq.rejected,
     36       ROUND(100.0 * pq.resolved / pq.total, 1)                 AS resolved_pct,
     37       ROUND(pq.avg_response_hours, 1)                          AS avg_response_hours,
     38       ROUND(pq.avg_resolution_days, 1)                         AS avg_resolution_days,
     39       ROUND(pq.avg_resolution_days - prev.avg_resolution_days, 1) AS change_vs_prev_quarter,
     40       RANK() OVER (PARTITION BY pq.quarter
     41                    ORDER BY pq.avg_resolution_days DESC NULLS LAST)      AS slowest_rank
     42FROM per_quarter pq
     43JOIN categories c ON c.category_id = pq.category_id
     44LEFT JOIN per_quarter prev
     45       ON prev.category_id = pq.category_id
     46      AND prev.quarter = pq.quarter - INTERVAL '3 months'
     47ORDER BY pq.quarter, slowest_rank;
     48}}}
     49
     50=== Решение во релациона алгебра ===
     51
     52{{{
     53Rec ← report_id γ MIN(changed_at)→received_at ( σ status='received' (status_logs) )
     54
     55Res ← report_id γ MIN(changed_at)→resolved_at ( σ status='resolved' (status_logs) )
     56
     57RT  ← π report_id, category_id, created_at,
     58        quarter(created_at)→quarter,
     59        received_at, resolved_at,
     60        (1 if status='resolved' else 0)→is_resolved,
     61        (1 if status='rejected' else 0)→is_rejected
     62      ( reports ⟕ Rec ⟕ Res )
     63
     64PQ  ← quarter, category_id γ COUNT(report_id)→total,
     65                              SUM(is_resolved)→resolved,
     66                              SUM(is_rejected)→rejected,
     67                              AVG(received_at − created_at)→avg_response,
     68                              AVG(resolved_at − created_at)→avg_resolution
     69      ( RT )
     70
     71Prev ← ρ Prev(prev_of, category_id, prev_avg)
     72       ( π quarter + 3 months, category_id, avg_resolution (PQ) )
     73
     74PQK ← π quarter, category_id, COALESCE(avg_resolution, −1)→sort_key (PQ)
     75
     76Rank ← a.quarter, a.category_id γ COUNT(b.category_id)→slower_count
     77       ( ρ a(PQK) ⟕ a.quarter = b.quarter ∧ b.sort_key > a.sort_key ρ b(PQK) )
     78
     79Result ← τ quarter, slowest_rank
     80         ( π quarter, name, total, resolved, rejected,
     81             100 · resolved / total→resolved_pct,
     82             avg_response, avg_resolution,
     83             avg_resolution − prev_avg→change_vs_prev_quarter,
     84             1 + slower_count→slowest_rank
     85           ( ( PQ ⋈ Rank ⋈ categories )
     86             ⟕ PQ.quarter = Prev.prev_of ∧ PQ.category_id = Prev.category_id Prev ) )
     87}}}
     88
     89Рангот (RANK) е изразен преку лево надворешно самоспојување: рангот на една категорија е 1 плус бројот на категории во истиот квартал со подолго време на решавање. Категориите без решени пријави добиваат клуч −1, што одговара на NULLS LAST.
     90
     91== Жаришта на повторувачки проблеми ==
     92
     93Општината сака да ги открие локациите каде истиот вид проблем постојано се повторува, бидејќи тоа укажува на потреба од трајно решение (на пример реконструкција на улица, дополнителни контејнери или замена на инсталации) наместо постојани интервенции. Градот се дели на мрежа од ќелии со големина околу 500 метри (координатите се заокружуваат на 0,005 степени). За секоја ќелија и категорија се пресметува: број на пријави, број на различни граѓани кои пријавиле, број на различни квартали во кои има пријави, број на моментално отворени пријави, просечно време на решавање и датум на последната пријава. Се прикажуваат само ќелиите со најмалку 3 пријави во најмалку 2 различни квартали, рангирани според бројот на пријави помножен со бројот на квартали. Одбиените пријави не се сметаат. Извештајот се користи на годишно и повеќегодишно ниво за планирање на инвестициите.
     94
     95=== Решение во SQL ===
     96
     97{{{#!sql
     98WITH resolution AS (
     99    SELECT report_id, MIN(changed_at) FILTER (WHERE status = 'resolved') AS resolved_at
     100    FROM status_logs
     101    GROUP BY report_id
     102),
     103located AS (
     104    SELECT r.report_id, r.category_id, r.citizen_id, r.status, r.created_at, r.location_text,
     105           ROUND(r.latitude  / 0.005) * 0.005 AS cell_lat,
     106           ROUND(r.longitude / 0.005) * 0.005 AS cell_lon,
     107           res.resolved_at
     108    FROM reports r
     109    JOIN resolution res ON res.report_id = r.report_id
     110    WHERE r.latitude IS NOT NULL
     111      AND r.status <> 'rejected'
     112)
     113SELECT RANK() OVER (ORDER BY COUNT(*) * COUNT(DISTINCT date_trunc('quarter', l.created_at)) DESC) AS hotspot_rank,
     114       l.cell_lat, l.cell_lon,
     115       c.name                                                    AS category,
     116       MAX(l.location_text)                                      AS example_address,
     117       COUNT(*)                                                  AS reports,
     118       COUNT(DISTINCT l.citizen_id)                              AS distinct_citizens,
     119       COUNT(DISTINCT date_trunc('quarter', l.created_at))       AS quarters_with_reports,
     120       COUNT(*) FILTER (WHERE l.status NOT IN ('resolved', 'rejected')) AS open_now,
     121       ROUND(AVG(EXTRACT(EPOCH FROM l.resolved_at - l.created_at) / 86400), 1) AS avg_resolution_days,
     122       to_char(MAX(l.created_at), 'DD.MM.YYYY')                  AS last_report
     123FROM located l
     124JOIN categories c ON c.category_id = l.category_id
     125GROUP BY l.cell_lat, l.cell_lon, c.name
     126HAVING COUNT(*) >= 3
     127   AND COUNT(DISTINCT date_trunc('quarter', l.created_at)) >= 2
     128ORDER BY hotspot_rank, reports DESC;
     129}}}
     130
     131=== Решение во релациона алгебра ===
     132
     133{{{
     134Res ← report_id γ MIN(changed_at)→resolved_at ( σ status='resolved' (status_logs) )
     135
     136L   ← π report_id, category_id, citizen_id, created_at, location_text,
     137        round(latitude / 0.005) · 0.005→cell_lat,
     138        round(longitude / 0.005) · 0.005→cell_lon,
     139        quarter(created_at)→quarter,
     140        resolved_at,
     141        (1 if status ∉ {'resolved','rejected'} else 0)→is_open
     142      ( σ latitude ≠ NULL ∧ status ≠ 'rejected' (reports) ⟕ Res )
     143
     144G   ← cell_lat, cell_lon, category_id
     145      γ COUNT(report_id)→reports,
     146        COUNT-DISTINCT(citizen_id)→distinct_citizens,
     147        COUNT-DISTINCT(quarter)→quarters_with_reports,
     148        SUM(is_open)→open_now,
     149        AVG(resolved_at − created_at)→avg_resolution,
     150        MAX(location_text)→example_address,
     151        MAX(created_at)→last_report
     152      ( L )
     153
     154H   ← π cell_lat, cell_lon, category_id, reports, distinct_citizens,
     155        quarters_with_reports, open_now, avg_resolution, example_address,
     156        last_report, reports · quarters_with_reports→score
     157      ( σ reports ≥ 3 ∧ quarters_with_reports ≥ 2 (G) )
     158
     159Rank ← a.cell_lat, a.cell_lon, a.category_id γ COUNT(b.category_id)→higher_count
     160       ( ρ a(H) ⟕ b.score > a.score ρ b(H) )
     161
     162Result ← τ hotspot_rank, reports desc
     163         ( π 1 + higher_count→hotspot_rank, cell_lat, cell_lon, name,
     164             example_address, reports, distinct_citizens,
     165             quarters_with_reports, open_now, avg_resolution, last_report
     166           ( H ⋈ Rank ⋈ categories ) )
     167}}}
     168
     169Во SQL решението групирањето е според името на категоријата, а во релационата алгебра според category_id. Двете се еквивалентни, бидејќи името на категоријата е единствено (category_name → category_id).
     170
     171== Користење на вештачка интелигенција ==
     172
     173[wiki:AdvancedReportsAIUsage Користење на вештачка интелигенција за напредните извештаи]