Coverage for src\tempstick_py\tempstick.py: 82%

88 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-11 22:38 -0700

1from datetime import datetime 

2import json 

3from urllib.error import HTTPError 

4import requests 

5 

6from benedict import benedict 

7 

8from furl import furl 

9 

10from .exceptions import FilterRemovesRange, InvalidApiKeyError 

11 

12from ._helpers import format_mac, format_datetime, set_attr_from_dict 

13 

14GET_SENSORS = "/api/v1/sensors/all" 

15GET_SENSOR = "/api/v1/sensors/{}" 

16GET_READINGS = "/api/v1/sensors/{}/readings" 

17REQUEST_TYPES = [ 

18 GET_SENSORS, 

19 GET_SENSOR, 

20 GET_READINGS, 

21] 

22 

23import logging 

24 

25logger = logging.getLogger(__name__) 

26 

27 

28class TempStickSensor: 

29 # __slots__ = ("id" "sensor_id", "sensor_name", "sensor_mac_address") 

30 

31 def __init__( 

32 self, 

33 id, 

34 sensor_id: str, 

35 sensor_name: str = None, 

36 sensor_mac_addr: str = None, 

37 ) -> None: 

38 self.id = id 

39 self.sensor_id = sensor_id 

40 self.sensor_name = sensor_name 

41 try: 

42 self.sensor_mac_address = format(sensor_mac_addr) 

43 except AttributeError: 

44 self.sensor_mac_address = None 

45 

46 @classmethod 

47 def from_get_sensor(cls, sensor: dict): 

48 set_attr_from_dict(cls, sensor) 

49 

50 return cls 

51 

52 @classmethod 

53 def get_sensors(cls, api_key: str) -> list: 

54 """Return list of 'TempStickSensor's given API key.""" 

55 data = make_request(GET_SENSORS, api_key) 

56 

57 data_b = benedict(data) 

58 

59 sensors_list = [cls.from_get_sensor(x) for x in data_b.get_list("data.items")] 

60 

61 return sensors_list 

62 

63 def get_readings(self, api_key: str, filter_cutoff: datetime = None) -> list: 

64 """Return list of readings, optionally filtered cutoff date 

65 

66 :param filter_cutoff: Filter all readings older than this datetime 

67 :type filter_cutoff: datetime, optional 

68 """ 

69 response = make_request(GET_READINGS, api_key, self.sensor_id) 

70 

71 print("response: ".format(response)) 

72 

73 data = response.get("data") 

74 readings = data.get("readings") 

75 

76 if filter_cutoff: 

77 try: 

78 filter_cutoff = format_datetime(filter_cutoff) 

79 except TypeError: 

80 logger.debug( 

81 "Filter cutoff already datetime. [{}]".format(filter_cutoff) 

82 ) 

83 

84 start = format_datetime(data.get("start")) 

85 

86 length_before = len(readings) 

87 if start < filter_cutoff: 87 ↛ 94line 87 didn't jump to line 94, because the condition on line 87 was never false

88 readings = [ 

89 x 

90 for x in readings 

91 if format_datetime(x.get("sensor_time")) > filter_cutoff 

92 ] 

93 

94 length_after = len(readings) 

95 

96 if length_after == 0: 

97 raise FilterRemovesRange( 

98 filter_cutoff, 

99 range_lower=start, 

100 range_upper=format_datetime(data.get("end")), 

101 ) 

102 elif (diff := length_before - length_after) > 0: 102 ↛ 105line 102 didn't jump to line 105, because the condition on line 102 was never false

103 print("{} readings removed.".format(diff)) 

104 

105 return readings 

106 

107 def get_sensor(self, api_key: str): 

108 """Return sensor values.""" 

109 sensor = make_request(GET_SENSOR, api_key, self.sensor_id) 

110 

111 sensor_obj = self.from_get_sensor(sensor) 

112 

113 set_attr_from_dict(self, sensor) 

114 

115 return self 

116 

117 

118def make_request(request_type: REQUEST_TYPES, api_key: str, sensor_id: str = None): 

119 url = furl("https://tempstickapi.com") 

120 url_adder = furl(request_type.format(sensor_id)) 

121 

122 print("Adder: {}".format(url_adder)) 

123 logger.info("Adder: {}".format(url_adder)) 

124 

125 url.join(url, url_adder) 

126 

127 payload = {} 

128 headers = {"X-API-KEY": api_key} 

129 

130 url_str = url.tostr() 

131 

132 print("URL String: {}".format(url_str)) 

133 

134 response = requests.get(url_str, headers=headers, data=payload) 

135 

136 try: 

137 response.raise_for_status() 

138 response_json = response.json() 

139 

140 print("Response Status: {}".format(response.status_code)) 

141 except HTTPError as http_err: 

142 print("HTTP error occured: {}".format(http_err)) 

143 raise 

144 except Exception as err: 

145 print("Other error occured: {}".format(err)) 

146 raise 

147 

148 if not isinstance(response_json, dict): 148 ↛ 154line 148 didn't jump to line 154, because the condition on line 148 was never false

149 try: 

150 response_json = json.loads(response_json) 

151 except ValueError as val_err: 

152 print("Could not parse as JSON: {}".format(response_json)) 

153 

154 response_type = response_json.get("type") 

155 if response_type == "error": 

156 raise InvalidApiKeyError(response_type, response_json.get("message")) 

157 

158 return response_json