Оптимизирована работа с памятью
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -564,6 +564,20 @@ impl LogoRegistry {
|
|||||||
log::warn!("No logo found for team='{}', sport='{}'", team, sport);
|
log::warn!("No logo found for team='{}', sport='{}'", team, sport);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Оставляет только указанные команды, удаляя все остальные
|
||||||
|
pub fn retain<F>(&mut self, mut predicate: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&str, &str) -> bool,
|
||||||
|
{
|
||||||
|
self.logos.retain(|(team, sport), _| predicate(team, sport));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Возвращает количество записей в реестре
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.logos.len()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_date(s: &str) -> (NaiveDate, String) {
|
fn parse_date(s: &str) -> (NaiveDate, String) {
|
||||||
|
|||||||
140
src/processor.rs
140
src/processor.rs
@@ -274,33 +274,6 @@ fn display_workbook_structure(workbook: &ExcelWorkbook) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn generate_and_submit_jobs(
|
|
||||||
workbook: &ExcelWorkbook,
|
|
||||||
config: &Config,
|
|
||||||
) -> Result<Vec<(String, String)>> {
|
|
||||||
info!("Preparing Nexrender jobs...");
|
|
||||||
|
|
||||||
let start_sheet = workbook
|
|
||||||
.get_sheet("Start")
|
|
||||||
.ok_or_else(|| anyhow!("Sheet 'Start' not found"))?;
|
|
||||||
let sport_sheet = workbook
|
|
||||||
.get_sheet("SPORT")
|
|
||||||
.ok_or_else(|| anyhow!("Sheet 'SPORT' not found"))?;
|
|
||||||
let teams_sheet = workbook
|
|
||||||
.get_sheet("TEAMS")
|
|
||||||
.ok_or_else(|| anyhow!("Sheet 'TEAMS' not found"))?;
|
|
||||||
let channel_sheet = workbook
|
|
||||||
.get_sheet("CHANELL")
|
|
||||||
.ok_or_else(|| anyhow!("Sheet 'CHANELL' not found"))?;
|
|
||||||
|
|
||||||
// Загружаем sport packs
|
|
||||||
let packs: HashMap<String, String> = sport_sheet
|
|
||||||
.rows
|
|
||||||
.iter()
|
|
||||||
.filter_map(|row| Some((row.get("SPORT")?.clone(), row.get("LINK")?.clone())))
|
|
||||||
.collect();
|
|
||||||
info!("Loaded {} sport packs", packs.len());
|
|
||||||
|
|
||||||
// Вспомогательная функция для поиска значения по части ключа
|
// Вспомогательная функция для поиска значения по части ключа
|
||||||
fn get_cell_fuzzy(row: &HashMap<String, String>, key_part: &str) -> Option<String> {
|
fn get_cell_fuzzy(row: &HashMap<String, String>, key_part: &str) -> Option<String> {
|
||||||
if let Some(val) = row.get(key_part) {
|
if let Some(val) = row.get(key_part) {
|
||||||
@@ -316,11 +289,40 @@ async fn generate_and_submit_jobs(
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загружаем team logos
|
async fn generate_and_submit_jobs(
|
||||||
|
workbook: &ExcelWorkbook,
|
||||||
|
config: &Config,
|
||||||
|
) -> Result<Vec<(String, String)>> {
|
||||||
|
info!("Preparing Nexrender jobs...");
|
||||||
|
|
||||||
|
// ========== ЗАГРУЖАЕМ SPORT PACKS ==========
|
||||||
|
let sport_sheet = workbook
|
||||||
|
.get_sheet("SPORT")
|
||||||
|
.ok_or_else(|| anyhow!("Sheet 'SPORT' not found"))?;
|
||||||
|
|
||||||
|
let packs: HashMap<String, String> = sport_sheet
|
||||||
|
.rows
|
||||||
|
.iter()
|
||||||
|
.filter_map(|row| Some((row.get("SPORT")?.clone(), row.get("LINK")?.clone())))
|
||||||
|
.collect();
|
||||||
|
info!("Loaded {} sport packs", packs.len());
|
||||||
|
// sport_sheet автоматически освободится при выходе из функции
|
||||||
|
|
||||||
|
// Лист SPORT больше не нужен
|
||||||
|
let sport_rows_count = sport_sheet.rows.len();
|
||||||
|
debug!("Dropped SPORT sheet ({} rows)", sport_rows_count);
|
||||||
|
|
||||||
|
// ========== ЗАГРУЖАЕМ TEAM LOGOS ==========
|
||||||
|
let teams_sheet = workbook
|
||||||
|
.get_sheet("TEAMS")
|
||||||
|
.ok_or_else(|| anyhow!("Sheet 'TEAMS' not found"))?;
|
||||||
|
|
||||||
debug!("TEAMS headers: {:?}", teams_sheet.headers);
|
debug!("TEAMS headers: {:?}", teams_sheet.headers);
|
||||||
|
if log_enabled!(Level::Debug) {
|
||||||
for (i, row) in teams_sheet.rows.iter().take(3).enumerate() {
|
for (i, row) in teams_sheet.rows.iter().take(3).enumerate() {
|
||||||
debug!("TEAMS row {}: {:?}", i, row);
|
debug!("TEAMS row {}: {:?}", i, row);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut logos = LogoRegistry::with_capacity(teams_sheet.rows.len());
|
let mut logos = LogoRegistry::with_capacity(teams_sheet.rows.len());
|
||||||
for row in &teams_sheet.rows {
|
for row in &teams_sheet.rows {
|
||||||
@@ -332,7 +334,8 @@ async fn generate_and_submit_jobs(
|
|||||||
logos.insert(team.clone(), sport.clone(), link.clone());
|
logos.insert(team.clone(), sport.clone(), link.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info!("Loaded {} team logos", teams_sheet.rows.len());
|
let total_teams = teams_sheet.rows.len();
|
||||||
|
info!("Loaded {} team logos", total_teams);
|
||||||
|
|
||||||
// Дебаг: выводим статистику по TEAMS
|
// Дебаг: выводим статистику по TEAMS
|
||||||
if log_enabled!(Level::Debug) {
|
if log_enabled!(Level::Debug) {
|
||||||
@@ -343,19 +346,24 @@ async fn generate_and_submit_jobs(
|
|||||||
.collect();
|
.collect();
|
||||||
debug!("Sports found in TEAMS sheet: {:?}", sports);
|
debug!("Sports found in TEAMS sheet: {:?}", sports);
|
||||||
|
|
||||||
for sport in sports.iter() {
|
for sport in sports.iter().take(5) {
|
||||||
let examples: Vec<_> = teams_sheet
|
let examples: Vec<_> = teams_sheet
|
||||||
.rows
|
.rows
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|r| r.get("SPORT") == Some(*sport))
|
.filter(|r| r.get("SPORT") == Some(*sport))
|
||||||
.take(5)
|
.take(3)
|
||||||
.filter_map(|r| r.get("TEAM"))
|
.filter_map(|r| r.get("TEAM"))
|
||||||
.collect();
|
.collect();
|
||||||
debug!(" TEAMS examples for '{}': {:?}", sport, examples);
|
debug!(" TEAMS examples for '{}': {:?}", sport, examples);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загружаем channel logos
|
|
||||||
|
// ========== ЗАГРУЖАЕМ CHANNEL LOGOS ==========
|
||||||
|
let channel_sheet = workbook
|
||||||
|
.get_sheet("CHANELL")
|
||||||
|
.ok_or_else(|| anyhow!("Sheet 'CHANELL' not found"))?;
|
||||||
|
|
||||||
let channels: HashMap<String, String> = channel_sheet
|
let channels: HashMap<String, String> = channel_sheet
|
||||||
.rows
|
.rows
|
||||||
.iter()
|
.iter()
|
||||||
@@ -367,19 +375,49 @@ async fn generate_and_submit_jobs(
|
|||||||
debug!("Channels loaded: {:?}", channels.keys().collect::<Vec<_>>());
|
debug!("Channels loaded: {:?}", channels.keys().collect::<Vec<_>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut jobs: Vec<JobData> = Vec::with_capacity(start_sheet.rows.len() * 3);
|
|
||||||
|
|
||||||
// Собираем статистику по ненайденным логотипам для дебага
|
// ========== ОБРАБАТЫВАЕМ START (только активные строки) ==========
|
||||||
|
let start_sheet = workbook
|
||||||
|
.get_sheet("Start")
|
||||||
|
.ok_or_else(|| anyhow!("Sheet 'Start' not found"))?;
|
||||||
|
|
||||||
|
let total_start_rows = start_sheet.rows.len();
|
||||||
|
|
||||||
|
// Сразу фильтруем только строки с STATE = "FALSE"
|
||||||
|
let active_rows: Vec<(usize, &HashMap<String, String>)> = start_sheet
|
||||||
|
.rows
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, row)| row.get("STATE").map(|s| s.as_str()) == Some("FALSE"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Found {} active rows (STATE='FALSE') out of {} total",
|
||||||
|
active_rows.len(),
|
||||||
|
total_start_rows
|
||||||
|
);
|
||||||
|
|
||||||
|
// Собираем использованные команды для очистки logos
|
||||||
|
let mut used_teams: HashSet<(String, String)> = HashSet::new();
|
||||||
|
let mut jobs: Vec<JobData> = Vec::with_capacity(active_rows.len() * 3);
|
||||||
|
|
||||||
|
// Для дебага
|
||||||
let mut missing_teams: HashSet<String> = HashSet::new();
|
let mut missing_teams: HashSet<String> = HashSet::new();
|
||||||
let mut missing_sports: HashSet<String> = HashSet::new();
|
let mut missing_sports: HashSet<String> = HashSet::new();
|
||||||
|
|
||||||
for (idx, row) in start_sheet.rows.iter().enumerate() {
|
for (idx, row) in active_rows {
|
||||||
if let Some(state) = row.get("STATE") {
|
|
||||||
if state == "FALSE" {
|
|
||||||
let team_a = row.get("TEAM A").cloned().unwrap_or_default();
|
let team_a = row.get("TEAM A").cloned().unwrap_or_default();
|
||||||
let team_b = row.get("TEAM B").cloned().unwrap_or_default();
|
let team_b = row.get("TEAM B").cloned().unwrap_or_default();
|
||||||
let sport = row.get("SPORT").cloned().unwrap_or_default();
|
let sport = row.get("SPORT").cloned().unwrap_or_default();
|
||||||
|
|
||||||
|
// Запоминаем использованные команды
|
||||||
|
if !team_a.is_empty() {
|
||||||
|
used_teams.insert((team_a.clone(), sport.clone()));
|
||||||
|
}
|
||||||
|
if !team_b.is_empty() {
|
||||||
|
used_teams.insert((team_b.clone(), sport.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
if log_enabled!(Level::Debug) {
|
if log_enabled!(Level::Debug) {
|
||||||
let logo_a = logos.find(&team_a, &sport);
|
let logo_a = logos.find(&team_a, &sport);
|
||||||
let logo_b = logos.find(&team_b, &sport);
|
let logo_b = logos.find(&team_b, &sport);
|
||||||
@@ -398,8 +436,16 @@ async fn generate_and_submit_jobs(
|
|||||||
jobs.extend(job.create_variants());
|
jobs.extend(job.create_variants());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
// Очищаем logos от неиспользуемых команд
|
||||||
|
let logos_before = logos.len();
|
||||||
|
logos.retain(|team, sport| used_teams.contains(&(team.to_string(), sport.to_string())));
|
||||||
|
info!(
|
||||||
|
"Retained {} used team logos (cleaned up {} unused)",
|
||||||
|
logos.len(),
|
||||||
|
logos_before - logos.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// Дебаг: выводим сводку по отсутствующим логотипам
|
// Дебаг: выводим сводку по отсутствующим логотипам
|
||||||
if log_enabled!(Level::Debug) && !missing_teams.is_empty() {
|
if log_enabled!(Level::Debug) && !missing_teams.is_empty() {
|
||||||
@@ -420,22 +466,23 @@ async fn generate_and_submit_jobs(
|
|||||||
return Ok(Vec::new());
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== ОТПРАВКА ЗАДАНИЙ ==========
|
||||||
cleanup_finished_jobs(&config.nexrender_api_url).await?;
|
cleanup_finished_jobs(&config.nexrender_api_url).await?;
|
||||||
|
|
||||||
let http_client = Client::new();
|
let http_client = Client::new();
|
||||||
let mut submitted_jobs: Vec<(String, String)> = Vec::with_capacity(jobs.len());
|
let mut submitted_jobs: Vec<(String, String)> = Vec::with_capacity(jobs.len());
|
||||||
|
|
||||||
// Отправляем задания группами по 3 (оригинал + today + tomorrow)
|
// Запускаем ВСЕ группы параллельно
|
||||||
for chunk in jobs.chunks(3) {
|
let mut all_tasks = Vec::new();
|
||||||
let mut tasks = Vec::with_capacity(3);
|
|
||||||
|
|
||||||
|
for chunk in jobs.chunks(3) {
|
||||||
for job in chunk {
|
for job in chunk {
|
||||||
let client = http_client.clone();
|
let client = http_client.clone();
|
||||||
let api_url = config.nexrender_api_url.clone();
|
let api_url = config.nexrender_api_url.clone();
|
||||||
let config_clone = config.clone();
|
let config_clone = config.clone();
|
||||||
let job_owned = job.clone();
|
let job_owned = job.clone();
|
||||||
|
|
||||||
tasks.push(tokio::spawn(async move {
|
all_tasks.push(tokio::spawn(async move {
|
||||||
let nexrender_job = job_owned.to_nexrender_job(&config_clone);
|
let nexrender_job = job_owned.to_nexrender_job(&config_clone);
|
||||||
info!("Submitting job: {}", job_owned.outfile_name);
|
info!("Submitting job: {}", job_owned.outfile_name);
|
||||||
|
|
||||||
@@ -463,9 +510,10 @@ async fn generate_and_submit_jobs(
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ждём завершения группы
|
// Ждём завершения ВСЕХ задач
|
||||||
for task in tasks {
|
for task in all_tasks {
|
||||||
match task.await {
|
match task.await {
|
||||||
Ok(Ok(Some((uid, outfile_name)))) => {
|
Ok(Ok(Some((uid, outfile_name)))) => {
|
||||||
info!("Job submitted: {} (UID: {})", outfile_name, uid);
|
info!("Job submitted: {} (UID: {})", outfile_name, uid);
|
||||||
@@ -482,8 +530,6 @@ async fn generate_and_submit_jobs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Successfully submitted {} jobs to Nexrender",
|
"Successfully submitted {} jobs to Nexrender",
|
||||||
submitted_jobs.len()
|
submitted_jobs.len()
|
||||||
|
|||||||
Reference in New Issue
Block a user