Package s3 :: Module s3track
[frames] | no frames]

Source Code for Module s3.s3track

  1  # -*- coding: utf-8 -*- 
  2   
  3  """ Simple Generic Location Tracking System 
  4   
  5      @copyright: 2011-2019 (c) Sahana Software Foundation 
  6      @license: MIT 
  7   
  8      Permission is hereby granted, free of charge, to any person 
  9      obtaining a copy of this software and associated documentation 
 10      files (the "Software"), to deal in the Software without 
 11      restriction, including without limitation the rights to use, 
 12      copy, modify, merge, publish, distribute, sublicense, and/or sell 
 13      copies of the Software, and to permit persons to whom the 
 14      Software is furnished to do so, subject to the following 
 15      conditions: 
 16   
 17      The above copyright notice and this permission notice shall be 
 18      included in all copies or substantial portions of the Software. 
 19   
 20      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 21      EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
 22      OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 23      NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
 24      HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 25      WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 26      FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
 27      OTHER DEALINGS IN THE SOFTWARE. 
 28   
 29  """ 
 30   
 31  from datetime import datetime, timedelta 
 32   
 33  from gluon import current, HTTP, FORM, INPUT, LABEL, TABLE 
 34  from gluon.storage import Storage 
 35   
 36  from s3dal import Table, Rows, Row 
 37  from s3rest import S3Method 
 38   
 39  __all__ = ("S3Trackable", 
 40             "S3Tracker", 
 41             "S3CheckInMethod", 
 42             "S3CheckOutMethod", 
 43             ) 
 44   
 45  UID = "uuid"                # field name for UIDs 
 46   
 47  TRACK_ID = "track_id"       # field name for track ID 
 48  LOCATION_ID = "location_id" # field name for base location 
 49   
 50  LOCATION = "gis_location"   # location tablename 
 51  PRESENCE = "sit_presence"   # presence tablename 
52 53 # ============================================================================= 54 -class S3Trackable(object):
55 """ 56 Trackable types instance(s) 57 """ 58
59 - def __init__(self, table=None, tablename=None, record=None, query=None, 60 record_id=None, record_ids=None, rtable=None):
61 """ 62 Constructor: 63 64 @param table: a Table object 65 @param tablename: a Str tablename 66 @param record: a Row object 67 @param query: a Query object 68 @param record_id: a record ID (if object is a Table) 69 @param record_ids: a list of record IDs (if object is a Table) 70 - these should be in ascending order 71 @param rtable: the resource table (for the recursive calls) 72 """ 73 74 db = current.db 75 s3db = current.s3db 76 77 self.records = [] 78 79 self.table = s3db.sit_trackable 80 self.rtable = rtable 81 82 # if isinstance(trackable, (Table, str)): 83 # if hasattr(trackable, "_tablename"): 84 # table = trackable 85 # tablename = table._tablename 86 # else: 87 # table = s3db[trackable] 88 # tablename = trackable 89 # fields = self.__get_fields(table) 90 # if not fields: 91 # raise SyntaxError("Table %s is not a trackable type" % table._tablename) 92 # query = (table._id > 0) 93 # if uid is None: 94 # if record_id is not None: 95 # if isinstance(record_id, (list, tuple)): 96 # query = (table._id.belongs(record_id)) 97 # else: 98 # query = (table._id == record_id) 99 # elif UID in table.fields: 100 # if not isinstance(uid, (list, tuple)): 101 # query = (table[UID].belongs(uid)) 102 # else: 103 # query = (table[UID] == uid) 104 # fields = [table[f] for f in fields] 105 # rows = db(query).select(*fields) 106 if table or tablename: 107 if table: 108 tablename = table._tablename 109 else: 110 table = s3db[tablename] 111 fields = self.__get_fields(table) 112 if not fields: 113 raise SyntaxError("Not a trackable type: %s" % tablename) 114 if record_ids: 115 query = (table._id.belongs(record_ids)) 116 limitby = (0, len(record_ids)) 117 orderby = table._id 118 elif record_id: 119 query = (table._id == record_id) 120 limitby = (0, 1) 121 orderby = None 122 else: 123 query = (table._id > 0) 124 limitby = None 125 orderby = table._id 126 fields = [table[f] for f in fields] 127 rows = db(query).select(limitby=limitby, orderby=orderby, *fields) 128 129 # elif isinstance(trackable, Row): 130 # fields = self.__get_fields(trackable) 131 # if not fields: 132 # raise SyntaxError("Required fields not present in the row") 133 # rows = Rows(records=[trackable], compact=False) 134 elif record: 135 fields = self.__get_fields(record) 136 if not fields: 137 raise SyntaxError("Required fields not present in the row") 138 rows = Rows(records=[record], compact=False) 139 140 # elif isinstance(trackable, Rows): 141 # rows = [r for r in trackable if self.__get_fields(r)] 142 # fail = len(trackable) - len(rows) 143 # if fail: 144 # raise SyntaxError("Required fields not present in %d of the rows" % fail) 145 # rows = Rows(records=rows, compact=False) 146 147 # elif isinstance(trackable, (Query, Expression)): 148 # tablename = db._adapter.get_table(trackable) 149 # self.rtable = s3db[tablename] 150 # fields = self.__get_fields(self.rtable) 151 # if not fields: 152 # raise SyntaxError("Table %s is not a trackable type" % table._tablename) 153 # query = trackable 154 # fields = [self.rtable[f] for f in fields] 155 # rows = db(query).select(*fields) 156 elif query: 157 tablename = db._adapter.get_table(query) 158 self.rtable = s3db[tablename] 159 fields = self.__get_fields(self.rtable) 160 if not fields: 161 raise SyntaxError("Table %s is not a trackable type" % table._tablename) 162 fields = [self.rtable[f] for f in fields] 163 rows = db(query).select(*fields) 164 165 # elif isinstance(trackable, Set): 166 # query = trackable.query 167 # tablename = db._adapter.get_table(query) 168 # table = s3db[tablename] 169 # fields = self.__get_fields(table) 170 # if not fields: 171 # raise SyntaxError("Table %s is not a trackable type" % table._tablename) 172 # fields = [table[f] for f in fields] 173 # rows = trackable.select(*fields) 174 175 else: 176 raise SyntaxError("Invalid parameters") 177 178 records = [] 179 for r in rows: 180 if self.__super_entity(r): 181 table = s3db[r.instance_type] 182 fields = self.__get_fields(table, super_entity=False) 183 if not fields: 184 raise SyntaxError("Table %s is not a trackable type" % table._tablename) 185 fields = [table[f] for f in fields] 186 row = db(table[UID] == r[UID]).select(limitby=(0, 1), 187 *fields).first() 188 if row: 189 records.append(row) 190 else: 191 records.append(r) 192 193 self.records = Rows(records=records, compact=False)
194 195 # ------------------------------------------------------------------------- 196 @staticmethod
197 - def __super_entity(trackable):
198 """ 199 Check whether a trackable is a super-entity 200 201 @param trackable: the trackable object 202 """ 203 204 if hasattr(trackable, "fields"): 205 keys = trackable.fields 206 else: 207 keys = trackable 208 209 return "instance_type" in keys
210 211 # ------------------------------------------------------------------------- 212 @classmethod
213 - def __get_fields(cls, trackable, super_entity=True):
214 """ 215 Check a trackable for presence of required fields 216 217 @param: the trackable object 218 """ 219 220 fields = [] 221 222 if hasattr(trackable, "fields"): 223 keys = trackable.fields 224 else: 225 keys = trackable 226 227 if super_entity and \ 228 cls.__super_entity(trackable) and UID in keys: 229 return ("instance_type", UID) 230 if LOCATION_ID in keys: 231 fields.append(LOCATION_ID) 232 if TRACK_ID in keys: 233 fields.append(TRACK_ID) 234 return fields 235 elif hasattr(trackable, "update_record") or \ 236 isinstance(trackable, Table) or \ 237 isinstance(trackable, Row): 238 return fields 239 240 return None
241 242 # -------------------------------------------------------------------------
243 - def get_location(self, 244 timestmp=None, 245 _fields=None, 246 _filter=None, 247 as_rows=False, 248 exclude=None, 249 empty = True):
250 """ 251 Get the current location of the instance(s) (at the given time) 252 253 @param timestmp: last datetime for presence (defaults to current time) 254 @param _fields: fields to retrieve from the location records (None for ALL) 255 @param _filter: filter for the locations 256 @param as_rows: return the result as Rows object 257 @param exclude: interlocks to break at (avoids circular check-ins) 258 @param empty: return None if no locations (set to False by gis.get_location_data()) 259 260 @return: a location record, or a list of location records (if multiple) 261 262 @ToDo: Also show Timestamp of when seen there 263 """ 264 265 if exclude is None: 266 exclude = [] 267 268 db = current.db 269 s3db = current.s3db 270 271 ptable = s3db[PRESENCE] 272 ltable = s3db[LOCATION] 273 274 if timestmp is None: 275 timestmp = datetime.utcnow() 276 277 locations = [] 278 for r in self.records: 279 location = None 280 if TRACK_ID in r: 281 query = ((ptable.deleted == False) & \ 282 (ptable[TRACK_ID] == r[TRACK_ID]) & \ 283 (ptable.timestmp <= timestmp)) 284 presence = db(query).select(orderby=~ptable.timestmp, 285 limitby=(0, 1)).first() 286 if presence: 287 if presence.interlock: 288 exclude = [r[TRACK_ID]] + exclude 289 tablename, record_id = presence.interlock.split(",", 1) 290 trackable = S3Trackable(tablename=tablename, record_id=record_id) 291 record = trackable.records.first() 292 if TRACK_ID not in record or \ 293 record[TRACK_ID] not in exclude: 294 location = trackable.get_location(timestmp=timestmp, 295 exclude=exclude, 296 _fields=_fields, 297 as_rows=True).first() 298 elif presence.location_id: 299 query = (ltable.id == presence.location_id) 300 if _filter is not None: 301 query = query & _filter 302 if _fields is None: 303 location = db(query).select(ltable.ALL, 304 limitby=(0, 1)).first() 305 else: 306 location = db(query).select(limitby=(0, 1), 307 *_fields).first() 308 309 if not location: 310 if len(self.records) > 1: 311 trackable = S3Trackable(record=r, rtable=self.rtable) 312 else: 313 trackable = self 314 location = trackable.get_base_location(_fields=_fields) 315 316 if location: 317 locations.append(location) 318 elif not empty: 319 # Ensure we return an entry for gis.get_location_data() so that indexes match 320 locations.append(Row({"lat": None, "lon": None})) 321 322 if as_rows: 323 return Rows(records=locations, compact=False) 324 325 if not locations: 326 return None 327 else: 328 return locations
329 330 # -------------------------------------------------------------------------
331 - def set_location(self, location, timestmp=None):
332 """ 333 Set the current location of instance(s) (at the given time) 334 335 @param location: the location (as Row or record ID) 336 @param timestmp: the datetime of the presence (defaults to current time) 337 338 @return: location 339 """ 340 341 ptable = current.s3db[PRESENCE] 342 343 if timestmp is None: 344 timestmp = datetime.utcnow() 345 346 if isinstance(location, S3Trackable): 347 location = location.get_base_location() 348 if isinstance(location, Rows): 349 location = location.first() 350 if isinstance(location, Row): 351 if "location_id" in location: 352 location = location.location_id 353 else: 354 location = location.id 355 356 # Log even a set of no location 357 #if not location: 358 # return 359 #else: 360 data = dict(location_id=location, timestmp=timestmp) 361 362 for r in self.records: 363 if TRACK_ID not in r: 364 # No track ID => set base location 365 if len(self.records) > 1: 366 trackable = S3Trackable(record=r) 367 else: 368 trackable = self 369 trackable.set_base_location(location) 370 elif r[TRACK_ID]: 371 data.update({TRACK_ID:r[TRACK_ID]}) 372 ptable.insert(**data) 373 self.__update_timestamp(r[TRACK_ID], timestmp) 374 375 return location
376 377 # -------------------------------------------------------------------------
378 - def check_in(self, table, record, timestmp=None):
379 """ 380 Bind the presence of the instance(s) to another instance 381 382 @param table: table name of the other resource 383 @param record: record in the other resource (as Row or record ID) 384 @param timestmp: datetime of the check-in 385 386 @return: nothing 387 """ 388 389 db = current.db 390 s3db = current.s3db 391 392 ptable = s3db[PRESENCE] 393 394 if isinstance(table, str): 395 table = s3db[table] 396 397 fields = self.__get_fields(table) 398 if not fields: 399 raise SyntaxError("No location data in %s" % table._tablename) 400 401 interlock = None 402 403 if isinstance(record, Rows): 404 record = record.first() 405 406 if not isinstance(record, Row): 407 if not self.__super_entity(table): 408 fields = (table._id,) 409 record = db(table._id == record).select(limitby=(0, 1), *fields).first() 410 411 if self.__super_entity(record): 412 413 # Get the instance table 414 table = s3db[record.instance_type] 415 if not self.__get_fields(table, super_entity=False): 416 raise SyntaxError("No trackable type: %s" % table._tablename) 417 418 # Get the instance record 419 query = (table[UID] == record[UID]) 420 record = db(query).select(table._id, limitby=(0, 1), *fields).first() 421 422 try: 423 record_id = record[table._id] if record else None 424 except AttributeError: 425 record_id = None 426 if record_id: 427 interlock = "%s,%s" % (table, record_id) 428 else: 429 raise SyntaxError("No record specified for %s" % table._tablename) 430 431 if interlock: 432 433 if timestmp is None: 434 timestmp = datetime.utcnow() 435 436 data = {"location_id": None, 437 "timestmp": timestmp, 438 "interlock": interlock, 439 } 440 441 q = (ptable.timestmp <= timestmp) & \ 442 (ptable.deleted == False) 443 for r in self.records: 444 445 if TRACK_ID not in r: 446 # Cannot check-in a non-trackable 447 continue 448 track_id = r[TRACK_ID] 449 450 query = (ptable[TRACK_ID] == track_id) & q 451 presence = db(query).select(ptable.interlock, 452 orderby = ~ptable.timestmp, 453 limitby = (0, 1), 454 ).first() 455 if presence and presence.interlock == interlock: 456 # Already checked-in to the same instance 457 continue 458 data[TRACK_ID] = track_id 459 460 ptable.insert(**data) 461 self.__update_timestamp(track_id, timestmp)
462 463 # -------------------------------------------------------------------------
464 - def check_out(self, table=None, record=None, timestmp=None):
465 """ 466 Make the last log entry before timestmp independent from 467 the referenced entity (if any) 468 469 @param timestmp: the date/time of the check-out, defaults 470 to current time 471 """ 472 473 db = current.db 474 s3db = current.s3db 475 476 ptable = s3db[PRESENCE] 477 478 if timestmp is None: 479 timestmp = datetime.utcnow() 480 481 interlock = None 482 if table is not None: 483 if isinstance(table, str): 484 table = s3db[table] 485 if isinstance(record, Rows): 486 record = record.first() 487 if self.__super_entity(table): 488 if not isinstance(record, Row): 489 record = table[record] 490 table = s3db[record.instance_type] 491 fields = self.__get_fields(table, super_entity=False) 492 if not fields: 493 raise SyntaxError("No trackable type: %s" % table._tablename) 494 query = table[UID] == record[UID] 495 record = db(query).select(limitby=(0, 1)).first() 496 if isinstance(record, Row) and table._id.name in record: 497 record = record[table._id.name] 498 if record: 499 interlock = "%s,%s" % (table, record) 500 else: 501 return 502 503 q = ((ptable.deleted == False) & (ptable.timestmp <= timestmp)) 504 505 for r in self.records: 506 if TRACK_ID not in r: 507 # Cannot check-out a non-trackable 508 continue 509 query = q & (ptable[TRACK_ID] == r[TRACK_ID]) 510 presence = db(query).select(orderby=~ptable.timestmp, 511 limitby=(0, 1)).first() 512 if presence and presence.interlock: 513 if interlock and presence.interlock != interlock: 514 continue 515 elif not interlock and table and \ 516 not presence.interlock.startswith("%s" % table): 517 continue 518 tablename, record_id = presence.interlock.split(",", 1) 519 trackable = S3Trackable(tablename=tablename, record_id=record_id) 520 location = trackable.get_location(_fields=["id"], 521 timestmp=timestmp, 522 as_rows=True).first() 523 if timestmp - presence.timestmp < timedelta(seconds=1): 524 timestmp = timestmp + timedelta(seconds=1) 525 data = dict(location_id=location.id, 526 timestmp=timestmp, 527 interlock=None) 528 data.update({TRACK_ID:r[TRACK_ID]}) 529 ptable.insert(**data) 530 self.__update_timestamp(r[TRACK_ID], timestmp)
531 532 # -------------------------------------------------------------------------
533 - def remove_location(self, location=None):
534 """ 535 Remove a location from the presence log of the instance(s) 536 537 @todo: implement 538 """ 539 raise NotImplementedError
540 541 # -------------------------------------------------------------------------
542 - def get_base_location(self, 543 _fields=None, 544 _filter=None, 545 as_rows=False, 546 empty=True):
547 """ 548 Get the base location of the instance(s) 549 550 @param _fields: fields to retrieve from the location records (None for ALL) 551 @param _filter: filter for the locations 552 @param as_rows: return the result as Rows object 553 @param empty: return None if no locations (set to False by gis.get_location_data()) 554 555 @return: the base location(s) of the current instance 556 """ 557 558 db = current.db 559 s3db = current.s3db 560 561 ltable = s3db[LOCATION] 562 rtable = self.rtable 563 564 locations = [] 565 for r in self.records: 566 location = None 567 query = None 568 if LOCATION_ID in r: 569 query = (ltable.id == r[LOCATION_ID]) 570 if rtable: 571 query = query & (rtable[LOCATION_ID] == ltable.id) 572 if TRACK_ID in r: 573 query = query & (rtable[TRACK_ID] == r[TRACK_ID]) 574 elif TRACK_ID in r: 575 q = (self.table[TRACK_ID] == r[TRACK_ID]) 576 trackable = db(q).select(limitby=(0, 1)).first() 577 table = s3db[trackable.instance_type] 578 if LOCATION_ID in table.fields: 579 query = ((table[TRACK_ID] == r[TRACK_ID]) & 580 (table[LOCATION_ID] == ltable.id)) 581 if query: 582 if _filter is not None: 583 query = query & _filter 584 if not _fields: 585 location = db(query).select(ltable.ALL, 586 limitby=(0, 1)).first() 587 else: 588 location = db(query).select(limitby=(0, 1), 589 *_fields).first() 590 if location: 591 locations.append(location) 592 elif not empty: 593 # Ensure we return an entry for gis.get_location_data() so that indexes match 594 locations.append(Row({"lat": None, "lon": None})) 595 596 if as_rows: 597 return Rows(records=locations, compact=False) 598 599 if not locations: 600 return None 601 elif len(locations) == 1: 602 return locations[0] 603 else: 604 return locations
605 606 # -------------------------------------------------------------------------
607 - def set_base_location(self, location=None):
608 """ 609 Set the base location of the instance(s) 610 611 @param location: the location for the base location as Row or record ID 612 613 @return: nothing 614 615 @note: instance tables without a location_id field will be ignored 616 """ 617 618 if isinstance(location, S3Trackable): 619 location = location.get_base_location() 620 if isinstance(location, Rows): 621 location = location.first() 622 if isinstance(location, Row): 623 location.get("id", None) 624 625 if not location or not str(location).isdigit(): 626 # Location not found 627 return 628 else: 629 data = {LOCATION_ID:location} 630 631 # Update records without track ID 632 for r in self.records: 633 if TRACK_ID in r: 634 continue 635 elif LOCATION_ID in r: 636 if hasattr(r, "update_record"): 637 r.update_record(**data) 638 else: 639 raise SyntaxError("Cannot relate record to a table.") 640 641 db = current.db 642 s3db = current.s3db 643 644 # Update records with track ID 645 # => this can happen table-wise = less queries 646 track_ids = [r[TRACK_ID] for r in self.records if TRACK_ID in r] 647 rows = db(self.table[TRACK_ID].belongs(track_ids)).select() 648 649 tables = [] 650 append = tables.append 651 types = set() 652 seen = types.add 653 for r in rows: 654 instance_type = r.instance_type 655 if instance_type not in types: 656 seen(instance_type) 657 table = s3db[instance_type] 658 if instance_type not in tables and LOCATION_ID in table.fields: 659 append(table) 660 else: 661 # No location ID in this type => ignore gracefully 662 continue 663 664 # Location specified => update all base locations 665 for table in tables: 666 db(table[TRACK_ID].belongs(track_ids)).update(**data) 667 668 # Refresh records 669 for r in self.records: 670 if LOCATION_ID in r: 671 r[LOCATION_ID] = location 672 673 return location
674 675 # -------------------------------------------------------------------------
676 - def __update_timestamp(self, track_id, timestamp):
677 """ 678 Update the timestamp of a trackable 679 680 @param track_id: the trackable ID (super-entity key) 681 @param timestamp: the timestamp 682 """ 683 684 if track_id: 685 if timestamp is None: 686 timestamp = datetime.utcnow() 687 current.db(self.table.track_id == track_id).update(track_timestmp=timestamp)
688
689 # ============================================================================= 690 -class S3Tracker(object):
691 """ 692 S3 Tracking system, can be instantiated once as global 's3tracker' object 693 """ 694
695 - def __init__(self):
696 """ 697 Constructor 698 """
699 700 # -------------------------------------------------------------------------
701 - def __call__(self, table=None, record_id=None, record_ids=None, 702 tablename=None, record=None, query=None):
703 """ 704 Get a tracking interface for a record or set of records 705 706 @param table: a Table object 707 @param record_id: a record ID (together with Table or tablename) 708 @param record_ids: a list/tuple of record IDs (together with Table or tablename) 709 @param tablename: a Str object 710 @param record: a Row object 711 @param query: a Query object 712 713 @return: a S3Trackable instance for the specified record(s) 714 """ 715 716 return S3Trackable(table=table, 717 tablename=tablename, 718 record_id=record_id, 719 record_ids=record_ids, 720 record=record, 721 query=query, 722 )
723 724 # -------------------------------------------------------------------------
725 - def get_all(self, entity, 726 location=None, 727 bbox=None, 728 timestmp=None):
729 """ 730 Get all instances of the given entity at the given location and time 731 """ 732 raise NotImplementedError
733 734 # -------------------------------------------------------------------------
735 - def get_checked_in(self, table, record, 736 instance_type=None, 737 timestmp=None):
738 """ 739 Get all trackables of the given type that are checked-in 740 to the given instance at the given time 741 """ 742 raise NotImplementedError
743
744 # ============================================================================= 745 -class S3CheckInMethod(S3Method):
746 """ 747 Custom Method to allow a trackable resource to check-in 748 """ 749 750 # ------------------------------------------------------------------------- 751 @staticmethod
752 - def apply_method(r, **attr):
753 """ 754 Apply method. 755 756 @param r: the S3Request 757 @param attr: controller options for this request 758 """ 759 760 if r.representation == "html": 761 762 T = current.T 763 s3db = current.s3db 764 response = current.response 765 table = r.table 766 tracker = S3Trackable(table, record_id=r.id) 767 768 title = T("Check-In") 769 770 get_vars = r.get_vars 771 772 # Are we being passed a location_id? 773 location_id = get_vars.get("location_id", None) 774 if not location_id: 775 # Are we being passed a lat and lon? 776 lat = get_vars.get("lat", None) 777 if lat is not None: 778 lon = get_vars.get("lon", None) 779 if lon is not None: 780 form_vars = Storage(lat = float(lat), 781 lon = float(lon), 782 ) 783 form = Storage(vars=form_vars) 784 s3db.gis_location_onvalidation(form) 785 location_id = s3db.gis_location.insert(**form_vars) 786 787 788 form = None 789 if not location_id: 790 # Give the user a form to check-in 791 792 # Test the formstyle 793 formstyle = current.deployment_settings.get_ui_formstyle() 794 row = formstyle("test", "test", "test", "test") 795 if isinstance(row, tuple): 796 # Formstyle with separate row for label (e.g. default Eden formstyle) 797 tuple_rows = True 798 else: 799 # Formstyle with just a single row (e.g. Bootstrap, Foundation or DRRPP) 800 tuple_rows = False 801 802 form_rows = [] 803 comment = "" 804 805 _id = "location_id" 806 label = LABEL("%s:" % T("Location")) 807 808 from s3.s3widgets import S3LocationSelector 809 field = table.location_id 810 #value = tracker.get_location(_fields=["id"], 811 # as_rows=True).first().id 812 value = None # We always want to create a new Location, not update the existing one 813 widget = S3LocationSelector(show_latlon = True)(field, value) 814 815 row = formstyle("%s__row" % _id, label, widget, comment) 816 if tuple_rows: 817 form_rows.append(row[0]) 818 form_rows.append(row[1]) 819 else: 820 form_rows.append(row) 821 822 _id = "submit" 823 label = "" 824 widget = INPUT(_type="submit", _value=T("Check-In")) 825 row = formstyle("%s__row" % _id, label, widget, comment) 826 if tuple_rows: 827 form_rows.append(row[0]) 828 form_rows.append(row[1]) 829 else: 830 form_rows.append(row) 831 832 if tuple_rows: 833 # Assume TRs 834 form = FORM(TABLE(*form_rows)) 835 else: 836 form = FORM(*form_rows) 837 838 if form.accepts(current.request.vars, current.session): 839 location_id = form.vars.get("location_id", None) 840 841 if location_id: 842 # We're not Checking-in in S3Track terms (that's about interlocking with another object) 843 #tracker.check_in() 844 #timestmp = form.vars.get("timestmp", None) 845 #if timestmp: 846 # # @ToDo: Convert from string 847 # pass 848 #tracker.set_location(location_id, timestmp=timestmp) 849 tracker.set_location(location_id) 850 response.confirmation = T("Checked-In successfully!") 851 852 response.view = "check-in.html" 853 output = dict(form = form, 854 title = title, 855 ) 856 return output 857 858 # @ToDo: JSON representation for check-in from mobile devices 859 else: 860 raise HTTP(415, current.ERROR.BAD_FORMAT)
861
862 # ============================================================================= 863 -class S3CheckOutMethod(S3Method):
864 """ 865 Custom Method to allow a trackable resource to check-out 866 """ 867 868 # ------------------------------------------------------------------------- 869 @staticmethod
870 - def apply_method(r, **attr):
871 """ 872 Apply method. 873 874 @param r: the S3Request 875 @param attr: controller options for this request 876 """ 877 878 if r.representation == "html": 879 880 T = current.T 881 882 response = current.response 883 tracker = S3Trackable(r.table, record_id=r.id) 884 885 title = T("Check-Out") 886 887 # Give the user a form to check-out 888 889 # Test the formstyle 890 formstyle = current.deployment_settings.get_ui_formstyle() 891 row = formstyle("test", "test", "test", "test") 892 if isinstance(row, tuple): 893 # Formstyle with separate row for label (e.g. default Eden formstyle) 894 tuple_rows = True 895 else: 896 # Formstyle with just a single row (e.g. Bootstrap, Foundation or DRRPP) 897 tuple_rows = False 898 899 form_rows = [] 900 comment = "" 901 902 _id = "submit" 903 label = "" 904 widget = INPUT(_type="submit", _value=T("Check-Out")) 905 row = formstyle("%s__row" % _id, label, widget, comment) 906 if tuple_rows: 907 form_rows.append(row[0]) 908 form_rows.append(row[1]) 909 else: 910 form_rows.append(row) 911 912 if tuple_rows: 913 # Assume TRs 914 form = FORM(TABLE(*form_rows)) 915 else: 916 form = FORM(*form_rows) 917 918 if form.accepts(current.request.vars, current.session): 919 # Check-Out 920 # We're not Checking-out in S3Track terms (that's about removing an interlock with another object) 921 # What we're doing is saying that we're now back at our base location 922 #tracker.check_out() 923 #timestmp = form_vars.get("timestmp", None) 924 #if timestmp: 925 # # @ToDo: Convert from string 926 # pass 927 #tracker.set_location(r.record.location_id, timestmp=timestmp) 928 tracker.set_location(r.record.location_id) 929 response.confirmation = T("Checked-Out successfully!") 930 931 response.view = "check-in.html" 932 output = dict(form = form, 933 title = title, 934 ) 935 return output 936 937 # @ToDo: JSON representation for check-out from mobile devices 938 else: 939 raise HTTP(415, current.ERROR.BAD_FORMAT)
940 941 # END ========================================================================= 942