<commit_before>use std::str;
use std::hashmap::HashMap;

use log::{HTTPLogRecord, LogProcessor};


// TODO: Should be moved outside
static NUMBER_OF_ITEMS: uint = 8u;

struct ObjectStats {
    client: ~str,
    requests: uint,
    request_time: uint,
    sent_bytes: uint
}

pub struct LogStats {
    priv clients: HashMap<~str, ObjectStats>
}

impl LogStats {
    pub fn new() -> ~LogStats {
        ~LogStats{clients: HashMap::new()}
    }

    pub fn print(&self) {
        let mut sorted: ~[&ObjectStats] = self.clients.values().collect();
        sorted.sort_by(|a, b| b.requests.cmp(&a.requests));
        print_sorted(sorted, "By requests");
        sorted.sort_by(|a, b| b.request_time.cmp(&a.request_time));
        print_sorted(sorted, "By request time");
        sorted.sort_by(|a, b| b.sent_bytes.cmp(&a.sent_bytes));
        print_sorted(sorted, "By sent bytes");
    }
}

impl LogProcessor for LogStats {
    fn process(&mut self, record: HTTPLogRecord) {
        self.clients.insert_or_update_with(
            record.remote_addr.to_owned(),
            ObjectStats{
                client: record.remote_addr.to_owned(),
                requests: 1,
                request_time: record.request_time,
                sent_bytes: record.sent_bytes,
                },
            |_, stats| {
                stats.requests += 1;
                stats.request_time += record.request_time;
                stats.sent_bytes += record.sent_bytes;
            });
    }
}

fn print_sorted(sorted: &[&ObjectStats], message: &str) {
    println!("\n  {}", message);
    println!("=====================================================\
              =============");
    println!("Client                                 \
              Requests   Duration   Bytes");
    println!("-----------------------------------------------------\
              -------------");
    for stats in sorted.iter().take(NUMBER_OF_ITEMS) {
        println!("{: <40} {: >6} {: >10} {: >7}",
                 stats.client, stats.requests,
                 format_duration(stats.request_time),
                 format_bytes(stats.sent_bytes));
    }
}

fn format_duration(duration: uint) -> ~str {
    let (mut n, mut pos, modifier) = if duration < 1000 {
            (duration, 3, ~"s")
        } else if duration < 60 * 1000 {
            (duration / 100, 1, ~"s")
        } else if duration < 60 * 60 * 1000 {
            (duration / 6000, 1, ~"m")
        } else {
            (duration / 360000, 1, ~"h")
        };
    while pos > 0 && n % 10 == 0 {
        n /= 10;<commit_msg>Remove 'client' field from ObjectStats struct<commit_after>use std::str;
use std::hashmap::HashMap;

use log::{HTTPLogRecord, LogProcessor};


// TODO: Should be moved outside
static NUMBER_OF_ITEMS: uint = 8u;

struct ObjectStats {
    requests: uint,
    request_time: uint,
    sent_bytes: uint
}

pub struct LogStats {
    priv clients: HashMap<~str, ObjectStats>
}

impl LogStats {
    pub fn new() -> ~LogStats {
        ~LogStats{clients: HashMap::new()}
    }

    pub fn print(&self) {
        let mut stats: ~[(&~str, &ObjectStats)] = self.clients.iter().collect();
        stats.sort_by(|&(_, a), &(_, b)| b.requests.cmp(&a.requests));
        print_sorted(stats, "By requests");
        stats.sort_by(|&(_, a), &(_, b)| b.request_time.cmp(&a.request_time));
        print_sorted(stats, "By request time");
        stats.sort_by(|&(_, a), &(_, b)| b.sent_bytes.cmp(&a.sent_bytes));
        print_sorted(stats, "By sent bytes");
    }
}

impl LogProcessor for LogStats {
    fn process(&mut self, record: HTTPLogRecord) {
        self.clients.insert_or_update_with(
            record.remote_addr.to_owned(),
            ObjectStats{
                requests: 1,
                request_time: record.request_time,
                sent_bytes: record.sent_bytes,
                },
            |_, stats| {
                stats.requests += 1;
                stats.request_time += record.request_time;
                stats.sent_bytes += record.sent_bytes;
            });
    }
}

fn print_sorted(sorted: &[(&~str, &ObjectStats)], message: &str) {
    println!("\n  {}", message);
    println!("=====================================================\
              =============");
    println!("Client                                 \
              Requests   Duration   Bytes");
    println!("-----------------------------------------------------\
              -------------");
    for &(client, stats) in sorted.iter().take(NUMBER_OF_ITEMS) {
        println!("{: <40} {: >6} {: >10} {: >7}",
                 *client, stats.requests,
                 format_duration(stats.request_time),
                 format_bytes(stats.sent_bytes));
    }
}

fn format_duration(duration: uint) -> ~str {
    let (mut n, mut pos, modifier) = if duration < 1000 {
            (duration, 3, ~"s")
        } else if duration < 60 * 1000 {
            (duration / 100, 1, ~"s")
        } else if duration < 60 * 60 * 1000 {
            (duration / 6000, 1, ~"m")
        } else {
            (duration / 360000, 1, ~"h")
        };
    while pos > 0 && n % 10 == 0 {
        n /= 10;
{'fraction_non_alphanumeric': 0.18963509316770186, 'fraction_numerical': 0.019409937888198756, 'mean_word_length': 1.8723522853957637, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2428207', 'n_tokens_mistral': 1638, 'n_tokens_neox': 1528, 'n_words': 346}