diff options
author | Alexandru DAMIAN <alexandru.damian@intel.com> | 2015-06-02 12:23:12 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2015-06-12 00:01:47 +0100 |
commit | 751e9182ac7f37506c3d85cade00ef6f3986eb7a (patch) | |
tree | f668ca76c534a09b5e3fc43b3bb7b67eb0c7416b /bitbake/lib | |
parent | afe06e313eac61f598f65e622ceb5951a9fabc9a (diff) | |
download | poky-751e9182ac7f37506c3d85cade00ef6f3986eb7a.tar.gz |
bitbake: convert all project-based view to JSON APIs
This patch converts all-project views that are REST-style URLs
to support JSON encoding if the "format=json" parameter is supplied.
The formatting code is enhanced to prevent following Django foreign
keys, to convert enum-values from int to string, and support for
timedelta.
(Bitbake rev: 7a6eb36b82c5f2e342777e479d9c401ded42453d)
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib')
-rwxr-xr-x | bitbake/lib/toaster/toastergui/views.py | 164 |
1 files changed, 99 insertions, 65 deletions
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index d4a9b4ca0d..f2626f8521 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py | |||
@@ -87,6 +87,55 @@ def _project_recent_build_list(prj): | |||
87 | list(prj.buildrequest_set.filter(state__in=[BuildRequest.REQ_COMPLETED, BuildRequest.REQ_FAILED]).order_by("-pk")[:3])) | 87 | list(prj.buildrequest_set.filter(state__in=[BuildRequest.REQ_COMPLETED, BuildRequest.REQ_FAILED]).order_by("-pk")[:3])) |
88 | 88 | ||
89 | 89 | ||
90 | def _template_renderer(template): | ||
91 | def func_wrapper(view): | ||
92 | def returned_wrapper(request, *args, **kwargs): | ||
93 | try: | ||
94 | context = view(request, *args, **kwargs) | ||
95 | except RedirectException as e: | ||
96 | return e.get_redirect_response() | ||
97 | |||
98 | if request.GET.get('format', None) == 'json': | ||
99 | # objects is a special keyword - it's a Page, but we need the actual objects here | ||
100 | # in XHR, the objects come in the "list" property | ||
101 | if "objects" in context: | ||
102 | context["list"] = context["objects"].object_list | ||
103 | del context["objects"] | ||
104 | |||
105 | # we're about to return; to keep up with the XHR API, we set the error to OK | ||
106 | context["error"] = "ok" | ||
107 | def _objtojson(obj): | ||
108 | from django.db.models.query import QuerySet | ||
109 | from django.db.models import Model, IntegerField | ||
110 | if isinstance(obj, datetime): | ||
111 | return obj.isoformat() | ||
112 | elif isinstance(obj, timedelta): | ||
113 | return obj.total_seconds() | ||
114 | elif isinstance(obj, QuerySet) or isinstance(obj, set): | ||
115 | return list(obj) | ||
116 | elif hasattr( obj, '__dict__'): | ||
117 | d = obj.__dict__ | ||
118 | nd = dict(d) | ||
119 | for di in d: | ||
120 | if di.startswith("_"): | ||
121 | del nd[di] | ||
122 | elif isinstance(d[di], Model): | ||
123 | nd[di] = d[di].pk | ||
124 | elif isinstance(d[di], int) and hasattr(obj, "get_%s_display" % di): | ||
125 | nd[di] = getattr(obj, "get_%s_display" % di)() | ||
126 | return nd | ||
127 | else: | ||
128 | raise TypeError("Unserializable object %s of type %s" % ( obj, type(obj))) | ||
129 | |||
130 | return HttpResponse(jsonfilter(context, default=_objtojson ), | ||
131 | content_type = "application/json; charset=utf-8") | ||
132 | else: | ||
133 | return render(request, template, context) | ||
134 | return returned_wrapper | ||
135 | return func_wrapper | ||
136 | |||
137 | |||
138 | |||
90 | def _build_page_range(paginator, index = 1): | 139 | def _build_page_range(paginator, index = 1): |
91 | try: | 140 | try: |
92 | page = paginator.page(index) | 141 | page = paginator.page(index) |
@@ -129,7 +178,7 @@ def _redirect_parameters(view, g, mandatory_parameters, *args, **kwargs): | |||
129 | if not i in params: | 178 | if not i in params: |
130 | params[i] = urllib.unquote(str(mandatory_parameters[i])) | 179 | params[i] = urllib.unquote(str(mandatory_parameters[i])) |
131 | 180 | ||
132 | return redirect(url + "?%s" % urllib.urlencode(params), permanent = False, *args, **kwargs) | 181 | return redirect(url + "?%s" % urllib.urlencode(params), permanent = False, **kwargs) |
133 | 182 | ||
134 | class RedirectException(Exception): | 183 | class RedirectException(Exception): |
135 | def __init__(self, view, g, mandatory_parameters, *args, **kwargs): | 184 | def __init__(self, view, g, mandatory_parameters, *args, **kwargs): |
@@ -141,7 +190,7 @@ class RedirectException(Exception): | |||
141 | self.okwargs = kwargs | 190 | self.okwargs = kwargs |
142 | 191 | ||
143 | def get_redirect_response(self): | 192 | def get_redirect_response(self): |
144 | return _redirect_parameters(self.view, self.g, self.mandatory_parameters, self.oargs, self.okwargs) | 193 | return _redirect_parameters(self.view, self.g, self.mandatory_parameters, self.oargs, **self.okwargs) |
145 | 194 | ||
146 | FIELD_SEPARATOR = ":" | 195 | FIELD_SEPARATOR = ":" |
147 | AND_VALUE_SEPARATOR = "!" | 196 | AND_VALUE_SEPARATOR = "!" |
@@ -1839,8 +1888,8 @@ if toastermain.settings.MANAGED: | |||
1839 | 1888 | ||
1840 | 1889 | ||
1841 | # shows the "all builds" page for managed mode; it displays build requests (at least started!) instead of actual builds | 1890 | # shows the "all builds" page for managed mode; it displays build requests (at least started!) instead of actual builds |
1891 | @_template_renderer("managed_builds.html") | ||
1842 | def builds(request): | 1892 | def builds(request): |
1843 | template = 'managed_builds.html' | ||
1844 | # define here what parameters the view needs in the GET portion in order to | 1893 | # define here what parameters the view needs in the GET portion in order to |
1845 | # be able to display something. 'count' and 'page' are mandatory for all views | 1894 | # be able to display something. 'count' and 'page' are mandatory for all views |
1846 | # that use paginators. | 1895 | # that use paginators. |
@@ -1850,12 +1899,10 @@ if toastermain.settings.MANAGED: | |||
1850 | try: | 1899 | try: |
1851 | context, pagesize, orderby = _build_list_helper(request, buildrequests, True) | 1900 | context, pagesize, orderby = _build_list_helper(request, buildrequests, True) |
1852 | except InvalidRequestException as e: | 1901 | except InvalidRequestException as e: |
1853 | return _redirect_parameters( builds, request.GET, e.response) | 1902 | raise RedirectException( builds, request.GET, e.response) |
1854 | 1903 | ||
1855 | response = render(request, template, context) | ||
1856 | _set_parameters_values(pagesize, orderby, request) | 1904 | _set_parameters_values(pagesize, orderby, request) |
1857 | return response | 1905 | return context |
1858 | |||
1859 | 1906 | ||
1860 | 1907 | ||
1861 | # helper function, to be used on "all builds" and "project builds" pages | 1908 | # helper function, to be used on "all builds" and "project builds" pages |
@@ -2151,8 +2198,8 @@ if toastermain.settings.MANAGED: | |||
2151 | 2198 | ||
2152 | 2199 | ||
2153 | # Shows the edit project page | 2200 | # Shows the edit project page |
2201 | @_template_renderer('project.html') | ||
2154 | def project(request, pid): | 2202 | def project(request, pid): |
2155 | template = "project.html" | ||
2156 | try: | 2203 | try: |
2157 | prj = Project.objects.get(id = pid) | 2204 | prj = Project.objects.get(id = pid) |
2158 | except Project.DoesNotExist: | 2205 | except Project.DoesNotExist: |
@@ -2207,10 +2254,7 @@ if toastermain.settings.MANAGED: | |||
2207 | except ProjectVariable.DoesNotExist: | 2254 | except ProjectVariable.DoesNotExist: |
2208 | context["distro"] = "-- not set yet" | 2255 | context["distro"] = "-- not set yet" |
2209 | 2256 | ||
2210 | response = render(request, template, context) | 2257 | return context |
2211 | response['Cache-Control'] = "no-cache, must-revalidate, no-store" | ||
2212 | response['Pragma'] = "no-cache" | ||
2213 | return response | ||
2214 | 2258 | ||
2215 | 2259 | ||
2216 | def xhr_projectbuild(request, pid): | 2260 | def xhr_projectbuild(request, pid): |
@@ -2680,8 +2724,8 @@ if toastermain.settings.MANAGED: | |||
2680 | 2724 | ||
2681 | return(vars_managed,sorted(vars_fstypes),vars_blacklist) | 2725 | return(vars_managed,sorted(vars_fstypes),vars_blacklist) |
2682 | 2726 | ||
2727 | @_template_renderer("projectconf.html") | ||
2683 | def projectconf(request, pid): | 2728 | def projectconf(request, pid): |
2684 | template = "projectconf.html" | ||
2685 | 2729 | ||
2686 | try: | 2730 | try: |
2687 | prj = Project.objects.get(id = pid) | 2731 | prj = Project.objects.get(id = pid) |
@@ -2730,21 +2774,20 @@ if toastermain.settings.MANAGED: | |||
2730 | except ProjectVariable.DoesNotExist: | 2774 | except ProjectVariable.DoesNotExist: |
2731 | pass | 2775 | pass |
2732 | 2776 | ||
2733 | return render(request, template, context) | 2777 | return context |
2734 | 2778 | ||
2779 | @_template_renderer('projectbuilds.html') | ||
2735 | def projectbuilds(request, pid): | 2780 | def projectbuilds(request, pid): |
2736 | template = 'projectbuilds.html' | ||
2737 | buildrequests = BuildRequest.objects.filter(project_id = pid).exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED) | 2781 | buildrequests = BuildRequest.objects.filter(project_id = pid).exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED) |
2738 | 2782 | ||
2739 | try: | 2783 | try: |
2740 | context, pagesize, orderby = _build_list_helper(request, buildrequests, False) | 2784 | context, pagesize, orderby = _build_list_helper(request, buildrequests, False) |
2741 | except InvalidRequestException as e: | 2785 | except InvalidRequestException as e: |
2742 | return _redirect_parameters(projectbuilds, request.GET, e.response, pid = pid) | 2786 | raise RedirectException('projectbuilds', request.GET, e.response, pid = pid) |
2743 | 2787 | ||
2744 | response = render(request, template, context) | ||
2745 | _set_parameters_values(pagesize, orderby, request) | 2788 | _set_parameters_values(pagesize, orderby, request) |
2746 | 2789 | ||
2747 | return response | 2790 | return context |
2748 | 2791 | ||
2749 | 2792 | ||
2750 | def _file_name_for_artifact(b, artifact_type, artifact_id): | 2793 | def _file_name_for_artifact(b, artifact_type, artifact_id): |
@@ -2861,32 +2904,8 @@ if toastermain.settings.MANAGED: | |||
2861 | return build_mru | 2904 | return build_mru |
2862 | 2905 | ||
2863 | 2906 | ||
2864 | def template_renderer(template): | ||
2865 | def func_wrapper(view): | ||
2866 | def returned_wrapper(request, *args, **kwargs): | ||
2867 | try: | ||
2868 | context = view(request, *args, **kwargs) | ||
2869 | except RedirectException as e: | ||
2870 | return e.get_redirect_response() | ||
2871 | |||
2872 | if request.GET.get('format', None) == 'json': | ||
2873 | # objects is a special keyword - it's a Page, but we need the actual objects here | ||
2874 | # in XHR, the objects come in the "list" property | ||
2875 | if "objects" in context: | ||
2876 | context["list"] = context["objects"].object_list | ||
2877 | del context["objects"] | ||
2878 | |||
2879 | # we're about to return; to keep up with the XHR API, we set the error to OK | ||
2880 | context["error"] = "ok" | ||
2881 | |||
2882 | return HttpResponse(jsonfilter(context, default=lambda obj: obj.isoformat() if isinstance(obj, datetime) else obj.__dict__ ), | ||
2883 | content_type = "application/json; charset=utf-8") | ||
2884 | else: | ||
2885 | return render(request, template, context) | ||
2886 | return returned_wrapper | ||
2887 | return func_wrapper | ||
2888 | 2907 | ||
2889 | @template_renderer("projects.html") | 2908 | @_template_renderer("projects.html") |
2890 | def projects(request): | 2909 | def projects(request): |
2891 | (pagesize, orderby) = _get_parameters_values(request, 10, 'updated:-') | 2910 | (pagesize, orderby) = _get_parameters_values(request, 10, 'updated:-') |
2892 | mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } | 2911 | mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } |
@@ -2995,18 +3014,19 @@ if toastermain.settings.MANAGED: | |||
2995 | _set_parameters_values(pagesize, orderby, request) | 3014 | _set_parameters_values(pagesize, orderby, request) |
2996 | return context | 3015 | return context |
2997 | 3016 | ||
3017 | @_template_renderer("buildrequestdetails.html") | ||
2998 | def buildrequestdetails(request, pid, brid): | 3018 | def buildrequestdetails(request, pid, brid): |
2999 | template = "buildrequestdetails.html" | ||
3000 | context = { | 3019 | context = { |
3001 | 'buildrequest' : BuildRequest.objects.get(pk = brid, project_id = pid) | 3020 | 'buildrequest' : BuildRequest.objects.get(pk = brid, project_id = pid) |
3002 | } | 3021 | } |
3003 | return render(request, template, context) | 3022 | return context |
3004 | 3023 | ||
3005 | 3024 | ||
3006 | else: | 3025 | else: |
3007 | # shows the "all builds" page for interactive mode; this is the old code, simply moved | 3026 | # shows the "all builds" page for interactive mode; this is the old code, simply moved |
3027 | |||
3028 | @_template_renderer('build.html') | ||
3008 | def builds(request): | 3029 | def builds(request): |
3009 | template = 'build.html' | ||
3010 | # define here what parameters the view needs in the GET portion in order to | 3030 | # define here what parameters the view needs in the GET portion in order to |
3011 | # be able to display something. 'count' and 'page' are mandatory for all views | 3031 | # be able to display something. 'count' and 'page' are mandatory for all views |
3012 | # that use paginators. | 3032 | # that use paginators. |
@@ -3014,7 +3034,7 @@ else: | |||
3014 | mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } | 3034 | mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } |
3015 | retval = _verify_parameters( request.GET, mandatory_parameters ) | 3035 | retval = _verify_parameters( request.GET, mandatory_parameters ) |
3016 | if retval: | 3036 | if retval: |
3017 | return _redirect_parameters( 'all-builds', request.GET, mandatory_parameters) | 3037 | raise RedirectException( 'all-builds', request.GET, mandatory_parameters) |
3018 | 3038 | ||
3019 | # boilerplate code that takes a request for an object type and returns a queryset | 3039 | # boilerplate code that takes a request for an object type and returns a queryset |
3020 | # for that object type. copypasta for all needed table searches | 3040 | # for that object type. copypasta for all needed table searches |
@@ -3194,55 +3214,69 @@ else: | |||
3194 | 3214 | ||
3195 | # merge daterange values | 3215 | # merge daterange values |
3196 | context.update(context_date) | 3216 | context.update(context_date) |
3197 | |||
3198 | response = render(request, template, context) | ||
3199 | _set_parameters_values(pagesize, orderby, request) | 3217 | _set_parameters_values(pagesize, orderby, request) |
3200 | return response | 3218 | |
3219 | return context | ||
3201 | 3220 | ||
3202 | 3221 | ||
3203 | 3222 | ||
3204 | 3223 | ||
3224 | @_template_renderer('landing_not_managed.html') | ||
3205 | def newproject(request): | 3225 | def newproject(request): |
3206 | return render(request, 'landing_not_managed.html') | 3226 | return {} |
3207 | 3227 | ||
3228 | @_template_renderer('landing_not_managed.html') | ||
3208 | def project(request, pid): | 3229 | def project(request, pid): |
3209 | return render(request, 'landing_not_managed.html') | 3230 | return {} |
3210 | 3231 | ||
3232 | @_template_renderer('landing_not_managed.html') | ||
3211 | def xhr_projectbuild(request, pid): | 3233 | def xhr_projectbuild(request, pid): |
3212 | return render(request, 'landing_not_managed.html') | 3234 | return {} |
3213 | 3235 | ||
3236 | @_template_renderer('landing_not_managed.html') | ||
3214 | def xhr_projectinfo(request): | 3237 | def xhr_projectinfo(request): |
3215 | return render(request, 'landing_not_managed.html') | 3238 | return {} |
3216 | 3239 | ||
3240 | @_template_renderer('landing_not_managed.html') | ||
3217 | def xhr_projectedit(request, pid): | 3241 | def xhr_projectedit(request, pid): |
3218 | return render(request, 'landing_not_managed.html') | 3242 | return {} |
3219 | 3243 | ||
3244 | @_template_renderer('landing_not_managed.html') | ||
3220 | def xhr_datatypeahead(request): | 3245 | def xhr_datatypeahead(request): |
3221 | return render(request, 'landing_not_managed.html') | 3246 | return {} |
3222 | 3247 | ||
3248 | @_template_renderer('landing_not_managed.html') | ||
3223 | def xhr_configvaredit(request, pid): | 3249 | def xhr_configvaredit(request, pid): |
3224 | return render(request, 'landing_not_managed.html') | 3250 | return {} |
3225 | 3251 | ||
3252 | @_template_renderer('landing_not_managed.html') | ||
3226 | def importlayer(request): | 3253 | def importlayer(request): |
3227 | return render(request, 'landing_not_managed.html') | 3254 | return {} |
3228 | 3255 | ||
3256 | @_template_renderer('landing_not_managed.html') | ||
3229 | def projectconf(request, pid): | 3257 | def projectconf(request, pid): |
3230 | return render(request, 'landing_not_managed.html') | 3258 | return {} |
3231 | 3259 | ||
3260 | @_template_renderer('landing_not_managed.html') | ||
3232 | def projectbuilds(request, pid): | 3261 | def projectbuilds(request, pid): |
3233 | return render(request, 'landing_not_managed.html') | 3262 | return {} |
3234 | 3263 | ||
3264 | @_template_renderer('landing_not_managed.html') | ||
3235 | def build_artifact(request, build_id, artifact_type, artifact_id): | 3265 | def build_artifact(request, build_id, artifact_type, artifact_id): |
3236 | return render(request, 'landing_not_managed.html') | 3266 | return {} |
3237 | 3267 | ||
3268 | @_template_renderer('landing_not_managed.html') | ||
3238 | def projects(request): | 3269 | def projects(request): |
3239 | return render(request, 'landing_not_managed.html') | 3270 | return {} |
3240 | 3271 | ||
3272 | @_template_renderer('landing_not_managed.html') | ||
3241 | def xhr_importlayer(request): | 3273 | def xhr_importlayer(request): |
3242 | return render(request, 'landing_not_managed.html') | 3274 | return {} |
3243 | 3275 | ||
3276 | @_template_renderer('landing_not_managed.html') | ||
3244 | def xhr_updatelayer(request): | 3277 | def xhr_updatelayer(request): |
3245 | return render(request, 'landing_not_managed.html') | 3278 | return {} |
3246 | 3279 | ||
3280 | @_template_renderer('landing_not_managed.html') | ||
3247 | def buildrequestdetails(request, pid, brid): | 3281 | def buildrequestdetails(request, pid, brid): |
3248 | return render(request, 'landing_not_managed.html') | 3282 | return {} |