1+ import logging
2+ import sys
3+ from datetime import datetime
4+ import pandas as pandas
5+ import pytz
6+
7+ # configure logging
8+ logger = logging .getLogger (__name__ )
9+ logger .setLevel (logging .DEBUG )
10+ handler = logging .StreamHandler (sys .stdout )
11+ handler .setLevel (logging .DEBUG )
12+ formatter = logging .Formatter (
13+ '%(asctime)s - %(name)s - %(levelname)s - %(message)s' )
14+ handler .setFormatter (formatter )
15+ logger .addHandler (handler )
16+
17+ # template definitions
18+ TEMPLATE_TIME_MS = '<TIME_MS>'
19+ TEMPLATE_TIME_STR = '<TIME_STR>'
20+
21+
22+ # MAIN MAPPER FUNCTION
23+
24+ def annotate_event (event , template_timestamps = False ):
25+ try :
26+ # check if event is valid
27+ valid = 'sourceId' in event and \
28+ 'metricId' in event and \
29+ 'value' in event and \
30+ ('timestamp' in event or template_timestamps )
31+
32+ # if the event is not valid, return None
33+ if not valid :
34+ return None
35+
36+ # otherwise, return mapped event
37+ rdf_event = map_observation_to_rdf (
38+ event , template_timestamps = template_timestamps )
39+ return remove_whitespace (rdf_event ) if rdf_event is not None else rdf_event
40+
41+ except Exception as e :
42+ print (e )
43+ return None
44+
45+
46+ def map_observation_to_rdf (event , template_timestamps = False ):
47+ result = map_observation (source_id = event ['sourceId' ],
48+ metric_id = event ['metricId' ],
49+ value = event ['value' ],
50+ timestamp = event ['timestamp' ] if not template_timestamps else None )
51+ return result
52+
53+
54+ # MAPPING FUNCTIONS OF HEADACHE PARTS
55+
56+ def map_observation (metric_id ,
57+ source_id ,
58+ value ,
59+ timestamp ):
60+ # extract patient ID from source ID
61+ split = source_id .split ("." , 1 )
62+ patient_id = split [0 ]
63+ source_id = split [1 ]
64+
65+ # replace spaces by underscores in metricId
66+ metric_id = metric_id .replace (' ' , '_' )
67+
68+ # add suffix to sensor (sourceId) based on metricId
69+ source_id = update_source_id (source_id , metric_id )
70+
71+ # generate timestamp
72+ uuid = generate_uuid (metric_id , patient_id )
73+ if timestamp :
74+ timestamp_utc = timestamp
75+ time_str = timestamp
76+ else :
77+ timestamp_utc = TEMPLATE_TIME_MS
78+ time_str = TEMPLATE_TIME_STR
79+
80+ # map value
81+ value_str , metric_group = map_value (value , metric_id )
82+
83+ # create RDF data
84+ if metric_group == "CONTEXT" :
85+ return OBSERVATION_TEMPLATE_CONTEXT_DAHCC % (uuid ,
86+ uuid , source_id ,
87+ uuid , uuid , metric_id ,
88+ uuid , str (timestamp_utc ),
89+ uuid , value
90+ )
91+ # return OBSERVATION_TEMPLATE_CONTEXT % (source_id,
92+ # patient_id, metric_id, uuid,
93+ # patient_id, metric_id, uuid,
94+ # metric_id, time_str, str(timestamp_utc), value_str)
95+ elif metric_group == "WEARABLE" :
96+ return OBSERVATION_TEMPLATE_CONTEXT_DAHCC % (uuid ,
97+ uuid , source_id ,
98+ uuid , uuid , metric_id ,
99+ uuid , str (timestamp_utc ),
100+ uuid , value
101+ )
102+
103+
104+ # return OBSERVATION_TEMPLATE_WEARABLE % (source_id,
105+ # patient_id, metric_id, uuid,
106+ # patient_id, metric_id, uuid,
107+ # metric_id, time_str, str(timestamp_utc), value_str)
108+
109+
110+ def map_value (value , metric_id ):
111+ value_type , metric_group = float , None
112+ if metric_id in METRIC_TYPES_CONTEXT :
113+ value_type , metric_group = METRIC_TYPES_CONTEXT [metric_id ], "CONTEXT"
114+ elif metric_id in METRIC_TYPES_WEARABLE :
115+ value_type , metric_group = METRIC_TYPES_WEARABLE [metric_id ], "WEARABLE"
116+
117+ if value_type == bool :
118+ processed_value = int (1 ) if value == 1 or value == '1' else int (0 )
119+ else :
120+ processed_value = value
121+
122+ return VALUE_TEMPLATE % (processed_value , TYPE_MAP [value_type ]), metric_group
123+
124+
125+ def update_source_id (source_id , metric_id ):
126+ if metric_id in SENSOR_SUFFIX_MAP :
127+ return '%s.%s' % (source_id , SENSOR_SUFFIX_MAP [metric_id ])
128+ else :
129+ return source_id
130+
131+
132+ # MAPPING TEMPLATES
133+
134+ EXAMPLE_OBSERVATION = """
135+ <https://dahcc.idlab.ugent.be/Protego/_participant1/obs0> <http://rdfs.org/ns/void#inDataset> <https://dahcc.idlab.ugent.be/Protego/_participant1> .
136+ <https://dahcc.idlab.ugent.be/Protego/_participant1/obs0> <https://saref.etsi.org/core/measurementMadeBy> <https://dahcc.idlab.ugent.be/Homelab/SensorsAndActuators/70:ee:50:67:30:b2> .
137+ <https://dahcc.idlab.ugent.be/Protego/_participant1/obs0> <https://saref.etsi.org/core/relatesToProperty> <https://dahcc.idlab.ugent.be/Homelab/SensorsAndActuators/org.dyamand.types.common.AtmosphericPressure> .
138+ <https://dahcc.idlab.ugent.be/Protego/_participant1/obs0> <https://saref.etsi.org/core/hasTimestamp> "2022-01-03T09:04:55.000000"^^<http://www.w3.org/2001/XMLSchema#dateTime> .
139+ <https://dahcc.idlab.ugent.be/Protego/_participant1/obs0> <https://saref.etsi.org/core/hasValue> "1013.1"^^<http://www.w3.org/2001/XMLSchema#float>
140+
141+ """
142+
143+ OBSERVATION_TEMPLATE_CONTEXT_DAHCC = """
144+ <https://dahcc.idlab.ugent.be/Protego/_participant1/obs%s> <http://rdfs.org/ns/void#inDataset> <https://dahcc.idlab.ugent.be/Protego/_participant1> .
145+ <https://dahcc.idlab.ugent.be/Protego/_participant1/obs%s> <https://saref.etsi.org/core/measurementMadeBy> <https://dahcc.idlab.ugent.be/Homelab/SensorsAndActuators/%s> .
146+ <https://dahcc.idlab.ugent.be/Protego/_participant1/obs%s> <http://purl.org/dc/terms/isVersionOf> <https://saref.etsi.org/core/Measurement> .
147+ <https://dahcc.idlab.ugent.be/Protego/_participant1/obs%s> <https://saref.etsi.org/core/relatesToProperty> <https://dahcc.idlab.ugent.be/Homelab/SensorsAndActuators/%s> .
148+ <https://dahcc.idlab.ugent.be/Protego/_participant1/obs%s> <https://saref.etsi.org/core/hasTimestamp> "%s"^^<http://www.w3.org/2001/XMLSchema#dateTime> .
149+ <https://dahcc.idlab.ugent.be/Protego/_participant1/obs%s> <https://saref.etsi.org/core/hasValue> "%s"^^<http://www.w3.org/2001/XMLSchema#float> .
150+
151+ """
152+
153+ OBSERVATION_TEMPLATE_CONTEXT = """
154+ <https://dahcc.idlab.ugent.be/Homelab/SensorsAndActuators/%s>
155+ <https://saref.etsi.org/core/makesMeasurement> <http://protego.idlab.ugent.be/%s/%s/obs%s> .
156+ <http://protego.idlab.ugent.be/%s/%s/obs%s>
157+ <https://saref.etsi.org/core/relatesToProperty> <https://dahcc.idlab.ugent.be/Homelab/SensorsAndActuators/%s> ;
158+ <https://saref.etsi.org/core/hasTimestamp> "%s"^^<http://www.w3.org/2001/XMLSchema#dateTime> ;
159+ <https://dahcc.idlab.ugent.be/Ontology/Sensors/hasTimestampUTC> "%s"^^<http://www.w3.org/2001/XMLSchema#integer> ;
160+ <https://saref.etsi.org/core/hasValue> %s .
161+ """
162+
163+ OBSERVATION_TEMPLATE_WEARABLE = """
164+ <https://dahcc.idlab.ugent.be/Homelab/SensorsAndWearables/%s>
165+ <https://saref.etsi.org/core/makesMeasurement> <http://protego.idlab.ugent.be/%s/%s/obs%s> .
166+ <http://protego.idlab.ugent.be/%s/%s/obs%s>
167+ <https://saref.etsi.org/core/relatesToProperty> <https://dahcc.idlab.ugent.be/Homelab/SensorsAndWearables/%s> ;
168+ <https://saref.etsi.org/core/hasTimestamp> "%s"^^<http://www.w3.org/2001/XMLSchema#dateTime> ;
169+ <https://dahcc.idlab.ugent.be/Ontology/Sensors/hasTimestampUTC> "%s"^^<http://www.w3.org/2001/XMLSchema#integer> ;
170+ <https://saref.etsi.org/core/hasValue> %s .
171+ """
172+
173+ VALUE_TEMPLATE = "\" %s\" ^^%s"
174+
175+ # CONVERTERS OF JSON VALUES TO ONTOLOGY CONCEPTS
176+
177+ METRIC_TYPES_CONTEXT = {
178+ "airquality.co2" : float ,
179+ "airquality.voc_total" : bool ,
180+ "energy.consumption" : float ,
181+ "energy.power" : float ,
182+ "environment.blind" : float ,
183+ "environment.button" : bool ,
184+ "environment.dimmer" : float ,
185+ "environment.light" : float ,
186+ "environment.lightswitch" : bool ,
187+ "environment.motion" : bool ,
188+ "environment.open" : bool ,
189+ "environment.relativehumidity" : float ,
190+ "environment.relay" : bool ,
191+ "environment.temperature" : float ,
192+ "environment.voltage" : float ,
193+ "environment.waterRunning::bool" : bool ,
194+ "mqtt.lastMessage" : str ,
195+ "org.dyamand.aqura.AquraLocationState_Protego User" : str ,
196+ "org.dyamand.aqura.AquraLocationState_Protego_User" : str ,
197+ "org.dyamand.types.airquality.CO2" : float ,
198+ "org.dyamand.types.common.AtmosphericPressure" : float ,
199+ "org.dyamand.types.common.Loudness" : float ,
200+ "org.dyamand.types.common.RelativeHumidity" : float ,
201+ "org.dyamand.types.common.Temperature" : float ,
202+ "people.presence.detected" : bool ,
203+ "people.presence.numberDetected" : float ,
204+ "weather.pressure" : float ,
205+ "weather.rainrate" : float ,
206+ "weather.windspeed" : float
207+ }
208+
209+ METRIC_TYPES_WEARABLE = {
210+ "org.dyamand.types.health.BodyTemperature" : float ,
211+ "org.dyamand.types.common.Load" : float ,
212+ "org.dyamand.types.health.DiastolicBloodPressure" : float ,
213+ "org.dyamand.types.health.HeartRate" : float ,
214+ "org.dyamand.types.health.SystolicBloodPressure" : float ,
215+ "org.dyamand.types.health.GlucoseLevel" : float ,
216+ "smartphone.acceleration.x" : float ,
217+ "smartphone.acceleration.y" : float ,
218+ "smartphone.acceleration.z" : float ,
219+ "smartphone.ambient_light" : float ,
220+ "smartphone.ambient_noise.amplitude" : float ,
221+ "smartphone.ambient_noise.frequency" : float ,
222+ "smartphone.application" : str ,
223+ "smartphone.gravity.x" : float ,
224+ "smartphone.gravity.y" : float ,
225+ "smartphone.gravity.z" : float ,
226+ "smartphone.gyroscope.x" : float ,
227+ "smartphone.gyroscope.y" : float ,
228+ "smartphone.gyroscope.z" : float ,
229+ "smartphone.keyboard" : str ,
230+ "smartphone.linear_acceleration.x" : float ,
231+ "smartphone.linear_acceleration.y" : float ,
232+ "smartphone.linear_acceleration.z" : float ,
233+ "smartphone.location.accuracy" : float ,
234+ "smartphone.location.altitude" : float ,
235+ "smartphone.location.bearing" : float ,
236+ "smartphone.location.latitude" : float ,
237+ "smartphone.location.longitude" : float ,
238+ "smartphone.magnetometer.x" : float ,
239+ "smartphone.magnetometer.y" : float ,
240+ "smartphone.magnetometer.z" : float ,
241+ "smartphone.proximity" : float ,
242+ "smartphone.rotation.x" : float ,
243+ "smartphone.rotation.y" : float ,
244+ "smartphone.rotation.z" : float ,
245+ "smartphone.screen" : str ,
246+ "smartphone.sleepAPI.API_confidence" : float ,
247+ "smartphone.sleepAPI.model_type" : str ,
248+ "smartphone.sleepAPI.t_start" : float ,
249+ "smartphone.sleepAPI.t_stop" : float ,
250+ "smartphone.step" : float ,
251+ "wearable.acceleration.x" : float ,
252+ "wearable.acceleration.y" : float ,
253+ "wearable.acceleration.z" : float ,
254+ "wearable.battery_level" : float ,
255+ "wearable.bvp" : float ,
256+ "wearable.gsr" : float ,
257+ "wearable.ibi" : float ,
258+ "wearable.on_wrist" : bool ,
259+ "wearable.skt" : float
260+ }
261+
262+ SENSOR_SUFFIX_MAP = {
263+ "org.dyamand.aqura.AquraLocationState_Protego_User" : "Tag" ,
264+ "wearable.acceleration.x" : 'Accelerometer' ,
265+ "wearable.acceleration.y" : 'Accelerometer' ,
266+ "wearable.acceleration.z" : 'Accelerometer' ,
267+ "wearable.battery_level" : 'BatteryLevelMeter' ,
268+ "wearable.bvp" : 'PPGSensor' ,
269+ "wearable.gsr" : 'GSRSensor' ,
270+ "wearable.ibi" : 'PPGSensor' ,
271+ "wearable.on_wrist" : 'OnWristDetector' ,
272+ "wearable.skt" : 'Thermopile'
273+ }
274+
275+ TYPE_MAP = {
276+ float : "<http://www.w3.org/2001/XMLSchema#float>" ,
277+ int : "<http://www.w3.org/2001/XMLSchema#integer>" ,
278+ bool : "<http://www.w3.org/2001/XMLSchema#integer>" ,
279+ str : "<http://www.w3.org/2001/XMLSchema#string>"
280+ }
281+
282+
283+ # HELPER FUNCTIONS
284+
285+ def convert_timestamp_to_string (timestamp ):
286+ return datetime .fromtimestamp (float (timestamp ) / 1000.0 ,
287+ pytz .timezone ('Europe/Brussels' )). \
288+ strftime ('%Y-%m-%dT%H:%M:%S' )
289+
290+
291+ uuid_map = {}
292+
293+
294+ def generate_uuid (metric_id , patient_id ):
295+ key = '%s' % (patient_id )
296+ if key not in uuid_map :
297+ uuid_map [key ] = 0
298+ result = uuid_map [key ]
299+ uuid_map [key ] += 1
300+ return result
301+
302+
303+ def remove_whitespace (given_str ):
304+ return ' ' .join (given_str .split ())
305+
306+
307+ def map_feather (file , sensors_to_map ):
308+ file = pandas .read_feather (file )
309+ for sensor in sensors_to_map :
310+ df = file [file ['Metric' ] == sensor ]
311+ for index in df .index :
312+ source = (df ['Sensor' ][index ])
313+ metric = (df ['Metric' ][index ])
314+ value = (df ['Value' ][index ])
315+ timestamp = (df ['Timestamp' ][index ])
316+ timestamp_str = str (timestamp )
317+ hour = timestamp_str [0 :2 ]
318+ minute = timestamp_str [3 :5 ]
319+ second = timestamp_str [6 :8 ]
320+ millisecond = '000Z'
321+ if (timestamp_str [9 :12 ] == '' ):
322+ continue
323+ else :
324+ millisecond = timestamp_str [9 :13 ] + 'Z'
325+
326+
327+ current_time = datetime .now ()
328+ year = current_time .strftime ("%Y" )
329+ month = current_time .strftime ("%m" )
330+ day = current_time .strftime ("%d" )
331+
332+ try :
333+ time_value = str (year ) + '-' + str (month ) + '-' + str (day ) + 'T' + str (hour ) + ':' + str (minute ) + ':' + str (second ) + '.' + str (millisecond )
334+ except Exception as e :
335+ print ('Exception is' + e )
336+
337+ event = {
338+ 'sourceId' : source ,
339+ 'metricId' : metric ,
340+ 'value' : value ,
341+ 'timestamp' : time_value
342+ }
343+ result = annotate_event (event )
344+
345+ with open ('/home/kush/Code/feather-RDF-mapper/data/rdfData/participant6.nt' , 'a' ) as file :
346+ pass
347+ file .write ('\n ' )
348+ file .write (result )
349+ if __name__ == '__main__' :
350+ file = 'data/dataset_participant6.feather'
351+ sensors = ['wearable.bvp' ]
352+ map_feather (file = file , sensors_to_map = sensors )
0 commit comments