95 lines
2.4 KiB
Rust
95 lines
2.4 KiB
Rust
use std::cmp::Ordering;
|
|
use std::collections::BTreeMap;
|
|
use std::rc::Rc;
|
|
use redis::{
|
|
aio::ConnectionManager,
|
|
RedisError,
|
|
};
|
|
use crate::tag::Tag;
|
|
|
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
|
struct ScoreKey {
|
|
score: f64,
|
|
tag: Rc<String>,
|
|
}
|
|
|
|
impl Eq for ScoreKey {
|
|
}
|
|
|
|
impl Ord for ScoreKey {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
if self.score == other.score {
|
|
self.tag.as_ref().cmp(other.tag.as_ref())
|
|
} else if self.score < other.score {
|
|
Ordering::Less
|
|
} else {
|
|
Ordering::Greater
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TrendAnalyzer {
|
|
/// in hours
|
|
period: u64,
|
|
/// *now* in hours
|
|
until: u64,
|
|
size: usize,
|
|
/// key contains name to avoid collision by just score
|
|
results: BTreeMap<ScoreKey, Rc<Tag>>,
|
|
score_threshold: Option<f64>,
|
|
}
|
|
|
|
impl TrendAnalyzer {
|
|
pub async fn run(
|
|
redis_man: &mut ConnectionManager,
|
|
size: usize,
|
|
periods: &[u64],
|
|
language: Option<String>,
|
|
) -> Result<Vec<Self>, RedisError> {
|
|
let until = (chrono::offset::Utc::now().naive_utc().timestamp() / 3600) as u64;
|
|
let mut analyzers: Vec<TrendAnalyzer> = periods.iter()
|
|
.cloned()
|
|
.map(|period| TrendAnalyzer {
|
|
period,
|
|
until,
|
|
size,
|
|
results: BTreeMap::new(),
|
|
score_threshold: None,
|
|
}).collect();
|
|
|
|
Tag::for_each(redis_man, language, |tag| {
|
|
let tag = Rc::new(tag);
|
|
for analyzer in &mut analyzers {
|
|
analyzer.process_tag(&tag);
|
|
}
|
|
}).await?;
|
|
|
|
Ok(analyzers)
|
|
}
|
|
|
|
pub fn process_tag(&mut self, tag: &Rc<Tag>) {
|
|
let score = tag.score(self.period, self.until);
|
|
|
|
if score <= 1.0 {
|
|
// discard downwards trends
|
|
return;
|
|
}
|
|
|
|
if self.score_threshold.map_or(false, |score_threshold| score < score_threshold) {
|
|
// score is below self.result[..self.size].score
|
|
return;
|
|
}
|
|
|
|
self.results.insert(ScoreKey { score, tag: tag.name.clone() }, tag.clone());
|
|
|
|
let mut least = self.results.keys().cloned().next().unwrap();
|
|
if self.results.len() > self.size {
|
|
self.results.remove(&least);
|
|
least = self.results.keys().cloned().next().unwrap().clone();
|
|
}
|
|
|
|
self.score_threshold = Some(least.score);
|
|
}
|
|
}
|