We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
record_event task fails for view events generated by an user who accesses a dashboard using a public sharing link
### Issue Summary

If I share a dashboard and access it through the generated public URL, the worker prints an error to the log when it tries to write the view event into the events log table from the record_event task.

### Steps to Reproduce

I was able to reproduce this issue by using the docker-compose.yml file provided in the repository (i.e. in a dev setup):

1. Setup a fresh instance of Redash
2. Create a new dashboard
3. Click the share button and enable public sharing
4. Copy the public sharing link (e.g. `http://localhost:5000/public/dashboards/CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J?org_slug=default`) and access it with a clean browser profile (or private window that has no redash login cookies around)

### What happens?
The following error can be seen in the worker logs:

```
[2017-09-07 08:03:40,006: ERROR/MainProcess] Task redash.tasks.record_event[41674041-c67b-4659-a151-99f9e73e3301] raised unexpected: DataError('(psycopg2.DataError) invalid input syntax for integer: "CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J"\nLINE 1: ...id, additional_properties, created_at) VALUES (1, \'CH6m72tek...\n                                                             ^\n',)
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/celery/app/trace.py", line 240, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/app/redash/worker.py", line 69, in __call__
    return TaskBase.__call__(self, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/celery/app/trace.py", line 438, in __protected_call__
    return self.run(*args, **kwargs)
  File "/app/redash/tasks/general.py", line 15, in record_event
    models.db.session.commit()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/scoping.py", line 157, in do
    return getattr(self.registry(), name)(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 874, in commit
    self.transaction.commit()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 461, in commit
    self._prepare_impl()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 441, in _prepare_impl
    self.session.flush()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 2137, in flush
    self._flush(objects)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 2257, in _flush
    transaction.rollback(_capture_exception=True)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/langhelpers.py", line 60, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 2221, in _flush
    flush_context.execute()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/unitofwork.py", line 389, in execute
    rec.execute(self)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/unitofwork.py", line 548, in execute
    uow
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/persistence.py", line 181, in save_obj
    mapper, table, insert)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/persistence.py", line 835, in _emit_insert_statements
    execute(statement, params)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 945, in execute
    return meth(self, multiparams, params)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/elements.py", line 263, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1053, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1189, in _execute_context
    context)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1393, in _handle_dbapi_exception
    exc_info
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/compat.py", line 202, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb, cause=cause)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1182, in _execute_context
    context)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/default.py", line 469, in do_execute
    cursor.execute(statement, parameters)
DataError: (psycopg2.DataError) invalid input syntax for integer: "CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J"
LINE 1: ...id, additional_properties, created_at) VALUES (1, 'CH6m72tek...
                                                             ^
 [SQL: 'INSERT INTO events (org_id, user_id, action, object_type, object_id, additional_properties, created_at) VALUES (%(org_id)s, %(user_id)s, %(action)s, %(object_type)s, %(object_id)s, %(additional_properties)s, %(created_at)s) RETURNING events.id'] [parameters: {'user_id': u'CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J', 'created_at': datetime.datetime(2017, 9, 7, 8, 3, 39), 'object_type': 'dashboard', 'org_id': 1, 'object_id': 1, 'additional_properties': '{"ip": "172.20.0.1", "public": true, "referer": null, "user_agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.0", "headless": false}', 'action': 'view'}]
```

Coincidentally, the value that fails to be set to the user_id field (`CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J`) is the token seen in the public sharing URL (`http://localhost:5000/public/dashboards/CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J?org_slug=default`)

### What should happen?
The view event is properly saved to the events table and the task does not crash.

### Technical details:
* Redash Version: Both stable release 2.0.0 and latest master with commit ID 2a22b98c77757f378d3a4ed1ec8eceb99f0a8b35
* How did you install Redash: Stable v2.0.0 by upgrading a previous version with the bin/upgrade script and the latest commit in master using the provided docker-compose configuration and following the developer environment setup.

I believe this issue was discussed in #1238 but that lacked a proper STR to actually fix the root cause of this issue. It did had some good ideas about what could cause the bug and it indeed seems that an anonymous user causes an incorrect user_id to be included in the view event.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `redash/handlers/base.py`
Content:
```
1 import time
2 
3 from flask import Blueprint, current_app, request
4 
5 from flask_login import current_user, login_required
6 from flask_restful import Resource, abort
7 from redash import settings
8 from redash.authentication import current_org
9 from redash.models import ApiUser
10 from redash.tasks import record_event as record_event_task
11 from redash.utils import json_dumps
12 from sqlalchemy.orm.exc import NoResultFound
13 
14 routes = Blueprint('redash', __name__, template_folder=settings.fix_assets_path('templates'))
15 
16 
17 class BaseResource(Resource):
18     decorators = [login_required]
19 
20     def __init__(self, *args, **kwargs):
21         super(BaseResource, self).__init__(*args, **kwargs)
22         self._user = None
23 
24     def dispatch_request(self, *args, **kwargs):
25         kwargs.pop('org_slug', None)
26 
27         return super(BaseResource, self).dispatch_request(*args, **kwargs)
28 
29     @property
30     def current_user(self):
31         return current_user._get_current_object()
32 
33     @property
34     def current_org(self):
35         return current_org._get_current_object()
36 
37     def record_event(self, options):
38         record_event(self.current_org, self.current_user, options)
39 
40     # TODO: this should probably be somewhere else
41     def update_model(self, model, updates):
42         for k, v in updates.items():
43             setattr(model, k, v)
44 
45 
46 def record_event(org, user, options):
47     if isinstance(user, ApiUser):
48         options.update({
49             'api_key': user.name,
50             'org_id': org.id
51         })
52     else:
53         options.update({
54             'user_id': user.id,
55             'org_id': org.id
56         })
57 
58     options.update({
59         'user_agent': request.user_agent.string,
60         'ip': request.remote_addr
61     })
62 
63     if 'timestamp' not in options:
64         options['timestamp'] = int(time.time())
65 
66     record_event_task.delay(options)
67 
68 
69 def require_fields(req, fields):
70     for f in fields:
71         if f not in req:
72             abort(400)
73 
74 
75 def get_object_or_404(fn, *args, **kwargs):
76     try:
77         rv = fn(*args, **kwargs)
78         if rv is None:
79             abort(404)
80     except NoResultFound:
81         abort(404)
82     return rv
83 
84 
85 def paginate(query_set, page, page_size, serializer):
86     count = query_set.count()
87 
88     if page < 1:
89         abort(400, message='Page must be positive integer.')
90 
91     if (page-1)*page_size+1 > count > 0:
92         abort(400, message='Page is out of range.')
93 
94     if page_size > 250 or page_size < 1:
95         abort(400, message='Page size is out of range (1-250).')
96 
97     results = query_set.paginate(page, page_size)
98 
99     return {
100         'count': count,
101         'page': page,
102         'page_size': page_size,
103         'results': [serializer(result) for result in results.items],
104     }
105 
106 
107 def org_scoped_rule(rule):
108     if settings.MULTI_ORG:
109         return "/<org_slug:org_slug>{}".format(rule)
110 
111     return rule
112 
113 
114 def json_response(response):
115     return current_app.response_class(json_dumps(response), mimetype='application/json')
116
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/redash/handlers/base.py b/redash/handlers/base.py
--- a/redash/handlers/base.py
+++ b/redash/handlers/base.py
@@ -44,7 +44,7 @@
 
 
 def record_event(org, user, options):
-    if isinstance(user, ApiUser):
+    if user.is_api_user():
         options.update({
             'api_key': user.name,
             'org_id': org.id
@@ -52,6 +52,7 @@
     else:
         options.update({
             'user_id': user.id,
+            'user_name': user.name,
             'org_id': org.id
         })
 
@@ -88,7 +89,7 @@
     if page < 1:
         abort(400, message='Page must be positive integer.')
 
-    if (page-1)*page_size+1 > count > 0:
+    if (page - 1) * page_size + 1 > count > 0:
         abort(400, message='Page is out of range.')
 
     if page_size > 250 or page_size < 1:

{"golden_diff": "diff --git a/redash/handlers/base.py b/redash/handlers/base.py\n--- a/redash/handlers/base.py\n+++ b/redash/handlers/base.py\n@@ -44,7 +44,7 @@\n \n \n def record_event(org, user, options):\n-    if isinstance(user, ApiUser):\n+    if user.is_api_user():\n         options.update({\n             'api_key': user.name,\n             'org_id': org.id\n@@ -52,6 +52,7 @@\n     else:\n         options.update({\n             'user_id': user.id,\n+            'user_name': user.name,\n             'org_id': org.id\n         })\n \n@@ -88,7 +89,7 @@\n     if page < 1:\n         abort(400, message='Page must be positive integer.')\n \n-    if (page-1)*page_size+1 > count > 0:\n+    if (page - 1) * page_size + 1 > count > 0:\n         abort(400, message='Page is out of range.')\n \n     if page_size > 250 or page_size < 1:\n", "issue": "record_event task fails for view events generated by an user who accesses a dashboard using a public sharing link\n### Issue Summary\r\n\r\nIf I share a dashboard and access it through the generated public URL, the worker prints an error to the log when it tries to write the view event into the events log table from the record_event task.\r\n\r\n### Steps to Reproduce\r\n\r\nI was able to reproduce this issue by using the docker-compose.yml file provided in the repository (i.e. in a dev setup):\r\n\r\n1. Setup a fresh instance of Redash\r\n2. Create a new dashboard\r\n3. Click the share button and enable public sharing\r\n4. Copy the public sharing link (e.g. `http://localhost:5000/public/dashboards/CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J?org_slug=default`) and access it with a clean browser profile (or private window that has no redash login cookies around)\r\n\r\n### What happens?\r\nThe following error can be seen in the worker logs:\r\n\r\n```\r\n[2017-09-07 08:03:40,006: ERROR/MainProcess] Task redash.tasks.record_event[41674041-c67b-4659-a151-99f9e73e3301] raised unexpected: DataError('(psycopg2.DataError) invalid input syntax for integer: \"CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J\"\\nLINE 1: ...id, additional_properties, created_at) VALUES (1, \\'CH6m72tek...\\n                                                             ^\\n',)\r\nTraceback (most recent call last):\r\n  File \"/usr/local/lib/python2.7/dist-packages/celery/app/trace.py\", line 240, in trace_task\r\n    R = retval = fun(*args, **kwargs)\r\n  File \"/app/redash/worker.py\", line 69, in __call__\r\n    return TaskBase.__call__(self, *args, **kwargs)\r\n  File \"/usr/local/lib/python2.7/dist-packages/celery/app/trace.py\", line 438, in __protected_call__\r\n    return self.run(*args, **kwargs)\r\n  File \"/app/redash/tasks/general.py\", line 15, in record_event\r\n    models.db.session.commit()\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/scoping.py\", line 157, in do\r\n    return getattr(self.registry(), name)(*args, **kwargs)\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py\", line 874, in commit\r\n    self.transaction.commit()\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py\", line 461, in commit\r\n    self._prepare_impl()\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py\", line 441, in _prepare_impl\r\n    self.session.flush()\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py\", line 2137, in flush\r\n    self._flush(objects)\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py\", line 2257, in _flush\r\n    transaction.rollback(_capture_exception=True)\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/langhelpers.py\", line 60, in __exit__\r\n    compat.reraise(exc_type, exc_value, exc_tb)\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py\", line 2221, in _flush\r\n    flush_context.execute()\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/unitofwork.py\", line 389, in execute\r\n    rec.execute(self)\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/unitofwork.py\", line 548, in execute\r\n    uow\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/persistence.py\", line 181, in save_obj\r\n    mapper, table, insert)\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/persistence.py\", line 835, in _emit_insert_statements\r\n    execute(statement, params)\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py\", line 945, in execute\r\n    return meth(self, multiparams, params)\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/elements.py\", line 263, in _execute_on_connection\r\n    return connection._execute_clauseelement(self, multiparams, params)\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py\", line 1053, in _execute_clauseelement\r\n    compiled_sql, distilled_params\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py\", line 1189, in _execute_context\r\n    context)\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py\", line 1393, in _handle_dbapi_exception\r\n    exc_info\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/compat.py\", line 202, in raise_from_cause\r\n    reraise(type(exception), exception, tb=exc_tb, cause=cause)\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py\", line 1182, in _execute_context\r\n    context)\r\n  File \"/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/default.py\", line 469, in do_execute\r\n    cursor.execute(statement, parameters)\r\nDataError: (psycopg2.DataError) invalid input syntax for integer: \"CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J\"\r\nLINE 1: ...id, additional_properties, created_at) VALUES (1, 'CH6m72tek...\r\n                                                             ^\r\n [SQL: 'INSERT INTO events (org_id, user_id, action, object_type, object_id, additional_properties, created_at) VALUES (%(org_id)s, %(user_id)s, %(action)s, %(object_type)s, %(object_id)s, %(additional_properties)s, %(created_at)s) RETURNING events.id'] [parameters: {'user_id': u'CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J', 'created_at': datetime.datetime(2017, 9, 7, 8, 3, 39), 'object_type': 'dashboard', 'org_id': 1, 'object_id': 1, 'additional_properties': '{\"ip\": \"172.20.0.1\", \"public\": true, \"referer\": null, \"user_agent\": \"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.0\", \"headless\": false}', 'action': 'view'}]\r\n```\r\n\r\nCoincidentally, the value that fails to be set to the user_id field (`CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J`) is the token seen in the public sharing URL (`http://localhost:5000/public/dashboards/CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J?org_slug=default`)\r\n\r\n### What should happen?\r\nThe view event is properly saved to the events table and the task does not crash.\r\n\r\n### Technical details:\r\n* Redash Version: Both stable release 2.0.0 and latest master with commit ID 2a22b98c77757f378d3a4ed1ec8eceb99f0a8b35\r\n* How did you install Redash: Stable v2.0.0 by upgrading a previous version with the bin/upgrade script and the latest commit in master using the provided docker-compose configuration and following the developer environment setup.\r\n\r\nI believe this issue was discussed in #1238 but that lacked a proper STR to actually fix the root cause of this issue. It did had some good ideas about what could cause the bug and it indeed seems that an anonymous user causes an incorrect user_id to be included in the view event.\n", "before_files": [{"content": "import time\n\nfrom flask import Blueprint, current_app, request\n\nfrom flask_login import current_user, login_required\nfrom flask_restful import Resource, abort\nfrom redash import settings\nfrom redash.authentication import current_org\nfrom redash.models import ApiUser\nfrom redash.tasks import record_event as record_event_task\nfrom redash.utils import json_dumps\nfrom sqlalchemy.orm.exc import NoResultFound\n\nroutes = Blueprint('redash', __name__, template_folder=settings.fix_assets_path('templates'))\n\n\nclass BaseResource(Resource):\n    decorators = [login_required]\n\n    def __init__(self, *args, **kwargs):\n        super(BaseResource, self).__init__(*args, **kwargs)\n        self._user = None\n\n    def dispatch_request(self, *args, **kwargs):\n        kwargs.pop('org_slug', None)\n\n        return super(BaseResource, self).dispatch_request(*args, **kwargs)\n\n    @property\n    def current_user(self):\n        return current_user._get_current_object()\n\n    @property\n    def current_org(self):\n        return current_org._get_current_object()\n\n    def record_event(self, options):\n        record_event(self.current_org, self.current_user, options)\n\n    # TODO: this should probably be somewhere else\n    def update_model(self, model, updates):\n        for k, v in updates.items():\n            setattr(model, k, v)\n\n\ndef record_event(org, user, options):\n    if isinstance(user, ApiUser):\n        options.update({\n            'api_key': user.name,\n            'org_id': org.id\n        })\n    else:\n        options.update({\n            'user_id': user.id,\n            'org_id': org.id\n        })\n\n    options.update({\n        'user_agent': request.user_agent.string,\n        'ip': request.remote_addr\n    })\n\n    if 'timestamp' not in options:\n        options['timestamp'] = int(time.time())\n\n    record_event_task.delay(options)\n\n\ndef require_fields(req, fields):\n    for f in fields:\n        if f not in req:\n            abort(400)\n\n\ndef get_object_or_404(fn, *args, **kwargs):\n    try:\n        rv = fn(*args, **kwargs)\n        if rv is None:\n            abort(404)\n    except NoResultFound:\n        abort(404)\n    return rv\n\n\ndef paginate(query_set, page, page_size, serializer):\n    count = query_set.count()\n\n    if page < 1:\n        abort(400, message='Page must be positive integer.')\n\n    if (page-1)*page_size+1 > count > 0:\n        abort(400, message='Page is out of range.')\n\n    if page_size > 250 or page_size < 1:\n        abort(400, message='Page size is out of range (1-250).')\n\n    results = query_set.paginate(page, page_size)\n\n    return {\n        'count': count,\n        'page': page,\n        'page_size': page_size,\n        'results': [serializer(result) for result in results.items],\n    }\n\n\ndef org_scoped_rule(rule):\n    if settings.MULTI_ORG:\n        return \"/<org_slug:org_slug>{}\".format(rule)\n\n    return rule\n\n\ndef json_response(response):\n    return current_app.response_class(json_dumps(response), mimetype='application/json')\n", "path": "redash/handlers/base.py"}], "after_files": [{"content": "import time\n\nfrom flask import Blueprint, current_app, request\n\nfrom flask_login import current_user, login_required\nfrom flask_restful import Resource, abort\nfrom redash import settings\nfrom redash.authentication import current_org\nfrom redash.models import ApiUser\nfrom redash.tasks import record_event as record_event_task\nfrom redash.utils import json_dumps\nfrom sqlalchemy.orm.exc import NoResultFound\n\nroutes = Blueprint('redash', __name__, template_folder=settings.fix_assets_path('templates'))\n\n\nclass BaseResource(Resource):\n    decorators = [login_required]\n\n    def __init__(self, *args, **kwargs):\n        super(BaseResource, self).__init__(*args, **kwargs)\n        self._user = None\n\n    def dispatch_request(self, *args, **kwargs):\n        kwargs.pop('org_slug', None)\n\n        return super(BaseResource, self).dispatch_request(*args, **kwargs)\n\n    @property\n    def current_user(self):\n        return current_user._get_current_object()\n\n    @property\n    def current_org(self):\n        return current_org._get_current_object()\n\n    def record_event(self, options):\n        record_event(self.current_org, self.current_user, options)\n\n    # TODO: this should probably be somewhere else\n    def update_model(self, model, updates):\n        for k, v in updates.items():\n            setattr(model, k, v)\n\n\ndef record_event(org, user, options):\n    if user.is_api_user():\n        options.update({\n            'api_key': user.name,\n            'org_id': org.id\n        })\n    else:\n        options.update({\n            'user_id': user.id,\n            'user_name': user.name,\n            'org_id': org.id\n        })\n\n    options.update({\n        'user_agent': request.user_agent.string,\n        'ip': request.remote_addr\n    })\n\n    if 'timestamp' not in options:\n        options['timestamp'] = int(time.time())\n\n    record_event_task.delay(options)\n\n\ndef require_fields(req, fields):\n    for f in fields:\n        if f not in req:\n            abort(400)\n\n\ndef get_object_or_404(fn, *args, **kwargs):\n    try:\n        rv = fn(*args, **kwargs)\n        if rv is None:\n            abort(404)\n    except NoResultFound:\n        abort(404)\n    return rv\n\n\ndef paginate(query_set, page, page_size, serializer):\n    count = query_set.count()\n\n    if page < 1:\n        abort(400, message='Page must be positive integer.')\n\n    if (page - 1) * page_size + 1 > count > 0:\n        abort(400, message='Page is out of range.')\n\n    if page_size > 250 or page_size < 1:\n        abort(400, message='Page size is out of range (1-250).')\n\n    results = query_set.paginate(page, page_size)\n\n    return {\n        'count': count,\n        'page': page,\n        'page_size': page_size,\n        'results': [serializer(result) for result in results.items],\n    }\n\n\ndef org_scoped_rule(rule):\n    if settings.MULTI_ORG:\n        return \"/<org_slug:org_slug>{}\".format(rule)\n\n    return rule\n\n\ndef json_response(response):\n    return current_app.response_class(json_dumps(response), mimetype='application/json')\n", "path": "redash/handlers/base.py"}]}