bitbake: toaster: all projects data and sorts
Implement the 'last build' data methods, enhance variable display, add empty page and empty sort support. [YOCTO #6682] (Bitbake rev: cc6ca17e80844ecb4a777276d5f5177ebbcd93f9) Signed-off-by: David Reyna <David.Reyna@windriver.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
parent
6768a3069d
commit
8c476c27bb
|
@ -106,7 +106,7 @@ class BuildRequest(models.Model):
|
|||
(REQ_ARCHIVE, "archive"),
|
||||
)
|
||||
|
||||
search_allowed_fields = ("brtarget__target",)
|
||||
search_allowed_fields = ("brtarget__target", "build__project__name")
|
||||
|
||||
project = models.ForeignKey(Project)
|
||||
build = models.OneToOneField(Build, null = True) # TODO: toasterui should set this when Build is created
|
||||
|
|
|
@ -102,6 +102,69 @@ class Project(models.Model):
|
|||
def __unicode__(self):
|
||||
return "%s (%s, %s)" % (self.name, self.release, self.bitbake_version)
|
||||
|
||||
def get_current_machine_name(self):
|
||||
try:
|
||||
return self.projectvariable_set.get(name="MACHINE").value
|
||||
except (ProjectVariable.DoesNotExist,IndexError):
|
||||
return( "None" );
|
||||
|
||||
def get_number_of_builds(self):
|
||||
try:
|
||||
return len(Build.objects.filter( project = self.id ))
|
||||
except (Build.DoesNotExist,IndexError):
|
||||
return( 0 )
|
||||
|
||||
def get_last_build_id(self):
|
||||
try:
|
||||
return Build.objects.filter( project = self.id ).order_by('-completed_on')[0].id
|
||||
except (Build.DoesNotExist,IndexError):
|
||||
return( -1 )
|
||||
|
||||
def get_last_outcome(self):
|
||||
build_id = self.get_last_build_id
|
||||
if (-1 == build_id):
|
||||
return( "" )
|
||||
try:
|
||||
return Build.objects.filter( id = self.get_last_build_id )[ 0 ].outcome
|
||||
except (Build.DoesNotExist,IndexError):
|
||||
return( "not_found" )
|
||||
|
||||
def get_last_target(self):
|
||||
build_id = self.get_last_build_id
|
||||
if (-1 == build_id):
|
||||
return( "" )
|
||||
try:
|
||||
return Target.objects.filter(build = build_id)[0].target
|
||||
except (Target.DoesNotExist,IndexError):
|
||||
return( "not_found" )
|
||||
|
||||
def get_last_errors(self):
|
||||
build_id = self.get_last_build_id
|
||||
if (-1 == build_id):
|
||||
return( 0 )
|
||||
try:
|
||||
return Build.objects.filter(id = build_id)[ 0 ].errors_no
|
||||
except (Build.DoesNotExist,IndexError):
|
||||
return( "not_found" )
|
||||
|
||||
def get_last_warnings(self):
|
||||
build_id = self.get_last_build_id
|
||||
if (-1 == build_id):
|
||||
return( 0 )
|
||||
try:
|
||||
return Build.objects.filter(id = build_id)[ 0 ].warnings_no
|
||||
except (Build.DoesNotExist,IndexError):
|
||||
return( "not_found" )
|
||||
|
||||
def get_last_imgfiles(self):
|
||||
build_id = self.get_last_build_id
|
||||
if (-1 == build_id):
|
||||
return( "" )
|
||||
try:
|
||||
return Variable.objects.filter(build = build_id, variable_name = "IMAGE_FSTYPES")[ 0 ].variable_value
|
||||
except (Variable.DoesNotExist,IndexError):
|
||||
return( "not_found" )
|
||||
|
||||
# returns a queryset of compatible layers for a project
|
||||
def compatible_layerversions(self, release = None, layer_name = None):
|
||||
if release == None:
|
||||
|
|
|
@ -56,6 +56,13 @@
|
|||
</td>
|
||||
<td class="target">{% for t in build.target_set.all %} <a href="{% url "builddashboard" build.id %}"> {{t.target}} </a> <br />{% endfor %}</td>
|
||||
<td class="machine"><a href="{% url "builddashboard" build.id %}">{{build.machine}}</a></td>
|
||||
{% if MANAGED %}
|
||||
<td class="project">
|
||||
{% if build.project %}
|
||||
<a href="{% url 'project' build.project.id %}">{{build.project.name}}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="started_on"><a href="{% url "builddashboard" build.id %}">{{build.started_on|date:"d/m/y H:i"}}</a></td>
|
||||
<td class="completed_on"><a href="{% url "builddashboard" build.id %}">{{build.completed_on|date:"d/m/y H:i"}}</a></td>
|
||||
<td class="failed_tasks error">
|
||||
|
@ -91,13 +98,6 @@
|
|||
<a href="{%url "builddashboard" build.id%}#images">{{fstypes|get_dict_value:build.id}}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if MANAGED %}
|
||||
<td class="project">
|
||||
{% if build.project %}
|
||||
<a href="{% url 'project' build.project.id %}">{{build.project.name}}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
|
||||
|
||||
|
@ -114,6 +114,11 @@
|
|||
<td class="machine">
|
||||
<a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}">{{buildrequest.machine}}</a>
|
||||
</td>
|
||||
{% if MANAGED %}
|
||||
<td class="project">
|
||||
<a href="{% url 'project' buildrequest.project.id %}">{{buildrequest.project.name}}</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="started_on">
|
||||
<a href="{% url "buildrequestdetails" buildrequest.project.id buildrequest.id %}">{{buildrequest.created|date:"d/m/y H:i"}}</a>
|
||||
</td>
|
||||
|
@ -132,9 +137,6 @@
|
|||
</td>
|
||||
<td class="output"> {# we have no output here #}
|
||||
</td>
|
||||
<td class="project">
|
||||
<a href="{% url 'project' buildrequest.project.id %}">{{buildrequest.project.name}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{%endif%}
|
||||
{% endfor %}
|
||||
|
|
|
@ -12,25 +12,61 @@
|
|||
|
||||
<div class="page-header top-air">
|
||||
<h1>
|
||||
All projects
|
||||
{% if request.GET.filter and objects.paginator.count > 0 or request.GET.search and objects.paginator.count > 0 %}
|
||||
{{objects.paginator.count}} project{{objects.paginator.count|pluralize}} found
|
||||
{%elif request.GET.filter and objects.paginator.count == 0 or request.GET.search and objects.paginator.count == 0 %}
|
||||
No projects found
|
||||
{%else%}
|
||||
All projects
|
||||
{%endif%}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{% include "basetable_top_projectbuilds.html" %}
|
||||
{% if objects.paginator.count == 0 %}
|
||||
<div class="row-fluid">
|
||||
<div class="alert">
|
||||
<form class="no-results input-append" id="searchform">
|
||||
<input id="search" name="search" class="input-xxlarge" type="text" value="{{request.GET.search}}"/>{% if request.GET.search %}<a href="javascript:$('#search').val('');searchform.submit()" class="add-on btn" tabindex="-1"><i class="icon-remove"></i></a>{% endif %}
|
||||
<button class="btn" type="submit" value="Search">Search</button>
|
||||
<button class="btn btn-link" onclick="javascript:$('#search').val('');searchform.submit()">Show all projects</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %} {# We have builds to display #}
|
||||
{% include "basetable_top_projectbuilds.html" %}
|
||||
{% for o in objects %}
|
||||
<tr class="data">
|
||||
<td><a href="{% url 'project' o.id %}">{{o.name}}</a></td>
|
||||
<td><a href="{% url 'project' o.id %}">{{o.release.name}}</a></td>
|
||||
<td>{{o.get_current_machine_name}}</td>
|
||||
<td>{{o.get_number_of_builds}}</td>
|
||||
<td class="loutcome">{{o.get_last_outcome}}</td>
|
||||
<td class="ltarget">{{o.get_last_target}}</td>
|
||||
<td class="lerrors">{{o.get_last_errors}}</td>
|
||||
<td class="lwarnings">{{o.get_last_warnings}}</td>
|
||||
<td class="limagefiles">{{o.get_last_imgfiles}}</td>
|
||||
<td class="updated">{{o.updated|date:"d/m/y H:i"}}</td>
|
||||
<td><a href="{% url 'project' o.id %}#project-details">{{o.release.name}}</a></td>
|
||||
<td><a href="{% url 'project' o.id %}#machine-distro">{{o.get_current_machine_name}}</a></td>
|
||||
{% if o.get_number_of_builds == 0 %}
|
||||
<td class="muted">{{o.get_number_of_builds}}</td>
|
||||
<td class="updated"></td>
|
||||
<td class="loutcome"></td>
|
||||
<td class="ltarget"></td>
|
||||
<td class="lerrors"></td>
|
||||
<td class="lwarnings"></td>
|
||||
<td class="limagefiles"></td>
|
||||
{% else %}
|
||||
<td><a href="{% url 'projectbuilds' o.id %}">{{o.get_number_of_builds}}</a></td>
|
||||
<td class="updated"><a href="{% url "builddashboard" o.get_last_build_id %}">{{o.updated|date:"d/m/y H:i"}}</a></td>
|
||||
<td class="loutcome"><a href="{% url "builddashboard" o.get_last_build_id %}">{%if o.get_last_outcome == build_SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif o.get_last_outcome == build_FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a></td>
|
||||
<td class="ltarget"><a href="{% url "builddashboard" o.get_last_build_id %}">{{o.get_last_target}} </a></td>
|
||||
<td class="lerrors">{% if o.get_last_errors %}<a class="errors_no error" href="{% url "builddashboard" o.get_last_build_id %}#errors">{{o.get_last_errors}} error{{o.get_last_errors|pluralize}}</a>{%endif%}</td>
|
||||
<td class="lwarnings">{% if o.get_last_warnings %}<a class="warnings_no warning" href="{% url "builddashboard" o.get_last_build_id %}#warnings">{{o.get_last_warnings}} warning{{o.get_last_warnings|pluralize}}</a>{%endif%}</td>
|
||||
<td class="limagefiles">
|
||||
{% if o.get_last_outcome == build_SUCCEEDED %}
|
||||
<a href="{%url "builddashboard" o.get_last_build_id %}#images">{{fstypes|get_dict_value:o.id}}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% include "basetable_bottom.html" %}
|
||||
{% include "basetable_bottom.html" %}
|
||||
{% endif %} {# empty #}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
|
|
@ -1758,20 +1758,10 @@ if toastermain.settings.MANAGED:
|
|||
buildrequests = BuildRequest.objects.exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED)
|
||||
|
||||
try:
|
||||
context, pagesize, orderby = _build_list_helper(request, buildrequests)
|
||||
context, pagesize, orderby = _build_list_helper(request, buildrequests, True)
|
||||
except InvalidRequestException as e:
|
||||
return _redirect_parameters( builds, request.GET, e.response)
|
||||
|
||||
context['tablecols'].append(
|
||||
{'name': 'Project', 'clclass': 'projectx',
|
||||
'filter': {'class': 'project',
|
||||
'label': 'Project:',
|
||||
'options': map(lambda x: (x.name,'project:%d' % x.id,x.build_set.filter(outcome__lt=BuildRequest.REQ_INPROGRESS).count()), Project.objects.all()),
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
response = render(request, template, context)
|
||||
_save_parameters_cookies(response, pagesize, orderby, request)
|
||||
return response
|
||||
|
@ -1779,7 +1769,7 @@ if toastermain.settings.MANAGED:
|
|||
|
||||
|
||||
# helper function, to be used on "all builds" and "project builds" pages
|
||||
def _build_list_helper(request, buildrequests):
|
||||
def _build_list_helper(request, buildrequests, insert_projects):
|
||||
# ATTN: we use here the ordering parameters for interactive mode; the translation for BuildRequest fields will happen below
|
||||
default_orderby = 'completed_on:-'
|
||||
(pagesize, orderby) = _get_parameters_values(request, 10, default_orderby)
|
||||
|
@ -1893,6 +1883,16 @@ if toastermain.settings.MANAGED:
|
|||
'ordericon':_get_toggle_order_icon(request, "build__machine"),
|
||||
'dclass': 'span3'
|
||||
}, # a slightly wider column
|
||||
]
|
||||
}
|
||||
|
||||
if (insert_projects):
|
||||
context['tablecols'].append(
|
||||
{'name': 'Project', 'clclass': 'project',
|
||||
}
|
||||
)
|
||||
|
||||
context['tablecols'].append(
|
||||
{'name': 'Started on', 'clclass': 'started_on', 'hidden' : 1, # this is an unchecked box, which hides the column
|
||||
'qhelp': "The date and time you started the build",
|
||||
'orderfield': _get_toggle_order(request, "created", True),
|
||||
|
@ -1905,7 +1905,9 @@ if toastermain.settings.MANAGED:
|
|||
("This week's builds", 'created__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_all.filter(created__gte=(timezone.now()-timedelta(days=7))).count()),
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
context['tablecols'].append(
|
||||
{'name': 'Completed on',
|
||||
'qhelp': "The date and time the build finished",
|
||||
'orderfield': _get_toggle_order(request, "updated", True),
|
||||
|
@ -1919,7 +1921,9 @@ if toastermain.settings.MANAGED:
|
|||
("This week's builds", 'updated__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_all.filter(updated__gte=(timezone.now()-timedelta(days=7))).count()),
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
context['tablecols'].append(
|
||||
{'name': 'Failed tasks', 'clclass': 'failed_tasks', # specifing a clclass will enable the checkbox
|
||||
'qhelp': "How many tasks failed during the build",
|
||||
'filter' : {'class' : 'failed_tasks',
|
||||
|
@ -1931,7 +1935,9 @@ if toastermain.settings.MANAGED:
|
|||
queryset_all.filter(~Q(build__task_build__outcome=Task.OUTCOME_FAILED)).count()),
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
context['tablecols'].append(
|
||||
{'name': 'Errors', 'clclass': 'errors_no',
|
||||
'qhelp': "How many errors were encountered during the build (if any)",
|
||||
'orderfield': _get_toggle_order(request, "build__errors_no", True),
|
||||
|
@ -1946,7 +1952,9 @@ if toastermain.settings.MANAGED:
|
|||
queryset_all.filter(build__errors_no=0).count()),
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
context['tablecols'].append(
|
||||
{'name': 'Warnings', 'clclass': 'warnings_no',
|
||||
'qhelp': "How many warnings were encountered during the build (if any)",
|
||||
'orderfield': _get_toggle_order(request, "build__warnings_no", True),
|
||||
|
@ -1959,19 +1967,23 @@ if toastermain.settings.MANAGED:
|
|||
('Builds without warnings','build__warnings_no:0', queryset_all.filter(build__warnings_no=0).count()),
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
context['tablecols'].append(
|
||||
{'name': 'Time', 'clclass': 'time', 'hidden' : 1,
|
||||
'qhelp': "How long it took the build to finish",
|
||||
# 'orderfield': _get_toggle_order(request, "timespent", True),
|
||||
# 'ordericon':_get_toggle_order_icon(request, "timespent"),
|
||||
# 'orderfield': _get_toggle_order(request, "timespent", True),
|
||||
# 'ordericon':_get_toggle_order_icon(request, "timespent"),
|
||||
'orderkey' : 'timespent',
|
||||
},
|
||||
}
|
||||
)
|
||||
context['tablecols'].append(
|
||||
{'name': 'Image files', 'clclass': 'output',
|
||||
'qhelp': "The root file system types produced by the build. You can find them in your <code>/build/tmp/deploy/images/</code> directory",
|
||||
# TODO: compute image fstypes from Target_Image_File
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return context, pagesize, orderby
|
||||
|
||||
# new project
|
||||
|
@ -2898,7 +2910,7 @@ if toastermain.settings.MANAGED:
|
|||
buildrequests = BuildRequest.objects.filter(project_id = pid).exclude(state__lte = BuildRequest.REQ_INPROGRESS).exclude(state=BuildRequest.REQ_DELETED)
|
||||
|
||||
try:
|
||||
context, pagesize, orderby = _build_list_helper(request, buildrequests)
|
||||
context, pagesize, orderby = _build_list_helper(request, buildrequests, False)
|
||||
except InvalidRequestException as e:
|
||||
return _redirect_parameters(projectbuilds, request.GET, e.response, pid = pid)
|
||||
|
||||
|
@ -3019,7 +3031,7 @@ if toastermain.settings.MANAGED:
|
|||
def projects(request):
|
||||
template="projects.html"
|
||||
|
||||
(pagesize, orderby) = _get_parameters_values(request, 10, 'updated:+')
|
||||
(pagesize, orderby) = _get_parameters_values(request, 10, 'updated:-')
|
||||
mandatory_parameters = { 'count': pagesize, 'page' : 1, 'orderby' : orderby }
|
||||
retval = _verify_parameters( request.GET, mandatory_parameters )
|
||||
if retval:
|
||||
|
@ -3039,7 +3051,27 @@ if toastermain.settings.MANAGED:
|
|||
# build view-specific information; this is rendered specifically in the builds page, at the top of the page (i.e. Recent builds)
|
||||
build_mru = Build.objects.order_by("-started_on")[:3]
|
||||
|
||||
|
||||
# translate the project's build target strings
|
||||
fstypes_map = {};
|
||||
for project in project_info:
|
||||
try:
|
||||
targets = Target.objects.filter( build_id = project.get_last_build_id() )
|
||||
comma = "";
|
||||
extensions = "";
|
||||
for t in targets:
|
||||
if ( not t.is_image ):
|
||||
continue
|
||||
tif = Target_Image_File.objects.filter( target_id = t.id )
|
||||
for i in tif:
|
||||
s=re.sub('.*tar.bz2', 'tar.bz2', i.file_name)
|
||||
if s == i.file_name:
|
||||
s=re.sub('.*\.', '', i.file_name)
|
||||
if None == re.search(s,extensions):
|
||||
extensions += comma + s
|
||||
comma = ", "
|
||||
fstypes_map[project.id]=extensions
|
||||
except (Target.DoesNotExist,IndexError):
|
||||
fstypes_map[project.id]=project.get_last_imgfiles
|
||||
|
||||
context = {
|
||||
'mru' : build_mru,
|
||||
|
@ -3049,6 +3081,9 @@ if toastermain.settings.MANAGED:
|
|||
'default_orderby' : 'id:-',
|
||||
'search_term' : search_term,
|
||||
'total_count' : queryset_with_search.count(),
|
||||
'fstypes' : fstypes_map,
|
||||
'build_FAILED' : Build.FAILED,
|
||||
'build_SUCCEEDED' : Build.SUCCEEDED,
|
||||
'tablecols': [
|
||||
{'name': 'Project',
|
||||
'orderfield': _get_toggle_order(request, "name"),
|
||||
|
@ -3067,6 +3102,11 @@ if toastermain.settings.MANAGED:
|
|||
{'name': 'Number of builds',
|
||||
'qhelp': "How many builds have been run for the project",
|
||||
},
|
||||
{'name': 'Last build', 'clclass': 'updated',
|
||||
'orderfield': _get_toggle_order(request, "updated", True),
|
||||
'ordericon':_get_toggle_order_icon(request, "updated"),
|
||||
'orderkey' : 'updated',
|
||||
},
|
||||
{'name': 'Last outcome', 'clclass': 'loutcome',
|
||||
'qhelp': "Tells you if the last project build completed successfully or failed",
|
||||
},
|
||||
|
@ -3082,11 +3122,6 @@ if toastermain.settings.MANAGED:
|
|||
{'name': 'Last image files', 'clclass': 'limagefiles', 'hidden': 1,
|
||||
'qhelp': "The root file system types produced by the last project build",
|
||||
},
|
||||
{'name': 'Last updated', 'clclass': 'updated',
|
||||
'orderfield': _get_toggle_order(request, "updated"),
|
||||
'ordericon':_get_toggle_order_icon(request, "updated"),
|
||||
'orderkey' : 'updated',
|
||||
}
|
||||
]
|
||||
}
|
||||
return render(request, template, context)
|
||||
|
|
Loading…
Reference in New Issue