def mform(self, r, **attr): """ Get the mobile form for the target resource @param r: the S3Request instance @param attr: controller attributes @returns: a JSON string """ resource = self.resource msince = r.get_vars.get("msince") if msince: msince = s3_parse_datetime(msince) # Get the mobile form mform = S3MobileForm(resource).serialize(msince=msince) # Add controller and function for data exchange mform["controller"] = r.controller mform["function"] = r.function # Convert to JSON output = json.dumps(mform, separators=SEPARATORS) current.response.headers = {"Content-Type": "application/json"} return output
def __receive(self, r, **attr): """ Respond to an incoming push @param r: the S3Request @param attr: the controller attributes """ mixed = attr.get("mixed", False) get_vars = r.get_vars s3db = current.s3db db = current.db # Identify the sending repository repository_uuid = get_vars.get("repository") connector = None if repository_uuid: rtable = s3db.sync_repository query = rtable.uuid == repository_uuid row = current.db(query).select(limitby=(0, 1)).first() if row: connector = S3SyncRepository(row) # Check that the repository is registered and allowed to push if connector is None or not connector.accept_push: r.error(403, current.ERROR.NOT_PERMITTED) current.log.debug("S3Sync PUSH from %s (%s)" % (connector.name, connector.apitype)) # Get strategy and policy default_update_policy = S3ImportItem.POLICY.NEWER default_conflict_policy = S3ImportItem.POLICY.MASTER # Identify the synchronization task ttable = s3db.sync_task if not mixed: query = (ttable.repository_id == connector.id) & \ (ttable.resource_name == r.tablename) & \ (ttable.deleted != True) task = db(query).select(limitby=(0, 1)).first() else: task = None last_sync = None if task: strategy = task.strategy update_policy = task.update_policy or default_update_policy conflict_policy = task.conflict_policy or default_conflict_policy if update_policy not in ("THIS", "OTHER"): last_sync = task.last_pull else: policies = S3ImportItem.POLICY p = get_vars.get("update_policy", None) values = {"THIS": "OTHER", "OTHER": "THIS"} switch = lambda p: p in values and values[p] or p if p and p in policies: p = switch(p) update_policy = policies[p] else: update_policy = default_update_policy p = get_vars.get("conflict_policy", None) if p and p in policies: p = switch(p) conflict_policy = policies[p] else: conflict_policy = default_conflict_policy msince = get_vars.get("msince", None) if msince is not None: last_sync = s3_parse_datetime(msince) s = get_vars.get("strategy", None) if s: s = str(s).split(",") methods = S3ImportItem.METHOD strategy = [ method for method in methods.values() if method in s ] else: strategy = ttable.strategy.default # Get the source source = r.read_body() # Import resource resource = r.resource try: result = connector.receive( source, resource, strategy=strategy, update_policy=update_policy, conflict_policy=conflict_policy, last_sync=last_sync, onconflict=self.onconflict, mixed=mixed, ) except IOError: current.auth.permission.fail() except SyntaxError: e = sys.exc_info()[1] r.error(400, e) except NotImplementedError: r.error(405, "Synchronization method not supported for repository") log = self.log log.write( repository_id=connector.id, resource_name="mixed" if mixed else resource.tablename, transmission=log.IN, mode=log.PUSH, action="receive", remote=result.get("remote", False), result=result.get("status", log.NONE), message=result.get("message", ""), ) return result.get("response")
def __send(self, r, **attr): """ Respond to an incoming pull @param r: the S3Request @param attr: the controller attributes """ mixed = attr.get("mixed", False) get_vars = r.get_vars resource = r.resource # Identify the requesting repository repository_uuid = get_vars.get("repository") connector = None if repository_uuid: rtable = current.s3db.sync_repository query = rtable.uuid == repository_uuid row = current.db(query).select(limitby=(0, 1)).first() if row: connector = S3SyncRepository(row) if connector is None: # Use a dummy repository with Eden API connector = S3SyncRepository( Storage( id=None, name="unknown", apitype="eden", )) current.log.debug("S3Sync PULL from %s (%s)" % (connector.name, connector.apitype)) # Additional export parameters start = get_vars.get("start", None) if start is not None: try: start = int(start) except ValueError: start = None limit = get_vars.get("limit", None) if limit is not None: try: limit = int(limit) except ValueError: limit = None msince = get_vars.get("msince", None) if msince is not None: msince = s3_parse_datetime(msince) # Sync filters from peer filters = {} for k, v in get_vars.items(): if k[0] == "[" and "]" in k: tablename, urlvar = k[1:].split("]", 1) if urlvar: if not tablename or tablename == "~": tablename = resource.tablename f = filters.get(tablename, {}) u = f.get(urlvar, None) if u: u = "%s&%s" % (u, v) else: u = v f[urlvar] = u filters[tablename] = f if not filters: filters = None try: result = connector.send( resource, start=start, limit=limit, msince=msince, filters=filters, mixed=mixed, ) except NotImplementedError: r.error(405, "Synchronization method not supported for repository") log = self.log log.write( repository_id=connector.id, resource_name="mixed" if mixed else resource.tablename, transmission=log.IN, mode=log.PULL, action="send", remote=result.get("remote", False), result=result.get("status", log.NONE), message=result.get("message", ""), ) return result.get("response")
def __receive(self, r, **attr): """ Respond to an incoming push @param r: the S3Request @param attr: the controller attributes """ _debug("S3Sync.__receive") s3db = current.s3db db = current.db # Identify the sending repository repository = Storage(id=None) if "repository" in r.vars: ruid = r.vars["repository"] rtable = s3db.sync_repository row = db(rtable.uuid == ruid).select(limitby=(0, 1)).first() if row: repository = row if not repository.id or \ not repository.accept_push: r.error(403, current.ERROR.NOT_PERMITTED) # Get strategy and policy default_update_policy = S3ImportItem.POLICY.NEWER default_conflict_policy = S3ImportItem.POLICY.MASTER ttable = s3db.sync_task query = (ttable.repository_id == repository.id) & \ (ttable.resource_name == r.tablename) & \ (ttable.deleted != True) task = db(query).select(limitby=(0, 1)).first() last_sync = None if task: strategy = task.strategy update_policy = task.update_policy or default_update_policy conflict_policy = task.conflict_policy or default_conflict_policy if update_policy not in ("THIS", "OTHER"): last_sync = task.last_pull else: policies = S3ImportItem.POLICY p = r.get_vars.get("update_policy", None) values = {"THIS": "OTHER", "OTHER": "THIS"} switch = lambda p: p in values and values[p] or p if p and p in policies: p = switch(p) update_policy = policies[p] else: update_policy = default_update_policy p = r.get_vars.get("conflict_policy", None) if p and p in policies: p = switch(p) conflict_policy = policies[p] else: conflict_policy = default_conflict_policy msince = r.get_vars.get("msince", None) if msince is not None: last_sync = s3_parse_datetime(msince) s = r.get_vars.get("strategy", None) if s: s = str(s).split(",") methods = S3ImportItem.METHOD strategy = [method for method in methods.values() if method in s] else: strategy = ttable.strategy.default # Other parameters ignore_errors = True # Get the source source = r.read_body() # Import resource resource = r.resource onconflict = lambda item: self.onconflict(item, repository, resource) try: output = resource.import_xml(source, format="xml", ignore_errors=ignore_errors, strategy=strategy, update_policy=update_policy, conflict_policy=conflict_policy, last_sync=last_sync, onconflict=onconflict) except IOError: current.auth.permission.fail() except SyntaxError: e = sys.exc_info()[1] r.error(400, e) log = self.log if resource.error_tree is not None: # Validation error (log in any case) if ignore_errors: result = log.WARNING else: result = log.FATAL message = "%s" % resource.error for element in resource.error_tree.findall("resource"): error_msg = element.get("error", "unknown error") error_fields = element.findall("data[@error]") if error_fields: for field in error_fields: error_msg = field.get("error", "unknown error") if error_msg: msg = "(UID: %s) %s.%s=%s: %s" % \ (element.get("uuid", None), element.get("name", None), field.get("field", None), field.get("value", field.text), error_msg) message = "%s, %s" % (message, msg) else: msg = "(UID: %s) %s: %s" % \ (element.get("uuid", None), element.get("name", None), error_msg) message = "%s, %s" % (message, msg) else: result = log.SUCCESS message = "data received from peer" log.write(repository_id=repository.id, resource_name=resource.tablename, transmission=log.IN, mode=log.PUSH, result=result, message=message) return output
def __send(self, r, **attr): """ Respond to an incoming pull @param r: the S3Request @param attr: the controller attributes """ _debug("S3Sync.__send") resource = r.resource # Identify the requesting repository repository_id = None if "repository" in r.vars: db = current.db s3db = current.s3db ruid = r.vars["repository"] rtable = s3db.sync_repository ttable = s3db.sync_task left = ttable.on((rtable.id == ttable.repository_id) & \ (ttable.resource_name == resource.tablename)) row = db(rtable.uuid == ruid).select(rtable.id, ttable.id, left=left, limitby=(0, 1)).first() if row: repository_id = row[rtable.id] task_id = row[ttable.id] # Additional export parameters _vars = r.get_vars start = _vars.get("start", None) if start is not None: try: start = int(start) except ValueError: start = None limit = _vars.get("limit", None) if limit is not None: try: limit = int(limit) except ValueError: limit = None msince = _vars.get("msince", None) if msince is not None: msince = s3_parse_datetime(msince) # Sync filters from peer filters = {} for k, v in _vars.items(): if k[0] == "[" and "]" in k: tablename, urlvar = k[1:].split("]", 1) if urlvar: if not tablename or tablename == "~": tablename = resource.tablename f = filters.get(tablename, {}) u = f.get(urlvar, None) if u: u = "%s&%s" % (u, v) else: u = v f[urlvar] = u filters[tablename] = f if not filters: filters = None # Export the resource output = resource.export_xml(start=start, limit=limit, filters=filters, msince=msince) count = resource.results # Set content type header headers = current.response.headers headers["Content-Type"] = "text/xml" # Log the operation log = self.log log.write(repository_id=repository_id, resource_name=r.resource.tablename, transmission=log.IN, mode=log.PULL, result=log.SUCCESS, message="data sent to peer (%s records)" % count) return output
def __receive(self, r, **attr): """ Respond to an incoming push @param r: the S3Request @param attr: the controller attributes """ mixed = attr.get("mixed", False) get_vars = r.get_vars s3db = current.s3db db = current.db # Identify the sending repository repository_uuid = get_vars.get("repository") connector = None if repository_uuid: rtable = s3db.sync_repository query = rtable.uuid == repository_uuid row = current.db(query).select(limitby=(0, 1)).first() if row: connector = S3SyncRepository(row) if connector is None: # Repositories must be registered to push, so that we # can track sync times and log operations properly r.error(403, "Registration required") current.log.debug("S3Sync PUSH from %s (%s)" % (connector.name, connector.apitype, )) # Get strategy and policy default_update_policy = S3ImportItem.POLICY.NEWER default_conflict_policy = S3ImportItem.POLICY.MASTER # Identify the synchronization task ttable = s3db.sync_task if not mixed: query = (ttable.repository_id == connector.id) & \ (ttable.resource_name == r.tablename) & \ (ttable.deleted != True) task = db(query).select(limitby=(0, 1)).first() else: task = None last_sync = None if task: strategy = task.strategy update_policy = task.update_policy or default_update_policy conflict_policy = task.conflict_policy or default_conflict_policy if update_policy not in ("THIS", "OTHER"): last_sync = task.last_pull else: policies = S3ImportItem.POLICY p = get_vars.get("update_policy", None) values = {"THIS": "OTHER", "OTHER": "THIS"} switch = lambda p: p in values and values[p] or p if p and p in policies: p = switch(p) update_policy = policies[p] else: update_policy = default_update_policy p = get_vars.get("conflict_policy", None) if p and p in policies: p = switch(p) conflict_policy = policies[p] else: conflict_policy = default_conflict_policy msince = get_vars.get("msince", None) if msince is not None: last_sync = s3_parse_datetime(msince) s = get_vars.get("strategy", None) if s: s = str(s).split(",") methods = S3ImportItem.METHOD strategy = [method for method in methods.values() if method in s] else: strategy = ttable.strategy.default # Get the source source = r.read_body() # Import resource resource = r.resource try: result = connector.receive(source, resource, strategy = strategy, update_policy = update_policy, conflict_policy = conflict_policy, last_sync = last_sync, onconflict = self.onconflict, mixed = mixed, ) except IOError: current.auth.permission.fail() except SyntaxError: e = sys.exc_info()[1] r.error(400, e) except NotImplementedError: r.error(405, "Synchronization method not supported for repository") log = self.log log.write(repository_id = connector.id, resource_name = "mixed" if mixed else resource.tablename, transmission = log.IN, mode = log.PUSH, action = "receive", remote = result.get("remote", False), result = result.get("status", log.NONE), message = result.get("message", ""), ) return result.get("response")
def __send(self, r, **attr): """ Respond to an incoming pull @param r: the S3Request @param attr: the controller attributes """ mixed = attr.get("mixed", False) get_vars = r.get_vars resource = r.resource # Identify the requesting repository repository_uuid = get_vars.get("repository") connector = None if repository_uuid: rtable = current.s3db.sync_repository query = rtable.uuid == repository_uuid row = current.db(query).select(limitby=(0, 1)).first() if row: connector = S3SyncRepository(row) if connector is None: # Use a dummy repository with Eden API connector = S3SyncRepository(Storage(id = None, name = "unknown", apitype = "eden", )) current.log.debug("S3Sync PULL from %s (%s)" % (connector.name, connector.apitype)) # Additional export parameters start = get_vars.get("start", None) if start is not None: try: start = int(start) except ValueError: start = None limit = get_vars.get("limit", None) if limit is not None: try: limit = int(limit) except ValueError: limit = None msince = get_vars.get("msince", None) if msince is not None: msince = s3_parse_datetime(msince) # Sync filters from peer filters = {} for k, v in get_vars.items(): if k[0] == "[" and "]" in k: tablename, urlvar = k[1:].split("]", 1) if urlvar: if not tablename or tablename == "~": tablename = resource.tablename f = filters.get(tablename, {}) u = f.get(urlvar, None) if u: u = "%s&%s" % (u, v) else: u = v f[urlvar] = u filters[tablename] = f if not filters: filters = None try: result = connector.send(resource, start = start, limit = limit, msince = msince, filters = filters, mixed = mixed, ) except NotImplementedError: r.error(405, "Synchronization method not supported for repository") log = self.log log.write(repository_id = connector.id, resource_name = "mixed" if mixed else resource.tablename, transmission = log.IN, mode = log.PULL, action = "send", remote = result.get("remote", False), result = result.get("status", log.NONE), message = result.get("message", ""), ) return result.get("response")
def __receive(self, r, **attr): """ Respond to an incoming push @param r: the S3Request @param attr: the controller attributes """ _debug("S3Sync.__receive") s3db = current.s3db db = current.db # Identify the sending repository repository = Storage(id=None) if "repository" in r.vars: ruid = r.vars["repository"] rtable = s3db.sync_repository row = db(rtable.uuid == ruid).select(limitby=(0, 1)).first() if row: repository = row if not repository.id or \ not repository.accept_push: r.error(403, current.ERROR.NOT_PERMITTED) # Get strategy and policy default_update_policy = S3ImportItem.POLICY.NEWER default_conflict_policy = S3ImportItem.POLICY.MASTER ttable = s3db.sync_task query = (ttable.repository_id == repository.id) & \ (ttable.resource_name == r.tablename) & \ (ttable.deleted != True) task = db(query).select(limitby=(0, 1)).first() last_sync = None if task: strategy = task.strategy update_policy = task.update_policy or default_update_policy conflict_policy = task.conflict_policy or default_conflict_policy if update_policy not in ("THIS", "OTHER"): last_sync = task.last_pull else: policies = S3ImportItem.POLICY p = r.get_vars.get("update_policy", None) values = {"THIS": "OTHER", "OTHER": "THIS"} switch = lambda p: p in values and values[p] or p if p and p in policies: p = switch(p) update_policy = policies[p] else: update_policy = default_update_policy p = r.get_vars.get("conflict_policy", None) if p and p in policies: p = switch(p) conflict_policy = policies[p] else: conflict_policy = default_conflict_policy msince = r.get_vars.get("msince", None) if msince is not None: last_sync = s3_parse_datetime(msince) s = r.get_vars.get("strategy", None) if s: s = str(s).split(",") methods = S3ImportItem.METHOD strategy = [ method for method in methods.values() if method in s ] else: strategy = ttable.strategy.default # Other parameters ignore_errors = True # Get the source source = r.read_body() # Import resource resource = r.resource onconflict = lambda item: self.onconflict(item, repository, resource) try: output = resource.import_xml(source, format="xml", ignore_errors=ignore_errors, strategy=strategy, update_policy=update_policy, conflict_policy=conflict_policy, last_sync=last_sync, onconflict=onconflict) except IOError: current.auth.permission.fail() except SyntaxError: e = sys.exc_info()[1] r.error(400, e) log = self.log if resource.error_tree is not None: # Validation error (log in any case) if ignore_errors: result = log.WARNING else: result = log.FATAL message = "%s" % resource.error for element in resource.error_tree.findall("resource"): error_msg = element.get("error", "unknown error") error_fields = element.findall("data[@error]") if error_fields: for field in error_fields: error_msg = field.get("error", "unknown error") if error_msg: msg = "(UID: %s) %s.%s=%s: %s" % \ (element.get("uuid", None), element.get("name", None), field.get("field", None), field.get("value", field.text), error_msg) message = "%s, %s" % (message, msg) else: msg = "(UID: %s) %s: %s" % \ (element.get("uuid", None), element.get("name", None), error_msg) message = "%s, %s" % (message, msg) else: result = log.SUCCESS message = "data received from peer" log.write(repository_id=repository.id, resource_name=resource.tablename, transmission=log.IN, mode=log.PUSH, result=result, message=message) return output