= Simple Generic Location Tracking System = [[TOC]] S3 provides a simple generic location tracking system. This is implemented in {{{modules/eden/sit.py}}} and {{{modules/s3/s3track.py}}} and can be applied to virtually all entities. == Background == Location tracking means that an instance of an entity can have: - a base location - a current location X at time Y (a so-called "presence log" preserving all locations over time) The tracking system provides the data model and an API to handle both cases. == Base Location == The base location can be implemented for an entity by adding a field {{{location_id}}} to the table which references the {{{gis_location}}} table. As this is a pre-defined re-usable field, simply add it to your model: {{{ table = self.define_table(tablename, ... self.gis_location_id(), # <= add this to give the entity a base location ...) }}} == Presence Log == To capture the current location of an instance at several timepoints, you need to make your entity "trackable". This can be done by defining {{{sit_trackable}}} as super-entity: {{{ table = self.define_table(tablename, ... self.super_link("track_id", "sit_trackable"), # <= add the super-entity key ...) s3db.configure(table, super_entity="sit_trackable", # <= configure sit_trackable as super-entity ...) }}} There is no problem to have this together with other super-entities (the number of super-entities per table is not limited): {{{ table = self.define_table(tablename, ... super_link("site_id", "org_site"), super_link("track_id", "sit_trackable"), ...) s3db.configure(table, super_entity=("org_site", "sit_trackable"), # <= multiple allowed! ...) }}} == API == === S3Trackable === The API uses a wrapper class for database records - {{{S3Trackable}}}. You can create an S3Trackable by using the {{{s3tracker}}} function with the table and the record ID: {{{ trackable = s3tracker(db.my_table, 1) }}} Instead of the table, you can specify the tablename: {{{ trackable = s3tracker(tablename="my_table", record_id=1) }}} You can also specify a query: {{{ query = (db.my_table.uuid == "9207324b-5dc6-439b-9410-8920cbaf8a34") trackable = s3tracker(query=query) }}} or a record (must be a Row instance): {{{ record = db(db.my_table.uuid == "9207324b-5dc6-439b-9410-8920cbaf8a34").select(limitby=(0, 1)).first() trackable = s3tracker(record=record) }}} It is possible to specify multiple records for the same {{{S3Trackable}}}. If multiple records are specified or found, then they will be handled as '''one''' trackable object '''with multiple locations''', i.e. if you retrieve its location, you will get a list of locations back. If you update its location, all of its records will be updated the same way. It is possible to specify a super-entity for an {{{S3Trackable}}}. In this case, the tracker will automatically find the respective records in the instance table(s) and use them instead: {{{ trackable = s3tracker(db.pr_pentity, 1) # <= the super-entity is automatically replaced by the respective instance record (e.g. in pr_person) }}} === Base Location === To retrieve the base location of an object, use {{{get_base_location()}}}: {{{ base_location = s3tracker(db.pr_person, 1).get_base_location() }}} This will return the full gis_location record(s) for the respective base location(s). To set the base location of an object, use set_base_location(): {{{ s3tracker(db.pr_person, 1).set_base_location(25) # <= use the record ID of a gis_location }}} Other ways to specify the location: {{{ # Using a location record location = db(db.gis_location.id == 25).select(limitby=(0, 1)).first() s3tracker(db.pr_person, 1).set_base_location(location) # Using another object containing a location_id hospital = db(db.hms_hospital.id == 3).select(limitby=(0, 1)).first() s3tracker(db.pr_person, 1).set_base_location(hospital) # Using another trackable: trackable = s3tracker(db.hms_hospital, 3) s3tracker(db.pr_person, 1).set_base_location(trackable) }}} === Current location === When setting/retrieving the current location of an object, you can additionally specify a time point: {{{ from datetime import datetime, timedelta one_hour_ago = datetime.utcnow() - timedelta(hours=1) location = s3tracker(db.pr_person, 1).get_location(timestmp=one_hour_ago) }}} NOTE: all times in the tracking system are considered to be in UTC! To set a current location, you can use {{{set_location()}}}. {{{ s3tracker(db.pr_person, 1).set_location(25, timestmp=datetime.utcnow()) }}} If you omit the {{{timestmp}}} parameter, then the current server time is used. To specify the location, you have the same options in {{{set_location()}}} as in {{{set_base_location()}}}. === Check-in/out === It might be desirable to track one object together with another. For example, if a person is in a vehicle, and the vehicle's location changes, then the location of the person should change as well. This can be achieved by using the {{{check_in()}}} and {{{check_out()}}} methods. {{{ >>> s3tracker(db.vt_vehicle, 1).set_location(40) # <= set a current location for the vehicle >>> s3tracker(db.pr_person, 1).check_in(db.vt_vehicle, 1) # <= check in the person to the vehicle >>> location = s3tracker(db.pr_person, 1).get_location() # <= get the location of the person >>> location.id 40 >>> s3tracker(db.vt_vehicle, 1).set_location(73) # <= set a new location for the vehicle >>> location = s3tracker(db.pr_person, 1).get_location() # <= get the location of the person >>> location.id 73 >>> s3tracker(db.pr_person, 1).check_out(db.vt_vehicle, 1) # <= check out the person from the vehicle >>> s3tracker(db.vt_vehicle, 1).set_location(21) # <= set a new location for the vehicle >>> location = s3tracker(db.pr_person, 1).get_location() # <= get the location of the person >>> location.id 73 }}} When you set the location of a checked-in object explicitly using {{{set_location()}}}, then the object will automatically be checked-out from wherever it has been checked-in before. Both {{{check_in()}}} as well as {{{check_out()}}} accept the {{{timestmp}}} parameter to set the time of the check-in/out explicitly. If omitted, then the current server time is used. If you specify a super-entity instance to check-in to, then the respective instance record will automatically be used: {{{ s3tracker(db.pr_person, 1).check_in(db.org_site, 4) # <= checks-in the person to the respective org_site instance, i.e. hospital or office... }}} === Additional Options === When retrieving locations by {{{get_location()}}} or {{{get_base_location()}}}, you can specify additional parameters for the result: - '''_fields''' (list/tuple of {{{Field}}} instances) to select fields from {{{gis_location}}} ({{{None}}} for ALL) - '''_filter''' ({{{Query}}} instance) to filter the gis_location records - '''as_rows''' (boolean) return the result as {{{Rows}}} object (work like a DAL {{{select()}}}) == See Also == [wiki:S3 S3 Guide] BluePrint/Tracking DeveloperGuidelines