def main(): # Verify python version is >=3.5 # req_version is 3.5 due to usage of subprocess.run assert_version((3, 5)) if sys.platform.startswith("linux") and ("DISPLAY" not in os.environ or not os.environ["DISPLAY"]): raise Exception("DISPLAY environment variable not set") # Read settings from config config = load_config() args = parse_args(default_poll_time=config.getfloat("poll_time"), default_exclude_title=config.getboolean("exclude_title")) setup_logging(name="aw-watcher-window", testing=args.testing, verbose=args.verbose, log_stderr=True, log_file=True) client = ActivityWatchClient("aw-watcher-window", testing=args.testing) bucket_id = "{}_{}".format(client.client_name, client.client_hostname) event_type = "currentwindow" client.create_bucket(bucket_id, event_type, queued=True) logger.info("aw-watcher-window started") sleep(1) # wait for server to start with client: heartbeat_loop(client, bucket_id, poll_time=args.poll_time, exclude_title=args.exclude_title)
class Detector: def __init__(self) -> None: self.client = ActivityWatchClient("status-checker") # TODO: Move to aw-client? # TODO: Doesn't care if the event was old (as can happen if you have a dead watcher) def _get_last_event(self, bucket_id: str) -> Event: last_events = self.client.get_events(bucket_id, limit=1) if last_events: return last_events[0] else: raise Exception("no event found") def get_bucket_id(self, type: str) -> str: # TODO: Doesn't care about hostname # TODO (maybe): Create a better way to query buckets buckets = self.client.get_buckets() # print(buckets) window_bucket = find( lambda bucket: bucket["type"] == type and "testing" not in bucket[ "id"], buckets.values()) if window_bucket is None: raise Exception("Bucket not found") return window_bucket["id"] def detect(self, bucket_id: str, filter_str: str) -> Optional[Event]: last_event = self._get_last_event(bucket_id) return last_event if find(lambda label: filter_str in label.lower(), last_event.labels) else None
def main() -> None: now = datetime.now(tz=timezone.utc) td30d = timedelta(days=7) aw = ActivityWatchClient() print("Querying...") data = aw.query(_query, [(now - td30d, now)]) events = [{ "timestamp": e["timestamp"], "duration": timedelta(seconds=e["duration"]), **e["data"], } for e in data[0]["events"]] df = pd.json_normalize(events) df["timestamp"] = pd.to_datetime(df["timestamp"], infer_datetime_format=True) df.set_index("timestamp", inplace=True) print(df) answer = input("Do you want to export to CSV? (y/N): ") if answer == "y": filename = "output.csv" df.to_csv(filename) print(f"Wrote to {filename}")
def main(): import argparse parser = argparse.ArgumentParser("A watcher for ping") parser.add_argument("--testing", action="store_true") args = parser.parse_args() logging.basicConfig(level=logging.DEBUG if args.testing else logging.INFO) client = ActivityWatchClient("aw-watcher-network", testing=args.testing) bucketname = "{}_{}".format(client.client_name, client.client_hostname) eventtype = "ping" client.create_bucket(bucketname, eventtype) logger.info("Starting to log ping") while True: t = datetime.utcnow() sleeptime = 60 - (t.second + t.microsecond/1000000.0) sleep(sleeptime) timestamp = datetime.now(pytz.utc) wifiname = Wireless().current() try: out = ping(30) client.send_events(bucketname, createEvents(out,timestamp,wifiname)) logger.info("Sent events") except Exception as e: import sys, traceback traceback.print_exc() logger.error("Exception thrown while pinging {}".format(e))
def __init__(self, testing=False): config_section = "aw-watcher-table" if not testing else "aw-watcher-table-testing" self.settings = Settings(watcher_config[config_section]) self.client = ActivityWatchClient("aw-watcher-table", testing=testing) self.bucket_id = "{}_{}".format(self.client.client_name, self.client.client_hostname)
def main(): # Verify python version is >=3.5 # req_version is 3.5 due to usage of subprocess.run assert_version((3, 5)) if sys.platform.startswith("linux") and ("DISPLAY" not in os.environ or not os.environ["DISPLAY"]): raise Exception("DISPLAY environment variable not set") # Read settings from config config = load_config() args = parse_args(default_poll_time=config.getfloat("poll_time")) setup_logging(name="aw-watcher-window", testing=args.testing, verbose=args.verbose, log_stderr=True, log_file=True) client = ActivityWatchClient("aw-watcher-window", testing=args.testing) bucket_id = "{}_{}".format(client.client_name, client.client_hostname) event_type = "currentwindow" client.create_bucket(bucket_id, event_type, queued=True) logger.info("aw-watcher-window started") sleep(1) # wait for server to start with client: heartbeat_loop(client, bucket_id, poll_time=args.poll_time, exclude_title=args.exclude_title)
def bucket_report(bucket_name,start_date,end_date): client = ActivityWatchClient("aw-watcher-cli", testing=False) bucket_id = "{}_{}".format(bucket_name, client.hostname) query = "RETURN = query_bucket('{}');".format(bucket_id) events = client.query(query,start_date,end_date)[0] if len(events) == 0: print("No events") exit() events.reverse() #oder from oldest to newest # first date date = parser.parse(events[0]["timestamp"],default=datetime.now()) - timedelta(hours=6) #- CST print(date.date()) #print first date day_duration = 0 total_duration = 0 cnt = 1 for event in events: total_duration += event["duration"] timestamp = parser.parse(event["timestamp"],default=datetime.now()) - timedelta(hours=6) #- CST if date.date() != timestamp.date(): date = timestamp print("sum:\t{}".format(format_duration(day_duration))) #duration from prev block print("") day_duration = event["duration"] #reset duration print(date.date()) #print next date else: day_duration += event["duration"] print("{}.\t{}\t{}".format(cnt,format_duration(event["duration"]),event["data"]["label"])) cnt += 1 #print last day_duration when last event is reached if event == events[-1]: print("sum:\t{}".format(format_duration(day_duration))) #Print total report duration print("total:\t{}".format(format_duration(total_duration)))
def __init__(self, testing=False, send_commands=True, send_heartbeats=True): # Create client self._client = ActivityWatchClient(client_id, testing=testing) self._client.connect() self._init_buckets() # Settings self.pulsetime = 10 self.send_commands = send_commands self.send_heartbeats = send_heartbeats # Initialize the EventQueue self._event_queue = EventQueue(callback=self._handle_event, time_buffer=(self.pulsetime / 2)) self._event_handlers = { 'preopen': self._preopen, 'preexec': self._preexec, 'precmd': self._precmd, 'preclose': self._preclose } self._terminal_sessions = {}
def __init__(self, testing=False): # Read settings from config configsection = "aw-watcher-afk" if not testing else "aw-watcher-afk-testing" self.settings = Settings(watcher_config[configsection]) self.client = ActivityWatchClient("aw-watcher-afk", testing=testing) self.bucketname = "{}_{}".format(self.client.client_name, self.client.client_hostname)
def __init__(self): self.aw_client = ActivityWatchClient() self.all_buckets = {} self.bucket = self._get_main_bucket() self.aw_watcher_window_id = self.bucket['id'] self._all_buckets_events = {}
def client(): c = ActivityWatchClient("client-test", testing=True) yield c # Delete test buckets after all tests needing the fixture have been run buckets = c.get_buckets() for bucket_id in buckets: if bucket_id.startswith("test-"): c.delete_bucket(bucket_id)
def __init__(self, poll_time: int = 5, timeout: int = 180) -> None: self.client = ActivityWatchClient("aw-watcher-afk", testing=False) self.bucketname = "{}_{}".format( self.client.client_name, self.client.client_hostname ) self.poll_time = poll_time self.timeout = timeout self.initiated_shutdown: bool = False
def windows_watcher_run() -> None: client = ActivityWatchClient("aw-watcher-window", testing=False) bucket_id = "{}_{}".format(client.client_name, client.client_hostname) event_type = "currentwindow" client.create_bucket(bucket_id, event_type, queued=True) sleep(1) # wait for server to start with client: heartbeat_loop(client, bucket_id, poll_time=2.0, exclude_title=False)
def shutdown(bucket_name,start_time,end_time,activity): client = ActivityWatchClient("aw-watcher-cli", testing=False) bucket_id = "{}_{}".format(bucket_name, client.hostname) event_type = "cli_activity" client.create_bucket(bucket_id, event_type=event_type) shutdown_data = {"label": activity} shutdown_event = Event(timestamp=start_time, data=shutdown_data, duration=(end_time -start_time)) inserted_event = client.insert_event(bucket_id, shutdown_event) rd = relativedelta.relativedelta (end_time, start_time) print("Spent {:02}:{:02}:{:02} doing {}:".format(rd.hours, rd.minutes, rd.seconds,activity))
def main(): import argparse poll_time = 1.0 parser = argparse.ArgumentParser("A watcher for windows in X11") parser.add_argument("--testing", action="store_true") args = parser.parse_args() logging.basicConfig(level=logging.DEBUG if args.testing else logging.INFO) client = ActivityWatchClient("x11watcher", testing=args.testing) bucketname = "{}_{}".format(client.client_name, client.client_hostname) eventtype = "currentwindow" client.create_bucket(bucketname, eventtype) # get_only_active = True last_window = [] while True: try: # wids = xprop.get_window_ids() active_window_id = xprop.get_active_window_id() if active_window_id == "0x0": print("Failed to find active window, id found was 0x0") sleep(poll_time) continue # Always true, getting all windows currently not supported # if get_only_active: current_window = xprop.get_windows([active_window_id], active_window_id)[0] # else: # current_windows = get_windows(wids, active_window_id) """ if last_windows != current_windows: last_windows = current_windows print("Windows changed") client.send_event(Event(windows=last_windows, timestamp=datetime.now())) print(current_windows) """ if last_window != current_window: last_window = current_window print("Window changed") labels = ["title:" + current_window["name"]] labels.extend(["class:" + cls for cls in set(current_window["class"])]) client.send_event(bucketname, Event(label=labels, timestamp=datetime.now(pytz.utc))) print(current_window) except Exception as e: logger.error("Exception thrown while trying to get active window: {}".format(e)) traceback.print_exc(e) sleep(poll_time)
def test_filter_short(): # TODO: This was used in dev and does not work. awapi = ActivityWatchClient("cleaner", testing=True) events = awapi.get_events(BUCKET_WEB, limit=-1) filter_short(events, threshold=1) events = awapi.get_events(BUCKET_WINDOW, limit=-1) filter_short(events, threshold=1) events = awapi.get_events(BUCKET_AFK, limit=-1) filter_short(events, threshold=30)
def aw_client(): # TODO: Could it be possible to write a sisterclass of ActivityWatchClient # which calls aw_server.api directly? Would it be of use? Would add another # layer of integration tests that are actually more like unit tests. c = ActivityWatchClient("client-test", testing=True) yield c # Delete test buckets after all tests needing the fixture have been run buckets = c.get_buckets() for bucket_id in buckets: if bucket_id.startswith("test-"): c.delete_bucket(bucket_id)
def get_begin_time(bucket_name: str = BUCKET_NAME) -> datetime.datetime: client = ActivityWatchClient() buckets = client.get_buckets() selected_bucket = None for bucket in buckets.values(): if bucket["id"] == bucket_name + "_" + bucket["hostname"]: selected_bucket = bucket if selected_bucket is None: raise ValueError( f"bucket {bucket_name} does not exist, run project watcher") return datetime.datetime.fromisoformat(selected_bucket["created"]).replace( tzinfo=get_localzone(), hour=0, minute=0, second=0)
def test_filter_short(): # TODO: This was used in dev and does not work. awapi = ActivityWatchClient("cleaner", testing=True) events = awapi.get_events("aw-watcher-web-test", limit=-1) filter_short(events, threshold=1) events = awapi.get_events("aw-watcher-window-testing_erb-main2-arch", limit=-1) filter_short(events, threshold=1) events = awapi.get_events("aw-watcher-afk-testing_erb-main2-arch", limit=-1) filter_short(events, threshold=30)
def _get_window_events(n=1000): client = ActivityWatchClient("aw-analyser", testing=True) buckets = client.get_buckets() bucket_id = None for _bid in buckets.keys(): if "window" in _bid and "testing" not in _bid: bucket_id = _bid if bucket_id: return client.get_events(bucket_id, limit=n) else: print("Did not find bucket") return []
def get_events( hostname: str, since: datetime, end: datetime, include_smartertime="auto", include_toggl=None, testing: bool = False, ) -> List[Event]: awc = ActivityWatchClient("test", testing=testing) query = build_query(hostname) logger.debug(f"Query:\n{query}") result = awc.query(query, timeperiods=[(since, end)]) events = [Event(**e) for e in result[0]] if include_smartertime: events = union_no_overlap( events, _get_events_smartertime(since, filepath=include_smartertime)) events = sorted(events, key=lambda e: e.timestamp) if include_toggl: events = union_no_overlap( events, _get_events_toggl(since, filepath=include_toggl)) events = sorted(events, key=lambda e: e.timestamp) # Filter by time events = [ e for e in events if since.astimezone(timezone.utc) < e.timestamp and e.timestamp + e.duration < end.astimezone(timezone.utc) ] assert all(since.astimezone(timezone.utc) < e.timestamp for e in events) assert all(e.timestamp + e.duration < end.astimezone(timezone.utc) for e in events) # Filter out events without data (which sometimes happens for whatever reason) events = [e for e in events if e.data] for event in events: if "app" not in event.data: if "url" in event.data: event.data["app"] = urlparse(event.data["url"]).netloc else: print("Unexpected event: ", event) events = [e for e in events if e.data] return events
def main(): global DRYRUN if "--wet" in sys.argv: DRYRUN = False global aw aw = ActivityWatchClient(testing=True) buckets = aw.get_buckets() print("Buckets: ") print("\n".join([" - " + bid for bid in buckets.keys()]) + "\n") bid_to_redact = input( "In which bucket are the events you want to redact? (* for all): " ) assert bid_to_redact == "*" or bid_to_redact in buckets, "Not a valid option" regex_or_string = input( "Do you want to search by regex or string? (regex/string): " ) assert regex_or_string in ["regex", "string"], "Not a valid option" print("\nNOTE: Matching is not case sensitive!") pattern: Union[str, Pattern] if regex_or_string == "string": pattern = input("Enter a string indicating sensitive content: ").lower() else: pattern = re.compile( input("Enter a regex indicating sensitive content: ").lower() ) print("") if DRYRUN: print( "NOTE: Performing a dry run, no events will be modified. Run with --wet to modify events." ) else: print( "WARNING: Note that this performs an operation that cannot be undone. We strongly recommend that you backup/export your data before proceeding." ) input("Press ENTER to continue, or Ctrl-C to abort") if bid_to_redact == "*": for bucket_id in buckets.keys(): if bucket_id.startswith("aw-watcher-afk"): return _redact_bucket(bucket_id, pattern) else: _redact_bucket(bid_to_redact, pattern)
def get_activity( project: str, begin: Optional[datetime.datetime] = None, end: Optional[datetime.datetime] = None, activity_buckets: Sequence[str] = DEFAULT_ACTIVITY_BUCKETS ) -> Activity: if begin is None: begin = get_begin_time() if end is None: end = get_default_end_time() time_periods = [(begin, end)] client = ActivityWatchClient() query = _get_activity_query(project, activity_buckets) events: List[ActivityData] = client.query(query, time_periods) return Activity(events[0]['aw_watcher_window_events'])
def delete_prev_buckets(): logging.info("Deleting old buckets") client = ActivityWatchClient("aw-fake-client", testing=True) client.client_hostname = hostname client.connect() try: client.delete_bucket(window_bucket_name) except HTTPError: pass try: client.delete_bucket(afk_bucket_name) except HTTPError: pass
def setup_client(): logging.info("Setting up client") client = ActivityWatchClient("aw-fake-client", testing=True) client.client_hostname = hostname eventtype = "currentwindow" client.create_bucket(window_bucket_name, eventtype) eventtype = "afkstatus" client.create_bucket(afk_bucket_name, eventtype) client.connect() return client
def get_events(hostname: str, since: datetime, end: datetime, include_smartertime='auto', include_toggl=None) -> List[Event]: awc = ActivityWatchClient("test", testing=False) # print(query_complete) query = build_query(hostname) result = awc.query(query, start=since, end=end) events = [Event(**e) for e in result[0]] if include_smartertime: events = _union_no_overlap( events, _get_events_smartertime(since, filepath=include_smartertime)) events = sorted(events, key=lambda e: e.timestamp) if include_toggl: events = _union_no_overlap( events, _get_events_toggl(since, filepath=include_toggl)) events = sorted(events, key=lambda e: e.timestamp) # Filter by time events = [ e for e in events if since.astimezone(timezone.utc) < e.timestamp and e.timestamp + e.duration < end.astimezone(timezone.utc) ] assert all(since.astimezone(timezone.utc) < e.timestamp for e in events) assert all(e.timestamp + e.duration < end.astimezone(timezone.utc) for e in events) # Filter out events without data (which sometimes happens for whatever reason) events = [e for e in events if e.data] for event in events: if 'app' not in event.data: if 'url' in event.data: event.data['app'] = urlparse(event.data['url']).netloc else: print('Unexpected event: ', event) events = [e for e in events if e.data] return events
def end_current_event(start_ts, category, item, end_dt=None): start_dt = datetime.utcfromtimestamp(start_ts) if end_dt is None: end_dt = datetime.utcnow() app.logger.debug('Instantiating client (127.0.0.1:5600)') client = ActivityWatchClient( "aw-watcher-manual-flaskweb", host='127.0.0.1:5600' ) bucket_id = "{}_{}".format("aw-watcher-manual-flaskweb", client.hostname) app.logger.debug('Creating bucket: %s', bucket_id) client.create_bucket(bucket_id, event_type="currentwindow") t = 'manual: %s/%s' % (category, item) evt = Event( timestamp=start_dt, duration=(end_dt - start_dt).total_seconds(), data={'app': t, 'title': t, 'category': category, 'item': item} ) inserted_event = client.insert_event(bucket_id, evt) app.logger.info('Inserted event: %s', inserted_event)
def test_full(): now = datetime.now(timezone.utc) client_name = "aw-test-client" bucket_name = "test-bucket" bucket_etype = "test" # Test context manager with ActivityWatchClient(client_name, testing=True) as client: time.sleep(1) # Check that client name is set correctly assert client.client_name == client_name # Delete bucket before creating it, and handle error if it doesn't already exist try: client.delete_bucket(bucket_name) except HTTPError: pass # Create bucket client.create_bucket(bucket_name, bucket_etype) # Check bucket buckets = client.get_buckets() assert bucket_name in buckets assert bucket_name == buckets[bucket_name]["id"] assert bucket_etype == buckets[bucket_name]["type"] # Insert events e1 = create_unique_event() e2 = create_unique_event() e3 = create_unique_event() events = [e1, e2, e3] client.insert_events(bucket_name, events) # Get events fetched_events = client.get_events(bucket_name, limit=len(events)) # Assert events assert [(e.timestamp, e.duration, e.data) for e in fetched_events] == [ (e.timestamp, e.duration, e.data) for e in events ] # Check eventcount eventcount = client.get_eventcount(bucket_name) assert eventcount == len(events) result = client.query( f"RETURN = query_bucket('{bucket_name}');", timeperiods=[(now - timedelta(hours=1), now + timedelta(hours=1))], ) assert len(result) == 1 assert len(result[0]) == 3 # Delete bucket client.delete_bucket(bucket_name)
def all_active_webactivity(): """Returns activity during non-afk events or when tab is audible""" awapi = ActivityWatchClient("test", testing=True) start = datetime.now() - timedelta(days=7) tabevents = awapi.get_events("aw-watcher-web-chrome", start=start) afkevents = awapi.get_events("aw-watcher-afk_erb-laptop2-arch", start=start) afkevents_notafk = list( filter(lambda e: e.data["status"] == "not-afk", afkevents)) tabevents_audible = list( filter(lambda e: "audible" in e.data and e.data["audible"], tabevents)) # TODO: Implement merge # activeevents = merge(afkevents_notafk, tabevents_audible) # This isn't perfect, buggy when a notafk/audible events is contained by another activeevents = afkevents_notafk + tabevents_audible return filter_period_intersect(tabevents, activeevents)
def main(dryrun=True): # TODO: Use wordlist instead of argument sensitive_words = ["sensitive"] aw = ActivityWatchClient(testing=True) re_word = r'\b{}\b' buckets = aw.get_buckets() for bid in buckets.keys(): if "window" not in bid: continue print("Checking bucket: {}".format(bid)) events = aw.get_events(bid, limit=-1) old_matches = set() for event in events: for key, val in event.data.items(): if isinstance(val, str): matches = [re.findall(re_word.format(word), val.lower()) for word in sensitive_words] matches = set(sum(matches, [])) if matches: event.data[key] = "REDACTED" if val not in old_matches: print(f"{'(DRYRUN) ' if dryrun else ''} Matches: {matches}, redacting: {val}") old_matches.add(val) if not dryrun: aw.insert_event(bid, event)
def heartbeat(bucket_name,start_time,activity): client = ActivityWatchClient("aw-watcher-cli", testing=False) bucket_id = "{}_{}".format(bucket_name, client.hostname) event_type = "cli_ping" client.create_bucket(bucket_id, event_type=event_type) # Asynchronous loop with client: heartbeat_data = {"label": "heartbeat"} now = datetime.now(timezone.utc) heartbeat_event = Event(timestamp=now, data=heartbeat_data) sleeptime = 1 #Sending hearbeat until keyboard interrup second = 1 while(True): # The duration between the heartbeats will be less than pulsetime, so they will get merged. rd = relativedelta.relativedelta (now, start_time) print("Doing: {} for {:02}:{:02}:{:02}".format(activity,rd.hours, rd.minutes, rd.seconds),end="\r") client.heartbeat(bucket_id, heartbeat_event, pulsetime=sleeptime+1, queued=True) # Sleep a second until next heartbeat sleep(sleeptime) # Update timestamp for next heartbeat heartbeat_event.timestamp = datetime.now(timezone.utc) # update now now = heartbeat_event.timestamp second += 1
def main(): import argparse parser = argparse.ArgumentParser("A watcher for ping") parser.add_argument("--testing", action="store_true") args = parser.parse_args() logging.basicConfig(level=logging.DEBUG if args.testing else logging.INFO) client = ActivityWatchClient("aw-watcher-network", testing=args.testing) bucketname = "{}_{}".format(client.client_name, client.client_hostname) eventtype = "ping" client.create_bucket(bucketname, eventtype) logger.info("Starting to log ping") while True: t = datetime.utcnow() sleeptime = 60 - (t.second + t.microsecond / 1000000.0) sleep(sleeptime) timestamp = datetime.now(pytz.utc) wifiname = Wireless().current() try: out = ping(30) client.send_events(bucketname, createEvents(out, timestamp, wifiname)) logger.info("Sent events") except Exception as e: import sys, traceback traceback.print_exc() logger.error("Exception thrown while pinging {}".format(e))
def main(): import argparse poll_time = 1.0 parser = argparse.ArgumentParser("A watcher for windows in X11") parser.add_argument("--testing", action="store_true") args = parser.parse_args() logging.basicConfig(level=logging.DEBUG if args.testing else logging.INFO) client = ActivityWatchClient("x11watcher", testing=args.testing) bucketname = "{}_{}".format(client.client_name, client.client_hostname) eventtype = "currentwindow" client.create_bucket(bucketname, eventtype) # get_only_active = True last_window = [] while True: try: # wids = xprop.get_window_ids() active_window_id = xprop.get_active_window_id() if active_window_id == "0x0": print("Failed to find active window, id found was 0x0") sleep(poll_time) continue # Always true, getting all windows currently not supported # if get_only_active: current_window = xprop.get_windows([active_window_id], active_window_id)[0] # else: # current_windows = get_windows(wids, active_window_id) """ if last_windows != current_windows: last_windows = current_windows print("Windows changed") client.send_event(Event(windows=last_windows, timestamp=datetime.now())) print(current_windows) """ if last_window != current_window: last_window = current_window print("Window changed") labels = ["title:" + current_window["name"]] labels.extend( ["class:" + cls for cls in set(current_window["class"])]) client.send_event( bucketname, Event(label=labels, timestamp=datetime.now(pytz.utc))) print(current_window) except Exception as e: logger.error( "Exception thrown while trying to get active window: {}". format(e)) traceback.print_exc(e) sleep(poll_time)
def test_failqueue(): client_name = "aw-test-client-" + str(randint(0, 10000)) bucket_id = "test-bucket-" + str(randint(0, 10000)) bucket_etype = "test" input("Make sure aw-server isn't running, then press enter > ") client1 = ActivityWatchClient(client_name, testing=True) client1.create_bucket(bucket_id, bucket_etype, queued=True) print("Creating events") events = [create_unique_event() for _ in range(3)] for i, e in enumerate(events): e.timestamp += timedelta(seconds=i) client1.heartbeat(bucket_id, e, pulsetime=1, queued=True) print("Trying to send events (should fail)") with client1: time.sleep(1) input("Start aw-server with --testing, then press enter > ") client2 = ActivityWatchClient(client_name, testing=True) client2.create_bucket(bucket_id, bucket_etype, queued=True) # Here the previously queued events should be sent with client2: time.sleep(1) print("Getting events") recv_events = client2.get_events(bucket_id) print("Asserting latest event") pprint(recv_events) pprint(recv_events[0].data['label']) pprint(events[2].data['label']) assert recv_events[0].data['label'] == events[2].data['label']
#!/usr/bin/env python3 from time import sleep from datetime import datetime, timedelta, timezone from aw_core.models import Event from aw_client import ActivityWatchClient client = ActivityWatchClient("test-client", testing=True) now = datetime.now(timezone.utc) start = now query = "RETURN=0;" res = client.query(query, "1970-01-01", "2100-01-01") print(res) # Should print 0 bucket_id = "{}_{}".format("test-client-bucket", client.hostname) event_type = "dummydata" client.create_bucket(bucket_id, event_type="test") def insert_events(label: str, count: int): global now events = [] for i in range(count): e = Event(timestamp=now, duration=timedelta(seconds=1), data={"label": label}) events.append(e) now = now + timedelta(seconds=1) client.insert_events(bucket_id, events)
# Usage: # python3 test_continous_events.py aw-watcher-afk-testing_{hostname} # # Example: # python3 test_continous_events.py aw-watcher-afk-testing_erb-laptop-ubuntu import sys from datetime import timedelta from aw_client import ActivityWatchClient client = ActivityWatchClient("aw-watcher-afk-test", testing=True) print(client.get_buckets()) bucket_id = sys.argv[1] events = client.get_events(bucket_id) # For example "aw-watcher-afk-testing_erb-laptop-ubuntu" print("\n\n") last_event = None wrong_events = 0 for event in sorted(events, key=lambda e: e.timestamp): if last_event: # The diff is the gap between the two events, should be zero # In reality it is currently sometimes negative and almost always larger than 1s diff = (event.timestamp - last_event.timestamp) - last_event.duration print("{} at {}".format(event.label, event.timestamp)) print("Duration: {}".format(event.duration)) if not timedelta(seconds=1) > abs(diff):
#!/usr/bin/env python3 from time import sleep from datetime import datetime, timedelta, timezone from aw_core.models import Event from aw_client import ActivityWatchClient # We'll run with testing=True so we don't mess up any production instance. # Make sure you've started aw-server with the `--testing` flag as well. client = ActivityWatchClient("test-client", testing=True) # Make the bucket_id unique for both the client and host # The convention is to use client-name_hostname as bucket name, # but if you have multiple buckets in one client you can add a # suffix such as client-name-event-type or similar bucket_id = "{}_{}".format("test-client-bucket", client.hostname) # A short and descriptive event type name # Will be used by visualizers (such as aw-webui) to detect what type and format the events are in # Can for example be "currentwindow", "afkstatus", "ping" or "currentsong" event_type = "dummydata" # First we need a bucket to send events/heartbeats to. # If the bucket already exists aw-server will simply return 304 NOT MODIFIED, # so run this every time the clients starts up to verify that the bucket exists. # If the client was unable to connect to aw-server or something failed # during the creation of the bucket, an exception will be raised. client.create_bucket(bucket_id, event_type="test") # Asynchronous loop example with client:
#!/usr/bin/env python3 from datetime import datetime, timezone from aw_core.models import Event from aw_client import ActivityWatchClient # We'll run with testing=True so we don't mess up any production instance. # Make sure you've started aw-server with the `--testing` flag as well. client = ActivityWatchClient("test-client", testing=True) bucket_id = "{}_{}".format("test-client-bucket", client.hostname) client.create_bucket(bucket_id, event_type="dummydata") shutdown_data = {"label": "some interesting data"} now = datetime.now(timezone.utc) shutdown_event = Event(timestamp=now, data=shutdown_data) inserted_event = client.insert_event(bucket_id, shutdown_event) events = client.get_events(bucket_id=bucket_id, limit=1) print(events) # Should print a single event in a list client.delete_bucket(bucket_id)
class AFKWatcher: def __init__(self, testing=False): # Read settings from config configsection = "aw-watcher-afk" if not testing else "aw-watcher-afk-testing" self.settings = Settings(watcher_config[configsection]) self.client = ActivityWatchClient("aw-watcher-afk", testing=testing) self.bucketname = "{}_{}".format(self.client.client_name, self.client.client_hostname) def ping(self, afk: bool, timestamp: datetime, duration: float = 0): data = {"status": "afk" if afk else "not-afk"} e = Event(timestamp=timestamp, duration=duration, data=data) pulsetime = self.settings.timeout + self.settings.poll_time self.client.heartbeat(self.bucketname, e, pulsetime=pulsetime, queued=True) def run(self): logger.info("aw-watcher-afk started") # Initialization sleep(1) eventtype = "afkstatus" self.client.create_bucket(self.bucketname, eventtype, queued=True) # Start afk checking loop with self.client: self.heartbeat_loop() def heartbeat_loop(self): afk = False while True: try: if system in ["Darwin", "Linux"] and os.getppid() == 1: # TODO: This won't work with PyInstaller which starts a bootloader process which will become the parent. # There is a solution however. # See: https://github.com/ActivityWatch/aw-qt/issues/19#issuecomment-316741125 logger.info("afkwatcher stopped because parent process died") break now = datetime.now(timezone.utc) seconds_since_input = seconds_since_last_input() last_input = now - timedelta(seconds=seconds_since_input) logger.debug("Seconds since last input: {}".format(seconds_since_input)) # If no longer AFK if afk and seconds_since_input < self.settings.timeout: logger.info("No longer AFK") self.ping(afk, timestamp=last_input) afk = False self.ping(afk, timestamp=last_input) # If becomes AFK elif not afk and seconds_since_input >= self.settings.timeout: logger.info("Became AFK") self.ping(afk, timestamp=last_input) afk = True self.ping(afk, timestamp=last_input, duration=seconds_since_input) # Send a heartbeat if no state change was made else: if afk: self.ping(afk, timestamp=last_input, duration=seconds_since_input) else: self.ping(afk, timestamp=last_input) sleep(self.settings.poll_time) except KeyboardInterrupt: logger.info("aw-watcher-afk stopped by keyboard interrupt") break