redash/serializers/__init__.py (282 lines of code) (raw):

""" This will eventually replace all the `to_dict` methods of the different model classes we have. This will ensure cleaner code and better separation of concerns. """ from funcy import project from flask_login import current_user from rq.job import JobStatus from rq.timeouts import JobTimeoutException from redash import models from redash.permissions import has_access, view_only from redash.utils import json_loads from redash.models.parameterized_query import ParameterizedQuery from .query_result import ( serialize_query_result, serialize_query_result_to_dsv, serialize_query_result_to_xlsx, ) def public_widget(widget): res = { "id": widget.id, "width": widget.width, "options": json_loads(widget.options), "text": widget.text, "updated_at": widget.updated_at, "created_at": widget.created_at, } v = widget.visualization if v and v.id: res["visualization"] = { "type": v.type, "name": v.name, "description": v.description, "options": json_loads(v.options), "updated_at": v.updated_at, "created_at": v.created_at, "query": { "id": v.query_rel.id, "name": v.query_rel.name, "description": v.query_rel.description, "options": v.query_rel.options, }, } return res def public_dashboard(dashboard): dashboard_dict = project( serialize_dashboard(dashboard, with_favorite_state=False), ("name", "layout", "dashboard_filters_enabled", "updated_at", "created_at", "options"), ) widget_list = ( models.Widget.query.filter(models.Widget.dashboard_id == dashboard.id) .outerjoin(models.Visualization) .outerjoin(models.Query) ) dashboard_dict["widgets"] = [public_widget(w) for w in widget_list] return dashboard_dict class Serializer(object): pass class QuerySerializer(Serializer): def __init__(self, object_or_list, **kwargs): self.object_or_list = object_or_list self.options = kwargs def serialize(self): if isinstance(self.object_or_list, models.Query): result = serialize_query(self.object_or_list, **self.options) if ( self.options.get("with_favorite_state", True) and not current_user.is_api_user() ): result["is_favorite"] = models.Favorite.is_favorite( current_user.id, self.object_or_list ) else: result = [ serialize_query(query, **self.options) for query in self.object_or_list ] if self.options.get("with_favorite_state", True): favorite_ids = models.Favorite.are_favorites( current_user.id, self.object_or_list ) for query in result: query["is_favorite"] = query["id"] in favorite_ids return result def serialize_query( query, with_stats=False, with_visualizations=False, with_user=True, with_last_modified_by=True, ): d = { "id": query.id, "latest_query_data_id": query.latest_query_data_id, "name": query.name, "description": query.description, "query": query.query_text, "query_hash": query.query_hash, "schedule": query.schedule, "api_key": query.api_key, "is_archived": query.is_archived, "is_draft": query.is_draft, "updated_at": query.updated_at, "created_at": query.created_at, "data_source_id": query.data_source_id, "options": query.options, "version": query.version, "tags": query.tags or [], "is_safe": query.parameterized.is_safe, } if with_user: d["user"] = query.user.to_dict() else: d["user_id"] = query.user_id if with_last_modified_by: d["last_modified_by"] = ( query.last_modified_by.to_dict() if query.last_modified_by is not None else None ) else: d["last_modified_by_id"] = query.last_modified_by_id if with_stats: if query.latest_query_data is not None: d["retrieved_at"] = query.retrieved_at d["runtime"] = query.runtime else: d["retrieved_at"] = None d["runtime"] = None if with_visualizations: d["visualizations"] = [ serialize_visualization(vis, with_query=False) for vis in query.visualizations ] return d def serialize_visualization(object, with_query=True): d = { "id": object.id, "type": object.type, "name": object.name, "description": object.description, "options": json_loads(object.options), "updated_at": object.updated_at, "created_at": object.created_at, } if with_query: d["query"] = serialize_query(object.query_rel) return d def serialize_widget(object): d = { "id": object.id, "width": object.width, "options": json_loads(object.options), "dashboard_id": object.dashboard_id, "text": object.text, "updated_at": object.updated_at, "created_at": object.created_at, } if object.visualization and object.visualization.id: d["visualization"] = serialize_visualization(object.visualization) return d def serialize_alert(alert, full=True): d = { "id": alert.id, "name": alert.name, "options": alert.options, "state": alert.state, "last_triggered_at": alert.last_triggered_at, "updated_at": alert.updated_at, "created_at": alert.created_at, "rearm": alert.rearm, } if full: d["query"] = serialize_query(alert.query_rel) d["user"] = alert.user.to_dict() else: d["query_id"] = alert.query_id d["user_id"] = alert.user_id return d def serialize_dashboard(obj, with_widgets=False, user=None, with_favorite_state=True): layout = json_loads(obj.layout) widgets = [] if with_widgets: for w in obj.widgets: if w.visualization_id is None: widgets.append(serialize_widget(w)) elif user and has_access(w.visualization.query_rel, user, view_only): widgets.append(serialize_widget(w)) else: widget = project( serialize_widget(w), ( "id", "width", "dashboard_id", "options", "created_at", "updated_at", ), ) widget["restricted"] = True widgets.append(widget) else: widgets = None d = { "id": obj.id, "slug": obj.name_as_slug, "name": obj.name, "user_id": obj.user_id, "user": { "id": obj.user.id, "name": obj.user.name, "email": obj.user.email, "profile_image_url": obj.user.profile_image_url, }, "layout": layout, "dashboard_filters_enabled": obj.dashboard_filters_enabled, "widgets": widgets, "options": obj.options, "is_archived": obj.is_archived, "is_draft": obj.is_draft, "tags": obj.tags or [], "updated_at": obj.updated_at, "created_at": obj.created_at, "version": obj.version, } return d class DashboardSerializer(Serializer): def __init__(self, object_or_list, **kwargs): self.object_or_list = object_or_list self.options = kwargs def serialize(self): if isinstance(self.object_or_list, models.Dashboard): result = serialize_dashboard(self.object_or_list, **self.options) if ( self.options.get("with_favorite_state", True) and not current_user.is_api_user() ): result["is_favorite"] = models.Favorite.is_favorite( current_user.id, self.object_or_list ) else: result = [ serialize_dashboard(obj, **self.options) for obj in self.object_or_list ] if self.options.get("with_favorite_state", True): favorite_ids = models.Favorite.are_favorites( current_user.id, self.object_or_list ) for obj in result: obj["is_favorite"] = obj["id"] in favorite_ids return result def serialize_job(job): # TODO: this is mapping to the old Job class statuses. Need to update the client side and remove this STATUSES = { JobStatus.QUEUED: 1, JobStatus.STARTED: 2, JobStatus.FINISHED: 3, JobStatus.FAILED: 4, } job_status = job.get_status() if job.is_started: updated_at = job.started_at or 0 else: updated_at = 0 status = STATUSES[job_status] result = query_result_id = None if job.is_cancelled: error = "Query cancelled by user." status = 4 elif isinstance(job.result, Exception): error = str(job.result) status = 4 elif isinstance(job.result, dict) and "error" in job.result: error = job.result["error"] status = 4 else: error = "" result = query_result_id = job.result return { "job": { "id": job.id, "updated_at": updated_at, "status": status, "error": error, "result": result, "query_result_id": query_result_id, } }