expand MCP tools: browse_library, stats, analyze, preview, suggest, config tools (#17)
This commit is contained in:
@@ -180,6 +180,211 @@ impl ScheduleEngineService {
|
||||
Ok(schedule)
|
||||
}
|
||||
|
||||
pub async fn preview_schedule(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
from: DateTime<Utc>,
|
||||
duration_hours: u32,
|
||||
) -> DomainResult<GeneratedSchedule> {
|
||||
let channel = self
|
||||
.channel_query
|
||||
.find_by_id(channel_id)
|
||||
.await?
|
||||
.ok_or(DomainError::ChannelNotFound(channel_id))?;
|
||||
|
||||
let tz: Tz = channel
|
||||
.timezone()
|
||||
.parse()
|
||||
.map_err(|_| DomainError::TimezoneError(channel.timezone().to_owned()))?;
|
||||
|
||||
let history = self
|
||||
.schedule_query
|
||||
.find_playback_history(channel_id)
|
||||
.await?;
|
||||
let latest_schedule = self.schedule_query.find_latest(channel_id).await?;
|
||||
let generation = latest_schedule
|
||||
.as_ref()
|
||||
.map(|s| s.generation() + 1)
|
||||
.unwrap_or(1);
|
||||
|
||||
let mut block_continuity = self
|
||||
.schedule_query
|
||||
.find_last_slot_per_block(channel_id)
|
||||
.await?;
|
||||
|
||||
let valid_from = from;
|
||||
let valid_until = from + Duration::hours(duration_hours as i64);
|
||||
let start_date = from.with_timezone(&tz).date_naive();
|
||||
let end_date = valid_until.with_timezone(&tz).date_naive();
|
||||
|
||||
let mut slots: Vec<ScheduledSlot> = Vec::new();
|
||||
let mut current_date = start_date;
|
||||
|
||||
while current_date <= end_date {
|
||||
let weekday = Weekday::from(current_date.weekday());
|
||||
|
||||
for block in channel.schedule_config().blocks_for(weekday) {
|
||||
let naive_start = current_date.and_time(block.start_time());
|
||||
|
||||
let block_start_utc = match tz.from_local_datetime(&naive_start).earliest() {
|
||||
Some(dt) => dt.with_timezone(&Utc),
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let block_end_utc =
|
||||
block_start_utc + Duration::minutes(block.duration_mins() as i64);
|
||||
|
||||
let slot_start = block_start_utc.max(valid_from);
|
||||
let slot_end = block_end_utc.min(valid_until);
|
||||
|
||||
if slot_end <= slot_start {
|
||||
continue;
|
||||
}
|
||||
|
||||
let last_item_id = block_continuity.get(&block.id());
|
||||
|
||||
let mut block_slots = self
|
||||
.resolve_block(
|
||||
block,
|
||||
BlockTimeWindow {
|
||||
start: slot_start,
|
||||
end: slot_end,
|
||||
},
|
||||
RotationContext {
|
||||
history: &history,
|
||||
policy: channel.rotation_policy(),
|
||||
generation,
|
||||
last_item_id,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(last_slot) = block_slots.last() {
|
||||
block_continuity.insert(block.id(), last_slot.item().id().clone());
|
||||
}
|
||||
|
||||
slots.append(&mut block_slots);
|
||||
}
|
||||
|
||||
current_date = current_date.succ_opt().ok_or_else(|| {
|
||||
DomainError::validation("Date overflow during schedule generation")
|
||||
})?;
|
||||
}
|
||||
|
||||
slots.sort_by_key(|s| s.start_at());
|
||||
|
||||
Ok(GeneratedSchedule::new(
|
||||
channel_id,
|
||||
valid_from,
|
||||
valid_until,
|
||||
generation,
|
||||
slots,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn preview_config(
|
||||
&self,
|
||||
channel_id: ChannelId,
|
||||
config: &crate::models::ScheduleConfig,
|
||||
from: DateTime<Utc>,
|
||||
duration_hours: u32,
|
||||
) -> DomainResult<GeneratedSchedule> {
|
||||
let channel = self
|
||||
.channel_query
|
||||
.find_by_id(channel_id)
|
||||
.await?
|
||||
.ok_or(DomainError::ChannelNotFound(channel_id))?;
|
||||
|
||||
let tz: Tz = channel
|
||||
.timezone()
|
||||
.parse()
|
||||
.map_err(|_| DomainError::TimezoneError(channel.timezone().to_owned()))?;
|
||||
|
||||
let history = self
|
||||
.schedule_query
|
||||
.find_playback_history(channel_id)
|
||||
.await?;
|
||||
let latest_schedule = self.schedule_query.find_latest(channel_id).await?;
|
||||
let generation = latest_schedule
|
||||
.as_ref()
|
||||
.map(|s| s.generation() + 1)
|
||||
.unwrap_or(1);
|
||||
|
||||
let mut block_continuity = self
|
||||
.schedule_query
|
||||
.find_last_slot_per_block(channel_id)
|
||||
.await?;
|
||||
|
||||
let valid_from = from;
|
||||
let valid_until = from + Duration::hours(duration_hours as i64);
|
||||
let start_date = from.with_timezone(&tz).date_naive();
|
||||
let end_date = valid_until.with_timezone(&tz).date_naive();
|
||||
|
||||
let mut slots: Vec<ScheduledSlot> = Vec::new();
|
||||
let mut current_date = start_date;
|
||||
|
||||
while current_date <= end_date {
|
||||
let weekday = Weekday::from(current_date.weekday());
|
||||
|
||||
for block in config.blocks_for(weekday) {
|
||||
let naive_start = current_date.and_time(block.start_time());
|
||||
|
||||
let block_start_utc = match tz.from_local_datetime(&naive_start).earliest() {
|
||||
Some(dt) => dt.with_timezone(&Utc),
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let block_end_utc =
|
||||
block_start_utc + Duration::minutes(block.duration_mins() as i64);
|
||||
|
||||
let slot_start = block_start_utc.max(valid_from);
|
||||
let slot_end = block_end_utc.min(valid_until);
|
||||
|
||||
if slot_end <= slot_start {
|
||||
continue;
|
||||
}
|
||||
|
||||
let last_item_id = block_continuity.get(&block.id());
|
||||
|
||||
let mut block_slots = self
|
||||
.resolve_block(
|
||||
block,
|
||||
BlockTimeWindow {
|
||||
start: slot_start,
|
||||
end: slot_end,
|
||||
},
|
||||
RotationContext {
|
||||
history: &history,
|
||||
policy: channel.rotation_policy(),
|
||||
generation,
|
||||
last_item_id,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(last_slot) = block_slots.last() {
|
||||
block_continuity.insert(block.id(), last_slot.item().id().clone());
|
||||
}
|
||||
|
||||
slots.append(&mut block_slots);
|
||||
}
|
||||
|
||||
current_date = current_date.succ_opt().ok_or_else(|| {
|
||||
DomainError::validation("Date overflow during schedule generation")
|
||||
})?;
|
||||
}
|
||||
|
||||
slots.sort_by_key(|s| s.start_at());
|
||||
|
||||
Ok(GeneratedSchedule::new(
|
||||
channel_id,
|
||||
valid_from,
|
||||
valid_until,
|
||||
generation,
|
||||
slots,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_current_broadcast(
|
||||
schedule: &GeneratedSchedule,
|
||||
now: DateTime<Utc>,
|
||||
|
||||
Reference in New Issue
Block a user