Coverage for portality/lib/plausible.py: 77%

53 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-09-20 23:33 +0100

1# ~~ PlausibleAnalytics:ExternalService~~ 

2import json 

3import logging 

4import os 

5import requests 

6 

7from functools import wraps 

8from threading import Thread 

9 

10from portality.core import app 

11from flask import request 

12 

13logger = logging.getLogger(__name__) 

14 

15# Keep track of when this is misconfigured so we don't spam the logs with skip messages 

16_failstate = False 

17 

18 

19def create_logfile(log_dir=None): 

20 filepath = __name__ + '.log' 

21 if log_dir is not None: 

22 if not os.path.exists(log_dir): 

23 os.makedirs(log_dir) 

24 filepath = os.path.join(log_dir, filepath) 

25 fh = logging.FileHandler(filepath) 

26 fh.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) 

27 logger.addHandler(fh) 

28 

29 

30def send_event(goal: str, on_completed=None, **props_kwargs): 

31 """ Send event data to Plausible Analytics. (ref: https://plausible.io/docs/events-api ) 

32 """ 

33 

34 plausible_api_url = app.config.get('PLAUSIBLE_API_URL', '') 

35 if not app.config.get('PLAUSIBLE_URL', '') and not plausible_api_url: 

36 global _failstate 

37 if not _failstate: 

38 logger.warning('skip send_event, PLAUSIBLE_URL undefined') 

39 _failstate = True 

40 return 

41 

42 # prepare request payload 

43 payload = {'name': goal, 

44 'url': app.config.get('BASE_URL', 'http://localhost'), 

45 'domain': app.config.get('PLAUSIBLE_SITE_NAME', 'localhost'), } 

46 if props_kwargs: 

47 payload['props'] = json.dumps(props_kwargs) 

48 

49 headers = {} 

50 if request: 

51 headers = {"X-Forwarded-For": request.remote_addr} # this works because we have ProxyFix on the app 

52 

53 def _send(): 

54 resp = requests.post(plausible_api_url, json=payload, headers=headers) 

55 if on_completed: 

56 if resp.status_code >= 300: 

57 logger.warning(f'send plausible event api fail. [{resp.status_code}][{resp.text}]') 

58 

59 on_completed(resp) 

60 

61 Thread(target=_send).start() 

62 

63 

64def pa_event(goal, action, label='', 

65 record_value_of_which_arg='', **prop_kwargs): 

66 """ 

67 Decorator for Flask view functions, sending event data to Plausible 

68 Analytics. 

69 """ 

70 

71 def decorator(fn): 

72 @wraps(fn) 

73 def decorated_view(*args, **kwargs): 

74 # define event label 

75 el = label 

76 if record_value_of_which_arg in kwargs: 

77 el = kwargs[record_value_of_which_arg] 

78 

79 # prepare event props payload 

80 event_payload = { 

81 'action': action, 

82 'label': el, 

83 } 

84 if prop_kwargs: 

85 event_payload.update(prop_kwargs) 

86 

87 # send event 

88 send_event(goal, **event_payload) 

89 

90 return fn(*args, **kwargs) 

91 

92 return decorated_view 

93 

94 return decorator