Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #73 from ActivityWatch/dev/github-ci
ci: switched to GitHub Actions for CI
  • Loading branch information
ErikBjare committed Dec 7, 2020
2 parents 0f77bf2 + 2f44d8d commit 3e81e29
Show file tree
Hide file tree
Showing 17 changed files with 367 additions and 160 deletions.
52 changes: 52 additions & 0 deletions .github/workflows/build.yml
@@ -0,0 +1,52 @@
name: Build

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:
name: Test on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
env:
RELEASE: false
SKIP_WEBUI: true
strategy:
fail-fast: false
matrix:
os: [ubuntu-18.04, windows-latest, macOS-latest]
python_version: [3.6]
steps:
- uses: actions/checkout@v2
with:
submodules: 'recursive'
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python_version }}
- name: Create virtualenv
shell: bash
run: |
pip install virtualenv
python -m virtualenv venv
- name: Install dependencies
shell: bash
run: |
pip install poetry
source venv/bin/activate || source venv/Scripts/activate
make build
- name: Run tests
shell: bash
run: |
source venv/bin/activate || source venv/Scripts/activate
make test
make typecheck
bash <(curl -s https://codecov.io/bash)
- name: Package
shell: bash
run: |
source venv/bin/activate || source venv/Scripts/activate
pip install pyinstaller
make package
15 changes: 15 additions & 0 deletions .github/workflows/lint.yml
@@ -0,0 +1,15 @@
name: Lint

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: psf/black@stable
23 changes: 0 additions & 23 deletions .travis.yml

This file was deleted.

13 changes: 11 additions & 2 deletions aw_server/__about__.py
Expand Up @@ -21,7 +21,16 @@ def detect_version_ci() -> Optional[str]:

def detect_version_git() -> Optional[str]:
try:
return basever + ".dev+" + str(subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).strip(), "utf8")
return (
basever
+ ".dev+"
+ str(
subprocess.check_output(
["git", "rev-parse", "--short", "HEAD"]
).strip(),
"utf8",
)
)
except Exception as e:
# Unable to get current commit with git
return None
Expand All @@ -43,7 +52,7 @@ def detect_version():
return basever + ".dev+unknown"


__version__ = 'v0.8.dev+c6433ea'
__version__ = "v0.8.dev+c6433ea"


def assign_static_version():
Expand Down
106 changes: 77 additions & 29 deletions aw_server/api.py
Expand Up @@ -26,11 +26,11 @@
def get_device_id() -> str:
path = Path(get_data_dir("aw-server")) / "device_id"
if path.exists():
with open(path, 'r') as f:
with open(path, "r") as f:
return f.read()
else:
uuid = str(uuid4())
with open(path, 'w') as f:
with open(path, "w") as f:
f.write(uuid)
return uuid

Expand All @@ -39,8 +39,11 @@ def check_bucket_exists(f):
@functools.wraps(f)
def g(self, bucket_id, *args, **kwargs):
if bucket_id not in self.db.buckets():
raise NotFound("NoSuchBucket", "There's no bucket named {}".format(bucket_id))
raise NotFound(
"NoSuchBucket", "There's no bucket named {}".format(bucket_id)
)
return f(self, bucket_id, *args, **kwargs)

return g


Expand All @@ -53,10 +56,10 @@ def __init__(self, db, testing) -> None:
def get_info(self) -> Dict[str, Dict]:
"""Get server info"""
payload = {
'hostname': gethostname(),
'version': __version__,
'testing': self.testing,
'device_id': get_device_id(),
"hostname": gethostname(),
"version": __version__,
"testing": self.testing,
"device_id": get_device_id(),
}
return payload

Expand Down Expand Up @@ -106,18 +109,29 @@ def import_bucket(self, bucket_data: Any):
type=bucket_data["type"],
client=bucket_data["client"],
hostname=bucket_data["hostname"],
created=(bucket_data["created"]
if isinstance(bucket_data["created"], datetime)
else iso8601.parse_date(bucket_data["created"])),
created=(
bucket_data["created"]
if isinstance(bucket_data["created"], datetime)
else iso8601.parse_date(bucket_data["created"])
),
)
self.create_events(
bucket_id,
[Event(**e) if isinstance(e, dict) else e for e in bucket_data["events"]],
)
self.create_events(bucket_id, [Event(**e) if isinstance(e, dict) else e for e in bucket_data["events"]])

def import_all(self, buckets: Dict[str, Any]):
for bid, bucket in buckets.items():
self.import_bucket(bucket)

def create_bucket(self, bucket_id: str, event_type: str, client: str,
hostname: str, created: Optional[datetime] = None)-> bool:
def create_bucket(
self,
bucket_id: str,
event_type: str,
client: str,
hostname: str,
created: Optional[datetime] = None,
) -> bool:
"""
Create bucket.
Returns True if successful, otherwise false if a bucket with the given ID already existed.
Expand All @@ -131,7 +145,7 @@ def create_bucket(self, bucket_id: str, event_type: str, client: str,
type=event_type,
client=client,
hostname=hostname,
created=created
created=created,
)
return True

Expand All @@ -143,14 +157,20 @@ def delete_bucket(self, bucket_id: str) -> None:
return None

@check_bucket_exists
def get_events(self, bucket_id: str, limit: int = -1,
start: datetime = None, end: datetime = None) -> List[Event]:
def get_events(
self,
bucket_id: str,
limit: int = -1,
start: datetime = None,
end: datetime = None,
) -> List[Event]:
"""Get events from a bucket"""
logger.debug("Received get request for events in bucket '{}'".format(bucket_id))
if limit is None: # Let limit = None also mean "no limit"
limit = -1
events = [event.to_json_dict() for event in
self.db[bucket_id].get(limit, start, end)]
events = [
event.to_json_dict() for event in self.db[bucket_id].get(limit, start, end)
]
return events

@check_bucket_exists
Expand All @@ -161,10 +181,13 @@ def create_events(self, bucket_id: str, events: List[Event]) -> Optional[Event]:
return self.db[bucket_id].insert(events[0] if len(events) == 1 else events)

@check_bucket_exists
def get_eventcount(self, bucket_id: str,
start: datetime = None, end: datetime = None) -> int:
def get_eventcount(
self, bucket_id: str, start: datetime = None, end: datetime = None
) -> int:
"""Get eventcount from a bucket"""
logger.debug("Received get request for eventcount in bucket '{}'".format(bucket_id))
logger.debug(
"Received get request for eventcount in bucket '{}'".format(bucket_id)
)
return self.db[bucket_id].get_eventcount(start, end)

@check_bucket_exists
Expand Down Expand Up @@ -195,8 +218,15 @@ def heartbeat(self, bucket_id: str, heartbeat: Event, pulsetime: float) -> Event
Inspired by: https://wakatime.com/developers#heartbeats
"""
logger.debug("Received heartbeat in bucket '{}'\n\ttimestamp: {}, duration: {}, pulsetime: {}\n\tdata: {}".format(
bucket_id, heartbeat.timestamp, heartbeat.duration, pulsetime, heartbeat.data))
logger.debug(
"Received heartbeat in bucket '{}'\n\ttimestamp: {}, duration: {}, pulsetime: {}\n\tdata: {}".format(
bucket_id,
heartbeat.timestamp,
heartbeat.duration,
pulsetime,
heartbeat.data,
)
)

# The endtime here is set such that in the event that the heartbeat is older than an
# existing event we should try to merge it with the last event before the heartbeat instead.
Expand All @@ -220,16 +250,32 @@ def heartbeat(self, bucket_id: str, heartbeat: Event, pulsetime: float) -> Event
merged = heartbeat_merge(last_event, heartbeat, pulsetime)
if merged is not None:
# Heartbeat was merged into last_event
logger.debug("Received valid heartbeat, merging. (bucket: {})".format(bucket_id))
logger.debug(
"Received valid heartbeat, merging. (bucket: {})".format(
bucket_id
)
)
self.last_event[bucket_id] = merged
self.db[bucket_id].replace_last(merged)
return merged
else:
logger.info("Received heartbeat after pulse window, inserting as new event. (bucket: {})".format(bucket_id))
logger.info(
"Received heartbeat after pulse window, inserting as new event. (bucket: {})".format(
bucket_id
)
)
else:
logger.debug("Received heartbeat with differing data, inserting as new event. (bucket: {})".format(bucket_id))
logger.debug(
"Received heartbeat with differing data, inserting as new event. (bucket: {})".format(
bucket_id
)
)
else:
logger.info("Received heartbeat, but bucket was previously empty, inserting as new event. (bucket: {})".format(bucket_id))
logger.info(
"Received heartbeat, but bucket was previously empty, inserting as new event. (bucket: {})".format(
bucket_id
)
)

self.db[bucket_id].insert(heartbeat)
self.last_event[bucket_id] = heartbeat
Expand All @@ -238,7 +284,9 @@ def heartbeat(self, bucket_id: str, heartbeat: Event, pulsetime: float) -> Event
def query2(self, name, query, timeperiods, cache):
result = []
for timeperiod in timeperiods:
period = timeperiod.split("/")[:2] # iso8601 timeperiods are separated by a slash
period = timeperiod.split("/")[
:2
] # iso8601 timeperiods are separated by a slash
starttime = iso8601.parse_date(period[0])
endtime = iso8601.parse_date(period[1])
query = str().join(query)
Expand All @@ -249,7 +297,7 @@ def query2(self, name, query, timeperiods, cache):
def get_log(self):
"""Get the server log in json format"""
payload = []
with open(get_log_file_path(), 'r') as log_file:
with open(get_log_file_path(), "r") as log_file:
for line in log_file.readlines()[::-1]:
payload.append(json.loads(line))
return payload, 200
4 changes: 2 additions & 2 deletions aw_server/config.py
Expand Up @@ -7,13 +7,13 @@
"host": "localhost",
"port": "5600",
"storage": "peewee",
"cors_origins": ""
"cors_origins": "",
}
default_config["server-testing"] = {
"host": "localhost",
"port": "5666",
"storage": "peewee",
"cors_origins": ""
"cors_origins": "",
}

config = load_config("aw-server", default_config)
2 changes: 2 additions & 0 deletions aw_server/exceptions.py
Expand Up @@ -6,11 +6,13 @@ def __init__(self, type: str, message: str) -> None:
super().__init__(message)
self.type = type


class NotFound(werkzeug.exceptions.NotFound):
def __init__(self, type: str, message: str) -> None:
super().__init__(message)
self.type = type


class Unauthorized(werkzeug.exceptions.Unauthorized):
def __init__(self, type: str, message: str) -> None:
super().__init__(message)
Expand Down
2 changes: 1 addition & 1 deletion aw_server/log.py
Expand Up @@ -22,4 +22,4 @@ def log(self, levelname, message, *args):
levelno = logging.DEBUG
else:
raise Exception("Unknown level " + type)
self.logger.log(levelno, '{} ({}): {}'.format(code, self.address_string(), msg))
self.logger.log(levelno, "{} ({}): {}".format(code, self.address_string(), msg))

0 comments on commit 3e81e29

Please sign in to comment.