#!/usr/bin/env Rscript # Stratified sample of «Отдел по работе с клиентами» dialogs (last N days), # grouped by already-classified topic, for building the Забота KB. suppressPackageStartupMessages({ library(RPostgres); library(DBI); library(data.table) }) # Запускать из этой папки: cd scripts/analytics && Rscript sample_dialogs.R source("db_connect.R") LOOKBACK <- as.integer(Sys.getenv("LOOKBACK_DAYS", "30")) PER_TOPIC <- as.integer(Sys.getenv("PER_TOPIC", "14")) OUT <- Sys.getenv("OUT_DIR", "/tmp/zabota_sample") dir.create(OUT, recursive = TRUE, showWarnings = FALSE) con <- zabota_connect() cat("connected\n") sql <- sprintf(" WITH d AS ( SELECT f.dialog_id, f.\"chatId\" AS chat_id, f.created_at, t.topic_name, f.msg_count FROM mv_chat_dialog_features f JOIN chat_dialog_topic t ON t.dialog_id = f.dialog_id WHERE f.created_at >= now() - INTERVAL '%d days' AND BTRIM(f.first_manager_dept) LIKE 'Отдел по работе с клиентами%%' AND f.msg_count > 1 ), msgs AS ( SELECT dm.dialog_id, m.date AS message_date, m.\"senderRole\" AS sender_role, m.text AS message_text FROM mv_chat_dialog_messages dm JOIN mv_coconut_chat_message m ON m.id = dm.message_id WHERE m.type IS DISTINCT FROM 'system' AND NULLIF(BTRIM(m.text), '') IS NOT NULL AND m.\"senderRole\" IN ('client','support') ), agg AS ( SELECT dialog_id, COUNT(*) FILTER (WHERE sender_role='support') AS n_support, COUNT(*) FILTER (WHERE sender_role='client') AS n_client FROM msgs GROUP BY 1 ), picked AS ( SELECT d.*, a.n_support, a.n_client, row_number() OVER (PARTITION BY d.topic_name ORDER BY a.n_support DESC, d.created_at DESC) AS rn FROM d JOIN agg a ON a.dialog_id = d.dialog_id WHERE a.n_support >= 1 AND a.n_client >= 1 ) SELECT p.dialog_id, p.chat_id, p.created_at, p.topic_name, p.n_support, p.n_client, m.message_date, m.sender_role, m.message_text FROM picked p JOIN msgs m ON m.dialog_id = p.dialog_id WHERE p.rn <= %d ORDER BY p.topic_name, p.dialog_id, m.message_date ", LOOKBACK, PER_TOPIC) dt <- as.data.table(dbGetQuery(con, sql)) cat("rows:", nrow(dt), " dialogs:", uniqueN(dt$dialog_id), "\n") dbDisconnect(con) # --- light PII masking: emails and RU mobile numbers only --- # Deliberately narrow: dates, times and prices must survive intact, they are the # substance of the answers we are mining. mask <- function(x) { x <- as.character(x) x <- gsub("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}", "", x, perl = TRUE) x <- gsub("(?", x, perl = TRUE) x <- gsub("(?", x, perl = TRUE) x } dt[, message_text := mask(message_text)] dt[, message_text := substr(message_text, 1, 900)] fwrite(dt, file.path(OUT, "dialogs_raw.csv")) label <- function(r) ifelse(r == "client", "КЛИЕНТ", "МЕНЕДЖЕР") slug <- function(s) { s <- gsub("[^\\p{L}\\p{N}]+", "_", s, perl = TRUE) substr(tolower(s), 1, 48) } setorder(dt, topic_name, dialog_id, message_date) topics <- dt[, .(n_dialogs = uniqueN(dialog_id)), by = topic_name][order(-n_dialogs)] print(topics) fwrite(topics, file.path(OUT, "topics_sampled.csv")) for (tp in topics$topic_name) { sub <- dt[topic_name == tp] lines <- c(sprintf("# Тема: %s", tp), sprintf("_Диалогов в выборке: %d_", uniqueN(sub$dialog_id)), "") for (did in unique(sub$dialog_id)) { d <- sub[dialog_id == did] lines <- c(lines, sprintf("## dialog %s — %s", did, format(d$created_at[1], "%Y-%m-%d")), sprintf("[%s] %s", label(d$sender_role), gsub("[\r\n]+", " / ", d$message_text)), "") } writeLines(lines, file.path(OUT, paste0("topic_", slug(tp), ".md"))) } cat("written to", OUT, "\n")