xref: /OK3568_Linux_fs/yocto/poky/bitbake/lib/toaster/toastergui/views.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#
2*4882a593Smuzhiyun# BitBake Toaster Implementation
3*4882a593Smuzhiyun#
4*4882a593Smuzhiyun# Copyright (C) 2013        Intel Corporation
5*4882a593Smuzhiyun#
6*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only
7*4882a593Smuzhiyun#
8*4882a593Smuzhiyun
9*4882a593Smuzhiyunimport re
10*4882a593Smuzhiyun
11*4882a593Smuzhiyunfrom django.db.models import F, Q, Sum
12*4882a593Smuzhiyunfrom django.db import IntegrityError
13*4882a593Smuzhiyunfrom django.shortcuts import render, redirect, get_object_or_404
14*4882a593Smuzhiyunfrom django.utils.http import urlencode
15*4882a593Smuzhiyunfrom orm.models import Build, Target, Task, Layer, Layer_Version, Recipe
16*4882a593Smuzhiyunfrom orm.models import LogMessage, Variable, Package_Dependency, Package
17*4882a593Smuzhiyunfrom orm.models import Task_Dependency, Package_File
18*4882a593Smuzhiyunfrom orm.models import Target_Installed_Package, Target_File
19*4882a593Smuzhiyunfrom orm.models import TargetKernelFile, TargetSDKFile, Target_Image_File
20*4882a593Smuzhiyunfrom orm.models import BitbakeVersion, CustomImageRecipe
21*4882a593Smuzhiyun
22*4882a593Smuzhiyunfrom django.urls import reverse, resolve
23*4882a593Smuzhiyunfrom django.core.exceptions import ObjectDoesNotExist
24*4882a593Smuzhiyunfrom django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
25*4882a593Smuzhiyunfrom django.http import HttpResponseNotFound, JsonResponse
26*4882a593Smuzhiyunfrom django.utils import timezone
27*4882a593Smuzhiyunfrom datetime import timedelta, datetime
28*4882a593Smuzhiyunfrom toastergui.templatetags.projecttags import json as jsonfilter
29*4882a593Smuzhiyunfrom decimal import Decimal
30*4882a593Smuzhiyunimport json
31*4882a593Smuzhiyunimport os
32*4882a593Smuzhiyunfrom os.path import dirname
33*4882a593Smuzhiyunimport mimetypes
34*4882a593Smuzhiyun
35*4882a593Smuzhiyunimport logging
36*4882a593Smuzhiyun
37*4882a593Smuzhiyunlogger = logging.getLogger("toaster")
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun# Project creation and managed build enable
40*4882a593Smuzhiyunproject_enable = ('1' == os.environ.get('TOASTER_BUILDSERVER'))
41*4882a593Smuzhiyunis_project_specific = ('1' == os.environ.get('TOASTER_PROJECTSPECIFIC'))
42*4882a593Smuzhiyun
43*4882a593Smuzhiyunclass MimeTypeFinder(object):
44*4882a593Smuzhiyun    # setting this to False enables additional non-standard mimetypes
45*4882a593Smuzhiyun    # to be included in the guess
46*4882a593Smuzhiyun    _strict = False
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun    # returns the mimetype for a file path as a string,
49*4882a593Smuzhiyun    # or 'application/octet-stream' if the type couldn't be guessed
50*4882a593Smuzhiyun    @classmethod
51*4882a593Smuzhiyun    def get_mimetype(self, path):
52*4882a593Smuzhiyun        guess = mimetypes.guess_type(path, self._strict)
53*4882a593Smuzhiyun        guessed_type = guess[0]
54*4882a593Smuzhiyun        if guessed_type is None:
55*4882a593Smuzhiyun            guessed_type = 'application/octet-stream'
56*4882a593Smuzhiyun        return guessed_type
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun# single point to add global values into the context before rendering
59*4882a593Smuzhiyundef toaster_render(request, page, context):
60*4882a593Smuzhiyun    context['project_enable'] = project_enable
61*4882a593Smuzhiyun    context['project_specific'] = is_project_specific
62*4882a593Smuzhiyun    return render(request, page, context)
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun# all new sessions should come through the landing page;
66*4882a593Smuzhiyun# determine in which mode we are running in, and redirect appropriately
67*4882a593Smuzhiyundef landing(request):
68*4882a593Smuzhiyun    # in build mode, we redirect to the command-line builds page
69*4882a593Smuzhiyun    # if there are any builds for the default (cli builds) project
70*4882a593Smuzhiyun    default_project = Project.objects.get_or_create_default_project()
71*4882a593Smuzhiyun    default_project_builds = Build.objects.filter(project = default_project)
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun    # we only redirect to projects page if there is a user-generated project
74*4882a593Smuzhiyun    num_builds = Build.objects.all().count()
75*4882a593Smuzhiyun    user_projects = Project.objects.filter(is_default = False)
76*4882a593Smuzhiyun    has_user_project = user_projects.count() > 0
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun    if num_builds == 0 and has_user_project:
79*4882a593Smuzhiyun        return redirect(reverse('all-projects'), permanent = False)
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun    if num_builds > 0:
82*4882a593Smuzhiyun        return redirect(reverse('all-builds'), permanent = False)
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun    context = {'lvs_nos' : Layer_Version.objects.all().count()}
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun    return toaster_render(request, 'landing.html', context)
87*4882a593Smuzhiyun
88*4882a593Smuzhiyundef objtojson(obj):
89*4882a593Smuzhiyun    from django.db.models.query import QuerySet
90*4882a593Smuzhiyun    from django.db.models import Model
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun    if isinstance(obj, datetime):
93*4882a593Smuzhiyun        return obj.isoformat()
94*4882a593Smuzhiyun    elif isinstance(obj, timedelta):
95*4882a593Smuzhiyun        return obj.total_seconds()
96*4882a593Smuzhiyun    elif isinstance(obj, QuerySet) or isinstance(obj, set):
97*4882a593Smuzhiyun        return list(obj)
98*4882a593Smuzhiyun    elif isinstance(obj, Decimal):
99*4882a593Smuzhiyun        return str(obj)
100*4882a593Smuzhiyun    elif type(obj).__name__ == "RelatedManager":
101*4882a593Smuzhiyun        return [x.pk for x in obj.all()]
102*4882a593Smuzhiyun    elif hasattr( obj, '__dict__') and isinstance(obj, Model):
103*4882a593Smuzhiyun        d = obj.__dict__
104*4882a593Smuzhiyun        nd = dict(d)
105*4882a593Smuzhiyun        for di in d.keys():
106*4882a593Smuzhiyun            if di.startswith("_"):
107*4882a593Smuzhiyun                del nd[di]
108*4882a593Smuzhiyun            elif isinstance(d[di], Model):
109*4882a593Smuzhiyun                nd[di] = d[di].pk
110*4882a593Smuzhiyun            elif isinstance(d[di], int) and hasattr(obj, "get_%s_display" % di):
111*4882a593Smuzhiyun                nd[di] = getattr(obj, "get_%s_display" % di)()
112*4882a593Smuzhiyun        return nd
113*4882a593Smuzhiyun    elif isinstance( obj, type(lambda x:x)):
114*4882a593Smuzhiyun        import inspect
115*4882a593Smuzhiyun        return inspect.getsourcelines(obj)[0]
116*4882a593Smuzhiyun    else:
117*4882a593Smuzhiyun        raise TypeError("Unserializable object %s (%s) of type %s" % ( obj, dir(obj), type(obj)))
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun
120*4882a593Smuzhiyundef _lv_to_dict(prj, x = None):
121*4882a593Smuzhiyun    if x is None:
122*4882a593Smuzhiyun        def wrapper(x):
123*4882a593Smuzhiyun            return _lv_to_dict(prj, x)
124*4882a593Smuzhiyun        return wrapper
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun    return {"id": x.pk,
127*4882a593Smuzhiyun            "name": x.layer.name,
128*4882a593Smuzhiyun            "tooltip": "%s | %s" % (x.layer.vcs_url,x.get_vcs_reference()),
129*4882a593Smuzhiyun            "detail": "(%s" % x.layer.vcs_url + (")" if x.release is None else " | "+x.get_vcs_reference()+")"),
130*4882a593Smuzhiyun            "giturl": x.layer.vcs_url,
131*4882a593Smuzhiyun            "layerdetailurl" : reverse('layerdetails', args=(prj.id,x.pk)),
132*4882a593Smuzhiyun            "revision" : x.get_vcs_reference(),
133*4882a593Smuzhiyun           }
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun
136*4882a593Smuzhiyundef _build_page_range(paginator, index = 1):
137*4882a593Smuzhiyun    try:
138*4882a593Smuzhiyun        page = paginator.page(index)
139*4882a593Smuzhiyun    except PageNotAnInteger:
140*4882a593Smuzhiyun        page = paginator.page(1)
141*4882a593Smuzhiyun    except  EmptyPage:
142*4882a593Smuzhiyun        page = paginator.page(paginator.num_pages)
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun    page.page_range = [page.number]
146*4882a593Smuzhiyun    crt_range = 0
147*4882a593Smuzhiyun    for i in range(1,5):
148*4882a593Smuzhiyun        if (page.number + i) <= paginator.num_pages:
149*4882a593Smuzhiyun            page.page_range = page.page_range + [ page.number + i]
150*4882a593Smuzhiyun            crt_range +=1
151*4882a593Smuzhiyun        if (page.number - i) > 0:
152*4882a593Smuzhiyun            page.page_range =  [page.number -i] + page.page_range
153*4882a593Smuzhiyun            crt_range +=1
154*4882a593Smuzhiyun        if crt_range == 4:
155*4882a593Smuzhiyun            break
156*4882a593Smuzhiyun    return page
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun
159*4882a593Smuzhiyundef _verify_parameters(g, mandatory_parameters):
160*4882a593Smuzhiyun    miss = []
161*4882a593Smuzhiyun    for mp in mandatory_parameters:
162*4882a593Smuzhiyun        if not mp in g:
163*4882a593Smuzhiyun            miss.append(mp)
164*4882a593Smuzhiyun    if len(miss):
165*4882a593Smuzhiyun        return miss
166*4882a593Smuzhiyun    return None
167*4882a593Smuzhiyun
168*4882a593Smuzhiyundef _redirect_parameters(view, g, mandatory_parameters, *args, **kwargs):
169*4882a593Smuzhiyun    try:
170*4882a593Smuzhiyun        from urllib import unquote, urlencode
171*4882a593Smuzhiyun    except ImportError:
172*4882a593Smuzhiyun        from urllib.parse import unquote, urlencode
173*4882a593Smuzhiyun    url = reverse(view, kwargs=kwargs)
174*4882a593Smuzhiyun    params = {}
175*4882a593Smuzhiyun    for i in g:
176*4882a593Smuzhiyun        params[i] = g[i]
177*4882a593Smuzhiyun    for i in mandatory_parameters:
178*4882a593Smuzhiyun        if not i in params:
179*4882a593Smuzhiyun            params[i] = unquote(str(mandatory_parameters[i]))
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun    return redirect(url + "?%s" % urlencode(params), permanent = False, **kwargs)
182*4882a593Smuzhiyun
183*4882a593Smuzhiyunclass RedirectException(Exception):
184*4882a593Smuzhiyun    def __init__(self, view, g, mandatory_parameters, *args, **kwargs):
185*4882a593Smuzhiyun        super(RedirectException, self).__init__()
186*4882a593Smuzhiyun        self.view = view
187*4882a593Smuzhiyun        self.g = g
188*4882a593Smuzhiyun        self.mandatory_parameters = mandatory_parameters
189*4882a593Smuzhiyun        self.oargs  = args
190*4882a593Smuzhiyun        self.okwargs = kwargs
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun    def get_redirect_response(self):
193*4882a593Smuzhiyun        return _redirect_parameters(self.view, self.g, self.mandatory_parameters, self.oargs, **self.okwargs)
194*4882a593Smuzhiyun
195*4882a593SmuzhiyunFIELD_SEPARATOR = ":"
196*4882a593SmuzhiyunAND_VALUE_SEPARATOR = "!"
197*4882a593SmuzhiyunOR_VALUE_SEPARATOR = "|"
198*4882a593SmuzhiyunDESCENDING = "-"
199*4882a593Smuzhiyun
200*4882a593Smuzhiyundef __get_q_for_val(name, value):
201*4882a593Smuzhiyun    if "OR" in value or "AND" in value:
202*4882a593Smuzhiyun        result = None
203*4882a593Smuzhiyun        for x in value.split("OR"):
204*4882a593Smuzhiyun             x = __get_q_for_val(name, x)
205*4882a593Smuzhiyun             result = result | x if result else x
206*4882a593Smuzhiyun        return result
207*4882a593Smuzhiyun    if "AND" in value:
208*4882a593Smuzhiyun        result = None
209*4882a593Smuzhiyun        for x in value.split("AND"):
210*4882a593Smuzhiyun            x = __get_q_for_val(name, x)
211*4882a593Smuzhiyun            result = result & x if result else x
212*4882a593Smuzhiyun        return result
213*4882a593Smuzhiyun    if value.startswith("NOT"):
214*4882a593Smuzhiyun        value = value[3:]
215*4882a593Smuzhiyun        if value == 'None':
216*4882a593Smuzhiyun            value = None
217*4882a593Smuzhiyun        kwargs = { name : value }
218*4882a593Smuzhiyun        return ~Q(**kwargs)
219*4882a593Smuzhiyun    else:
220*4882a593Smuzhiyun        if value == 'None':
221*4882a593Smuzhiyun            value = None
222*4882a593Smuzhiyun        kwargs = { name : value }
223*4882a593Smuzhiyun        return Q(**kwargs)
224*4882a593Smuzhiyun
225*4882a593Smuzhiyundef _get_filtering_query(filter_string):
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun    search_terms = filter_string.split(FIELD_SEPARATOR)
228*4882a593Smuzhiyun    and_keys = search_terms[0].split(AND_VALUE_SEPARATOR)
229*4882a593Smuzhiyun    and_values = search_terms[1].split(AND_VALUE_SEPARATOR)
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun    and_query = None
232*4882a593Smuzhiyun    for kv in zip(and_keys, and_values):
233*4882a593Smuzhiyun        or_keys = kv[0].split(OR_VALUE_SEPARATOR)
234*4882a593Smuzhiyun        or_values = kv[1].split(OR_VALUE_SEPARATOR)
235*4882a593Smuzhiyun        query = None
236*4882a593Smuzhiyun        for key, val in zip(or_keys, or_values):
237*4882a593Smuzhiyun            x = __get_q_for_val(key, val)
238*4882a593Smuzhiyun            query = query | x if query else x
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun        and_query = and_query & query if and_query else query
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun    return and_query
243*4882a593Smuzhiyun
244*4882a593Smuzhiyundef _get_toggle_order(request, orderkey, toggle_reverse = False):
245*4882a593Smuzhiyun    if toggle_reverse:
246*4882a593Smuzhiyun        return "%s:+" % orderkey if request.GET.get('orderby', "") == "%s:-" % orderkey else "%s:-" % orderkey
247*4882a593Smuzhiyun    else:
248*4882a593Smuzhiyun        return "%s:-" % orderkey if request.GET.get('orderby', "") == "%s:+" % orderkey else "%s:+" % orderkey
249*4882a593Smuzhiyun
250*4882a593Smuzhiyundef _get_toggle_order_icon(request, orderkey):
251*4882a593Smuzhiyun    if request.GET.get('orderby', "") == "%s:+"%orderkey:
252*4882a593Smuzhiyun        return "down"
253*4882a593Smuzhiyun    elif request.GET.get('orderby', "") == "%s:-"%orderkey:
254*4882a593Smuzhiyun        return "up"
255*4882a593Smuzhiyun    else:
256*4882a593Smuzhiyun        return None
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun# we check that the input comes in a valid form that we can recognize
259*4882a593Smuzhiyundef _validate_input(field_input, model):
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun    invalid = None
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun    if field_input:
264*4882a593Smuzhiyun        field_input_list = field_input.split(FIELD_SEPARATOR)
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun        # Check we have only one colon
267*4882a593Smuzhiyun        if len(field_input_list) != 2:
268*4882a593Smuzhiyun            invalid = "We have an invalid number of separators: " + field_input + " -> " + str(field_input_list)
269*4882a593Smuzhiyun            return None, invalid
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun        # Check we have an equal number of terms both sides of the colon
272*4882a593Smuzhiyun        if len(field_input_list[0].split(AND_VALUE_SEPARATOR)) != len(field_input_list[1].split(AND_VALUE_SEPARATOR)):
273*4882a593Smuzhiyun            invalid = "Not all arg names got values"
274*4882a593Smuzhiyun            return None, invalid + str(field_input_list)
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun        # Check we are looking for a valid field
277*4882a593Smuzhiyun        valid_fields = [f.name for f in model._meta.get_fields()]
278*4882a593Smuzhiyun        for field in field_input_list[0].split(AND_VALUE_SEPARATOR):
279*4882a593Smuzhiyun            if True in [field.startswith(x) for x in valid_fields]:
280*4882a593Smuzhiyun                break
281*4882a593Smuzhiyun        else:
282*4882a593Smuzhiyun           return None, (field, valid_fields)
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun    return field_input, invalid
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun# uses search_allowed_fields in orm/models.py to create a search query
287*4882a593Smuzhiyun# for these fields with the supplied input text
288*4882a593Smuzhiyundef _get_search_results(search_term, queryset, model):
289*4882a593Smuzhiyun    search_object = None
290*4882a593Smuzhiyun    for st in search_term.split(" "):
291*4882a593Smuzhiyun        queries = None
292*4882a593Smuzhiyun        for field in model.search_allowed_fields:
293*4882a593Smuzhiyun            query = Q(**{field + '__icontains': st})
294*4882a593Smuzhiyun            queries = queries | query if queries else query
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun        search_object = search_object & queries if search_object else queries
297*4882a593Smuzhiyun    queryset = queryset.filter(search_object)
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun    return queryset
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun# function to extract the search/filter/ordering parameters from the request
303*4882a593Smuzhiyun# it uses the request and the model to validate input for the filter and orderby values
304*4882a593Smuzhiyundef _search_tuple(request, model):
305*4882a593Smuzhiyun    ordering_string, invalid = _validate_input(request.GET.get('orderby', ''), model)
306*4882a593Smuzhiyun    if invalid:
307*4882a593Smuzhiyun        raise BaseException("Invalid ordering model:" + str(model) + str(invalid))
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun    filter_string, invalid = _validate_input(request.GET.get('filter', ''), model)
310*4882a593Smuzhiyun    if invalid:
311*4882a593Smuzhiyun        raise BaseException("Invalid filter " + str(invalid))
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun    search_term = request.GET.get('search', '')
314*4882a593Smuzhiyun    return (filter_string, search_term, ordering_string)
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun# returns a lazy-evaluated queryset for a filter/search/order combination
318*4882a593Smuzhiyundef _get_queryset(model, queryset, filter_string, search_term, ordering_string, ordering_secondary=''):
319*4882a593Smuzhiyun    if filter_string:
320*4882a593Smuzhiyun        filter_query = _get_filtering_query(filter_string)
321*4882a593Smuzhiyun        queryset = queryset.filter(filter_query)
322*4882a593Smuzhiyun    else:
323*4882a593Smuzhiyun        queryset = queryset.all()
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun    if search_term:
326*4882a593Smuzhiyun        queryset = _get_search_results(search_term, queryset, model)
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun    if ordering_string:
329*4882a593Smuzhiyun        column, order = ordering_string.split(':')
330*4882a593Smuzhiyun        if column == re.sub('-','',ordering_secondary):
331*4882a593Smuzhiyun            ordering_secondary=''
332*4882a593Smuzhiyun        if order.lower() == DESCENDING:
333*4882a593Smuzhiyun            column = '-' + column
334*4882a593Smuzhiyun        if ordering_secondary:
335*4882a593Smuzhiyun            queryset = queryset.order_by(column, ordering_secondary)
336*4882a593Smuzhiyun        else:
337*4882a593Smuzhiyun            queryset = queryset.order_by(column)
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun    # insure only distinct records (e.g. from multiple search hits) are returned
340*4882a593Smuzhiyun    return queryset.distinct()
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun# returns the value of entries per page and the name of the applied sorting field.
343*4882a593Smuzhiyun# if the value is given explicitly as a GET parameter it will be the first selected,
344*4882a593Smuzhiyun# otherwise the cookie value will be used.
345*4882a593Smuzhiyundef _get_parameters_values(request, default_count, default_order):
346*4882a593Smuzhiyun    current_url = resolve(request.path_info).url_name
347*4882a593Smuzhiyun    pagesize = request.GET.get('count', request.session.get('%s_count' % current_url, default_count))
348*4882a593Smuzhiyun    orderby = request.GET.get('orderby', request.session.get('%s_orderby' % current_url, default_order))
349*4882a593Smuzhiyun    return (pagesize, orderby)
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun# set cookies for parameters. this is usefull in case parameters are set
353*4882a593Smuzhiyun# manually from the GET values of the link
354*4882a593Smuzhiyundef _set_parameters_values(pagesize, orderby, request):
355*4882a593Smuzhiyun    from django.urls import resolve
356*4882a593Smuzhiyun    current_url = resolve(request.path_info).url_name
357*4882a593Smuzhiyun    request.session['%s_count' % current_url] = pagesize
358*4882a593Smuzhiyun    request.session['%s_orderby' % current_url] =orderby
359*4882a593Smuzhiyun
360*4882a593Smuzhiyun# date range: normalize GUI's dd/mm/yyyy to date object
361*4882a593Smuzhiyundef _normalize_input_date(date_str,default):
362*4882a593Smuzhiyun    date_str=re.sub('/', '-', date_str)
363*4882a593Smuzhiyun    # accept dd/mm/yyyy to d/m/yy
364*4882a593Smuzhiyun    try:
365*4882a593Smuzhiyun        date_in = datetime.strptime(date_str, "%d-%m-%Y")
366*4882a593Smuzhiyun    except ValueError:
367*4882a593Smuzhiyun        # courtesy try with two digit year
368*4882a593Smuzhiyun        try:
369*4882a593Smuzhiyun            date_in = datetime.strptime(date_str, "%d-%m-%y")
370*4882a593Smuzhiyun        except ValueError:
371*4882a593Smuzhiyun            return default
372*4882a593Smuzhiyun    date_in = date_in.replace(tzinfo=default.tzinfo)
373*4882a593Smuzhiyun    return date_in
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun# convert and normalize any received date range filter, for example:
376*4882a593Smuzhiyun# "completed_on__gte!completed_on__lt:01/03/2015!02/03/2015_daterange" to
377*4882a593Smuzhiyun# "completed_on__gte!completed_on__lt:2015-03-01!2015-03-02"
378*4882a593Smuzhiyundef _modify_date_range_filter(filter_string):
379*4882a593Smuzhiyun    # was the date range radio button selected?
380*4882a593Smuzhiyun    if 0 >  filter_string.find('_daterange'):
381*4882a593Smuzhiyun        return filter_string,''
382*4882a593Smuzhiyun    # normalize GUI dates to database format
383*4882a593Smuzhiyun    filter_string = filter_string.replace('_daterange','').replace(':','!');
384*4882a593Smuzhiyun    filter_list = filter_string.split('!');
385*4882a593Smuzhiyun    if 4 != len(filter_list):
386*4882a593Smuzhiyun        return filter_string
387*4882a593Smuzhiyun    today = timezone.localtime(timezone.now())
388*4882a593Smuzhiyun    date_id = filter_list[1]
389*4882a593Smuzhiyun    date_from = _normalize_input_date(filter_list[2],today)
390*4882a593Smuzhiyun    date_to = _normalize_input_date(filter_list[3],today)
391*4882a593Smuzhiyun    # swap dates if manually set dates are out of order
392*4882a593Smuzhiyun    if  date_to < date_from:
393*4882a593Smuzhiyun        date_to,date_from = date_from,date_to
394*4882a593Smuzhiyun    # convert to strings, make 'date_to' inclusive by moving to begining of next day
395*4882a593Smuzhiyun    date_from_str = date_from.strftime("%Y-%m-%d")
396*4882a593Smuzhiyun    date_to_str = (date_to+timedelta(days=1)).strftime("%Y-%m-%d")
397*4882a593Smuzhiyun    filter_string=filter_list[0]+'!'+filter_list[1]+':'+date_from_str+'!'+date_to_str
398*4882a593Smuzhiyun    daterange_selected = re.sub('__.*','', date_id)
399*4882a593Smuzhiyun    return filter_string,daterange_selected
400*4882a593Smuzhiyun
401*4882a593Smuzhiyundef _add_daterange_context(queryset_all, request, daterange_list):
402*4882a593Smuzhiyun    # calculate the exact begining of local today and yesterday
403*4882a593Smuzhiyun    today_begin = timezone.localtime(timezone.now())
404*4882a593Smuzhiyun    yesterday_begin = today_begin - timedelta(days=1)
405*4882a593Smuzhiyun    # add daterange persistent
406*4882a593Smuzhiyun    context_date = {}
407*4882a593Smuzhiyun    context_date['last_date_from'] = request.GET.get('last_date_from',timezone.localtime(timezone.now()).strftime("%d/%m/%Y"))
408*4882a593Smuzhiyun    context_date['last_date_to'  ] = request.GET.get('last_date_to'  ,context_date['last_date_from'])
409*4882a593Smuzhiyun    # calculate the date ranges, avoid second sort for 'created'
410*4882a593Smuzhiyun    # fetch the respective max range from the database
411*4882a593Smuzhiyun    context_date['daterange_filter']=''
412*4882a593Smuzhiyun    for key in daterange_list:
413*4882a593Smuzhiyun        queryset_key = queryset_all.order_by(key)
414*4882a593Smuzhiyun        try:
415*4882a593Smuzhiyun            context_date['dateMin_'+key]=timezone.localtime(getattr(queryset_key.first(),key)).strftime("%d/%m/%Y")
416*4882a593Smuzhiyun        except AttributeError:
417*4882a593Smuzhiyun            context_date['dateMin_'+key]=timezone.localtime(timezone.now())
418*4882a593Smuzhiyun        try:
419*4882a593Smuzhiyun            context_date['dateMax_'+key]=timezone.localtime(getattr(queryset_key.last(),key)).strftime("%d/%m/%Y")
420*4882a593Smuzhiyun        except AttributeError:
421*4882a593Smuzhiyun            context_date['dateMax_'+key]=timezone.localtime(timezone.now())
422*4882a593Smuzhiyun    return context_date,today_begin,yesterday_begin
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun##
426*4882a593Smuzhiyun# build dashboard for a single build, coming in as argument
427*4882a593Smuzhiyun# Each build may contain multiple targets and each target
428*4882a593Smuzhiyun# may generate multiple image files. display them all.
429*4882a593Smuzhiyun#
430*4882a593Smuzhiyundef builddashboard( request, build_id ):
431*4882a593Smuzhiyun    template = "builddashboard.html"
432*4882a593Smuzhiyun    if Build.objects.filter( pk=build_id ).count( ) == 0 :
433*4882a593Smuzhiyun        return redirect( builds )
434*4882a593Smuzhiyun    build = Build.objects.get( pk = build_id );
435*4882a593Smuzhiyun    layerVersionId = Layer_Version.objects.filter( build = build_id );
436*4882a593Smuzhiyun    recipeCount = Recipe.objects.filter( layer_version__id__in = layerVersionId ).count( );
437*4882a593Smuzhiyun    tgts = Target.objects.filter( build_id = build_id ).order_by( 'target' );
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun    # set up custom target list with computed package and image data
440*4882a593Smuzhiyun    targets = []
441*4882a593Smuzhiyun    ntargets = 0
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun    # True if at least one target for this build has an SDK artifact
444*4882a593Smuzhiyun    # or image file
445*4882a593Smuzhiyun    has_artifacts = False
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun    for t in tgts:
448*4882a593Smuzhiyun        elem = {}
449*4882a593Smuzhiyun        elem['target'] = t
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun        target_has_images = False
452*4882a593Smuzhiyun        image_files = []
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun        npkg = 0
455*4882a593Smuzhiyun        pkgsz = 0
456*4882a593Smuzhiyun        package = None
457*4882a593Smuzhiyun        # Chunk the query to avoid "too many SQL variables" error
458*4882a593Smuzhiyun        package_set = t.target_installed_package_set.all()
459*4882a593Smuzhiyun        package_set_len = len(package_set)
460*4882a593Smuzhiyun        for ps_start in range(0,package_set_len,500):
461*4882a593Smuzhiyun            ps_stop = min(ps_start+500,package_set_len)
462*4882a593Smuzhiyun            for package in Package.objects.filter(id__in = [x.package_id for x in package_set[ps_start:ps_stop]]):
463*4882a593Smuzhiyun                pkgsz = pkgsz + package.size
464*4882a593Smuzhiyun                if package.installed_name:
465*4882a593Smuzhiyun                    npkg = npkg + 1
466*4882a593Smuzhiyun        elem['npkg'] = npkg
467*4882a593Smuzhiyun        elem['pkgsz'] = pkgsz
468*4882a593Smuzhiyun        ti = Target_Image_File.objects.filter(target_id = t.id)
469*4882a593Smuzhiyun        for i in ti:
470*4882a593Smuzhiyun            ndx = i.file_name.rfind('/')
471*4882a593Smuzhiyun            if ndx < 0:
472*4882a593Smuzhiyun                ndx = 0;
473*4882a593Smuzhiyun            f = i.file_name[ndx + 1:]
474*4882a593Smuzhiyun            image_files.append({
475*4882a593Smuzhiyun                'id': i.id,
476*4882a593Smuzhiyun                'path': f,
477*4882a593Smuzhiyun                'size': i.file_size,
478*4882a593Smuzhiyun                'suffix': i.suffix
479*4882a593Smuzhiyun            })
480*4882a593Smuzhiyun        if len(image_files) > 0:
481*4882a593Smuzhiyun            target_has_images = True
482*4882a593Smuzhiyun        elem['targetHasImages'] = target_has_images
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun        elem['imageFiles'] = image_files
485*4882a593Smuzhiyun        elem['target_kernel_artifacts'] = t.targetkernelfile_set.all()
486*4882a593Smuzhiyun
487*4882a593Smuzhiyun        target_sdk_files = t.targetsdkfile_set.all()
488*4882a593Smuzhiyun        target_sdk_artifacts_count = target_sdk_files.count()
489*4882a593Smuzhiyun        elem['target_sdk_artifacts_count'] = target_sdk_artifacts_count
490*4882a593Smuzhiyun        elem['target_sdk_artifacts'] = target_sdk_files
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun        if target_has_images or target_sdk_artifacts_count > 0:
493*4882a593Smuzhiyun            has_artifacts = True
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun        targets.append(elem)
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun    ##
498*4882a593Smuzhiyun    # how many packages in this build - ignore anonymous ones
499*4882a593Smuzhiyun    #
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun    packageCount = 0
502*4882a593Smuzhiyun    packages = Package.objects.filter( build_id = build_id )
503*4882a593Smuzhiyun    for p in packages:
504*4882a593Smuzhiyun        if ( p.installed_name ):
505*4882a593Smuzhiyun            packageCount = packageCount + 1
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun    logmessages = list(LogMessage.objects.filter( build = build_id ))
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun    context = {
510*4882a593Smuzhiyun            'build'           : build,
511*4882a593Smuzhiyun            'project'         : build.project,
512*4882a593Smuzhiyun            'hasArtifacts'    : has_artifacts,
513*4882a593Smuzhiyun            'ntargets'        : ntargets,
514*4882a593Smuzhiyun            'targets'         : targets,
515*4882a593Smuzhiyun            'recipecount'     : recipeCount,
516*4882a593Smuzhiyun            'packagecount'    : packageCount,
517*4882a593Smuzhiyun            'logmessages'     : logmessages,
518*4882a593Smuzhiyun    }
519*4882a593Smuzhiyun    return toaster_render( request, template, context )
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun
523*4882a593Smuzhiyundef generateCoveredList2( revlist = None ):
524*4882a593Smuzhiyun    if not revlist:
525*4882a593Smuzhiyun        revlist = []
526*4882a593Smuzhiyun    covered_list =  [ x for x in revlist if x.outcome == Task.OUTCOME_COVERED ]
527*4882a593Smuzhiyun    while len(covered_list):
528*4882a593Smuzhiyun        revlist =  [ x for x in revlist if x.outcome != Task.OUTCOME_COVERED ]
529*4882a593Smuzhiyun        if len(revlist) > 0:
530*4882a593Smuzhiyun            return revlist
531*4882a593Smuzhiyun
532*4882a593Smuzhiyun        newlist = _find_task_revdep_list(covered_list)
533*4882a593Smuzhiyun
534*4882a593Smuzhiyun        revlist = list(set(revlist + newlist))
535*4882a593Smuzhiyun        covered_list =  [ x for x in revlist if x.outcome == Task.OUTCOME_COVERED ]
536*4882a593Smuzhiyun    return revlist
537*4882a593Smuzhiyun
538*4882a593Smuzhiyundef task( request, build_id, task_id ):
539*4882a593Smuzhiyun    template = "task.html"
540*4882a593Smuzhiyun    tasks_list = Task.objects.filter( pk=task_id )
541*4882a593Smuzhiyun    if tasks_list.count( ) == 0:
542*4882a593Smuzhiyun        return redirect( builds )
543*4882a593Smuzhiyun    task_object = tasks_list[ 0 ];
544*4882a593Smuzhiyun    dependencies = sorted(
545*4882a593Smuzhiyun        _find_task_dep( task_object ),
546*4882a593Smuzhiyun        key=lambda t:'%s_%s %s'%(t.recipe.name, t.recipe.version, t.task_name))
547*4882a593Smuzhiyun    reverse_dependencies = sorted(
548*4882a593Smuzhiyun        _find_task_revdep( task_object ),
549*4882a593Smuzhiyun        key=lambda t:'%s_%s %s'%( t.recipe.name, t.recipe.version, t.task_name ))
550*4882a593Smuzhiyun    coveredBy = '';
551*4882a593Smuzhiyun    if ( task_object.outcome == Task.OUTCOME_COVERED ):
552*4882a593Smuzhiyun#        _list = generateCoveredList( task )
553*4882a593Smuzhiyun        coveredBy = sorted(generateCoveredList2( _find_task_revdep( task_object ) ), key = lambda x: x.recipe.name)
554*4882a593Smuzhiyun    log_head = ''
555*4882a593Smuzhiyun    log_body = ''
556*4882a593Smuzhiyun    if task_object.outcome == task_object.OUTCOME_FAILED:
557*4882a593Smuzhiyun        pass
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun    uri_list= [ ]
560*4882a593Smuzhiyun    variables = Variable.objects.filter(build=build_id)
561*4882a593Smuzhiyun    v=variables.filter(variable_name='SSTATE_DIR')
562*4882a593Smuzhiyun    if v.count() > 0:
563*4882a593Smuzhiyun        uri_list.append(v[0].variable_value)
564*4882a593Smuzhiyun    v=variables.filter(variable_name='SSTATE_MIRRORS')
565*4882a593Smuzhiyun    if (v.count() > 0):
566*4882a593Smuzhiyun        for mirror in v[0].variable_value.split('\\n'):
567*4882a593Smuzhiyun            s=re.sub('.* ','',mirror.strip(' \t\n\r'))
568*4882a593Smuzhiyun            if len(s):
569*4882a593Smuzhiyun                uri_list.append(s)
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun    context = {
572*4882a593Smuzhiyun            'build'           : Build.objects.filter( pk = build_id )[ 0 ],
573*4882a593Smuzhiyun            'object'          : task_object,
574*4882a593Smuzhiyun            'task'            : task_object,
575*4882a593Smuzhiyun            'covered_by'      : coveredBy,
576*4882a593Smuzhiyun            'deps'            : dependencies,
577*4882a593Smuzhiyun            'rdeps'           : reverse_dependencies,
578*4882a593Smuzhiyun            'log_head'        : log_head,
579*4882a593Smuzhiyun            'log_body'        : log_body,
580*4882a593Smuzhiyun            'showing_matches' : False,
581*4882a593Smuzhiyun            'uri_list'        : uri_list,
582*4882a593Smuzhiyun            'task_in_tasks_table_pg':  int(task_object.order / 25) + 1
583*4882a593Smuzhiyun    }
584*4882a593Smuzhiyun    if request.GET.get( 'show_matches', "" ):
585*4882a593Smuzhiyun        context[ 'showing_matches' ] = True
586*4882a593Smuzhiyun        context[ 'matching_tasks' ] = Task.objects.filter(
587*4882a593Smuzhiyun            sstate_checksum=task_object.sstate_checksum ).filter(
588*4882a593Smuzhiyun            build__completed_on__lt=task_object.build.completed_on).exclude(
589*4882a593Smuzhiyun            order__isnull=True).exclude(outcome=Task.OUTCOME_NA).order_by('-build__completed_on')
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun    return toaster_render( request, template, context )
592*4882a593Smuzhiyun
593*4882a593Smuzhiyundef recipe(request, build_id, recipe_id, active_tab="1"):
594*4882a593Smuzhiyun    template = "recipe.html"
595*4882a593Smuzhiyun    if Recipe.objects.filter(pk=recipe_id).count() == 0 :
596*4882a593Smuzhiyun        return redirect(builds)
597*4882a593Smuzhiyun
598*4882a593Smuzhiyun    recipe_object = Recipe.objects.get(pk=recipe_id)
599*4882a593Smuzhiyun    layer_version = Layer_Version.objects.get(pk=recipe_object.layer_version_id)
600*4882a593Smuzhiyun    layer  = Layer.objects.get(pk=layer_version.layer_id)
601*4882a593Smuzhiyun    tasks_list  = Task.objects.filter(recipe_id = recipe_id, build_id = build_id).exclude(order__isnull=True).exclude(task_name__endswith='_setscene').exclude(outcome=Task.OUTCOME_NA)
602*4882a593Smuzhiyun    package_count = Package.objects.filter(recipe_id = recipe_id).filter(build_id = build_id).filter(size__gte=0).count()
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun    if active_tab != '1' and active_tab != '3' and active_tab != '4' :
605*4882a593Smuzhiyun        active_tab = '1'
606*4882a593Smuzhiyun    tab_states = {'1': '', '3': '', '4': ''}
607*4882a593Smuzhiyun    tab_states[active_tab] = 'active'
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun    context = {
610*4882a593Smuzhiyun            'build'   : Build.objects.get(pk=build_id),
611*4882a593Smuzhiyun            'object'  : recipe_object,
612*4882a593Smuzhiyun            'layer_version' : layer_version,
613*4882a593Smuzhiyun            'layer'   : layer,
614*4882a593Smuzhiyun            'tasks'   : tasks_list,
615*4882a593Smuzhiyun            'package_count' : package_count,
616*4882a593Smuzhiyun            'tab_states' : tab_states,
617*4882a593Smuzhiyun    }
618*4882a593Smuzhiyun    return toaster_render(request, template, context)
619*4882a593Smuzhiyun
620*4882a593Smuzhiyundef recipe_packages(request, build_id, recipe_id):
621*4882a593Smuzhiyun    template = "recipe_packages.html"
622*4882a593Smuzhiyun    if Recipe.objects.filter(pk=recipe_id).count() == 0 :
623*4882a593Smuzhiyun        return redirect(builds)
624*4882a593Smuzhiyun
625*4882a593Smuzhiyun    (pagesize, orderby) = _get_parameters_values(request, 10, 'name:+')
626*4882a593Smuzhiyun    mandatory_parameters = { 'count': pagesize,  'page' : 1, 'orderby': orderby }
627*4882a593Smuzhiyun    retval = _verify_parameters( request.GET, mandatory_parameters )
628*4882a593Smuzhiyun    if retval:
629*4882a593Smuzhiyun        return _redirect_parameters( 'recipe_packages', request.GET, mandatory_parameters, build_id = build_id, recipe_id = recipe_id)
630*4882a593Smuzhiyun    (filter_string, search_term, ordering_string) = _search_tuple(request, Package)
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun    recipe_object = Recipe.objects.get(pk=recipe_id)
633*4882a593Smuzhiyun    queryset = Package.objects.filter(recipe_id = recipe_id).filter(build_id = build_id).filter(size__gte=0)
634*4882a593Smuzhiyun    package_count = queryset.count()
635*4882a593Smuzhiyun    queryset = _get_queryset(Package, queryset, filter_string, search_term, ordering_string, 'name')
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun    packages = _build_page_range(Paginator(queryset, pagesize),request.GET.get('page', 1))
638*4882a593Smuzhiyun
639*4882a593Smuzhiyun    context = {
640*4882a593Smuzhiyun            'build'   : Build.objects.get(pk=build_id),
641*4882a593Smuzhiyun            'recipe'  : recipe_object,
642*4882a593Smuzhiyun            'objects'  : packages,
643*4882a593Smuzhiyun            'object_count' : package_count,
644*4882a593Smuzhiyun            'tablecols':[
645*4882a593Smuzhiyun                {
646*4882a593Smuzhiyun                    'name':'Package',
647*4882a593Smuzhiyun                    'orderfield': _get_toggle_order(request,"name"),
648*4882a593Smuzhiyun                    'ordericon': _get_toggle_order_icon(request,"name"),
649*4882a593Smuzhiyun                    'orderkey': "name",
650*4882a593Smuzhiyun                },
651*4882a593Smuzhiyun                {
652*4882a593Smuzhiyun                    'name':'Version',
653*4882a593Smuzhiyun                },
654*4882a593Smuzhiyun                {
655*4882a593Smuzhiyun                    'name':'Size',
656*4882a593Smuzhiyun                    'orderfield': _get_toggle_order(request,"size", True),
657*4882a593Smuzhiyun                    'ordericon': _get_toggle_order_icon(request,"size"),
658*4882a593Smuzhiyun                    'orderkey': 'size',
659*4882a593Smuzhiyun                    'dclass': 'sizecol span2',
660*4882a593Smuzhiyun                },
661*4882a593Smuzhiyun           ]
662*4882a593Smuzhiyun       }
663*4882a593Smuzhiyun    response = toaster_render(request, template, context)
664*4882a593Smuzhiyun    _set_parameters_values(pagesize, orderby, request)
665*4882a593Smuzhiyun    return response
666*4882a593Smuzhiyun
667*4882a593Smuzhiyunfrom django.http import HttpResponse
668*4882a593Smuzhiyundef xhr_dirinfo(request, build_id, target_id):
669*4882a593Smuzhiyun    top = request.GET.get('start', '/')
670*4882a593Smuzhiyun    return HttpResponse(_get_dir_entries(build_id, target_id, top), content_type = "application/json")
671*4882a593Smuzhiyun
672*4882a593Smuzhiyunfrom django.utils.functional import Promise
673*4882a593Smuzhiyunfrom django.utils.encoding import force_text
674*4882a593Smuzhiyunclass LazyEncoder(json.JSONEncoder):
675*4882a593Smuzhiyun    def default(self, obj):
676*4882a593Smuzhiyun        if isinstance(obj, Promise):
677*4882a593Smuzhiyun            return force_text(obj)
678*4882a593Smuzhiyun        return super(LazyEncoder, self).default(obj)
679*4882a593Smuzhiyun
680*4882a593Smuzhiyunfrom toastergui.templatetags.projecttags import filtered_filesizeformat
681*4882a593Smuzhiyunimport os
682*4882a593Smuzhiyundef _get_dir_entries(build_id, target_id, start):
683*4882a593Smuzhiyun    node_str = {
684*4882a593Smuzhiyun        Target_File.ITYPE_REGULAR   : '-',
685*4882a593Smuzhiyun        Target_File.ITYPE_DIRECTORY : 'd',
686*4882a593Smuzhiyun        Target_File.ITYPE_SYMLINK   : 'l',
687*4882a593Smuzhiyun        Target_File.ITYPE_SOCKET    : 's',
688*4882a593Smuzhiyun        Target_File.ITYPE_FIFO      : 'p',
689*4882a593Smuzhiyun        Target_File.ITYPE_CHARACTER : 'c',
690*4882a593Smuzhiyun        Target_File.ITYPE_BLOCK     : 'b',
691*4882a593Smuzhiyun    }
692*4882a593Smuzhiyun    response = []
693*4882a593Smuzhiyun    objects  = Target_File.objects.filter(target__exact=target_id, directory__path=start)
694*4882a593Smuzhiyun    target_packages = Target_Installed_Package.objects.filter(target__exact=target_id).values_list('package_id', flat=True)
695*4882a593Smuzhiyun    for o in objects:
696*4882a593Smuzhiyun        # exclude root inode '/'
697*4882a593Smuzhiyun        if o.path == '/':
698*4882a593Smuzhiyun            continue
699*4882a593Smuzhiyun        try:
700*4882a593Smuzhiyun            entry = {}
701*4882a593Smuzhiyun            entry['parent'] = start
702*4882a593Smuzhiyun            entry['name'] = os.path.basename(o.path)
703*4882a593Smuzhiyun            entry['fullpath'] = o.path
704*4882a593Smuzhiyun
705*4882a593Smuzhiyun            # set defaults, not all dentries have packages
706*4882a593Smuzhiyun            entry['installed_package'] = None
707*4882a593Smuzhiyun            entry['package_id'] = None
708*4882a593Smuzhiyun            entry['package'] = None
709*4882a593Smuzhiyun            entry['link_to'] = None
710*4882a593Smuzhiyun            if o.inodetype == Target_File.ITYPE_DIRECTORY:
711*4882a593Smuzhiyun                entry['isdir'] = 1
712*4882a593Smuzhiyun                # is there content in directory
713*4882a593Smuzhiyun                entry['childcount'] = Target_File.objects.filter(target__exact=target_id, directory__path=o.path).all().count()
714*4882a593Smuzhiyun            else:
715*4882a593Smuzhiyun                entry['isdir'] = 0
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun                # resolve the file to get the package from the resolved file
718*4882a593Smuzhiyun                resolved_id = o.sym_target_id
719*4882a593Smuzhiyun                resolved_path = o.path
720*4882a593Smuzhiyun                if target_packages.count():
721*4882a593Smuzhiyun                    while resolved_id != "" and resolved_id is not None:
722*4882a593Smuzhiyun                        tf = Target_File.objects.get(pk=resolved_id)
723*4882a593Smuzhiyun                        resolved_path = tf.path
724*4882a593Smuzhiyun                        resolved_id = tf.sym_target_id
725*4882a593Smuzhiyun
726*4882a593Smuzhiyun                    thisfile=Package_File.objects.all().filter(path__exact=resolved_path, package_id__in=target_packages)
727*4882a593Smuzhiyun                    if thisfile.count():
728*4882a593Smuzhiyun                        p = Package.objects.get(pk=thisfile[0].package_id)
729*4882a593Smuzhiyun                        entry['installed_package'] = p.installed_name
730*4882a593Smuzhiyun                        entry['package_id'] = str(p.id)
731*4882a593Smuzhiyun                        entry['package'] = p.name
732*4882a593Smuzhiyun                # don't use resolved path from above, show immediate link-to
733*4882a593Smuzhiyun                if o.sym_target_id != "" and o.sym_target_id is not None:
734*4882a593Smuzhiyun                    entry['link_to'] = Target_File.objects.get(pk=o.sym_target_id).path
735*4882a593Smuzhiyun            entry['size'] = filtered_filesizeformat(o.size)
736*4882a593Smuzhiyun            if entry['link_to'] is not None:
737*4882a593Smuzhiyun                entry['permission'] = node_str[o.inodetype] + o.permission
738*4882a593Smuzhiyun            else:
739*4882a593Smuzhiyun                entry['permission'] = node_str[o.inodetype] + o.permission
740*4882a593Smuzhiyun            entry['owner'] = o.owner
741*4882a593Smuzhiyun            entry['group'] = o.group
742*4882a593Smuzhiyun            response.append(entry)
743*4882a593Smuzhiyun
744*4882a593Smuzhiyun        except Exception as e:
745*4882a593Smuzhiyun            print("Exception ", e)
746*4882a593Smuzhiyun            traceback.print_exc()
747*4882a593Smuzhiyun
748*4882a593Smuzhiyun    # sort by directories first, then by name
749*4882a593Smuzhiyun    rsorted = sorted(response, key=lambda entry :  entry['name'])
750*4882a593Smuzhiyun    rsorted = sorted(rsorted, key=lambda entry :  entry['isdir'], reverse=True)
751*4882a593Smuzhiyun    return json.dumps(rsorted, cls=LazyEncoder).replace('</', '<\\/')
752*4882a593Smuzhiyun
753*4882a593Smuzhiyundef dirinfo(request, build_id, target_id, file_path=None):
754*4882a593Smuzhiyun    template = "dirinfo.html"
755*4882a593Smuzhiyun    objects = _get_dir_entries(build_id, target_id, '/')
756*4882a593Smuzhiyun    packages_sum = Package.objects.filter(id__in=Target_Installed_Package.objects.filter(target_id=target_id).values('package_id')).aggregate(Sum('installed_size'))
757*4882a593Smuzhiyun    dir_list = None
758*4882a593Smuzhiyun    if file_path is not None:
759*4882a593Smuzhiyun        """
760*4882a593Smuzhiyun        Link from the included package detail file list page and is
761*4882a593Smuzhiyun        requesting opening the dir info to a specific file path.
762*4882a593Smuzhiyun        Provide the list of directories to expand and the full path to
763*4882a593Smuzhiyun        highlight in the page.
764*4882a593Smuzhiyun        """
765*4882a593Smuzhiyun        # Aassume target's path separator matches host's, that is, os.sep
766*4882a593Smuzhiyun        sep = os.sep
767*4882a593Smuzhiyun        dir_list = []
768*4882a593Smuzhiyun        head = file_path
769*4882a593Smuzhiyun        while head != sep:
770*4882a593Smuzhiyun            (head, tail) = os.path.split(head)
771*4882a593Smuzhiyun            if head != sep:
772*4882a593Smuzhiyun                dir_list.insert(0, head)
773*4882a593Smuzhiyun
774*4882a593Smuzhiyun    build = Build.objects.get(pk=build_id)
775*4882a593Smuzhiyun
776*4882a593Smuzhiyun    context = { 'build': build,
777*4882a593Smuzhiyun                'project': build.project,
778*4882a593Smuzhiyun                'target': Target.objects.get(pk=target_id),
779*4882a593Smuzhiyun                'packages_sum': packages_sum['installed_size__sum'],
780*4882a593Smuzhiyun                'objects': objects,
781*4882a593Smuzhiyun                'dir_list': dir_list,
782*4882a593Smuzhiyun                'file_path': file_path,
783*4882a593Smuzhiyun              }
784*4882a593Smuzhiyun    return toaster_render(request, template, context)
785*4882a593Smuzhiyun
786*4882a593Smuzhiyundef _find_task_dep(task_object):
787*4882a593Smuzhiyun    tdeps = Task_Dependency.objects.filter(task=task_object).filter(depends_on__order__gt=0)
788*4882a593Smuzhiyun    tdeps = tdeps.exclude(depends_on__outcome=Task.OUTCOME_NA).select_related("depends_on")
789*4882a593Smuzhiyun    return [x.depends_on for x in tdeps]
790*4882a593Smuzhiyun
791*4882a593Smuzhiyundef _find_task_revdep(task_object):
792*4882a593Smuzhiyun    tdeps = Task_Dependency.objects.filter(depends_on=task_object).filter(task__order__gt=0)
793*4882a593Smuzhiyun    tdeps = tdeps.exclude(task__outcome = Task.OUTCOME_NA).select_related("task", "task__recipe", "task__build")
794*4882a593Smuzhiyun
795*4882a593Smuzhiyun    # exclude self-dependencies to prevent infinite dependency loop
796*4882a593Smuzhiyun    # in generateCoveredList2()
797*4882a593Smuzhiyun    tdeps = tdeps.exclude(task=task_object)
798*4882a593Smuzhiyun
799*4882a593Smuzhiyun    return [tdep.task for tdep in tdeps]
800*4882a593Smuzhiyun
801*4882a593Smuzhiyundef _find_task_revdep_list(tasklist):
802*4882a593Smuzhiyun    tdeps = Task_Dependency.objects.filter(depends_on__in=tasklist).filter(task__order__gt=0)
803*4882a593Smuzhiyun    tdeps = tdeps.exclude(task__outcome=Task.OUTCOME_NA).select_related("task", "task__recipe", "task__build")
804*4882a593Smuzhiyun
805*4882a593Smuzhiyun    # exclude self-dependencies to prevent infinite dependency loop
806*4882a593Smuzhiyun    # in generateCoveredList2()
807*4882a593Smuzhiyun    tdeps = tdeps.exclude(task=F('depends_on'))
808*4882a593Smuzhiyun
809*4882a593Smuzhiyun    return [tdep.task for tdep in tdeps]
810*4882a593Smuzhiyun
811*4882a593Smuzhiyundef _find_task_provider(task_object):
812*4882a593Smuzhiyun    task_revdeps = _find_task_revdep(task_object)
813*4882a593Smuzhiyun    for tr in task_revdeps:
814*4882a593Smuzhiyun        if tr.outcome != Task.OUTCOME_COVERED:
815*4882a593Smuzhiyun            return tr
816*4882a593Smuzhiyun    for tr in task_revdeps:
817*4882a593Smuzhiyun        trc = _find_task_provider(tr)
818*4882a593Smuzhiyun        if trc is not None:
819*4882a593Smuzhiyun            return trc
820*4882a593Smuzhiyun    return None
821*4882a593Smuzhiyun
822*4882a593Smuzhiyundef configuration(request, build_id):
823*4882a593Smuzhiyun    template = 'configuration.html'
824*4882a593Smuzhiyun
825*4882a593Smuzhiyun    var_names = ('BB_VERSION', 'BUILD_SYS', 'NATIVELSBSTRING', 'TARGET_SYS',
826*4882a593Smuzhiyun                 'MACHINE', 'DISTRO', 'DISTRO_VERSION', 'TUNE_FEATURES', 'TARGET_FPU')
827*4882a593Smuzhiyun    context = dict(Variable.objects.filter(build=build_id, variable_name__in=var_names)\
828*4882a593Smuzhiyun                                           .values_list('variable_name', 'variable_value'))
829*4882a593Smuzhiyun    build = Build.objects.get(pk=build_id)
830*4882a593Smuzhiyun    context.update({'objectname': 'configuration',
831*4882a593Smuzhiyun                    'object_search_display':'variables',
832*4882a593Smuzhiyun                    'filter_search_display':'variables',
833*4882a593Smuzhiyun                    'build': build,
834*4882a593Smuzhiyun                    'project': build.project,
835*4882a593Smuzhiyun                    'targets': Target.objects.filter(build=build_id)})
836*4882a593Smuzhiyun    return toaster_render(request, template, context)
837*4882a593Smuzhiyun
838*4882a593Smuzhiyun
839*4882a593Smuzhiyundef configvars(request, build_id):
840*4882a593Smuzhiyun    template = 'configvars.html'
841*4882a593Smuzhiyun    (pagesize, orderby) = _get_parameters_values(request, 100, 'variable_name:+')
842*4882a593Smuzhiyun    mandatory_parameters = { 'count': pagesize,  'page' : 1, 'orderby' : orderby, 'filter' : 'description__regex:.+' }
843*4882a593Smuzhiyun    retval = _verify_parameters( request.GET, mandatory_parameters )
844*4882a593Smuzhiyun    (filter_string, search_term, ordering_string) = _search_tuple(request, Variable)
845*4882a593Smuzhiyun    if retval:
846*4882a593Smuzhiyun        # if new search, clear the default filter
847*4882a593Smuzhiyun        if search_term and len(search_term):
848*4882a593Smuzhiyun            mandatory_parameters['filter']=''
849*4882a593Smuzhiyun        return _redirect_parameters( 'configvars', request.GET, mandatory_parameters, build_id = build_id)
850*4882a593Smuzhiyun
851*4882a593Smuzhiyun    queryset = Variable.objects.filter(build=build_id).exclude(variable_name__istartswith='B_').exclude(variable_name__istartswith='do_')
852*4882a593Smuzhiyun    queryset_with_search =  _get_queryset(Variable, queryset, None, search_term, ordering_string, 'variable_name').exclude(variable_value='',vhistory__file_name__isnull=True)
853*4882a593Smuzhiyun    queryset = _get_queryset(Variable, queryset, filter_string, search_term, ordering_string, 'variable_name')
854*4882a593Smuzhiyun    # remove records where the value is empty AND there are no history files
855*4882a593Smuzhiyun    queryset = queryset.exclude(variable_value='',vhistory__file_name__isnull=True)
856*4882a593Smuzhiyun
857*4882a593Smuzhiyun    variables = _build_page_range(Paginator(queryset, pagesize), request.GET.get('page', 1))
858*4882a593Smuzhiyun
859*4882a593Smuzhiyun    # show all matching files (not just the last one)
860*4882a593Smuzhiyun    file_filter= search_term + ":"
861*4882a593Smuzhiyun    if filter_string.find('/conf/') > 0:
862*4882a593Smuzhiyun        file_filter += 'conf/(local|bblayers).conf'
863*4882a593Smuzhiyun    if filter_string.find('conf/machine/') > 0:
864*4882a593Smuzhiyun        file_filter += 'conf/machine/'
865*4882a593Smuzhiyun    if filter_string.find('conf/distro/') > 0:
866*4882a593Smuzhiyun        file_filter += 'conf/distro/'
867*4882a593Smuzhiyun    if filter_string.find('/bitbake.conf') > 0:
868*4882a593Smuzhiyun        file_filter += '/bitbake.conf'
869*4882a593Smuzhiyun    build_dir=re.sub("/tmp/log/.*","",Build.objects.get(pk=build_id).cooker_log_path)
870*4882a593Smuzhiyun
871*4882a593Smuzhiyun    build = Build.objects.get(pk=build_id)
872*4882a593Smuzhiyun
873*4882a593Smuzhiyun    context = {
874*4882a593Smuzhiyun                'objectname': 'configvars',
875*4882a593Smuzhiyun                'object_search_display':'BitBake variables',
876*4882a593Smuzhiyun                'filter_search_display':'variables',
877*4882a593Smuzhiyun                'file_filter': file_filter,
878*4882a593Smuzhiyun                'build': build,
879*4882a593Smuzhiyun                'project': build.project,
880*4882a593Smuzhiyun                'objects' : variables,
881*4882a593Smuzhiyun                'total_count':queryset_with_search.count(),
882*4882a593Smuzhiyun                'default_orderby' : 'variable_name:+',
883*4882a593Smuzhiyun                'search_term':search_term,
884*4882a593Smuzhiyun            # Specifies the display of columns for the table, appearance in "Edit columns" box, toggling default show/hide, and specifying filters for columns
885*4882a593Smuzhiyun                'tablecols' : [
886*4882a593Smuzhiyun                {'name': 'Variable',
887*4882a593Smuzhiyun                 'qhelp': "BitBake is a generic task executor that considers a list of tasks with dependencies and handles metadata that consists of variables in a certain format that get passed to the tasks",
888*4882a593Smuzhiyun                 'orderfield': _get_toggle_order(request, "variable_name"),
889*4882a593Smuzhiyun                 'ordericon':_get_toggle_order_icon(request, "variable_name"),
890*4882a593Smuzhiyun                },
891*4882a593Smuzhiyun                {'name': 'Value',
892*4882a593Smuzhiyun                 'qhelp': "The value assigned to the variable",
893*4882a593Smuzhiyun                },
894*4882a593Smuzhiyun                {'name': 'Set in file',
895*4882a593Smuzhiyun                 'qhelp': "The last configuration file that touched the variable value",
896*4882a593Smuzhiyun                 'clclass': 'file', 'hidden' : 0,
897*4882a593Smuzhiyun                 'orderkey' : 'vhistory__file_name',
898*4882a593Smuzhiyun                 'filter' : {
899*4882a593Smuzhiyun                    'class' : 'vhistory__file_name',
900*4882a593Smuzhiyun                    'label': 'Show:',
901*4882a593Smuzhiyun                    'options' : [
902*4882a593Smuzhiyun                               ('Local configuration variables', 'vhistory__file_name__contains:'+build_dir+'/conf/',queryset_with_search.filter(vhistory__file_name__contains=build_dir+'/conf/').count(), 'Select this filter to see variables set by the <code>local.conf</code> and <code>bblayers.conf</code> configuration files inside the <code>/build/conf/</code> directory'),
903*4882a593Smuzhiyun                               ('Machine configuration variables', 'vhistory__file_name__contains:conf/machine/',queryset_with_search.filter(vhistory__file_name__contains='conf/machine').count(), 'Select this filter to see variables set by the configuration file(s) inside your layers <code>/conf/machine/</code> directory'),
904*4882a593Smuzhiyun                               ('Distro configuration variables', 'vhistory__file_name__contains:conf/distro/',queryset_with_search.filter(vhistory__file_name__contains='conf/distro').count(), 'Select this filter to see variables set by the configuration file(s) inside your layers <code>/conf/distro/</code> directory'),
905*4882a593Smuzhiyun                               ('Layer configuration variables', 'vhistory__file_name__contains:conf/layer.conf',queryset_with_search.filter(vhistory__file_name__contains='conf/layer.conf').count(), 'Select this filter to see variables set by the <code>layer.conf</code> configuration file inside your layers'),
906*4882a593Smuzhiyun                               ('bitbake.conf variables', 'vhistory__file_name__contains:/bitbake.conf',queryset_with_search.filter(vhistory__file_name__contains='/bitbake.conf').count(), 'Select this filter to see variables set by the <code>bitbake.conf</code> configuration file'),
907*4882a593Smuzhiyun                               ]
908*4882a593Smuzhiyun                             },
909*4882a593Smuzhiyun                },
910*4882a593Smuzhiyun                {'name': 'Description',
911*4882a593Smuzhiyun                 'qhelp': "A brief explanation of the variable",
912*4882a593Smuzhiyun                 'clclass': 'description', 'hidden' : 0,
913*4882a593Smuzhiyun                 'dclass': "span4",
914*4882a593Smuzhiyun                 'filter' : {
915*4882a593Smuzhiyun                    'class' : 'description',
916*4882a593Smuzhiyun                    'label': 'Show:',
917*4882a593Smuzhiyun                    'options' : [
918*4882a593Smuzhiyun                               ('Variables with description', 'description__regex:.+', queryset_with_search.filter(description__regex='.+').count(), 'We provide descriptions for the most common BitBake variables. The list of descriptions lives in <code>meta/conf/documentation.conf</code>'),
919*4882a593Smuzhiyun                               ]
920*4882a593Smuzhiyun                            },
921*4882a593Smuzhiyun                },
922*4882a593Smuzhiyun                ],
923*4882a593Smuzhiyun            }
924*4882a593Smuzhiyun
925*4882a593Smuzhiyun    response = toaster_render(request, template, context)
926*4882a593Smuzhiyun    _set_parameters_values(pagesize, orderby, request)
927*4882a593Smuzhiyun    return response
928*4882a593Smuzhiyun
929*4882a593Smuzhiyundef bfile(request, build_id, package_id):
930*4882a593Smuzhiyun    template = 'bfile.html'
931*4882a593Smuzhiyun    files = Package_File.objects.filter(package = package_id)
932*4882a593Smuzhiyun    build = Build.objects.get(pk=build_id)
933*4882a593Smuzhiyun    context = {
934*4882a593Smuzhiyun        'build': build,
935*4882a593Smuzhiyun        'project': build.project,
936*4882a593Smuzhiyun        'objects' : files
937*4882a593Smuzhiyun    }
938*4882a593Smuzhiyun    return toaster_render(request, template, context)
939*4882a593Smuzhiyun
940*4882a593Smuzhiyun
941*4882a593Smuzhiyun# A set of dependency types valid for both included and built package views
942*4882a593SmuzhiyunOTHER_DEPENDS_BASE = [
943*4882a593Smuzhiyun    Package_Dependency.TYPE_RSUGGESTS,
944*4882a593Smuzhiyun    Package_Dependency.TYPE_RPROVIDES,
945*4882a593Smuzhiyun    Package_Dependency.TYPE_RREPLACES,
946*4882a593Smuzhiyun    Package_Dependency.TYPE_RCONFLICTS,
947*4882a593Smuzhiyun    ]
948*4882a593Smuzhiyun
949*4882a593Smuzhiyun# value for invalid row id
950*4882a593SmuzhiyunINVALID_KEY = -1
951*4882a593Smuzhiyun
952*4882a593Smuzhiyun"""
953*4882a593SmuzhiyunGiven a package id, target_id retrieves two sets of this image and package's
954*4882a593Smuzhiyundependencies.  The return value is a dictionary consisting of two other
955*4882a593Smuzhiyunlists: a list of 'runtime' dependencies, that is, having RDEPENDS
956*4882a593Smuzhiyunvalues in source package's recipe, and a list of other dependencies, that is
957*4882a593Smuzhiyunthe list of possible recipe variables as found in OTHER_DEPENDS_BASE plus
958*4882a593Smuzhiyunthe RRECOMMENDS or TRECOMMENDS value.
959*4882a593SmuzhiyunThe lists are built in the sort order specified for the package runtime
960*4882a593Smuzhiyundependency views.
961*4882a593Smuzhiyun"""
962*4882a593Smuzhiyundef _get_package_dependencies(package_id, target_id = INVALID_KEY):
963*4882a593Smuzhiyun    runtime_deps = []
964*4882a593Smuzhiyun    other_deps = []
965*4882a593Smuzhiyun    other_depends_types = OTHER_DEPENDS_BASE
966*4882a593Smuzhiyun
967*4882a593Smuzhiyun    if target_id != INVALID_KEY :
968*4882a593Smuzhiyun        rdepends_type = Package_Dependency.TYPE_TRDEPENDS
969*4882a593Smuzhiyun        other_depends_types +=  [Package_Dependency.TYPE_TRECOMMENDS]
970*4882a593Smuzhiyun    else :
971*4882a593Smuzhiyun        rdepends_type = Package_Dependency.TYPE_RDEPENDS
972*4882a593Smuzhiyun        other_depends_types += [Package_Dependency.TYPE_RRECOMMENDS]
973*4882a593Smuzhiyun
974*4882a593Smuzhiyun    package = Package.objects.get(pk=package_id)
975*4882a593Smuzhiyun    if target_id != INVALID_KEY :
976*4882a593Smuzhiyun        alldeps = package.package_dependencies_source.filter(target_id__exact = target_id)
977*4882a593Smuzhiyun    else :
978*4882a593Smuzhiyun        alldeps = package.package_dependencies_source.all()
979*4882a593Smuzhiyun    for idep in alldeps:
980*4882a593Smuzhiyun        dep_package = Package.objects.get(pk=idep.depends_on_id)
981*4882a593Smuzhiyun        dep_entry = Package_Dependency.DEPENDS_DICT[idep.dep_type]
982*4882a593Smuzhiyun        if dep_package.version == '' :
983*4882a593Smuzhiyun            version = ''
984*4882a593Smuzhiyun        else :
985*4882a593Smuzhiyun            version = dep_package.version + "-" + dep_package.revision
986*4882a593Smuzhiyun        installed = False
987*4882a593Smuzhiyun        if target_id != INVALID_KEY :
988*4882a593Smuzhiyun            if Target_Installed_Package.objects.filter(target_id__exact = target_id, package_id__exact = dep_package.id).count() > 0:
989*4882a593Smuzhiyun                installed = True
990*4882a593Smuzhiyun        dep =   {
991*4882a593Smuzhiyun                'name' : dep_package.name,
992*4882a593Smuzhiyun                'version' : version,
993*4882a593Smuzhiyun                'size' : dep_package.size,
994*4882a593Smuzhiyun                'dep_type' : idep.dep_type,
995*4882a593Smuzhiyun                'dep_type_display' : dep_entry[0].capitalize(),
996*4882a593Smuzhiyun                'dep_type_help' : dep_entry[1] % (dep_package.name, package.name),
997*4882a593Smuzhiyun                'depends_on_id' : dep_package.id,
998*4882a593Smuzhiyun                'installed' : installed,
999*4882a593Smuzhiyun                }
1000*4882a593Smuzhiyun
1001*4882a593Smuzhiyun        if target_id != INVALID_KEY:
1002*4882a593Smuzhiyun                dep['alias'] = _get_package_alias(dep_package)
1003*4882a593Smuzhiyun
1004*4882a593Smuzhiyun        if idep.dep_type == rdepends_type :
1005*4882a593Smuzhiyun            runtime_deps.append(dep)
1006*4882a593Smuzhiyun        elif idep.dep_type in other_depends_types :
1007*4882a593Smuzhiyun            other_deps.append(dep)
1008*4882a593Smuzhiyun
1009*4882a593Smuzhiyun    rdep_sorted = sorted(runtime_deps, key=lambda k: k['name'])
1010*4882a593Smuzhiyun    odep_sorted = sorted(
1011*4882a593Smuzhiyun            sorted(other_deps, key=lambda k: k['name']),
1012*4882a593Smuzhiyun            key=lambda k: k['dep_type'])
1013*4882a593Smuzhiyun    retvalues = {'runtime_deps' : rdep_sorted, 'other_deps' : odep_sorted}
1014*4882a593Smuzhiyun    return retvalues
1015*4882a593Smuzhiyun
1016*4882a593Smuzhiyun# Return the count of packages dependent on package for this target_id image
1017*4882a593Smuzhiyundef _get_package_reverse_dep_count(package, target_id):
1018*4882a593Smuzhiyun    return package.package_dependencies_target.filter(target_id__exact=target_id, dep_type__exact = Package_Dependency.TYPE_TRDEPENDS).count()
1019*4882a593Smuzhiyun
1020*4882a593Smuzhiyun# Return the count of the packages that this package_id is dependent on.
1021*4882a593Smuzhiyun# Use one of the two RDEPENDS types, either TRDEPENDS if the package was
1022*4882a593Smuzhiyun# installed, or else RDEPENDS if only built.
1023*4882a593Smuzhiyundef _get_package_dependency_count(package, target_id, is_installed):
1024*4882a593Smuzhiyun    if is_installed :
1025*4882a593Smuzhiyun        return package.package_dependencies_source.filter(target_id__exact = target_id,
1026*4882a593Smuzhiyun            dep_type__exact = Package_Dependency.TYPE_TRDEPENDS).count()
1027*4882a593Smuzhiyun    else :
1028*4882a593Smuzhiyun        return package.package_dependencies_source.filter(dep_type__exact = Package_Dependency.TYPE_RDEPENDS).count()
1029*4882a593Smuzhiyun
1030*4882a593Smuzhiyundef _get_package_alias(package):
1031*4882a593Smuzhiyun    alias = package.installed_name
1032*4882a593Smuzhiyun    if alias is not None and alias != '' and alias != package.name:
1033*4882a593Smuzhiyun        return alias
1034*4882a593Smuzhiyun    else:
1035*4882a593Smuzhiyun        return ''
1036*4882a593Smuzhiyun
1037*4882a593Smuzhiyundef _get_fullpackagespec(package):
1038*4882a593Smuzhiyun    r = package.name
1039*4882a593Smuzhiyun    version_good = package.version is not None and  package.version != ''
1040*4882a593Smuzhiyun    revision_good = package.revision is not None and package.revision != ''
1041*4882a593Smuzhiyun    if version_good or revision_good:
1042*4882a593Smuzhiyun        r += '_'
1043*4882a593Smuzhiyun        if version_good:
1044*4882a593Smuzhiyun            r += package.version
1045*4882a593Smuzhiyun            if revision_good:
1046*4882a593Smuzhiyun                r += '-'
1047*4882a593Smuzhiyun        if revision_good:
1048*4882a593Smuzhiyun            r += package.revision
1049*4882a593Smuzhiyun    return r
1050*4882a593Smuzhiyun
1051*4882a593Smuzhiyundef package_built_detail(request, build_id, package_id):
1052*4882a593Smuzhiyun    template = "package_built_detail.html"
1053*4882a593Smuzhiyun    if Build.objects.filter(pk=build_id).count() == 0 :
1054*4882a593Smuzhiyun        return redirect(builds)
1055*4882a593Smuzhiyun
1056*4882a593Smuzhiyun    # follow convention for pagination w/ search although not used for this view
1057*4882a593Smuzhiyun    queryset = Package_File.objects.filter(package_id__exact=package_id)
1058*4882a593Smuzhiyun    (pagesize, orderby) = _get_parameters_values(request, 25, 'path:+')
1059*4882a593Smuzhiyun    mandatory_parameters = { 'count': pagesize,  'page' : 1, 'orderby' : orderby }
1060*4882a593Smuzhiyun    retval = _verify_parameters( request.GET, mandatory_parameters )
1061*4882a593Smuzhiyun    if retval:
1062*4882a593Smuzhiyun        return _redirect_parameters( 'package_built_detail', request.GET, mandatory_parameters, build_id = build_id, package_id = package_id)
1063*4882a593Smuzhiyun
1064*4882a593Smuzhiyun    (filter_string, search_term, ordering_string) = _search_tuple(request, Package_File)
1065*4882a593Smuzhiyun    paths = _get_queryset(Package_File, queryset, filter_string, search_term, ordering_string, 'path')
1066*4882a593Smuzhiyun
1067*4882a593Smuzhiyun    package = Package.objects.get(pk=package_id)
1068*4882a593Smuzhiyun    package.fullpackagespec = _get_fullpackagespec(package)
1069*4882a593Smuzhiyun    context = {
1070*4882a593Smuzhiyun            'build' : Build.objects.get(pk=build_id),
1071*4882a593Smuzhiyun            'package' : package,
1072*4882a593Smuzhiyun            'dependency_count' : _get_package_dependency_count(package, -1, False),
1073*4882a593Smuzhiyun            'objects' : paths,
1074*4882a593Smuzhiyun            'tablecols':[
1075*4882a593Smuzhiyun                {
1076*4882a593Smuzhiyun                    'name':'File',
1077*4882a593Smuzhiyun                    'orderfield': _get_toggle_order(request, "path"),
1078*4882a593Smuzhiyun                    'ordericon':_get_toggle_order_icon(request, "path"),
1079*4882a593Smuzhiyun                },
1080*4882a593Smuzhiyun                {
1081*4882a593Smuzhiyun                    'name':'Size',
1082*4882a593Smuzhiyun                    'orderfield': _get_toggle_order(request, "size", True),
1083*4882a593Smuzhiyun                    'ordericon':_get_toggle_order_icon(request, "size"),
1084*4882a593Smuzhiyun                    'dclass': 'sizecol span2',
1085*4882a593Smuzhiyun                },
1086*4882a593Smuzhiyun            ]
1087*4882a593Smuzhiyun    }
1088*4882a593Smuzhiyun    if paths.all().count() < 2:
1089*4882a593Smuzhiyun        context['disable_sort'] = True;
1090*4882a593Smuzhiyun
1091*4882a593Smuzhiyun    response = toaster_render(request, template, context)
1092*4882a593Smuzhiyun    _set_parameters_values(pagesize, orderby, request)
1093*4882a593Smuzhiyun    return response
1094*4882a593Smuzhiyun
1095*4882a593Smuzhiyundef package_built_dependencies(request, build_id, package_id):
1096*4882a593Smuzhiyun    template = "package_built_dependencies.html"
1097*4882a593Smuzhiyun    if Build.objects.filter(pk=build_id).count() == 0 :
1098*4882a593Smuzhiyun         return redirect(builds)
1099*4882a593Smuzhiyun
1100*4882a593Smuzhiyun    package = Package.objects.get(pk=package_id)
1101*4882a593Smuzhiyun    package.fullpackagespec = _get_fullpackagespec(package)
1102*4882a593Smuzhiyun    dependencies = _get_package_dependencies(package_id)
1103*4882a593Smuzhiyun    context = {
1104*4882a593Smuzhiyun            'build' : Build.objects.get(pk=build_id),
1105*4882a593Smuzhiyun            'package' : package,
1106*4882a593Smuzhiyun            'runtime_deps' : dependencies['runtime_deps'],
1107*4882a593Smuzhiyun            'other_deps' :   dependencies['other_deps'],
1108*4882a593Smuzhiyun            'dependency_count' : _get_package_dependency_count(package, -1,  False)
1109*4882a593Smuzhiyun    }
1110*4882a593Smuzhiyun    return toaster_render(request, template, context)
1111*4882a593Smuzhiyun
1112*4882a593Smuzhiyun
1113*4882a593Smuzhiyundef package_included_detail(request, build_id, target_id, package_id):
1114*4882a593Smuzhiyun    template = "package_included_detail.html"
1115*4882a593Smuzhiyun    if Build.objects.filter(pk=build_id).count() == 0 :
1116*4882a593Smuzhiyun        return redirect(builds)
1117*4882a593Smuzhiyun
1118*4882a593Smuzhiyun    # follow convention for pagination w/ search although not used for this view
1119*4882a593Smuzhiyun    (pagesize, orderby) = _get_parameters_values(request, 25, 'path:+')
1120*4882a593Smuzhiyun    mandatory_parameters = { 'count': pagesize,  'page' : 1, 'orderby' : orderby }
1121*4882a593Smuzhiyun    retval = _verify_parameters( request.GET, mandatory_parameters )
1122*4882a593Smuzhiyun    if retval:
1123*4882a593Smuzhiyun        return _redirect_parameters( 'package_included_detail', request.GET, mandatory_parameters, build_id = build_id, target_id = target_id, package_id = package_id)
1124*4882a593Smuzhiyun    (filter_string, search_term, ordering_string) = _search_tuple(request, Package_File)
1125*4882a593Smuzhiyun
1126*4882a593Smuzhiyun    queryset = Package_File.objects.filter(package_id__exact=package_id)
1127*4882a593Smuzhiyun    paths = _get_queryset(Package_File, queryset, filter_string, search_term, ordering_string, 'path')
1128*4882a593Smuzhiyun
1129*4882a593Smuzhiyun    package = Package.objects.get(pk=package_id)
1130*4882a593Smuzhiyun    package.fullpackagespec = _get_fullpackagespec(package)
1131*4882a593Smuzhiyun    package.alias = _get_package_alias(package)
1132*4882a593Smuzhiyun    target = Target.objects.get(pk=target_id)
1133*4882a593Smuzhiyun    context = {
1134*4882a593Smuzhiyun            'build' : Build.objects.get(pk=build_id),
1135*4882a593Smuzhiyun            'target'  : target,
1136*4882a593Smuzhiyun            'package' : package,
1137*4882a593Smuzhiyun            'reverse_count' : _get_package_reverse_dep_count(package, target_id),
1138*4882a593Smuzhiyun            'dependency_count' : _get_package_dependency_count(package, target_id, True),
1139*4882a593Smuzhiyun            'objects': paths,
1140*4882a593Smuzhiyun            'tablecols':[
1141*4882a593Smuzhiyun                {
1142*4882a593Smuzhiyun                    'name':'File',
1143*4882a593Smuzhiyun                    'orderfield': _get_toggle_order(request, "path"),
1144*4882a593Smuzhiyun                    'ordericon':_get_toggle_order_icon(request, "path"),
1145*4882a593Smuzhiyun                },
1146*4882a593Smuzhiyun                {
1147*4882a593Smuzhiyun                    'name':'Size',
1148*4882a593Smuzhiyun                    'orderfield': _get_toggle_order(request, "size", True),
1149*4882a593Smuzhiyun                    'ordericon':_get_toggle_order_icon(request, "size"),
1150*4882a593Smuzhiyun                    'dclass': 'sizecol span2',
1151*4882a593Smuzhiyun                },
1152*4882a593Smuzhiyun            ]
1153*4882a593Smuzhiyun    }
1154*4882a593Smuzhiyun    if paths.all().count() < 2:
1155*4882a593Smuzhiyun        context['disable_sort'] = True
1156*4882a593Smuzhiyun    response = toaster_render(request, template, context)
1157*4882a593Smuzhiyun    _set_parameters_values(pagesize, orderby, request)
1158*4882a593Smuzhiyun    return response
1159*4882a593Smuzhiyun
1160*4882a593Smuzhiyundef package_included_dependencies(request, build_id, target_id, package_id):
1161*4882a593Smuzhiyun    template = "package_included_dependencies.html"
1162*4882a593Smuzhiyun    if Build.objects.filter(pk=build_id).count() == 0 :
1163*4882a593Smuzhiyun        return redirect(builds)
1164*4882a593Smuzhiyun
1165*4882a593Smuzhiyun    package = Package.objects.get(pk=package_id)
1166*4882a593Smuzhiyun    package.fullpackagespec = _get_fullpackagespec(package)
1167*4882a593Smuzhiyun    package.alias = _get_package_alias(package)
1168*4882a593Smuzhiyun    target = Target.objects.get(pk=target_id)
1169*4882a593Smuzhiyun
1170*4882a593Smuzhiyun    dependencies = _get_package_dependencies(package_id, target_id)
1171*4882a593Smuzhiyun    context = {
1172*4882a593Smuzhiyun            'build' : Build.objects.get(pk=build_id),
1173*4882a593Smuzhiyun            'package' : package,
1174*4882a593Smuzhiyun            'target' : target,
1175*4882a593Smuzhiyun            'runtime_deps' : dependencies['runtime_deps'],
1176*4882a593Smuzhiyun            'other_deps' :   dependencies['other_deps'],
1177*4882a593Smuzhiyun            'reverse_count' : _get_package_reverse_dep_count(package, target_id),
1178*4882a593Smuzhiyun            'dependency_count' : _get_package_dependency_count(package, target_id, True)
1179*4882a593Smuzhiyun    }
1180*4882a593Smuzhiyun    return toaster_render(request, template, context)
1181*4882a593Smuzhiyun
1182*4882a593Smuzhiyundef package_included_reverse_dependencies(request, build_id, target_id, package_id):
1183*4882a593Smuzhiyun    template = "package_included_reverse_dependencies.html"
1184*4882a593Smuzhiyun    if Build.objects.filter(pk=build_id).count() == 0 :
1185*4882a593Smuzhiyun        return redirect(builds)
1186*4882a593Smuzhiyun
1187*4882a593Smuzhiyun    (pagesize, orderby) = _get_parameters_values(request, 25, 'package__name:+')
1188*4882a593Smuzhiyun    mandatory_parameters = { 'count': pagesize,  'page' : 1, 'orderby': orderby }
1189*4882a593Smuzhiyun    retval = _verify_parameters( request.GET, mandatory_parameters )
1190*4882a593Smuzhiyun    if retval:
1191*4882a593Smuzhiyun        return _redirect_parameters( 'package_included_reverse_dependencies', request.GET, mandatory_parameters, build_id = build_id, target_id = target_id, package_id = package_id)
1192*4882a593Smuzhiyun    (filter_string, search_term, ordering_string) = _search_tuple(request, Package_File)
1193*4882a593Smuzhiyun
1194*4882a593Smuzhiyun    queryset = Package_Dependency.objects.select_related('depends_on').filter(depends_on=package_id, target_id=target_id, dep_type=Package_Dependency.TYPE_TRDEPENDS)
1195*4882a593Smuzhiyun    objects = _get_queryset(Package_Dependency, queryset, filter_string, search_term, ordering_string, 'package__name')
1196*4882a593Smuzhiyun
1197*4882a593Smuzhiyun    package = Package.objects.get(pk=package_id)
1198*4882a593Smuzhiyun    package.fullpackagespec = _get_fullpackagespec(package)
1199*4882a593Smuzhiyun    package.alias = _get_package_alias(package)
1200*4882a593Smuzhiyun    target = Target.objects.get(pk=target_id)
1201*4882a593Smuzhiyun    for o in objects:
1202*4882a593Smuzhiyun        if o.package.version != '':
1203*4882a593Smuzhiyun            o.package.version += '-' + o.package.revision
1204*4882a593Smuzhiyun        o.alias = _get_package_alias(o.package)
1205*4882a593Smuzhiyun    context = {
1206*4882a593Smuzhiyun            'build' : Build.objects.get(pk=build_id),
1207*4882a593Smuzhiyun            'package' : package,
1208*4882a593Smuzhiyun            'target' : target,
1209*4882a593Smuzhiyun            'objects' : objects,
1210*4882a593Smuzhiyun            'reverse_count' : _get_package_reverse_dep_count(package, target_id),
1211*4882a593Smuzhiyun            'dependency_count' : _get_package_dependency_count(package, target_id, True),
1212*4882a593Smuzhiyun            'tablecols':[
1213*4882a593Smuzhiyun                {
1214*4882a593Smuzhiyun                    'name':'Package',
1215*4882a593Smuzhiyun                    'orderfield': _get_toggle_order(request, "package__name"),
1216*4882a593Smuzhiyun                    'ordericon': _get_toggle_order_icon(request, "package__name"),
1217*4882a593Smuzhiyun                },
1218*4882a593Smuzhiyun                {
1219*4882a593Smuzhiyun                    'name':'Version',
1220*4882a593Smuzhiyun                },
1221*4882a593Smuzhiyun                {
1222*4882a593Smuzhiyun                    'name':'Size',
1223*4882a593Smuzhiyun                    'orderfield': _get_toggle_order(request, "package__size", True),
1224*4882a593Smuzhiyun                    'ordericon': _get_toggle_order_icon(request, "package__size"),
1225*4882a593Smuzhiyun                    'dclass': 'sizecol span2',
1226*4882a593Smuzhiyun                },
1227*4882a593Smuzhiyun            ]
1228*4882a593Smuzhiyun    }
1229*4882a593Smuzhiyun    if objects.all().count() < 2:
1230*4882a593Smuzhiyun        context['disable_sort'] = True
1231*4882a593Smuzhiyun    response = toaster_render(request, template, context)
1232*4882a593Smuzhiyun    _set_parameters_values(pagesize, orderby, request)
1233*4882a593Smuzhiyun    return response
1234*4882a593Smuzhiyun
1235*4882a593Smuzhiyundef image_information_dir(request, build_id, target_id, packagefile_id):
1236*4882a593Smuzhiyun    # stubbed for now
1237*4882a593Smuzhiyun    return redirect(builds)
1238*4882a593Smuzhiyun    # the context processor that supplies data used across all the pages
1239*4882a593Smuzhiyun
1240*4882a593Smuzhiyun# a context processor which runs on every request; this provides the
1241*4882a593Smuzhiyun# projects and non_cli_projects (i.e. projects created by the user)
1242*4882a593Smuzhiyun# variables referred to in templates, which used to determine the
1243*4882a593Smuzhiyun# visibility of UI elements like the "New build" button
1244*4882a593Smuzhiyundef managedcontextprocessor(request):
1245*4882a593Smuzhiyun    projects = Project.objects.all()
1246*4882a593Smuzhiyun    ret = {
1247*4882a593Smuzhiyun        "projects": projects,
1248*4882a593Smuzhiyun        "non_cli_projects": projects.exclude(is_default=True),
1249*4882a593Smuzhiyun        "DEBUG" : toastermain.settings.DEBUG,
1250*4882a593Smuzhiyun        "TOASTER_BRANCH": toastermain.settings.TOASTER_BRANCH,
1251*4882a593Smuzhiyun        "TOASTER_REVISION" : toastermain.settings.TOASTER_REVISION,
1252*4882a593Smuzhiyun    }
1253*4882a593Smuzhiyun    return ret
1254*4882a593Smuzhiyun
1255*4882a593Smuzhiyun# REST-based API calls to return build/building status to external Toaster
1256*4882a593Smuzhiyun# managers and aggregators via JSON
1257*4882a593Smuzhiyun
1258*4882a593Smuzhiyundef _json_build_status(build_id,extend):
1259*4882a593Smuzhiyun    build_stat = None
1260*4882a593Smuzhiyun    try:
1261*4882a593Smuzhiyun        build = Build.objects.get( pk = build_id )
1262*4882a593Smuzhiyun        build_stat = {}
1263*4882a593Smuzhiyun        build_stat['id'] = build.id
1264*4882a593Smuzhiyun        build_stat['name'] = build.build_name
1265*4882a593Smuzhiyun        build_stat['machine'] = build.machine
1266*4882a593Smuzhiyun        build_stat['distro'] = build.distro
1267*4882a593Smuzhiyun        build_stat['start'] = build.started_on
1268*4882a593Smuzhiyun        # look up target name
1269*4882a593Smuzhiyun        target= Target.objects.get( build = build )
1270*4882a593Smuzhiyun        if target:
1271*4882a593Smuzhiyun            if target.task:
1272*4882a593Smuzhiyun                build_stat['target'] = '%s:%s' % (target.target,target.task)
1273*4882a593Smuzhiyun            else:
1274*4882a593Smuzhiyun                build_stat['target'] = '%s' % (target.target)
1275*4882a593Smuzhiyun        else:
1276*4882a593Smuzhiyun            build_stat['target'] = ''
1277*4882a593Smuzhiyun        # look up project name
1278*4882a593Smuzhiyun        project = Project.objects.get( build = build )
1279*4882a593Smuzhiyun        if project:
1280*4882a593Smuzhiyun            build_stat['project'] = project.name
1281*4882a593Smuzhiyun        else:
1282*4882a593Smuzhiyun            build_stat['project'] = ''
1283*4882a593Smuzhiyun        if Build.IN_PROGRESS == build.outcome:
1284*4882a593Smuzhiyun            now = timezone.now()
1285*4882a593Smuzhiyun            timediff = now - build.started_on
1286*4882a593Smuzhiyun            build_stat['seconds']='%.3f' % timediff.total_seconds()
1287*4882a593Smuzhiyun            build_stat['clone']='%d:%d' % (build.repos_cloned,build.repos_to_clone)
1288*4882a593Smuzhiyun            build_stat['parse']='%d:%d' % (build.recipes_parsed,build.recipes_to_parse)
1289*4882a593Smuzhiyun            tf = Task.objects.filter(build = build)
1290*4882a593Smuzhiyun            tfc = tf.count()
1291*4882a593Smuzhiyun            if tfc > 0:
1292*4882a593Smuzhiyun                tfd = tf.exclude(order__isnull=True).count()
1293*4882a593Smuzhiyun            else:
1294*4882a593Smuzhiyun                tfd = 0
1295*4882a593Smuzhiyun            build_stat['task']='%d:%d' % (tfd,tfc)
1296*4882a593Smuzhiyun        else:
1297*4882a593Smuzhiyun            build_stat['outcome'] = build.get_outcome_text()
1298*4882a593Smuzhiyun            timediff = build.completed_on - build.started_on
1299*4882a593Smuzhiyun            build_stat['seconds']='%.3f' % timediff.total_seconds()
1300*4882a593Smuzhiyun            build_stat['stop'] = build.completed_on
1301*4882a593Smuzhiyun            messages = LogMessage.objects.all().filter(build = build)
1302*4882a593Smuzhiyun            errors = len(messages.filter(level=LogMessage.ERROR) |
1303*4882a593Smuzhiyun                 messages.filter(level=LogMessage.EXCEPTION) |
1304*4882a593Smuzhiyun                 messages.filter(level=LogMessage.CRITICAL))
1305*4882a593Smuzhiyun            build_stat['errors'] = errors
1306*4882a593Smuzhiyun            warnings = len(messages.filter(level=LogMessage.WARNING))
1307*4882a593Smuzhiyun            build_stat['warnings'] = warnings
1308*4882a593Smuzhiyun        if extend:
1309*4882a593Smuzhiyun            build_stat['cooker_log'] = build.cooker_log_path
1310*4882a593Smuzhiyun    except Exception as e:
1311*4882a593Smuzhiyun        build_state = str(e)
1312*4882a593Smuzhiyun    return build_stat
1313*4882a593Smuzhiyun
1314*4882a593Smuzhiyundef json_builds(request):
1315*4882a593Smuzhiyun    build_table = []
1316*4882a593Smuzhiyun    builds = []
1317*4882a593Smuzhiyun    try:
1318*4882a593Smuzhiyun        builds = Build.objects.exclude(outcome=Build.IN_PROGRESS).order_by("-started_on")
1319*4882a593Smuzhiyun        for build in builds:
1320*4882a593Smuzhiyun            build_table.append(_json_build_status(build.id,False))
1321*4882a593Smuzhiyun    except Exception as e:
1322*4882a593Smuzhiyun        build_table = str(e)
1323*4882a593Smuzhiyun    return JsonResponse({'builds' : build_table, 'count' : len(builds)})
1324*4882a593Smuzhiyun
1325*4882a593Smuzhiyundef json_building(request):
1326*4882a593Smuzhiyun    build_table = []
1327*4882a593Smuzhiyun    builds = []
1328*4882a593Smuzhiyun    try:
1329*4882a593Smuzhiyun        builds = Build.objects.filter(outcome=Build.IN_PROGRESS).order_by("-started_on")
1330*4882a593Smuzhiyun        for build in builds:
1331*4882a593Smuzhiyun            build_table.append(_json_build_status(build.id,False))
1332*4882a593Smuzhiyun    except Exception as e:
1333*4882a593Smuzhiyun        build_table = str(e)
1334*4882a593Smuzhiyun    return JsonResponse({'building' : build_table, 'count' : len(builds)})
1335*4882a593Smuzhiyun
1336*4882a593Smuzhiyundef json_build(request,build_id):
1337*4882a593Smuzhiyun    return JsonResponse({'build' : _json_build_status(build_id,True)})
1338*4882a593Smuzhiyun
1339*4882a593Smuzhiyun
1340*4882a593Smuzhiyunimport toastermain.settings
1341*4882a593Smuzhiyun
1342*4882a593Smuzhiyunfrom orm.models import Project, ProjectLayer, ProjectVariable
1343*4882a593Smuzhiyunfrom bldcontrol.models import  BuildEnvironment
1344*4882a593Smuzhiyun
1345*4882a593Smuzhiyun# we have a set of functions if we're in managed mode, or
1346*4882a593Smuzhiyun# a default "page not available" simple functions for interactive mode
1347*4882a593Smuzhiyun
1348*4882a593Smuzhiyunif True:
1349*4882a593Smuzhiyun    from django.contrib.auth.models import User
1350*4882a593Smuzhiyun    from django.contrib.auth import authenticate, login
1351*4882a593Smuzhiyun
1352*4882a593Smuzhiyun    from orm.models import LayerSource, ToasterSetting, Release
1353*4882a593Smuzhiyun
1354*4882a593Smuzhiyun    import traceback
1355*4882a593Smuzhiyun
1356*4882a593Smuzhiyun    class BadParameterException(Exception):
1357*4882a593Smuzhiyun        ''' The exception raised on invalid POST requests '''
1358*4882a593Smuzhiyun        pass
1359*4882a593Smuzhiyun
1360*4882a593Smuzhiyun    # new project
1361*4882a593Smuzhiyun    def newproject(request):
1362*4882a593Smuzhiyun        if not project_enable:
1363*4882a593Smuzhiyun            return redirect( landing )
1364*4882a593Smuzhiyun
1365*4882a593Smuzhiyun        template = "newproject.html"
1366*4882a593Smuzhiyun        context = {
1367*4882a593Smuzhiyun            'email': request.user.email if request.user.is_authenticated else '',
1368*4882a593Smuzhiyun            'username': request.user.username if request.user.is_authenticated else '',
1369*4882a593Smuzhiyun            'releases': Release.objects.order_by("description"),
1370*4882a593Smuzhiyun        }
1371*4882a593Smuzhiyun
1372*4882a593Smuzhiyun        try:
1373*4882a593Smuzhiyun            context['defaultbranch'] = ToasterSetting.objects.get(name = "DEFAULT_RELEASE").value
1374*4882a593Smuzhiyun        except ToasterSetting.DoesNotExist:
1375*4882a593Smuzhiyun            pass
1376*4882a593Smuzhiyun
1377*4882a593Smuzhiyun        if request.method == "GET":
1378*4882a593Smuzhiyun            # render new project page
1379*4882a593Smuzhiyun            return toaster_render(request, template, context)
1380*4882a593Smuzhiyun        elif request.method == "POST":
1381*4882a593Smuzhiyun            mandatory_fields = ['projectname', 'ptype']
1382*4882a593Smuzhiyun            try:
1383*4882a593Smuzhiyun                ptype = request.POST.get('ptype')
1384*4882a593Smuzhiyun                if ptype == "import":
1385*4882a593Smuzhiyun                    mandatory_fields.append('importdir')
1386*4882a593Smuzhiyun                else:
1387*4882a593Smuzhiyun                    mandatory_fields.append('projectversion')
1388*4882a593Smuzhiyun                # make sure we have values for all mandatory_fields
1389*4882a593Smuzhiyun                missing = [field for field in mandatory_fields if len(request.POST.get(field, '')) == 0]
1390*4882a593Smuzhiyun                if missing:
1391*4882a593Smuzhiyun                    # set alert for missing fields
1392*4882a593Smuzhiyun                    raise BadParameterException("Fields missing: %s" % ", ".join(missing))
1393*4882a593Smuzhiyun
1394*4882a593Smuzhiyun                if not request.user.is_authenticated:
1395*4882a593Smuzhiyun                    user = authenticate(username = request.POST.get('username', '_anonuser'), password = 'nopass')
1396*4882a593Smuzhiyun                    if user is None:
1397*4882a593Smuzhiyun                        user = User.objects.create_user(username = request.POST.get('username', '_anonuser'), email = request.POST.get('email', ''), password = "nopass")
1398*4882a593Smuzhiyun
1399*4882a593Smuzhiyun                        user = authenticate(username = user.username, password = 'nopass')
1400*4882a593Smuzhiyun                    login(request, user)
1401*4882a593Smuzhiyun
1402*4882a593Smuzhiyun                #  save the project
1403*4882a593Smuzhiyun                if ptype == "import":
1404*4882a593Smuzhiyun                    if not os.path.isdir('%s/conf' % request.POST['importdir']):
1405*4882a593Smuzhiyun                        raise BadParameterException("Bad path or missing 'conf' directory (%s)" % request.POST['importdir'])
1406*4882a593Smuzhiyun                    from django.core import management
1407*4882a593Smuzhiyun                    management.call_command('buildimport', '--command=import', '--name=%s' % request.POST['projectname'], '--path=%s' % request.POST['importdir'], interactive=False)
1408*4882a593Smuzhiyun                    prj = Project.objects.get(name = request.POST['projectname'])
1409*4882a593Smuzhiyun                    prj.merged_attr = True
1410*4882a593Smuzhiyun                    prj.save()
1411*4882a593Smuzhiyun                else:
1412*4882a593Smuzhiyun                    release = Release.objects.get(pk = request.POST.get('projectversion', None ))
1413*4882a593Smuzhiyun                    prj = Project.objects.create_project(name = request.POST['projectname'], release = release)
1414*4882a593Smuzhiyun                    prj.user_id = request.user.pk
1415*4882a593Smuzhiyun                    if 'mergeattr' == request.POST.get('mergeattr', ''):
1416*4882a593Smuzhiyun                        prj.merged_attr = True
1417*4882a593Smuzhiyun                    prj.save()
1418*4882a593Smuzhiyun
1419*4882a593Smuzhiyun                return redirect(reverse(project, args=(prj.pk,)) + "?notify=new-project")
1420*4882a593Smuzhiyun
1421*4882a593Smuzhiyun            except (IntegrityError, BadParameterException) as e:
1422*4882a593Smuzhiyun                # fill in page with previously submitted values
1423*4882a593Smuzhiyun                for field in mandatory_fields:
1424*4882a593Smuzhiyun                    context.__setitem__(field, request.POST.get(field, "-- missing"))
1425*4882a593Smuzhiyun                if isinstance(e, IntegrityError) and "username" in str(e):
1426*4882a593Smuzhiyun                    context['alert'] = "Your chosen username is already used"
1427*4882a593Smuzhiyun                else:
1428*4882a593Smuzhiyun                    context['alert'] = str(e)
1429*4882a593Smuzhiyun                return toaster_render(request, template, context)
1430*4882a593Smuzhiyun
1431*4882a593Smuzhiyun        raise Exception("Invalid HTTP method for this page")
1432*4882a593Smuzhiyun
1433*4882a593Smuzhiyun    # new project
1434*4882a593Smuzhiyun    def newproject_specific(request, pid):
1435*4882a593Smuzhiyun        if not project_enable:
1436*4882a593Smuzhiyun            return redirect( landing )
1437*4882a593Smuzhiyun
1438*4882a593Smuzhiyun        project = Project.objects.get(pk=pid)
1439*4882a593Smuzhiyun        template = "newproject_specific.html"
1440*4882a593Smuzhiyun        context = {
1441*4882a593Smuzhiyun            'email': request.user.email if request.user.is_authenticated else '',
1442*4882a593Smuzhiyun            'username': request.user.username if request.user.is_authenticated else '',
1443*4882a593Smuzhiyun            'releases': Release.objects.order_by("description"),
1444*4882a593Smuzhiyun            'projectname': project.name,
1445*4882a593Smuzhiyun            'project_pk': project.pk,
1446*4882a593Smuzhiyun        }
1447*4882a593Smuzhiyun
1448*4882a593Smuzhiyun        # WORKAROUND: if we already know release, redirect 'newproject_specific' to 'project_specific'
1449*4882a593Smuzhiyun        if '1' == project.get_variable('INTERNAL_PROJECT_SPECIFIC_SKIPRELEASE'):
1450*4882a593Smuzhiyun            return redirect(reverse(project_specific, args=(project.pk,)))
1451*4882a593Smuzhiyun
1452*4882a593Smuzhiyun        try:
1453*4882a593Smuzhiyun            context['defaultbranch'] = ToasterSetting.objects.get(name = "DEFAULT_RELEASE").value
1454*4882a593Smuzhiyun        except ToasterSetting.DoesNotExist:
1455*4882a593Smuzhiyun            pass
1456*4882a593Smuzhiyun
1457*4882a593Smuzhiyun        if request.method == "GET":
1458*4882a593Smuzhiyun            # render new project page
1459*4882a593Smuzhiyun            return toaster_render(request, template, context)
1460*4882a593Smuzhiyun        elif request.method == "POST":
1461*4882a593Smuzhiyun            mandatory_fields = ['projectname', 'ptype']
1462*4882a593Smuzhiyun            try:
1463*4882a593Smuzhiyun                ptype = request.POST.get('ptype')
1464*4882a593Smuzhiyun                if ptype == "build":
1465*4882a593Smuzhiyun                    mandatory_fields.append('projectversion')
1466*4882a593Smuzhiyun                # make sure we have values for all mandatory_fields
1467*4882a593Smuzhiyun                missing = [field for field in mandatory_fields if len(request.POST.get(field, '')) == 0]
1468*4882a593Smuzhiyun                if missing:
1469*4882a593Smuzhiyun                    # set alert for missing fields
1470*4882a593Smuzhiyun                    raise BadParameterException("Fields missing: %s" % ", ".join(missing))
1471*4882a593Smuzhiyun
1472*4882a593Smuzhiyun                if not request.user.is_authenticated:
1473*4882a593Smuzhiyun                    user = authenticate(username = request.POST.get('username', '_anonuser'), password = 'nopass')
1474*4882a593Smuzhiyun                    if user is None:
1475*4882a593Smuzhiyun                        user = User.objects.create_user(username = request.POST.get('username', '_anonuser'), email = request.POST.get('email', ''), password = "nopass")
1476*4882a593Smuzhiyun
1477*4882a593Smuzhiyun                        user = authenticate(username = user.username, password = 'nopass')
1478*4882a593Smuzhiyun                    login(request, user)
1479*4882a593Smuzhiyun
1480*4882a593Smuzhiyun                #  save the project
1481*4882a593Smuzhiyun                if ptype == "analysis":
1482*4882a593Smuzhiyun                    release = None
1483*4882a593Smuzhiyun                else:
1484*4882a593Smuzhiyun                    release = Release.objects.get(pk = request.POST.get('projectversion', None ))
1485*4882a593Smuzhiyun
1486*4882a593Smuzhiyun                prj = Project.objects.create_project(name = request.POST['projectname'], release = release, existing_project = project)
1487*4882a593Smuzhiyun                prj.user_id = request.user.pk
1488*4882a593Smuzhiyun                prj.save()
1489*4882a593Smuzhiyun                return redirect(reverse(project_specific, args=(prj.pk,)) + "?notify=new-project")
1490*4882a593Smuzhiyun
1491*4882a593Smuzhiyun            except (IntegrityError, BadParameterException) as e:
1492*4882a593Smuzhiyun                # fill in page with previously submitted values
1493*4882a593Smuzhiyun                for field in mandatory_fields:
1494*4882a593Smuzhiyun                    context.__setitem__(field, request.POST.get(field, "-- missing"))
1495*4882a593Smuzhiyun                if isinstance(e, IntegrityError) and "username" in str(e):
1496*4882a593Smuzhiyun                    context['alert'] = "Your chosen username is already used"
1497*4882a593Smuzhiyun                else:
1498*4882a593Smuzhiyun                    context['alert'] = str(e)
1499*4882a593Smuzhiyun                return toaster_render(request, template, context)
1500*4882a593Smuzhiyun
1501*4882a593Smuzhiyun        raise Exception("Invalid HTTP method for this page")
1502*4882a593Smuzhiyun
1503*4882a593Smuzhiyun    # Shows the edit project page
1504*4882a593Smuzhiyun    def project(request, pid):
1505*4882a593Smuzhiyun        project = Project.objects.get(pk=pid)
1506*4882a593Smuzhiyun
1507*4882a593Smuzhiyun        if '1' == os.environ.get('TOASTER_PROJECTSPECIFIC'):
1508*4882a593Smuzhiyun            if request.GET:
1509*4882a593Smuzhiyun                #Example:request.GET=<QueryDict: {'setMachine': ['qemuarm']}>
1510*4882a593Smuzhiyun                params = urlencode(request.GET).replace('%5B%27','').replace('%27%5D','')
1511*4882a593Smuzhiyun                return redirect("%s?%s" % (reverse(project_specific, args=(project.pk,)),params))
1512*4882a593Smuzhiyun            else:
1513*4882a593Smuzhiyun                return redirect(reverse(project_specific, args=(project.pk,)))
1514*4882a593Smuzhiyun        context = {"project": project}
1515*4882a593Smuzhiyun        return toaster_render(request, "project.html", context)
1516*4882a593Smuzhiyun
1517*4882a593Smuzhiyun    # Shows the edit project-specific page
1518*4882a593Smuzhiyun    def project_specific(request, pid):
1519*4882a593Smuzhiyun        project = Project.objects.get(pk=pid)
1520*4882a593Smuzhiyun
1521*4882a593Smuzhiyun        # Are we refreshing from a successful project specific update clone?
1522*4882a593Smuzhiyun        if Project.PROJECT_SPECIFIC_CLONING_SUCCESS == project.get_variable(Project.PROJECT_SPECIFIC_STATUS):
1523*4882a593Smuzhiyun            return redirect(reverse(landing_specific,args=(project.pk,)))
1524*4882a593Smuzhiyun
1525*4882a593Smuzhiyun        context = {
1526*4882a593Smuzhiyun            "project": project,
1527*4882a593Smuzhiyun            "is_new" : project.get_variable(Project.PROJECT_SPECIFIC_ISNEW),
1528*4882a593Smuzhiyun            "default_image_recipe" : project.get_variable(Project.PROJECT_SPECIFIC_DEFAULTIMAGE),
1529*4882a593Smuzhiyun            "mru" : Build.objects.all().filter(project=project,outcome=Build.IN_PROGRESS),
1530*4882a593Smuzhiyun            }
1531*4882a593Smuzhiyun        if project.build_set.filter(outcome=Build.IN_PROGRESS).count() > 0:
1532*4882a593Smuzhiyun            context['build_in_progress_none_completed'] = True
1533*4882a593Smuzhiyun        else:
1534*4882a593Smuzhiyun            context['build_in_progress_none_completed'] = False
1535*4882a593Smuzhiyun        return toaster_render(request, "project.html", context)
1536*4882a593Smuzhiyun
1537*4882a593Smuzhiyun    # perform the final actions for the project specific page
1538*4882a593Smuzhiyun    def project_specific_finalize(cmnd, pid):
1539*4882a593Smuzhiyun        project = Project.objects.get(pk=pid)
1540*4882a593Smuzhiyun        callback = project.get_variable(Project.PROJECT_SPECIFIC_CALLBACK)
1541*4882a593Smuzhiyun        if "update" == cmnd:
1542*4882a593Smuzhiyun            # Delete all '_PROJECT_PREPARE_' builds
1543*4882a593Smuzhiyun            for b in Build.objects.all().filter(project=project):
1544*4882a593Smuzhiyun                delete_build = False
1545*4882a593Smuzhiyun                for t in b.target_set.all():
1546*4882a593Smuzhiyun                    if '_PROJECT_PREPARE_' == t.target:
1547*4882a593Smuzhiyun                        delete_build = True
1548*4882a593Smuzhiyun                if delete_build:
1549*4882a593Smuzhiyun                    from django.core import management
1550*4882a593Smuzhiyun                    management.call_command('builddelete', str(b.id), interactive=False)
1551*4882a593Smuzhiyun            # perform callback at this last moment if defined, in case Toaster gets shutdown next
1552*4882a593Smuzhiyun            default_target = project.get_variable(Project.PROJECT_SPECIFIC_DEFAULTIMAGE)
1553*4882a593Smuzhiyun            if callback:
1554*4882a593Smuzhiyun                callback = callback.replace("<IMAGE>",default_target)
1555*4882a593Smuzhiyun        if "cancel" == cmnd:
1556*4882a593Smuzhiyun            if callback:
1557*4882a593Smuzhiyun                callback = callback.replace("<IMAGE>","none")
1558*4882a593Smuzhiyun                callback = callback.replace("--update","--cancel")
1559*4882a593Smuzhiyun        # perform callback at this last moment if defined, in case this Toaster gets shutdown next
1560*4882a593Smuzhiyun        ret = ''
1561*4882a593Smuzhiyun        if callback:
1562*4882a593Smuzhiyun            ret = os.system('bash -c "%s"' % callback)
1563*4882a593Smuzhiyun            project.set_variable(Project.PROJECT_SPECIFIC_CALLBACK,'')
1564*4882a593Smuzhiyun        # Delete the temp project specific variables
1565*4882a593Smuzhiyun        project.set_variable(Project.PROJECT_SPECIFIC_ISNEW,'')
1566*4882a593Smuzhiyun        project.set_variable(Project.PROJECT_SPECIFIC_STATUS,Project.PROJECT_SPECIFIC_NONE)
1567*4882a593Smuzhiyun        # WORKAROUND: Release this workaround flag
1568*4882a593Smuzhiyun        project.set_variable('INTERNAL_PROJECT_SPECIFIC_SKIPRELEASE','')
1569*4882a593Smuzhiyun
1570*4882a593Smuzhiyun    # Shows the final landing page for project specific update
1571*4882a593Smuzhiyun    def landing_specific(request, pid):
1572*4882a593Smuzhiyun        project_specific_finalize("update", pid)
1573*4882a593Smuzhiyun        context = {
1574*4882a593Smuzhiyun            "install_dir": os.environ['TOASTER_DIR'],
1575*4882a593Smuzhiyun        }
1576*4882a593Smuzhiyun        return toaster_render(request, "landing_specific.html", context)
1577*4882a593Smuzhiyun
1578*4882a593Smuzhiyun    # Shows the related landing-specific page
1579*4882a593Smuzhiyun    def landing_specific_cancel(request, pid):
1580*4882a593Smuzhiyun        project_specific_finalize("cancel", pid)
1581*4882a593Smuzhiyun        context = {
1582*4882a593Smuzhiyun            "install_dir": os.environ['TOASTER_DIR'],
1583*4882a593Smuzhiyun            "status": "cancel",
1584*4882a593Smuzhiyun        }
1585*4882a593Smuzhiyun        return toaster_render(request, "landing_specific.html", context)
1586*4882a593Smuzhiyun
1587*4882a593Smuzhiyun    def jsunittests(request):
1588*4882a593Smuzhiyun        """ Provides a page for the js unit tests """
1589*4882a593Smuzhiyun        bbv = BitbakeVersion.objects.filter(branch="master").first()
1590*4882a593Smuzhiyun        release = Release.objects.filter(bitbake_version=bbv).first()
1591*4882a593Smuzhiyun
1592*4882a593Smuzhiyun        name = "_js_unit_test_prj_"
1593*4882a593Smuzhiyun
1594*4882a593Smuzhiyun        # If there is an existing project by this name delete it.
1595*4882a593Smuzhiyun        # We don't want Lots of duplicates cluttering up the projects.
1596*4882a593Smuzhiyun        Project.objects.filter(name=name).delete()
1597*4882a593Smuzhiyun
1598*4882a593Smuzhiyun        new_project = Project.objects.create_project(name=name,
1599*4882a593Smuzhiyun                                                     release=release)
1600*4882a593Smuzhiyun        # Add a layer
1601*4882a593Smuzhiyun        layer = new_project.get_all_compatible_layer_versions().first()
1602*4882a593Smuzhiyun
1603*4882a593Smuzhiyun        ProjectLayer.objects.get_or_create(layercommit=layer,
1604*4882a593Smuzhiyun                                           project=new_project)
1605*4882a593Smuzhiyun
1606*4882a593Smuzhiyun        # make sure we have a machine set for this project
1607*4882a593Smuzhiyun        ProjectVariable.objects.get_or_create(project=new_project,
1608*4882a593Smuzhiyun                                              name="MACHINE",
1609*4882a593Smuzhiyun                                              value="qemux86")
1610*4882a593Smuzhiyun        context = {'project': new_project}
1611*4882a593Smuzhiyun        return toaster_render(request, "js-unit-tests.html", context)
1612*4882a593Smuzhiyun
1613*4882a593Smuzhiyun    from django.views.decorators.csrf import csrf_exempt
1614*4882a593Smuzhiyun    @csrf_exempt
1615*4882a593Smuzhiyun    def xhr_testreleasechange(request, pid):
1616*4882a593Smuzhiyun        def response(data):
1617*4882a593Smuzhiyun            return HttpResponse(jsonfilter(data),
1618*4882a593Smuzhiyun                                content_type="application/json")
1619*4882a593Smuzhiyun
1620*4882a593Smuzhiyun        """ returns layer versions that would be deleted on the new
1621*4882a593Smuzhiyun        release__pk """
1622*4882a593Smuzhiyun        try:
1623*4882a593Smuzhiyun            prj = Project.objects.get(pk = pid)
1624*4882a593Smuzhiyun            new_release_id = request.GET['new_release_id']
1625*4882a593Smuzhiyun
1626*4882a593Smuzhiyun            # If we're already on this project do nothing
1627*4882a593Smuzhiyun            if prj.release.pk == int(new_release_id):
1628*4882a593Smuzhiyun                return reponse({"error": "ok", "rows": []})
1629*4882a593Smuzhiyun
1630*4882a593Smuzhiyun            retval = []
1631*4882a593Smuzhiyun
1632*4882a593Smuzhiyun            for project in prj.projectlayer_set.all():
1633*4882a593Smuzhiyun                release = Release.objects.get(pk = new_release_id)
1634*4882a593Smuzhiyun
1635*4882a593Smuzhiyun                layer_versions = prj.get_all_compatible_layer_versions()
1636*4882a593Smuzhiyun                layer_versions = layer_versions.filter(release = release)
1637*4882a593Smuzhiyun                layer_versions = layer_versions.filter(layer__name = project.layercommit.layer.name)
1638*4882a593Smuzhiyun
1639*4882a593Smuzhiyun                # there is no layer_version with the new release id,
1640*4882a593Smuzhiyun                # and the same name
1641*4882a593Smuzhiyun                if layer_versions.count() < 1:
1642*4882a593Smuzhiyun                    retval.append(project)
1643*4882a593Smuzhiyun
1644*4882a593Smuzhiyun            return response({"error":"ok",
1645*4882a593Smuzhiyun                             "rows": [_lv_to_dict(prj) for y in [x.layercommit for x in retval]]
1646*4882a593Smuzhiyun                            })
1647*4882a593Smuzhiyun
1648*4882a593Smuzhiyun        except Exception as e:
1649*4882a593Smuzhiyun            return response({"error": str(e) })
1650*4882a593Smuzhiyun
1651*4882a593Smuzhiyun    def xhr_configvaredit(request, pid):
1652*4882a593Smuzhiyun        try:
1653*4882a593Smuzhiyun            prj = Project.objects.get(id = pid)
1654*4882a593Smuzhiyun            # There are cases where user can add variables which hold values
1655*4882a593Smuzhiyun            # like http://, file:/// etc. In such case a simple split(":")
1656*4882a593Smuzhiyun            # would fail. One example is SSTATE_MIRRORS variable. So we use
1657*4882a593Smuzhiyun            # max_split var to handle them.
1658*4882a593Smuzhiyun            max_split = 1
1659*4882a593Smuzhiyun            # add conf variables
1660*4882a593Smuzhiyun            if 'configvarAdd' in request.POST:
1661*4882a593Smuzhiyun                t=request.POST['configvarAdd'].strip()
1662*4882a593Smuzhiyun                if ":" in t:
1663*4882a593Smuzhiyun                    variable, value = t.split(":", max_split)
1664*4882a593Smuzhiyun                else:
1665*4882a593Smuzhiyun                    variable = t
1666*4882a593Smuzhiyun                    value = ""
1667*4882a593Smuzhiyun
1668*4882a593Smuzhiyun                pt, created = ProjectVariable.objects.get_or_create(project = prj, name = variable, value = value)
1669*4882a593Smuzhiyun            # change conf variables
1670*4882a593Smuzhiyun            if 'configvarChange' in request.POST:
1671*4882a593Smuzhiyun                t=request.POST['configvarChange'].strip()
1672*4882a593Smuzhiyun                if ":" in t:
1673*4882a593Smuzhiyun                    variable, value = t.split(":", max_split)
1674*4882a593Smuzhiyun                else:
1675*4882a593Smuzhiyun                    variable = t
1676*4882a593Smuzhiyun                    value = ""
1677*4882a593Smuzhiyun
1678*4882a593Smuzhiyun                pt, created = ProjectVariable.objects.get_or_create(project = prj, name = variable)
1679*4882a593Smuzhiyun                pt.value=value
1680*4882a593Smuzhiyun                pt.save()
1681*4882a593Smuzhiyun            # remove conf variables
1682*4882a593Smuzhiyun            if 'configvarDel' in request.POST:
1683*4882a593Smuzhiyun                t=request.POST['configvarDel'].strip()
1684*4882a593Smuzhiyun                pt = ProjectVariable.objects.get(pk = int(t)).delete()
1685*4882a593Smuzhiyun
1686*4882a593Smuzhiyun            # return all project settings, filter out disallowed and elsewhere-managed variables
1687*4882a593Smuzhiyun            vars_managed,vars_fstypes,vars_disallowed = get_project_configvars_context()
1688*4882a593Smuzhiyun            configvars_query = ProjectVariable.objects.filter(project_id = pid).all()
1689*4882a593Smuzhiyun            for var in vars_managed:
1690*4882a593Smuzhiyun                configvars_query = configvars_query.exclude(name = var)
1691*4882a593Smuzhiyun            for var in vars_disallowed:
1692*4882a593Smuzhiyun                configvars_query = configvars_query.exclude(name = var)
1693*4882a593Smuzhiyun
1694*4882a593Smuzhiyun            return_data = {
1695*4882a593Smuzhiyun                "error": "ok",
1696*4882a593Smuzhiyun                'configvars': [(x.name, x.value, x.pk) for x in configvars_query]
1697*4882a593Smuzhiyun               }
1698*4882a593Smuzhiyun            try:
1699*4882a593Smuzhiyun                return_data['distro'] = ProjectVariable.objects.get(project = prj, name = "DISTRO").value,
1700*4882a593Smuzhiyun            except ProjectVariable.DoesNotExist:
1701*4882a593Smuzhiyun                pass
1702*4882a593Smuzhiyun            try:
1703*4882a593Smuzhiyun                return_data['dl_dir'] = ProjectVariable.objects.get(project = prj, name = "DL_DIR").value,
1704*4882a593Smuzhiyun            except ProjectVariable.DoesNotExist:
1705*4882a593Smuzhiyun                pass
1706*4882a593Smuzhiyun            try:
1707*4882a593Smuzhiyun                return_data['fstypes'] = ProjectVariable.objects.get(project = prj, name = "IMAGE_FSTYPES").value,
1708*4882a593Smuzhiyun            except ProjectVariable.DoesNotExist:
1709*4882a593Smuzhiyun                pass
1710*4882a593Smuzhiyun            try:
1711*4882a593Smuzhiyun                return_data['image_install:append'] = ProjectVariable.objects.get(project = prj, name = "IMAGE_INSTALL:append").value,
1712*4882a593Smuzhiyun            except ProjectVariable.DoesNotExist:
1713*4882a593Smuzhiyun                pass
1714*4882a593Smuzhiyun            try:
1715*4882a593Smuzhiyun                return_data['package_classes'] = ProjectVariable.objects.get(project = prj, name = "PACKAGE_CLASSES").value,
1716*4882a593Smuzhiyun            except ProjectVariable.DoesNotExist:
1717*4882a593Smuzhiyun                pass
1718*4882a593Smuzhiyun            try:
1719*4882a593Smuzhiyun                return_data['sstate_dir'] = ProjectVariable.objects.get(project = prj, name = "SSTATE_DIR").value,
1720*4882a593Smuzhiyun            except ProjectVariable.DoesNotExist:
1721*4882a593Smuzhiyun                pass
1722*4882a593Smuzhiyun
1723*4882a593Smuzhiyun            return HttpResponse(json.dumps( return_data ), content_type = "application/json")
1724*4882a593Smuzhiyun
1725*4882a593Smuzhiyun        except Exception as e:
1726*4882a593Smuzhiyun            return HttpResponse(json.dumps({"error":str(e) + "\n" + traceback.format_exc()}), content_type = "application/json")
1727*4882a593Smuzhiyun
1728*4882a593Smuzhiyun
1729*4882a593Smuzhiyun    def customrecipe_download(request, pid, recipe_id):
1730*4882a593Smuzhiyun        recipe = get_object_or_404(CustomImageRecipe, pk=recipe_id)
1731*4882a593Smuzhiyun
1732*4882a593Smuzhiyun        file_data = recipe.generate_recipe_file_contents()
1733*4882a593Smuzhiyun
1734*4882a593Smuzhiyun        response = HttpResponse(file_data, content_type='text/plain')
1735*4882a593Smuzhiyun        response['Content-Disposition'] = \
1736*4882a593Smuzhiyun                'attachment; filename="%s_%s.bb"' % (recipe.name,
1737*4882a593Smuzhiyun                                                     recipe.version)
1738*4882a593Smuzhiyun
1739*4882a593Smuzhiyun        return response
1740*4882a593Smuzhiyun
1741*4882a593Smuzhiyun    def importlayer(request, pid):
1742*4882a593Smuzhiyun        template = "importlayer.html"
1743*4882a593Smuzhiyun        context = {
1744*4882a593Smuzhiyun            'project': Project.objects.get(id=pid),
1745*4882a593Smuzhiyun        }
1746*4882a593Smuzhiyun        return toaster_render(request, template, context)
1747*4882a593Smuzhiyun
1748*4882a593Smuzhiyun    def layerdetails(request, pid, layerid):
1749*4882a593Smuzhiyun        project = Project.objects.get(pk=pid)
1750*4882a593Smuzhiyun        layer_version = Layer_Version.objects.get(pk=layerid)
1751*4882a593Smuzhiyun
1752*4882a593Smuzhiyun        project_layers = ProjectLayer.objects.filter(
1753*4882a593Smuzhiyun            project=project).values_list("layercommit_id",
1754*4882a593Smuzhiyun                                         flat=True)
1755*4882a593Smuzhiyun
1756*4882a593Smuzhiyun        context = {
1757*4882a593Smuzhiyun            'project': project,
1758*4882a593Smuzhiyun            'layer_source': LayerSource.types_dict(),
1759*4882a593Smuzhiyun            'layerversion': layer_version,
1760*4882a593Smuzhiyun            'layerdeps': {
1761*4882a593Smuzhiyun                "list": [
1762*4882a593Smuzhiyun                    {
1763*4882a593Smuzhiyun                        "id": dep.id,
1764*4882a593Smuzhiyun                        "name": dep.layer.name,
1765*4882a593Smuzhiyun                        "layerdetailurl": reverse('layerdetails',
1766*4882a593Smuzhiyun                                                  args=(pid, dep.pk)),
1767*4882a593Smuzhiyun                        "vcs_url": dep.layer.vcs_url,
1768*4882a593Smuzhiyun                        "vcs_reference": dep.get_vcs_reference()
1769*4882a593Smuzhiyun                    }
1770*4882a593Smuzhiyun                    for dep in layer_version.get_alldeps(project.id)]
1771*4882a593Smuzhiyun            },
1772*4882a593Smuzhiyun            'projectlayers': list(project_layers)
1773*4882a593Smuzhiyun        }
1774*4882a593Smuzhiyun
1775*4882a593Smuzhiyun        return toaster_render(request, 'layerdetails.html', context)
1776*4882a593Smuzhiyun
1777*4882a593Smuzhiyun
1778*4882a593Smuzhiyun    def get_project_configvars_context():
1779*4882a593Smuzhiyun        # Vars managed outside of this view
1780*4882a593Smuzhiyun        vars_managed = {
1781*4882a593Smuzhiyun            'MACHINE', 'BBLAYERS'
1782*4882a593Smuzhiyun        }
1783*4882a593Smuzhiyun
1784*4882a593Smuzhiyun        vars_disallowed  = {
1785*4882a593Smuzhiyun            'PARALLEL_MAKE','BB_NUMBER_THREADS',
1786*4882a593Smuzhiyun            'BB_DISKMON_DIRS','BB_NUMBER_THREADS','CVS_PROXY_HOST','CVS_PROXY_PORT',
1787*4882a593Smuzhiyun            'PARALLEL_MAKE','TMPDIR',
1788*4882a593Smuzhiyun            'all_proxy','ftp_proxy','http_proxy ','https_proxy'
1789*4882a593Smuzhiyun            }
1790*4882a593Smuzhiyun
1791*4882a593Smuzhiyun        vars_fstypes = Target_Image_File.SUFFIXES
1792*4882a593Smuzhiyun
1793*4882a593Smuzhiyun        return(vars_managed,sorted(vars_fstypes),vars_disallowed)
1794*4882a593Smuzhiyun
1795*4882a593Smuzhiyun    def projectconf(request, pid):
1796*4882a593Smuzhiyun
1797*4882a593Smuzhiyun        try:
1798*4882a593Smuzhiyun            prj = Project.objects.get(id = pid)
1799*4882a593Smuzhiyun        except Project.DoesNotExist:
1800*4882a593Smuzhiyun            return HttpResponseNotFound("<h1>Project id " + pid + " is unavailable</h1>")
1801*4882a593Smuzhiyun
1802*4882a593Smuzhiyun        # remove disallowed and externally managed varaibles from this list
1803*4882a593Smuzhiyun        vars_managed,vars_fstypes,vars_disallowed = get_project_configvars_context()
1804*4882a593Smuzhiyun        configvars = ProjectVariable.objects.filter(project_id = pid).all()
1805*4882a593Smuzhiyun        for var in vars_managed:
1806*4882a593Smuzhiyun            configvars = configvars.exclude(name = var)
1807*4882a593Smuzhiyun        for var in vars_disallowed:
1808*4882a593Smuzhiyun            configvars = configvars.exclude(name = var)
1809*4882a593Smuzhiyun
1810*4882a593Smuzhiyun        context = {
1811*4882a593Smuzhiyun            'project':          prj,
1812*4882a593Smuzhiyun            'configvars':       configvars,
1813*4882a593Smuzhiyun            'vars_managed':     vars_managed,
1814*4882a593Smuzhiyun            'vars_fstypes':     vars_fstypes,
1815*4882a593Smuzhiyun            'vars_disallowed':  vars_disallowed,
1816*4882a593Smuzhiyun        }
1817*4882a593Smuzhiyun
1818*4882a593Smuzhiyun        try:
1819*4882a593Smuzhiyun            context['distro'] =  ProjectVariable.objects.get(project = prj, name = "DISTRO").value
1820*4882a593Smuzhiyun            context['distro_defined'] = "1"
1821*4882a593Smuzhiyun        except ProjectVariable.DoesNotExist:
1822*4882a593Smuzhiyun            pass
1823*4882a593Smuzhiyun        try:
1824*4882a593Smuzhiyun            if ProjectVariable.objects.get(project = prj, name = "DL_DIR").value == "${TOPDIR}/../downloads":
1825*4882a593Smuzhiyun                be = BuildEnvironment.objects.get(pk = str(1))
1826*4882a593Smuzhiyun                dl_dir = os.path.join(dirname(be.builddir), "downloads")
1827*4882a593Smuzhiyun                context['dl_dir'] =  dl_dir
1828*4882a593Smuzhiyun                pv, created = ProjectVariable.objects.get_or_create(project = prj, name = "DL_DIR")
1829*4882a593Smuzhiyun                pv.value = dl_dir
1830*4882a593Smuzhiyun                pv.save()
1831*4882a593Smuzhiyun            else:
1832*4882a593Smuzhiyun                context['dl_dir'] = ProjectVariable.objects.get(project = prj, name = "DL_DIR").value
1833*4882a593Smuzhiyun            context['dl_dir_defined'] = "1"
1834*4882a593Smuzhiyun        except (ProjectVariable.DoesNotExist, BuildEnvironment.DoesNotExist):
1835*4882a593Smuzhiyun            pass
1836*4882a593Smuzhiyun        try:
1837*4882a593Smuzhiyun            context['fstypes'] =  ProjectVariable.objects.get(project = prj, name = "IMAGE_FSTYPES").value
1838*4882a593Smuzhiyun            context['fstypes_defined'] = "1"
1839*4882a593Smuzhiyun        except ProjectVariable.DoesNotExist:
1840*4882a593Smuzhiyun            pass
1841*4882a593Smuzhiyun        try:
1842*4882a593Smuzhiyun            context['image_install:append'] =  ProjectVariable.objects.get(project = prj, name = "IMAGE_INSTALL:append").value
1843*4882a593Smuzhiyun            context['image_install_append_defined'] = "1"
1844*4882a593Smuzhiyun        except ProjectVariable.DoesNotExist:
1845*4882a593Smuzhiyun            pass
1846*4882a593Smuzhiyun        try:
1847*4882a593Smuzhiyun            context['package_classes'] =  ProjectVariable.objects.get(project = prj, name = "PACKAGE_CLASSES").value
1848*4882a593Smuzhiyun            context['package_classes_defined'] = "1"
1849*4882a593Smuzhiyun        except ProjectVariable.DoesNotExist:
1850*4882a593Smuzhiyun            pass
1851*4882a593Smuzhiyun        try:
1852*4882a593Smuzhiyun            if ProjectVariable.objects.get(project = prj, name = "SSTATE_DIR").value == "${TOPDIR}/../sstate-cache":
1853*4882a593Smuzhiyun                be = BuildEnvironment.objects.get(pk = str(1))
1854*4882a593Smuzhiyun                sstate_dir = os.path.join(dirname(be.builddir), "sstate-cache")
1855*4882a593Smuzhiyun                context['sstate_dir'] = sstate_dir
1856*4882a593Smuzhiyun                pv, created = ProjectVariable.objects.get_or_create(project = prj, name = "SSTATE_DIR")
1857*4882a593Smuzhiyun                pv.value = sstate_dir
1858*4882a593Smuzhiyun                pv.save()
1859*4882a593Smuzhiyun            else:
1860*4882a593Smuzhiyun                context['sstate_dir'] = ProjectVariable.objects.get(project = prj, name = "SSTATE_DIR").value
1861*4882a593Smuzhiyun            context['sstate_dir_defined'] = "1"
1862*4882a593Smuzhiyun        except (ProjectVariable.DoesNotExist, BuildEnvironment.DoesNotExist):
1863*4882a593Smuzhiyun            pass
1864*4882a593Smuzhiyun
1865*4882a593Smuzhiyun        return toaster_render(request, "projectconf.html", context)
1866*4882a593Smuzhiyun
1867*4882a593Smuzhiyun    def _file_names_for_artifact(build, artifact_type, artifact_id):
1868*4882a593Smuzhiyun        """
1869*4882a593Smuzhiyun        Return a tuple (file path, file name for the download response) for an
1870*4882a593Smuzhiyun        artifact of type artifact_type with ID artifact_id for build; if
1871*4882a593Smuzhiyun        artifact type is not supported, returns (None, None)
1872*4882a593Smuzhiyun        """
1873*4882a593Smuzhiyun        file_name = None
1874*4882a593Smuzhiyun        response_file_name = None
1875*4882a593Smuzhiyun
1876*4882a593Smuzhiyun        if artifact_type == "cookerlog":
1877*4882a593Smuzhiyun            file_name = build.cooker_log_path
1878*4882a593Smuzhiyun            response_file_name = "cooker.log"
1879*4882a593Smuzhiyun
1880*4882a593Smuzhiyun        elif artifact_type == "imagefile":
1881*4882a593Smuzhiyun            file_name = Target_Image_File.objects.get(target__build = build, pk = artifact_id).file_name
1882*4882a593Smuzhiyun
1883*4882a593Smuzhiyun        elif artifact_type == "targetkernelartifact":
1884*4882a593Smuzhiyun            target = TargetKernelFile.objects.get(pk=artifact_id)
1885*4882a593Smuzhiyun            file_name = target.file_name
1886*4882a593Smuzhiyun
1887*4882a593Smuzhiyun        elif artifact_type == "targetsdkartifact":
1888*4882a593Smuzhiyun            target = TargetSDKFile.objects.get(pk=artifact_id)
1889*4882a593Smuzhiyun            file_name = target.file_name
1890*4882a593Smuzhiyun
1891*4882a593Smuzhiyun        elif artifact_type == "licensemanifest":
1892*4882a593Smuzhiyun            file_name = Target.objects.get(build = build, pk = artifact_id).license_manifest_path
1893*4882a593Smuzhiyun
1894*4882a593Smuzhiyun        elif artifact_type == "packagemanifest":
1895*4882a593Smuzhiyun            file_name = Target.objects.get(build = build, pk = artifact_id).package_manifest_path
1896*4882a593Smuzhiyun
1897*4882a593Smuzhiyun        elif artifact_type == "tasklogfile":
1898*4882a593Smuzhiyun            file_name = Task.objects.get(build = build, pk = artifact_id).logfile
1899*4882a593Smuzhiyun
1900*4882a593Smuzhiyun        elif artifact_type == "logmessagefile":
1901*4882a593Smuzhiyun            file_name = LogMessage.objects.get(build = build, pk = artifact_id).pathname
1902*4882a593Smuzhiyun
1903*4882a593Smuzhiyun        if file_name and not response_file_name:
1904*4882a593Smuzhiyun            response_file_name = os.path.basename(file_name)
1905*4882a593Smuzhiyun
1906*4882a593Smuzhiyun        return (file_name, response_file_name)
1907*4882a593Smuzhiyun
1908*4882a593Smuzhiyun    def build_artifact(request, build_id, artifact_type, artifact_id):
1909*4882a593Smuzhiyun        """
1910*4882a593Smuzhiyun        View which returns a build artifact file as a response
1911*4882a593Smuzhiyun        """
1912*4882a593Smuzhiyun        file_name = None
1913*4882a593Smuzhiyun        response_file_name = None
1914*4882a593Smuzhiyun
1915*4882a593Smuzhiyun        try:
1916*4882a593Smuzhiyun            build = Build.objects.get(pk = build_id)
1917*4882a593Smuzhiyun            file_name, response_file_name = _file_names_for_artifact(
1918*4882a593Smuzhiyun                build, artifact_type, artifact_id
1919*4882a593Smuzhiyun            )
1920*4882a593Smuzhiyun
1921*4882a593Smuzhiyun            if file_name and response_file_name:
1922*4882a593Smuzhiyun                fsock = open(file_name, "rb")
1923*4882a593Smuzhiyun                content_type = MimeTypeFinder.get_mimetype(file_name)
1924*4882a593Smuzhiyun
1925*4882a593Smuzhiyun                response = HttpResponse(fsock, content_type = content_type)
1926*4882a593Smuzhiyun
1927*4882a593Smuzhiyun                disposition = "attachment; filename=" + response_file_name
1928*4882a593Smuzhiyun                response["Content-Disposition"] = disposition
1929*4882a593Smuzhiyun
1930*4882a593Smuzhiyun                return response
1931*4882a593Smuzhiyun            else:
1932*4882a593Smuzhiyun                return toaster_render(request, "unavailable_artifact.html")
1933*4882a593Smuzhiyun        except (ObjectDoesNotExist, IOError):
1934*4882a593Smuzhiyun            return toaster_render(request, "unavailable_artifact.html")
1935*4882a593Smuzhiyun
1936