Skip to content
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

217 add functionality to send callback messages #225

Merged
merged 11 commits into from
Jan 10, 2024
47 changes: 47 additions & 0 deletions oda_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import requests
import ast
import json
import re

try:
# compatibility in some remaining environments
Expand Down Expand Up @@ -1339,3 +1340,49 @@
p.meta_data = p.meta

return d

class ProgressReporter(object):
"""
The class allows to report task progress to end user
"""
def __init__(self):
self._callback = None
callback_file = ".oda_api_callback" # perhaps it would be better to define this constant in a common lib
volodymyrss marked this conversation as resolved.
Show resolved Hide resolved
if not os.path.isfile(callback_file):
return
with open(callback_file, 'r') as file:
self._callback = file.read().strip()

@property
def enabled(self):
return self._callback is not None

def report_progress(self, stage: str=None, progress: int=50, substage: str=None, subprogress: int=None, message:str=None):
"""
Report progress via callback URL
:param stage: current stage description string
:param progress: current stage progress in %
:param substage: current substage description string
:param subprogress: current substage progress in %
:param message: message to pass
"""
callback_payload = dict(stage=stage, progress=progress, substage=substage, subprogress=subprogress, message=message)
callback_payload = {k: v for k, v in callback_payload.items() if v is not None}

okolo marked this conversation as resolved.
Show resolved Hide resolved
if not self.enabled:
logger.info('no callback registered, skipping')
return

logger.info('will perform callback: %s', self._callback)

if re.match('^file://', self._callback):
with open(self._callback.replace('file://', ''), "w") as f:
dsavchenko marked this conversation as resolved.
Show resolved Hide resolved
json.dump(callback_payload, f)
logger.info('stored callback in a file %s', self._callback)

elif re.match('^https?://', self._callback):
r = requests.get(self._callback, params=callback_payload)
volodymyrss marked this conversation as resolved.
Show resolved Hide resolved
logger.info('callback %s returns %s : %s', self._callback, r, r.text)

Check warning on line 1385 in oda_api/api.py

View check run for this annotation

Codecov / codecov/patch

oda_api/api.py#L1383-L1385

Added lines #L1383 - L1385 were not covered by tests

else:
raise NotImplementedError

Check warning on line 1388 in oda_api/api.py

View check run for this annotation

Codecov / codecov/patch

oda_api/api.py#L1388

Added line #L1388 was not covered by tests
34 changes: 34 additions & 0 deletions tests/test_progress_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from oda_api.api import ProgressReporter
import os
import json

callback_file = ".oda_api_callback"

def test_progress_reporter_disabled():
if os.path.isfile(callback_file):
os.remove(callback_file)

Check warning on line 9 in tests/test_progress_report.py

View check run for this annotation

Codecov / codecov/patch

tests/test_progress_report.py#L9

Added line #L9 was not covered by tests
# if callback is not available
pr = ProgressReporter()
assert not pr.enabled
pr.report_progress(stage='simulation', progress=50, substage='spectra', subprogress=30, message='some message')
burnout87 marked this conversation as resolved.
Show resolved Hide resolved

def test_progress_reporter_enabled():
# if callback is available
try:
dump_file = 'callback'
with open(callback_file, 'w') as file:
print(f'file://{os.getcwd()}/{dump_file}', file=file)

pr = ProgressReporter()
assert pr.enabled
params = dict(stage='simulation', progress=50, substage='spectra', subprogress=30, message='some message')
pr.report_progress(**params)
with open(dump_file) as json_file:
passed_params = json.load(json_file)
assert len([k for k in passed_params.keys() if k not in params]) == 0
finally:
if os.path.isfile(callback_file):
os.remove(callback_file)
if os.path.isfile(dump_file):
os.remove(dump_file)

Loading