Skip to content

Commit 5dac898

Browse files
0xbrayoErikBjare
andauthored
docs: misc improved docs, including rust watcher example (#132)
* feat: add configuration data foraw-server-rust * feat: add a custom visualizations entry * feat:remove the Lastfm importer mention * fix: minor change * fix: syntax * feat: create rust examples * feat: add rust sample watchers * Apply suggestions from code review --------- Co-authored-by: Erik Bjäreholt <[email protected]>
1 parent cace212 commit 5dac898

File tree

8 files changed

+203
-5
lines changed

8 files changed

+203
-5
lines changed

src/configuration.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ aw-server-python
2020
aw-server-rust
2121
--------------
2222

23-
TODO
23+
- :code:`host` Hostname to start the server on. Currently only :code:`localhost` or :code:`127.0.0.1` are supported.
24+
- :code:`port` Port number to start the server on.
25+
- :code:`cors` List of allowed origins for CORS (Cross-Origin Resource Sharing). Useful in testing and development to let other origins access the ActivityWatch API, such as aw-webui in development mode on port 27180.
2426

2527
aw-client
2628
---------

src/examples/cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "aw-minimal-client-rs"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
aw-client-rust = { git = "https://github.com/ActivityWatch/aw-server-rust.git", branch = "master" }
8+
aw-models = { git = "https://github.com/ActivityWatch/aw-server-rust.git", branch = "master" }
9+
serde_json = "1.0"
10+
tokio = { version = "1.0", features = ["full"] }
11+
chrono = "0.4.19"

src/examples/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
# so run this every time the clients starts up to verify that the bucket exists.
2626
# If the client was unable to connect to aw-server or something failed
2727
# during the creation of the bucket, an exception will be raised.
28-
client.create_bucket(bucket_id, event_type="test")
28+
client.create_bucket(bucket_id, event_type=event_type)
2929

3030
# Asynchronous loop example
3131
# This context manager starts the queue dispatcher thread and stops it when done, always use it when setting queued=True.

src/examples/client.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use aw_client_rust::AwClient;
2+
use aw_models::{Bucket, Event};
3+
use chrono::TimeDelta;
4+
use serde_json::{Map, Value};
5+
6+
async fn create_bucket(
7+
aw_client: &AwClient,
8+
bucket_id: String,
9+
event_type: String,
10+
) -> Result<(), Box<dyn std::error::Error>> {
11+
let res = aw_client
12+
.create_bucket(&Bucket {
13+
id: bucket_id,
14+
bid: None,
15+
_type: event_type,
16+
data: Map::new(),
17+
metadata: Default::default(),
18+
last_updated: None,
19+
hostname: "".to_string(),
20+
client: "test-client".to_string(),
21+
created: None,
22+
events: None,
23+
})
24+
.await?;
25+
26+
Ok(res)
27+
}
28+
29+
#[tokio::main]
30+
async fn main() {
31+
let port = 5666; // the testing port
32+
let aw_client = AwClient::new("localhost", port, "test-client").unwrap();
33+
let bucket_id = format!("test-client-bucket_{}", aw_client.hostname);
34+
let event_type = "dummy_data".to_string();
35+
36+
create_bucket(&aw_client, bucket_id.clone(), event_type)
37+
.await
38+
.unwrap();
39+
40+
let sleeptime = 1.0;
41+
for i in 0..5 {
42+
// Create a sample event to send as heartbeat
43+
let mut heartbeat_data = Map::new();
44+
heartbeat_data.insert("label".to_string(), Value::String("heartbeat".to_string()));
45+
46+
let now = chrono::Utc::now();
47+
48+
let heartbeat_event = Event {
49+
id: None,
50+
timestamp: now,
51+
duration: TimeDelta::seconds(1),
52+
data: heartbeat_data,
53+
};
54+
55+
println!("Sending heartbeat event {}", i);
56+
// The rust client does not support queued heartbeats, or commit intervals
57+
aw_client
58+
.heartbeat(&bucket_id, &heartbeat_event, sleeptime + 1.0)
59+
.await
60+
.unwrap();
61+
62+
// Sleep a second until next heartbeat (eventually drifts due to time spent in the loop)
63+
tokio::time::sleep(tokio::time::Duration::from_secs_f64(sleeptime)).await;
64+
}
65+
66+
// Sleep a bit more to allow the last heartbeat to be sent
67+
tokio::time::sleep(tokio::time::Duration::from_secs_f64(sleeptime)).await;
68+
69+
// Synchoronous example, insert an event
70+
let mut event_data = Map::new();
71+
event_data.insert(
72+
"label".to_string(),
73+
Value::String("non-heartbeat event".to_string()),
74+
);
75+
let now = chrono::Utc::now();
76+
let event = Event {
77+
id: None,
78+
timestamp: now,
79+
duration: TimeDelta::seconds(1),
80+
data: event_data,
81+
};
82+
aw_client.insert_event(&bucket_id, &event).await.unwrap();
83+
84+
// fetch the last 10 events
85+
// should include the first 5 heartbeats and the last event
86+
let events = aw_client
87+
.get_events(&bucket_id, None, None, Some(10))
88+
.await
89+
.unwrap();
90+
println!("Events: {:?}", events);
91+
92+
// Delete the bucket
93+
aw_client.delete_bucket(&bucket_id).await.unwrap();
94+
}

src/examples/minimal_client.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use aw_client_rust::AwClient;
2+
use aw_models::{Bucket, Event};
3+
use chrono::TimeDelta;
4+
use serde_json::{Map, Value};
5+
6+
async fn create_bucket(
7+
aw_client: &AwClient,
8+
bucket_id: String,
9+
) -> Result<(), Box<dyn std::error::Error>> {
10+
let res = aw_client
11+
.create_bucket(&Bucket {
12+
id: bucket_id,
13+
bid: None,
14+
_type: "dummy_data".to_string(),
15+
data: Map::new(),
16+
metadata: Default::default(),
17+
last_updated: None,
18+
hostname: "".to_string(),
19+
client: "test-client".to_string(),
20+
created: None,
21+
events: None,
22+
})
23+
.await?;
24+
25+
Ok(res)
26+
}
27+
28+
#[tokio::main]
29+
async fn main() {
30+
let port = 5666; // the testing port
31+
let aw_client = AwClient::new("localhost", port, "test-client").unwrap();
32+
let bucket_id = format!("test-client-bucket_{}", aw_client.hostname);
33+
34+
create_bucket(&aw_client, bucket_id.clone()).await.unwrap();
35+
36+
let mut shutdown_data = Map::new();
37+
shutdown_data.insert(
38+
"label".to_string(),
39+
Value::String("some interesting data".to_string()),
40+
);
41+
42+
let now = chrono::Utc::now();
43+
let shutdown_event = Event {
44+
id: None,
45+
timestamp: now,
46+
duration: TimeDelta::seconds(420),
47+
data: shutdown_data,
48+
};
49+
aw_client.insert_event(&bucket_id, &shutdown_event).await.unwrap();
50+
51+
let events = aw_client.get_events(&bucket_id, None, None, Some(1)).await.unwrap();
52+
print!("{:?}", events); // prints a single event
53+
54+
aw_client.delete_bucket(&bucket_id).await.unwrap();
55+
}

src/examples/writing-watchers.rst

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
Writing your first watcher
2-
==========================
1+
Writing your first watcher in Python
2+
====================================
33

44
Writing watchers for ActivityWatch is pretty easy, all you need is the :code:`aw-client` library.
55

@@ -34,3 +34,38 @@ This example will describe how to:
3434

3535
.. literalinclude:: client.py
3636

37+
Writing your first watcher in Rust
38+
==================================
39+
40+
To get started with writing watchers in Rust, you need to add the ``aw-client-rust`` and ``aw-model`` crates to your ``Cargo.toml`` file.
41+
The most up-to-date versions depend directly on :gh-aw:`aw-server-rust`.
42+
43+
.. literalinclude:: Cargo.toml
44+
45+
Minimal client
46+
--------------
47+
48+
Below is a minimal template client to quickly get started. Mirrors the python example above.
49+
This example will:
50+
51+
* create a bucket
52+
* insert an event
53+
* fetch an event from an aw-server bucket
54+
* delete the bucket again
55+
56+
.. literalinclude:: minimal_client.rs
57+
58+
Reference client
59+
----------------
60+
61+
Below is a example of a watcher with more in-depth comments. Mirrors the python example above.
62+
This example will describe how to:
63+
* create buckets
64+
* send events by heartbeats
65+
* insert events without heartbeats
66+
* do synchronous as well as asyncronous requests
67+
* fetch events from an aw-server bucket
68+
* delete buckets
69+
70+
.. literalinclude:: client.rs
71+

src/importers.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ ActivityWatch can't track everything, so sometimes you might want to import data
66
- :gh-aw:`aw-import-ical`, supports importing from ``.ical`` files or syncing with Google Calendar.
77
- :gh-aw:`aw-importer-smartertime`, imports from `smartertime`_ (Android time tracker).
88
- :gh-aw:`aw-import-screentime`, attempt at importing from macOS's Screen Time (and potentially iOS through syncing)
9-
- LastFM importer, :gh-user:`ErikBjare` has code for it somewhere, ask him if you're interested.
109

1110

1211
.. _smartertime: https://play.google.com/store/apps/details?id=com.smartertime&hl=en

src/watchers.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ If you want to more accurately track media consumption.
4444
- :gh-aw:`aw-watcher-openvr` - (not working yet) Watches active VR applications.
4545
- :gh:`RundownRhino/aw-watcher-mpv-sender` - (WIP) Watches mpv and reports the currently playing video.
4646
- :gh:`2e3s/aw-watcher-media-player` - Watches the currently playing media which is reported by most players to the system.
47+
- :gh:`brayo-pip/aw-watcher-lastfm` - Watches the currently playing track on Last.fm(supports most streaming services including Apple Music).
4748

4849
.. _other-watchers:
4950

@@ -76,6 +77,7 @@ Custom watchers might not be supported by the default visualizations, but Activi
7677

7778
- :gh-aw:`aw-watcher-input`
7879
- :gh:`Alwinator/aw-watcher-utilization`
80+
- :gh:`2e3s/aw-watcher-media-player`
7981

8082
.. note::
8183

0 commit comments

Comments
 (0)