-
-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow ActivityWatch to Ignoring / Filtering #302
base: master
Are you sure you want to change the base?
Changes from all commits
921791b
298e798
df0abb1
96f2d0a
fb58336
b87e32e
41b030a
1b56e03
1886ebe
c056e50
75ca368
f1f63c3
9cc06c7
6318204
124192b
14165e1
20ab632
051bb64
18a3496
9275009
3531ead
b8330f1
5a51fb9
82345cf
bb787fd
8d7ce89
aab4ac2
b574baf
5738246
85c08a3
7b2508a
dba809b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,8 +2,6 @@ use std::collections::HashMap; | |
|
||
use chrono::DateTime; | ||
use chrono::Duration; | ||
use chrono::NaiveDateTime; | ||
use chrono::TimeZone; | ||
use chrono::Utc; | ||
|
||
use rusqlite::Connection; | ||
|
@@ -173,6 +171,8 @@ fn _migrate_v3_to_v4(conn: &Connection) { | |
|
||
pub struct DatastoreInstance { | ||
buckets_cache: HashMap<String, Bucket>, | ||
ignored_apps: Vec<String>, | ||
ignored_titles: Vec<String>, | ||
first_init: bool, | ||
pub db_version: i32, | ||
} | ||
|
@@ -201,13 +201,57 @@ impl DatastoreInstance { | |
|
||
let mut ds = DatastoreInstance { | ||
buckets_cache: HashMap::new(), | ||
ignored_apps: vec![], | ||
ignored_titles: vec![], | ||
first_init, | ||
db_version, | ||
}; | ||
ds.get_stored_buckets(conn)?; | ||
ds.get_stored_ignores(conn)?; | ||
Ok(ds) | ||
} | ||
|
||
fn get_stored_ignores(&mut self, conn: &Connection) -> Result<(), DatastoreError> { | ||
let result = self.get_key_value(conn, "IGNORE_FILTERS_APPS"); | ||
if result.is_ok() { | ||
let unwrapped_key_value = &result.unwrap(); | ||
let parts = unwrapped_key_value.split(","); | ||
for part in parts { | ||
self.ignored_apps.push(part.to_uppercase()); | ||
} | ||
|
||
/*let apps_exist = unwrapped_key_value.value.get("APPS"); | ||
if apps_exist.is_some() { | ||
let apps = apps_exist.unwrap().as_array().unwrap(); | ||
for key in apps { | ||
let value = key.as_str().unwrap().to_string(); | ||
self.ignored_apps.push(value.to_uppercase()); | ||
} | ||
}*/ | ||
} | ||
|
||
let result = self.get_key_value(conn, "IGNORE_FILTERS_TITLES"); | ||
if result.is_ok() { | ||
let unwrapped_key_value = &result.unwrap(); | ||
let parts = unwrapped_key_value.split(","); | ||
for part in parts { | ||
self.ignored_titles.push(part.to_uppercase()); | ||
} | ||
|
||
/* let titles_exist = unwrapped_key_value.value.get("TITLES"); | ||
if titles_exist.is_some() { | ||
let titles = titles_exist.unwrap().as_array().unwrap(); | ||
for key in titles { | ||
let value = key.as_str().unwrap().to_string(); | ||
self.ignored_titles.push(value.to_uppercase()); | ||
} | ||
} */ | ||
|
||
info!("Ignoring {} titles & {} apps.", self.ignored_titles.len(), self.ignored_apps.len()); | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn get_stored_buckets(&mut self, conn: &Connection) -> Result<(), DatastoreError> { | ||
let mut stmt = match conn.prepare( | ||
" | ||
|
@@ -233,10 +277,7 @@ impl DatastoreInstance { | |
Some(starttime_ns) => { | ||
let seconds: i64 = starttime_ns / 1_000_000_000; | ||
let subnanos: u32 = (starttime_ns % 1_000_000_000) as u32; | ||
Some(TimeZone::from_utc_datetime( | ||
&Utc, | ||
&NaiveDateTime::from_timestamp_opt(seconds, subnanos).unwrap(), | ||
)) | ||
Some(DateTime::from_timestamp(seconds, subnanos).unwrap()) | ||
} | ||
None => None, | ||
}; | ||
|
@@ -246,10 +287,7 @@ impl DatastoreInstance { | |
Some(endtime_ns) => { | ||
let seconds: i64 = endtime_ns / 1_000_000_000; | ||
let subnanos: u32 = (endtime_ns % 1_000_000_000) as u32; | ||
Some(TimeZone::from_utc_datetime( | ||
&Utc, | ||
&NaiveDateTime::from_timestamp_opt(seconds, subnanos).unwrap(), | ||
)) | ||
Some(DateTime::from_timestamp(seconds, subnanos).unwrap()) | ||
} | ||
None => None, | ||
}; | ||
|
@@ -450,8 +488,34 @@ impl DatastoreInstance { | |
))) | ||
} | ||
}; | ||
for event in &mut events { | ||
|
||
'event: for event in &mut events { | ||
let app = match event.data.get("app") { | ||
Some(data) => data.as_str().unwrap().to_string().to_uppercase(), | ||
None => String::new() | ||
}; | ||
let title = match event.data.get("title") { | ||
Some(data) => data.as_str().unwrap().to_string().to_uppercase(), | ||
None => String::new() | ||
}; | ||
|
||
if !app.is_empty() { | ||
for key in self.ignored_apps.iter().cloned() { | ||
if app.contains(&key) { | ||
continue 'event; | ||
} | ||
} | ||
} | ||
if !title.is_empty() { | ||
for key in self.ignored_titles.iter().cloned() { | ||
if title.contains(&key) { | ||
continue 'event; | ||
} | ||
} | ||
} | ||
|
||
let starttime_nanos = event.timestamp.timestamp_nanos_opt().unwrap(); | ||
|
||
let duration_nanos = match event.duration.num_nanoseconds() { | ||
Some(nanos) => nanos, | ||
None => { | ||
|
@@ -561,6 +625,26 @@ impl DatastoreInstance { | |
bucket_id: &str, | ||
event: &Event, | ||
) -> Result<(), DatastoreError> { | ||
|
||
let app_exists = event.data.get("app"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Copy-paste of the same code above. Better to put it in a generic function. Unittests also wouldn't hurt. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, this should be made into a generic gunction -- the second path was because in my original implementation I found there was the second path that also created data entries (heartbeat)... So I duplicated the code to verify it would cover all the cases... Revamping the code is fine and I will check into moving this up to a higher layer, I just want to make sure I don't miss any code paths that plugins or other entities can call that will generate this data.... |
||
if app_exists.is_some() { | ||
let app = app_exists.unwrap().as_str().unwrap().to_string().to_uppercase(); | ||
for key in self.ignored_apps.iter().cloned() { | ||
if app.contains(&key) { | ||
return Ok(()); | ||
} | ||
} | ||
} | ||
let title_exists = event.data.get("title"); | ||
if title_exists.is_some() { | ||
let title = title_exists.unwrap().as_str().unwrap().to_string().to_uppercase(); | ||
for key in self.ignored_titles.iter().cloned() { | ||
if title.contains(&key) { | ||
return Ok(()); | ||
} | ||
} | ||
} | ||
|
||
let mut bucket = self.get_bucket(bucket_id)?; | ||
|
||
let mut stmt = match conn.prepare( | ||
|
@@ -689,10 +773,7 @@ impl DatastoreInstance { | |
|
||
Ok(Event { | ||
id: Some(id), | ||
timestamp: TimeZone::from_utc_datetime( | ||
&Utc, | ||
&NaiveDateTime::from_timestamp_opt(time_seconds, time_subnanos).unwrap(), | ||
), | ||
timestamp: DateTime::from_timestamp(time_seconds, time_subnanos).unwrap(), | ||
duration: Duration::nanoseconds(duration_ns), | ||
data, | ||
}) | ||
|
@@ -784,10 +865,7 @@ impl DatastoreInstance { | |
|
||
Ok(Event { | ||
id: Some(id), | ||
timestamp: TimeZone::from_utc_datetime( | ||
&Utc, | ||
&NaiveDateTime::from_timestamp_opt(time_seconds, time_subnanos).unwrap(), | ||
), | ||
timestamp: DateTime::from_timestamp(time_seconds, time_subnanos).unwrap(), | ||
duration: Duration::nanoseconds(duration_ns), | ||
data, | ||
}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
use std::collections::HashMap; | ||
use std::convert::{TryFrom, TryInto}; | ||
use std::fmt; | ||
|
||
use super::functions; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[package] | ||
name = "aw-server" | ||
version = "0.12.1" | ||
version = "0.13.1" | ||
authors = ["Johan Bjäreholt <[email protected]>", "Erik Bjäreholt <[email protected]>"] | ||
edition = "2021" | ||
|
||
|
@@ -14,9 +14,8 @@ name = "aw-server" | |
path = "src/main.rs" | ||
|
||
[dependencies] | ||
rocket = { version = "0.5.0-rc.3", features = ["json"] } | ||
rocket_cors = { version = "0.6.0-alpha2" } | ||
multipart = { version = "0.18", default-features = false, features = ["server"] } | ||
rocket = { version = "0.5.0", features = ["json"] } | ||
rocket_cors = { version = "0.6.0" } | ||
serde = { version = "1.0", features = ["derive"] } | ||
serde_json = "1.0" | ||
chrono = { version = "0.4", features = ["serde"] } | ||
|
@@ -36,6 +35,9 @@ aw-models = { path = "../aw-models" } | |
aw-transform = { path = "../aw-transform" } | ||
aw-query = { path = "../aw-query" } | ||
|
||
[target.'cfg(target_os="linux")'.dependencies] | ||
sd-notify = "0.4.2" | ||
|
||
[target.'cfg(all(target_os="linux", target_arch="x86"))'.dependencies] | ||
jemallocator = "0.5.0" | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should not be the datastores responsibility to ignore apps/titles imo. It should not manipulate any data. I think it's better to have this logic in the rest endpoint for insert_event and heartbeat.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll have to re-look but I could have sworn there was more than just insert_event and heartbeat that could trigger the insert, which is why I was trying to consolidate the code only in two paths rather than have multiple places for the code... But maybe I miss read the code and only insert_event and heardbeat call these two datastore code paths...