diff options
author | Alexandru DAMIAN <alexandru.damian@intel.com> | 2015-05-19 16:14:29 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2015-06-12 00:01:47 +0100 |
commit | afe06e313eac61f598f65e622ceb5951a9fabc9a (patch) | |
tree | 02e37805b7b10a30c77fc3c82987bf435b221423 /bitbake/lib/toaster/toastergui/views.py | |
parent | 94aa0d5408f71a2e9fd48fd2c0e283acfc9ac2a2 (diff) | |
download | poky-afe06e313eac61f598f65e622ceb5951a9fabc9a.tar.gz |
bitbake: toaster: move project data typeahead to the REST API
This patch enables JSON requests on the project REST endpoint,
and replaces the universal queries "xhr_datatypeahead" with the
`project` type to the REST project endpoint.
The patch adds a decorator that takes a context returned by a view
and either renders the template specified as the decorator argument,
or converts the context to JSON.
Normal "search", "filter" and "order" options for view work as normal
on the JSON API format. To enable the JSON return, set the "format"
GET parameter to "json".
In order to demonstrate the functionality, the "New build" button
is switched from using the xhr_datatypeahead to the project
REST API with JSON formatting. Additionally, the XHR APIs that
perform actions with the project id passed as parameter are removed,
and the needed URLs are populated from the project JSON API returns after
the project has been selected.
(Bitbake rev: 15a2274eba13d19b864f337057d61c75ff7849cc)
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/toaster/toastergui/views.py')
-rwxr-xr-x | bitbake/lib/toaster/toastergui/views.py | 89 |
1 files changed, 56 insertions, 33 deletions
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py index 0e248a7435..d4a9b4ca0d 100755 --- a/bitbake/lib/toaster/toastergui/views.py +++ b/bitbake/lib/toaster/toastergui/views.py | |||
@@ -129,7 +129,19 @@ def _redirect_parameters(view, g, mandatory_parameters, *args, **kwargs): | |||
129 | if not i in params: | 129 | if not i in params: |
130 | params[i] = urllib.unquote(str(mandatory_parameters[i])) | 130 | params[i] = urllib.unquote(str(mandatory_parameters[i])) |
131 | 131 | ||
132 | return redirect(url + "?%s" % urllib.urlencode(params), *args, **kwargs) | 132 | return redirect(url + "?%s" % urllib.urlencode(params), permanent = False, *args, **kwargs) |
133 | |||
134 | class RedirectException(Exception): | ||
135 | def __init__(self, view, g, mandatory_parameters, *args, **kwargs): | ||
136 | super(RedirectException, self).__init__() | ||
137 | self.view = view | ||
138 | self.g = g | ||
139 | self.mandatory_parameters = mandatory_parameters | ||
140 | self.oargs = args | ||
141 | self.okwargs = kwargs | ||
142 | |||
143 | def get_redirect_response(self): | ||
144 | return _redirect_parameters(self.view, self.g, self.mandatory_parameters, self.oargs, self.okwargs) | ||
133 | 145 | ||
134 | FIELD_SEPARATOR = ":" | 146 | FIELD_SEPARATOR = ":" |
135 | AND_VALUE_SEPARATOR = "!" | 147 | AND_VALUE_SEPARATOR = "!" |
@@ -2200,14 +2212,6 @@ if toastermain.settings.MANAGED: | |||
2200 | response['Pragma'] = "no-cache" | 2212 | response['Pragma'] = "no-cache" |
2201 | return response | 2213 | return response |
2202 | 2214 | ||
2203 | # This is a wrapper for xhr_projectbuild which allows for a project id | ||
2204 | # which only becomes known client side. | ||
2205 | def xhr_build(request): | ||
2206 | if request.POST.has_key("project_id"): | ||
2207 | pid = request.POST['project_id'] | ||
2208 | return xhr_projectbuild(request, pid) | ||
2209 | else: | ||
2210 | raise BadParameterException("invalid project id") | ||
2211 | 2215 | ||
2212 | def xhr_projectbuild(request, pid): | 2216 | def xhr_projectbuild(request, pid): |
2213 | try: | 2217 | try: |
@@ -2333,7 +2337,7 @@ if toastermain.settings.MANAGED: | |||
2333 | # returns layers for current project release that are not in the project set, matching the name | 2337 | # returns layers for current project release that are not in the project set, matching the name |
2334 | if request.GET['type'] == "layers": | 2338 | if request.GET['type'] == "layers": |
2335 | # all layers for the current project | 2339 | # all layers for the current project |
2336 | queryset_all = prj.compatible_layerversions().filter(layer__name__icontains=request.GET.get('value','')) | 2340 | queryset_all = prj.compatible_layerversions().filter(layer__name__icontains=request.GET.get('search','')) |
2337 | 2341 | ||
2338 | # but not layers with equivalent layers already in project | 2342 | # but not layers with equivalent layers already in project |
2339 | if not request.GET.has_key('include_added'): | 2343 | if not request.GET.has_key('include_added'): |
@@ -2348,7 +2352,7 @@ if toastermain.settings.MANAGED: | |||
2348 | # returns layer dependencies for a layer, excluding current project layers | 2352 | # returns layer dependencies for a layer, excluding current project layers |
2349 | if request.GET['type'] == "layerdeps": | 2353 | if request.GET['type'] == "layerdeps": |
2350 | queryset = prj.compatible_layerversions().exclude(pk__in = [x.id for x in prj.projectlayer_equivalent_set()]).filter( | 2354 | queryset = prj.compatible_layerversions().exclude(pk__in = [x.id for x in prj.projectlayer_equivalent_set()]).filter( |
2351 | layer__name__in = [ x.depends_on.layer.name for x in LayerVersionDependency.objects.filter(layer_version_id = request.GET['value'])]) | 2355 | layer__name__in = [ x.depends_on.layer.name for x in LayerVersionDependency.objects.filter(layer_version_id = request.GET['search'])]) |
2352 | 2356 | ||
2353 | final_list = set([x.get_equivalents_wpriority(prj)[0] for x in queryset]) | 2357 | final_list = set([x.get_equivalents_wpriority(prj)[0] for x in queryset]) |
2354 | 2358 | ||
@@ -2361,7 +2365,7 @@ if toastermain.settings.MANAGED: | |||
2361 | 2365 | ||
2362 | retval = [] | 2366 | retval = [] |
2363 | for i in prj.projectlayer_set.all(): | 2367 | for i in prj.projectlayer_set.all(): |
2364 | lv = prj.compatible_layerversions(release = Release.objects.get(pk=request.GET['value'])).filter(layer__name = i.layercommit.layer.name) | 2368 | lv = prj.compatible_layerversions(release = Release.objects.get(pk=request.GET['search'])).filter(layer__name = i.layercommit.layer.name) |
2365 | # there is no layer_version with the new release id, and the same name | 2369 | # there is no layer_version with the new release id, and the same name |
2366 | if lv.count() < 1: | 2370 | if lv.count() < 1: |
2367 | retval.append(i) | 2371 | retval.append(i) |
@@ -2374,10 +2378,10 @@ if toastermain.settings.MANAGED: | |||
2374 | # returns layer versions that provide the named targets | 2378 | # returns layer versions that provide the named targets |
2375 | if request.GET['type'] == "layers4target": | 2379 | if request.GET['type'] == "layers4target": |
2376 | # we return data only if the recipe can't be provided by the current project layer set | 2380 | # we return data only if the recipe can't be provided by the current project layer set |
2377 | if reduce(lambda x, y: x + y, [x.recipe_layer_version.filter(name=request.GET['value']).count() for x in prj.projectlayer_equivalent_set()], 0): | 2381 | if reduce(lambda x, y: x + y, [x.recipe_layer_version.filter(name=request.GET['search']).count() for x in prj.projectlayer_equivalent_set()], 0): |
2378 | final_list = [] | 2382 | final_list = [] |
2379 | else: | 2383 | else: |
2380 | queryset_all = prj.compatible_layerversions().filter(recipe_layer_version__name = request.GET['value']) | 2384 | queryset_all = prj.compatible_layerversions().filter(recipe_layer_version__name = request.GET['search']) |
2381 | 2385 | ||
2382 | # exclude layers in the project | 2386 | # exclude layers in the project |
2383 | queryset_all = queryset_all.exclude(pk__in = [x.id for x in prj.projectlayer_equivalent_set()]) | 2387 | queryset_all = queryset_all.exclude(pk__in = [x.id for x in prj.projectlayer_equivalent_set()]) |
@@ -2389,7 +2393,7 @@ if toastermain.settings.MANAGED: | |||
2389 | 2393 | ||
2390 | # returns targets provided by current project layers | 2394 | # returns targets provided by current project layers |
2391 | if request.GET['type'] == "targets": | 2395 | if request.GET['type'] == "targets": |
2392 | search_token = request.GET.get('value','') | 2396 | search_token = request.GET.get('search','') |
2393 | queryset_all = Recipe.objects.filter(layer_version__layer__name__in = [x.layercommit.layer.name for x in prj.projectlayer_set.all().select_related("layercommit__layer")]).filter(Q(name__icontains=search_token) | Q(layer_version__layer__name__icontains=search_token)) | 2397 | queryset_all = Recipe.objects.filter(layer_version__layer__name__in = [x.layercommit.layer.name for x in prj.projectlayer_set.all().select_related("layercommit__layer")]).filter(Q(name__icontains=search_token) | Q(layer_version__layer__name__icontains=search_token)) |
2394 | 2398 | ||
2395 | # layer_equivalent_set = [] | 2399 | # layer_equivalent_set = [] |
@@ -2420,7 +2424,7 @@ if toastermain.settings.MANAGED: | |||
2420 | if 'project_id' in request.session: | 2424 | if 'project_id' in request.session: |
2421 | queryset_all = queryset_all.filter(layer_version__in = prj.projectlayer_equivalent_set()).order_by("name") | 2425 | queryset_all = queryset_all.filter(layer_version__in = prj.projectlayer_equivalent_set()).order_by("name") |
2422 | 2426 | ||
2423 | search_token = request.GET.get('value','') | 2427 | search_token = request.GET.get('search','') |
2424 | queryset_all = queryset_all.filter(Q(name__icontains=search_token) | Q(description__icontains=search_token)) | 2428 | queryset_all = queryset_all.filter(Q(name__icontains=search_token) | Q(description__icontains=search_token)) |
2425 | 2429 | ||
2426 | return HttpResponse(jsonfilter({ "error":"ok", | 2430 | return HttpResponse(jsonfilter({ "error":"ok", |
@@ -2432,15 +2436,6 @@ if toastermain.settings.MANAGED: | |||
2432 | ) | 2436 | ) |
2433 | }), content_type = "application/json") | 2437 | }), content_type = "application/json") |
2434 | 2438 | ||
2435 | # returns all projects | ||
2436 | if request.GET['type'] == "projects": | ||
2437 | queryset_all = Project.objects.all() | ||
2438 | ret = { "error": "ok", | ||
2439 | "list": map (lambda x: {"id":x.pk, "name": x.name}, | ||
2440 | queryset_all.filter(name__icontains=request.GET.get('value',''))[:8])} | ||
2441 | |||
2442 | return HttpResponse(jsonfilter(ret), content_type = "application/json") | ||
2443 | |||
2444 | raise Exception("Unknown request! " + request.GET.get('type', "No parameter supplied")) | 2439 | raise Exception("Unknown request! " + request.GET.get('type', "No parameter supplied")) |
2445 | except Exception as e: | 2440 | except Exception as e: |
2446 | return HttpResponse(jsonfilter({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json") | 2441 | return HttpResponse(jsonfilter({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json") |
@@ -2866,14 +2861,38 @@ if toastermain.settings.MANAGED: | |||
2866 | return build_mru | 2861 | return build_mru |
2867 | 2862 | ||
2868 | 2863 | ||
2869 | def projects(request): | 2864 | def template_renderer(template): |
2870 | template="projects.html" | 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 | ||
2871 | 2888 | ||
2889 | @template_renderer("projects.html") | ||
2890 | def projects(request): | ||
2872 | (pagesize, orderby) = _get_parameters_values(request, 10, 'updated:-') | 2891 | (pagesize, orderby) = _get_parameters_values(request, 10, 'updated:-') |
2873 | mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } | 2892 | mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby } |
2874 | retval = _verify_parameters( request.GET, mandatory_parameters ) | 2893 | retval = _verify_parameters( request.GET, mandatory_parameters ) |
2875 | if retval: | 2894 | if retval: |
2876 | return _redirect_parameters( 'all-projects', request.GET, mandatory_parameters) | 2895 | raise RedirectException( 'all-projects', request.GET, mandatory_parameters ) |
2877 | 2896 | ||
2878 | queryset_all = Project.objects.all() | 2897 | queryset_all = Project.objects.all() |
2879 | 2898 | ||
@@ -2886,6 +2905,14 @@ if toastermain.settings.MANAGED: | |||
2886 | # retrieve the objects that will be displayed in the table; projects a paginator and gets a page range to display | 2905 | # retrieve the objects that will be displayed in the table; projects a paginator and gets a page range to display |
2887 | project_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1)) | 2906 | project_info = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1)) |
2888 | 2907 | ||
2908 | # add fields needed in JSON dumps for API call support | ||
2909 | for p in project_info.object_list: | ||
2910 | p.id = p.pk | ||
2911 | p.xhrProjectDataTypeaheadUrl = reverse('xhr_datatypeahead', args=(p.id,)) | ||
2912 | p.projectPageUrl = reverse('project', args=(p.id,)) | ||
2913 | p.xhrProjectEditUrl = reverse('xhr_projectedit', args=(p.id,)) | ||
2914 | p.projectBuildUrl = reverse('xhr_projectbuild', args=(p.id,)) | ||
2915 | |||
2889 | # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) | 2916 | # build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds) |
2890 | build_mru = _managed_get_latest_builds() | 2917 | build_mru = _managed_get_latest_builds() |
2891 | 2918 | ||
@@ -2965,9 +2992,8 @@ if toastermain.settings.MANAGED: | |||
2965 | ] | 2992 | ] |
2966 | } | 2993 | } |
2967 | 2994 | ||
2968 | response = render(request, template, context) | ||
2969 | _set_parameters_values(pagesize, orderby, request) | 2995 | _set_parameters_values(pagesize, orderby, request) |
2970 | return response | 2996 | return context |
2971 | 2997 | ||
2972 | def buildrequestdetails(request, pid, brid): | 2998 | def buildrequestdetails(request, pid, brid): |
2973 | template = "buildrequestdetails.html" | 2999 | template = "buildrequestdetails.html" |
@@ -3185,9 +3211,6 @@ else: | |||
3185 | def xhr_projectbuild(request, pid): | 3211 | def xhr_projectbuild(request, pid): |
3186 | return render(request, 'landing_not_managed.html') | 3212 | return render(request, 'landing_not_managed.html') |
3187 | 3213 | ||
3188 | def xhr_build(request): | ||
3189 | return render(request, 'landing_not_managed.html') | ||
3190 | |||
3191 | def xhr_projectinfo(request): | 3214 | def xhr_projectinfo(request): |
3192 | return render(request, 'landing_not_managed.html') | 3215 | return render(request, 'landing_not_managed.html') |
3193 | 3216 | ||