Skip to contentSkip to conversation

Implement UI changes for "command line builds" project

PATCH Superseded Elliot Smith 2 participants 11 messages Oct 2, 2015 – Oct 5, 2015

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

cover Implement UI changes for "command line builds" project

This implements changes across the UI to hide irrelevant options for command-line builds, and show additional help icons explaining why those options are hidden.

For details of the changes, see the document attached to bug 8231 at https://bugzilla.yoctoproject.org/attachment.cgi?id=2760.

Notes:

* One of the changes in the design document has not been implemented:
it's not possible to hide the "customise image recipe" links and buttons (page 9) as these are not in the templates yet.

* The visual design differs slightly from the design document, as it incorporates a new layout for the build dashboard which is not implemented yet.

* The "New build" button is displayed on the command line builds project page, as we came to the conclusion that the logic in the JavaScript (i.e. show the new build button if there is no build input form on the page) should apply in the case of the builds page for the "command line builds" project.

Changes since af1e3160922a30b8a538959335a5f8945f2f3549 (toaster-next) are in
git://git.yoctoproject.org/poky-contrib, elliot/toaster/cli_builds_ui-8231
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=elliot/toaster/cli_builds_ui-8231

Related bug: https://bugzilla.yoctoproject.org/show_bug.cgi?id=8231

Elliot Smith (9):
toaster: Reorganise and lint tests
toaster: Replace "Run again" button with help text for cli builds
toaster: Show 'not applicable' for default project machine and release
toaster: Make the builds view the project page for "command line
builds"
toaster: Hide tabs and add info popups for command line builds
toaster: Show tooltip next to cli builds project name in all builds
toaster: More linting of tests
toaster: Clean up template code
toaster: Exclude "command line builds" project from projects typeahead

 .../lib/toaster/toastergui/static/css/default.css  |   5 +
 bitbake/lib/toaster/toastergui/templates/base.html |   5 +-
 .../lib/toaster/toastergui/templates/builds.html   |  19 +-
 .../toaster/toastergui/templates/mrb_section.html  |  66 +++--
 .../lib/toaster/toastergui/templates/projects.html |  23 +-
 .../toastergui/templates/projecttopbar.html        |  80 +++---
 .../toastergui/templatetags/project_url_tag.py     |  34 +++
 bitbake/lib/toaster/toastergui/tests.py            | 311 +++++++++++++++++----
 bitbake/lib/toaster/toastergui/typeaheads.py       |   4 +-
 9 files changed, 420 insertions(+), 127 deletions(-)
 create mode 100644 bitbake/lib/toaster/toastergui/templatetags/project_url_tag.py

--
Elliot Smith
Software Engineer
Intel OTC

---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.

#1 lore raw

PATCH 1/9 toaster: Reorganise and lint tests

Get test suite ready to accommodate new tests for how command line builds are shown on various pages.

---
 bitbake/lib/toaster/toastergui/tests.py | 83 +++++++++++++++++++--------------
 1 file changed, 49 insertions(+), 34 deletions(-)
bitbake/lib/toaster/toastergui/tests.py +49 -34
index 95790a2..7b1f9b5 100644
@@ -21,23 +21,20 @@
 
 """Test cases for Toaster GUI and ReST."""
 
-import re
-
 from django.test import TestCase
 from django.test.client import RequestFactory
 from django.core.urlresolvers import reverse
 from django.utils import timezone
 
-from orm.models import Project, Release, BitbakeVersion, Build, Package
+from orm.models import Project, Release, BitbakeVersion, Package
 from orm.models import ReleaseLayerSourcePriority, LayerSource, Layer, Build
 from orm.models import Layer_Version, Recipe, Machine, ProjectLayer, Target
 from orm.models import CustomImageRecipe
 from orm.models import Branch
 
 from toastergui.tables import SoftwareRecipesTable
-from django.utils import timezone
-import json
 from bs4 import BeautifulSoup
+import json
 import re
 
 PROJECT_NAME = "test project"
@@ -72,11 +69,11 @@ class ViewTests(TestCase):
                                             up_branch=branch)
 
         self.recipe1 = Recipe.objects.create(layer_source=layersrc,
-                                       name="base-recipe",
-                                       version="1.2",
-                                       summary="one recipe",
-                                       description="recipe",
-                                       layer_version=lver)
+                                             name="base-recipe",
+                                             version="1.2",
+                                             summary="one recipe",
+                                             description="recipe",
+                                             layer_version=lver)
 
         Machine.objects.create(layer_version=lver, name="wisk",
                                description="wisking machine")
@@ -337,11 +334,7 @@ class ViewTests(TestCase):
 
         rows = data['rows']
         row1 = next(x for x in rows if x['name'] == self.recipe1.name)
-        row1_btns = row1['static:add-del-layers']
-        row1_btns_data = row1['add-del-layers']
         row2 = next(x for x in rows if x['name'] == self.recipe2.name)
-        row2_btns = row2['static:add-del-layers']
-        row2_btns_data = row2['add-del-layers']
 
         self.assertEqual(response.status_code, 200, 'should be 200 OK status')
         self.assertEqual(len(rows), 2, 'should be 2 recipes')
@@ -472,7 +465,7 @@ class ProjectsPageTests(TestCase):
         self.assertTrue(self.PROJECT_NAME in response.content,
                         'default project "cli builds" should be in page')
 
-class ProjectBuildsDisplayTest(TestCase):
+class ProjectBuildsPageTests(TestCase):
     """ Test data at /project/X/builds is displayed correctly """
 
     def setUp(self):
@@ -517,6 +510,7 @@ class ProjectBuildsDisplayTest(TestCase):
         }
 
     def _get_rows_for_project(self, project_id):
+        """ Helper to retrieve HTML rows for a project """
         url = reverse("projectbuilds", args=(project_id,))
         response = self.client.get(url, follow=True)
         soup = BeautifulSoup(response.content)
@@ -524,52 +518,73 @@ class ProjectBuildsDisplayTest(TestCase):
 
     def test_show_builds_for_project(self):
         """ Builds for a project should be displayed """
-        build1a = Build.objects.create(**self.project1_build_success)
-        build1b = Build.objects.create(**self.project1_build_success)
+        Build.objects.create(**self.project1_build_success)
+        Build.objects.create(**self.project1_build_success)
         build_rows = self._get_rows_for_project(self.project1.id)
         self.assertEqual(len(build_rows), 2)
 
-    def test_show_builds_for_project_only(self):
+    def test_show_builds_project_only(self):
         """ Builds for other projects should be excluded """
-        build1a = Build.objects.create(**self.project1_build_success)
-        build1b = Build.objects.create(**self.project1_build_success)
-        build1c = Build.objects.create(**self.project1_build_success)
+        Build.objects.create(**self.project1_build_success)
+        Build.objects.create(**self.project1_build_success)
+        Build.objects.create(**self.project1_build_success)
 
         # shouldn't see these two
-        build2a = Build.objects.create(**self.project2_build_success)
-        build2b = Build.objects.create(**self.project2_build_in_progress)
+        Build.objects.create(**self.project2_build_success)
+        Build.objects.create(**self.project2_build_in_progress)
 
         build_rows = self._get_rows_for_project(self.project1.id)
         self.assertEqual(len(build_rows), 3)
 
-    def test_show_builds_exclude_in_progress(self):
+    def test_builds_exclude_in_progress(self):
         """ "in progress" builds should not be shown """
-        build1a = Build.objects.create(**self.project1_build_success)
-        build1b = Build.objects.create(**self.project1_build_success)
+        Build.objects.create(**self.project1_build_success)
+        Build.objects.create(**self.project1_build_success)
 
         # shouldn't see this one
-        build1c = Build.objects.create(**self.project1_build_in_progress)
+        Build.objects.create(**self.project1_build_in_progress)
 
         # shouldn't see these two either, as they belong to a different project
-        build2a = Build.objects.create(**self.project2_build_success)
-        build2b = Build.objects.create(**self.project2_build_in_progress)
+        Build.objects.create(**self.project2_build_success)
+        Build.objects.create(**self.project2_build_in_progress)
 
         build_rows = self._get_rows_for_project(self.project1.id)
         self.assertEqual(len(build_rows), 2)
 
-    def test_show_tasks_in_projectbuilds(self):
+    def test_tasks_in_projectbuilds(self):
+        """ Task should be shown as suffix on build name """
         build = Build.objects.create(**self.project1_build_success)
-        target = Target.objects.create(build=build, target='bash',
-                                       task='clean')
+        Target.objects.create(build=build, target='bash', task='clean')
         url = reverse("projectbuilds", args=(self.project1.id,))
         response = self.client.get(url, follow=True)
         result = re.findall('^ +bash:clean$', response.content, re.MULTILINE)
         self.assertEqual(len(result), 2)
 
+class AllBuildsPageTests(TestCase):
+    """ Tests for all builds page """
+
+    def setUp(self):
+        bbv = BitbakeVersion.objects.create(name="bbv1", giturl="/tmp/",
+                                            branch="master", dirpath="")
+        release = Release.objects.create(name="release1",
+                                         bitbake_version=bbv)
+        self.project1 = Project.objects.create_project(name=PROJECT_NAME,
+                                                       release=release)
+
+        # parameters for builds to associate with the projects
+        now = timezone.now()
+
+        self.project1_build_success = {
+            "project": self.project1,
+            "started_on": now,
+            "completed_on": now,
+            "outcome": Build.SUCCEEDED
+        }
+
     def test_show_tasks_in_allbuilds(self):
+        """ Task should be shown as suffix on build name """
         build = Build.objects.create(**self.project1_build_success)
-        target = Target.objects.create(build=build, target='bash',
-                                       task='clean')
+        Target.objects.create(build=build, target='bash', task='clean')
         url = reverse("all-builds")
         response = self.client.get(url, follow=True)
         result = re.findall('bash:clean', response.content, re.MULTILINE)
Signature · 14 lines
Elliot Smith
Software Engineer
Intel OTC

---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
#2 lore raw

PATCH 2/9 toaster: Replace "Run again" button with help text for cli builds

In reply to Elliot Smith

It's not possible to run a command-line build again, as Toaster doesn't have access to the data used for that build.

Replace the "Run again" button with an icon which pops up some help text to that effect.

Add test to check that the run again button is hidden and the help icon displayed instead for command-line builds.

[YOCTO #8231]

---
 .../lib/toaster/toastergui/static/css/default.css  |  5 +++
 .../toaster/toastergui/templates/mrb_section.html  | 41 ++++++++++++++--------
 bitbake/lib/toaster/toastergui/tests.py            | 32 +++++++++++++++++
 3 files changed, 64 insertions(+), 14 deletions(-)
bitbake/lib/toaster/toastergui/static/css/default.css +5 -0
index cce3e31..336a407 100644
@@ -15,6 +15,8 @@
 /* Styles for the help information */
 .get-help { color: #CCCCCC; }
 .get-help:hover, .icon-plus-sign:hover { color: #999999; cursor: pointer; }
+.get-help-green { color: #468847; }
+.get-help-green:hover { color: #347132; cursor: pointer; }
 .get-help-blue { color: #3A87AD; }
 .get-help-blue:hover { color: #005580; cursor: pointer; }
 .get-help-yellow { color: #C09853; }
@@ -161,6 +163,9 @@ table { table-layout: fixed; word-wrap: break-word; }
 .project-name .label { font-weight: normal; margin-bottom: 5px; margin-left: -15px; padding: 5px; }
 .project-name .label > a { color: #fff; font-weight: normal; }
 
+/* styles for showing help icons next to command-line builds */
+.build-result .get-help-green, .build-result .get-help-red, .build-result .get-help-blue { margin-right: 35px; margin-top: 8px; font-size: 16px; }
+
 /* Remove bottom margin for forms inside modal dialogs */
 #dependencies-modal-form { margin-bottom: 0px; }
 
bitbake/lib/toaster/toastergui/templates/mrb_section.html +27 -14

58 lines not on this page · raw message

bitbake/lib/toaster/toastergui/tests.py +32 -0
index 7b1f9b5..00ae9d4 100644
@@ -38,6 +38,7 @@ import json
 import re
 
 PROJECT_NAME = "test project"
+CLI_BUILDS_PROJECT_NAME = 'Command line builds'
 
 class ViewTests(TestCase):
     """Tests to verify view APIs."""
@@ -570,6 +571,12 @@ class AllBuildsPageTests(TestCase):
                                          bitbake_version=bbv)
         self.project1 = Project.objects.create_project(name=PROJECT_NAME,
                                                        release=release)
+        self.default_project = Project.objects.create_project(
+            name=CLI_BUILDS_PROJECT_NAME,
+            release=release
+        )
+        self.default_project.is_default = True
+        self.default_project.save()
 
         # parameters for builds to associate with the projects
         now = timezone.now()
@@ -581,6 +588,13 @@ class AllBuildsPageTests(TestCase):
             "outcome": Build.SUCCEEDED
         }
 
+        self.default_project_build_success = {
+            "project": self.default_project,
+            "started_on": now,
+            "completed_on": now,
+            "outcome": Build.SUCCEEDED
+        }
+
     def test_show_tasks_in_allbuilds(self):
         """ Task should be shown as suffix on build name """
         build = Build.objects.create(**self.project1_build_success)
@@ -589,3 +603,21 @@ class AllBuildsPageTests(TestCase):
         response = self.client.get(url, follow=True)
         result = re.findall('bash:clean', response.content, re.MULTILINE)
         self.assertEqual(len(result), 3)
+
+    def test_no_run_again_for_cli_build(self):
+        """ "Run again" button should not be shown for command-line builds """
+        build = Build.objects.create(**self.default_project_build_success)
+        url = reverse("all-builds")
+        response = self.client.get(url, follow=True)
+        soup = BeautifulSoup(response.content)
+
+        element_id = 'build-result-%d' % build.id
+
+        # shouldn't see a run again button for command-line builds
+        run_again_button = soup.select('#%s button' % element_id)
+        self.assertEqual(len(run_again_button), 0)
+
+        # should see a help icon for command-line builds
+        help_icon = soup.select('#%s i.get-help-green' % element_id)
+        self.assertEqual(len(help_icon), 1)
+
Signature · 14 lines
Elliot Smith
Software Engineer
Intel OTC

---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
#3 lore raw

PATCH 3/9 toaster: Show 'not applicable' for default project machine and release

In reply to Elliot Smith

The machine and release for the default project should show as 'not applicable' on the all projects page, as that information isn't available for command-line builds.

Modify the templates with some conditionals to check for the default project row, plus some data-* attributes to mark where that data is to make testing possible.

Add some tests for the all projects page to ensure that the correct machine/release are still shown for non-default projects, and 'not applicable' for the default project.

[YOCTO #8231]

---
 .../lib/toaster/toastergui/templates/projects.html |  16 ++-
 bitbake/lib/toaster/toastergui/tests.py            | 108 +++++++++++++++++++--
 2 files changed, 111 insertions(+), 13 deletions(-)
bitbake/lib/toaster/toastergui/templates/projects.html +13 -3
index c2d77b5..a7192c2 100644
@@ -36,17 +36,27 @@
   {% else %} {# We have builds to display #}
   {% include "basetable_top.html" %}
   {% for o in objects %}
-    <tr class="data">
+    <tr class="data" data-project="{{ o.id }}">
       <td><a href="{% url 'project' o.id %}">{{o.name}}</a></td>
       <td class="updated"><a href="{% url 'project' o.id %}">{{o.updated|date:"d/m/y H:i"}}</a></td>
-      <td>
+      <td data-project-field="release">
         {% if o.release %}
             <a href="{% url 'project' o.id %}#project-details">{{o.release.name}}</a>
+        {% elif o.is_default %}
+            <span class="muted">Not applicable</span>
+            <i class="icon-question-sign get-help hover-help" title="" data-original-title="This project does not have a release set. It simply collects information about the builds you start from the command line while Toaster is running" style="visibility: hidden;"></i>
         {% else %}
             No release available
         {% endif %}
       </td>
-      <td><a href="{% url 'project' o.id %}#machine-distro">{{o.get_current_machine_name}}</a></td>
+      <td data-project-field="machine">
+        {% if o.is_default %}
+            <span class="muted">Not applicable</span>
+            <i class="icon-question-sign get-help hover-help" title="" data-original-title="This project does not have a machine set. It simply collects information about the builds you start from the command line while Toaster is running" style="visibility: hidden;"></i>
+        {% else %}
+            <a href="{% url 'project' o.id %}#machine-distro">{{o.get_current_machine_name}}</a>
+        {% endif %}
+      </td>
       {% if o.get_number_of_builds == 0 %}
       <td class="muted">{{o.get_number_of_builds}}</td>
       <td class="loutcome"></td>
bitbake/lib/toaster/toastergui/tests.py +98 -10

145 lines not on this page · raw message

Signature · 14 lines
Elliot Smith
Software Engineer
Intel OTC

---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
#4 lore raw

PATCH 4/9 toaster: Make the builds view the project page for "command line builds"

In reply to Elliot Smith

Command line builds don't have configuration or layers which can be manipulated in Toaster, so these pages shouldn't be visible. However, the configuration page is the default page for the project view (/project/X/), which isn't correct for the command line builds project.

Modify all project page links across the application so that the command line builds project (aka the "default" project) always displays the builds tab.

Add a project_url tag for templates which contains the logic determining where the URL for a project links to, based on whether it is the default project or not.

[YOCTO #8231]

---
 bitbake/lib/toaster/toastergui/templates/base.html |  5 +--
 .../lib/toaster/toastergui/templates/builds.html   |  3 +-
 .../toaster/toastergui/templates/mrb_section.html  |  6 ++--
 .../lib/toaster/toastergui/templates/projects.html |  7 ++--
 .../toastergui/templatetags/project_url_tag.py     | 34 +++++++++++++++++++
 bitbake/lib/toaster/toastergui/tests.py            | 38 ++++++++++++++++++++--
 6 files changed, 82 insertions(+), 11 deletions(-)
 create mode 100644 bitbake/lib/toaster/toastergui/templatetags/project_url_tag.py
bitbake/lib/toaster/toastergui/templates/base.html +3 -2
index 640bc47..3f27790 100644
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 {% load static %}
 {% load projecttags %}
+{% load project_url_tag %}
 <html lang="en">
     <head>
         <title>{% if objectname %} {{objectname|title}} - {% endif %}Toaster</title>
@@ -35,7 +36,7 @@
         projectsTypeAheadUrl: {% url 'xhr_projectstypeahead' as prjurl%}{{prjurl|json}},
         {% if project.id %}
         projectId : {{project.id}},
-        projectPageUrl : {% url 'project' project.id as purl%}{{purl|json}},
+        projectPageUrl : {% url 'project' project.id as purl %}{{purl|json}},
         projectName : {{project.name|json}},
         recipesTypeAheadUrl: {% url 'xhr_recipestypeahead' project.id as paturl%}{{paturl|json}},
         layersTypeAheadUrl: {% url 'xhr_layerstypeahead' project.id as paturl%}{{paturl|json}},
@@ -133,7 +134,7 @@
                 <h6>Project:</h6>
                 <span id="project">
                   {% if project.id %}
-                  <a class="lead" href="{% url 'project' project.id %}">{{project.name}}</a>
+                  <a class="lead" href="{% project_url project %}">{{project.name}}</a>
                   {% else %}
                   <a class="lead" href="#"></a>
                   {% endif %}
bitbake/lib/toaster/toastergui/templates/builds.html +2 -1
index 2b35b01..6fbaf98 100644
@@ -2,6 +2,7 @@
 
 {% load static %}
 {% load projecttags %}
+{% load project_url_tag %}
 {% load humanize %}
 
 {% block extraheadcontent %}
@@ -104,7 +105,7 @@
               {% endif %}
             </td>
         <td>
-                <a href="{% url 'project' build.project.id %}">{{build.project.name}}</a>
+                <a href="{% project_url build.project %}">{{build.project.name}}</a>
             </td>
         </tr>
 
bitbake/lib/toaster/toastergui/templates/mrb_section.html +3 -3
index 5e96b39..b29f650 100644
@@ -1,8 +1,8 @@
 {% load static %}
 {% load projecttags %}
+{% load project_url_tag %}
 {% load humanize %}
 
-
 {%if mru and mru.count > 0%}
 
   {%if mrb_type == 'project' %}
@@ -22,7 +22,7 @@
     {% if mrb_type != 'project' %}
     project-name">
        <span class="label {%if build.outcome == build.SUCCEEDED%}label-success{%elif build.outcome == build.FAILED%}label-important{%else%}label-info{%endif%}">
-         <a href={% url 'project' build.project.pk %}>
+         <a href={% project_url build.project %}>
            {{build.project.name}}
          </a>
        </span>
@@ -106,7 +106,7 @@
                       pull-right"
                       onclick='scheduleBuild({% url 'projectbuilds' build.project.id as bpi %}{{bpi|json}},
                         {{build.project.name|json}},
-                        {% url 'project' build.project.id as bpurl %}{{bpurl|json}},
+                        {% url 'project' build.project.id as purl %}{{purl|json}},
                         {{build.target_set.all|get_tasks|json}})'>
 
                         Run again
bitbake/lib/toaster/toastergui/templates/projects.html +5 -2
index a7192c2..7c612e8 100644
@@ -2,6 +2,7 @@
 
 {% load static %}
 {% load projecttags %}
+{% load project_url_tag %}
 {% load humanize %}
 
 {% block pagecontent %}
@@ -37,8 +38,10 @@
   {% include "basetable_top.html" %}
   {% for o in objects %}
     <tr class="data" data-project="{{ o.id }}">
-      <td><a href="{% url 'project' o.id %}">{{o.name}}</a></td>
-      <td class="updated"><a href="{% url 'project' o.id %}">{{o.updated|date:"d/m/y H:i"}}</a></td>
+      <td data-project-field="name">
+          <a href="{% project_url o %}">{{o.name}}</a>
+      </td>
+      <td class="updated"><a href="{% project_url o %}">{{o.updated|date:"d/m/y H:i"}}</a></td>
       <td data-project-field="release">
         {% if o.release %}
             <a href="{% url 'project' o.id %}#project-details">{{o.release.name}}</a>
bitbake/lib/toaster/toastergui/templatetags/project_url_tag.py new file +34 -0
new file mode 100644
index 0000000..04770ac
@@ -0,0 +1,34 @@
+from django import template
+from django.core.urlresolvers import reverse
+
+register = template.Library()
+
+def project_url(parser, token):
+    """
+    Create a URL for a project's main page;
+    for non-default projects, this is the configuration page;
+    for the default project, this is the project builds page
+    """
+    try:
+        tag_name, project = token.split_contents()
+    except ValueError:
+        raise template.TemplateSyntaxError(
+            "%s tag requires exactly one argument" % tag_name
+        )
+    return ProjectUrlNode(project)
+
+class ProjectUrlNode(template.Node):
+    def __init__(self, project):
+        self.project = template.Variable(project)
+
+    def render(self, context):
+        try:
+            project = self.project.resolve(context)
+            if project.is_default:
+                return reverse('projectbuilds', args=(project.id,))
+            else:
+                return reverse('project', args=(project.id,))
+        except template.VariableDoesNotExist:
+            return ''
+
+register.tag('project_url', project_url)
bitbake/lib/toaster/toastergui/tests.py +35 -3
index 0b85d4f..12a91c3 100644
@@ -428,8 +428,8 @@ class LandingPageTests(TestCase):
         self.assertTrue('/builds' in response.url,
                         'should redirect to builds')
 
-class ProjectsPageTests(TestCase):
-    """ Tests for projects page """
+class AllProjectsPageTests(TestCase):
+    """ Tests for projects page /projects/ """
 
     MACHINE_NAME = 'delorean'
 
@@ -554,6 +554,38 @@ class ProjectsPageTests(TestCase):
         self.assertEqual(text, self.MACHINE_NAME,
                          'machine name should be shown for non-default project')
 
+    def test_project_page_links(self):
+        """
+        Test that links for the default project point to the builds
+        page /projects/X/builds for that project, and that links for
+        other projects point to their configuration pages /projects/X/
+        """
+
+        # need a build, otherwise project doesn't display at all
+        self._add_build_to_default_project()
+
+        # another project to test, which should show machine
+        self._add_non_default_project()
+
+        response = self.client.get(reverse('all-projects'), follow=True)
+        soup = BeautifulSoup(response.content)
+
+        # link for default project
+        row = soup.find('tr', attrs={'data-project': self.default_project.id})
+        cell = row.find('td', attrs={'data-project-field': 'name'})
+        url = cell.find('a')['href']
+        expected_url = reverse('projectbuilds', args=(self.default_project.id,))
+        self.assertEqual(url, expected_url,
+                         'link on default project name should point to builds')
+
+        # link for other project
+        row = soup.find('tr', attrs={'data-project': self.project.id})
+        cell = row.find('td', attrs={'data-project-field': 'name'})
+        url = cell.find('a')['href']
+        expected_url = reverse('project', args=(self.project.id,))
+        self.assertEqual(url, expected_url,
+                         'link on project name should point to configuration')
+
 class ProjectBuildsPageTests(TestCase):
     """ Test data at /project/X/builds is displayed correctly """
 
@@ -650,7 +682,7 @@ class ProjectBuildsPageTests(TestCase):
         self.assertEqual(len(result), 2)
 
 class AllBuildsPageTests(TestCase):
-    """ Tests for all builds page """
+    """ Tests for all builds page /builds/ """
 
     def setUp(self):
         bbv = BitbakeVersion.objects.create(name="bbv1", giturl="/tmp/",
Signature · 14 lines
Elliot Smith
Software Engineer
Intel OTC

---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
#5 lore raw

PATCH 5/9 toaster: Hide tabs and add info popups for command line builds

In reply to Elliot Smith

Command line builds don't have a configuration or
layers which can be modified through Toaster.

Change the project builds page for the command line builds project, to hide the tabs and add some info popups in appropriate places on that page.

[YOCTO #8231]

---
 .../toaster/toastergui/templates/mrb_section.html  |  4 ++
 .../toastergui/templates/projecttopbar.html        | 80 ++++++++++++----------
 bitbake/lib/toaster/toastergui/tests.py            | 30 ++++++++
 3 files changed, 78 insertions(+), 36 deletions(-)
bitbake/lib/toaster/toastergui/templates/mrb_section.html +4 -0
index b29f650..53f40d0 100644
@@ -8,6 +8,10 @@
   {%if mrb_type == 'project' %}
       <h2>
       Latest project builds
+
+      {% if project.is_default %}
+          <i class="icon-question-sign get-help heading-help" title="" data-original-title="Builds in this project cannot be started from Toaster: they are started from the command line"></i>
+      {% endif %}
       </h2>
   {% else %}
     <div class="page-header">
bitbake/lib/toaster/toastergui/templates/projecttopbar.html +44 -36

90 lines not on this page · raw message

bitbake/lib/toaster/toastergui/tests.py +30 -0
index 12a91c3..4dd2174 100644
@@ -596,8 +596,18 @@ class ProjectBuildsPageTests(TestCase):
                                          bitbake_version=bbv)
         self.project1 = Project.objects.create_project(name=PROJECT_NAME,
                                                        release=release)
+        self.project1.save()
+
         self.project2 = Project.objects.create_project(name=PROJECT_NAME,
                                                        release=release)
+        self.project2.save()
+
+        self.default_project = Project.objects.create_project(
+            name=CLI_BUILDS_PROJECT_NAME,
+            release=release
+        )
+        self.default_project.is_default = True
+        self.default_project.save()
 
         # parameters for builds to associate with the projects
         now = timezone.now()
@@ -630,6 +640,13 @@ class ProjectBuildsPageTests(TestCase):
             "outcome": Build.IN_PROGRESS
         }
 
+        self.default_project_build_success = {
+            "project": self.default_project,
+            "started_on": now,
+            "completed_on": now,
+            "outcome": Build.SUCCEEDED
+        }
+
     def _get_rows_for_project(self, project_id):
         """ Helper to retrieve HTML rows for a project """
         url = reverse("projectbuilds", args=(project_id,))
@@ -681,6 +698,19 @@ class ProjectBuildsPageTests(TestCase):
         result = re.findall('^ +bash:clean$', response.content, re.MULTILINE)
         self.assertEqual(len(result), 2)
 
+    def test_cli_builds_hidden(self):
+        """
+        Display for command line builds should hide tabs;
+        note that the latest builds section is already tested in
+        AllBuildsPageTests, as the template is the same
+        """
+        url = reverse("projectbuilds", args=(self.default_project.id,))
+        response = self.client.get(url, follow=True)
+        soup = BeautifulSoup(response.content)
+        tabs = soup.select('#project-topbar')
+        self.assertEqual(len(tabs), 0,
+                         'should be no top bar shown for command line builds')
+
 class AllBuildsPageTests(TestCase):
     """ Tests for all builds page /builds/ """
 
Signature · 14 lines
Elliot Smith
Software Engineer
Intel OTC

---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
#6 lore raw

PATCH 6/9 toaster: Show tooltip next to cli builds project name in all builds

In reply to Elliot Smith

In the all builds page, show an icon with tooltip next to the command line builds project name.

[YOCTO #8231]

---
 .../lib/toaster/toastergui/templates/builds.html   | 11 ++++---
 .../toaster/toastergui/templates/mrb_section.html  |  2 +-
 bitbake/lib/toaster/toastergui/tests.py            | 37 +++++++++++++++++++---
 3 files changed, 40 insertions(+), 10 deletions(-)
bitbake/lib/toaster/toastergui/templates/builds.html +7 -4
index 6fbaf98..566c279 100644
@@ -63,10 +63,10 @@
   {% include "basetable_top.html" %}
         <!-- Table data rows; the order needs to match the order of "tablecols" definitions; and the <td class value needs to match the tablecols clclass value for show/hide buttons to work -->
         {% for build in objects %}
-        <tr class="data">
+        <tr class="data" data-table-build-result="{{ build.id }}">
             <td class="outcome">
-        <a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a> &nbsp;
-        </td>
+                <a href="{% url "builddashboard" build.id %}">{%if build.outcome == build.SUCCEEDED%}<i class="icon-ok-sign success"></i>{%elif build.outcome == build.FAILED%}<i class="icon-minus-sign error"></i>{%else%}{%endif%}</a> &nbsp;
+            </td>
             <td class="target">
                 {% for t in build.target_set.all %}
                     <a href="{% url "builddashboard" build.id %}">
@@ -104,8 +104,11 @@
               <a href="{%url "builddashboard" build.id%}#images">{{fstypes|get_dict_value:build.id}}</a>
               {% endif %}
             </td>
-        <td>
+            <td class="project-name">
                 <a href="{% project_url build.project %}">{{build.project.name}}</a>
+                {% if build.project.is_default %}
+                    <i class="icon-question-sign get-help hover-help" title="" data-original-title="This project shows information about the builds you start from the command line while Toaster is running" style="visibility: hidden;"></i>
+                {% endif %}
             </td>
         </tr>
 
bitbake/lib/toaster/toastergui/templates/mrb_section.html +1 -1
index 53f40d0..55687a1 100644
@@ -22,7 +22,7 @@
   {% endif %}
   <div id="latest-builds">
   {% for build in mru %}
-  <div id="build-result-{{ build.id }}" class="alert build-result {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%}
+  <div data-latest-build-result="{{ build.id }}" class="alert build-result {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%}
     {% if mrb_type != 'project' %}
     project-name">
        <span class="label {%if build.outcome == build.SUCCEEDED%}label-success{%elif build.outcome == build.FAILED%}label-important{%else%}label-info{%endif%}">
bitbake/lib/toaster/toastergui/tests.py +32 -5
index 4dd2174..9769713 100644
@@ -749,7 +749,7 @@ class AllBuildsPageTests(TestCase):
         """ Task should be shown as suffix on build name """
         build = Build.objects.create(**self.project1_build_success)
         Target.objects.create(build=build, target='bash', task='clean')
-        url = reverse("all-builds")
+        url = reverse('all-builds')
         response = self.client.get(url, follow=True)
         result = re.findall('bash:clean', response.content, re.MULTILINE)
         self.assertEqual(len(result), 3)
@@ -757,17 +757,44 @@ class AllBuildsPageTests(TestCase):
     def test_no_run_again_for_cli_build(self):
         """ "Run again" button should not be shown for command-line builds """
         build = Build.objects.create(**self.default_project_build_success)
-        url = reverse("all-builds")
+        url = reverse('all-builds')
         response = self.client.get(url, follow=True)
         soup = BeautifulSoup(response.content)
 
-        element_id = 'build-result-%d' % build.id
+        attrs = {'data-latest-build-result': build.id}
+        result = soup.find('div', attrs=attrs)
 
         # shouldn't see a run again button for command-line builds
-        run_again_button = soup.select('#%s button' % element_id)
+        run_again_button = result.select('button')
         self.assertEqual(len(run_again_button), 0)
 
         # should see a help icon for command-line builds
-        help_icon = soup.select('#%s i.get-help-green' % element_id)
+        help_icon = result.select('i.get-help-green')
         self.assertEqual(len(help_icon), 1)
 
+    def test_tooltips_on_project_name(self):
+        """
+        A tooltip should be present next to the command line
+        builds project name in the all builds page, but not for
+        other projects
+        """
+        build1 = Build.objects.create(**self.project1_build_success)
+        default_build = Build.objects.create(**self.default_project_build_success)
+
+        url = reverse('all-builds')
+        response = self.client.get(url, follow=True)
+        soup = BeautifulSoup(response.content)
+
+        # no help icon on non-default project name
+        result = soup.find('tr', attrs={'data-table-build-result': build1.id})
+        name = result.select('td.project-name')[0]
+        icons = name.select('i.get-help')
+        self.assertEqual(len(icons), 0,
+                         'should not be a help icon for non-cli builds name')
+
+        # help icon on default project name
+        result = soup.find('tr', attrs={'data-table-build-result': default_build.id})
+        name = result.select('td.project-name')[0]
+        icons = name.select('i.get-help')
+        self.assertEqual(len(icons), 1,
+                         'should be a help icon for cli builds name')
Signature · 14 lines
Elliot Smith
Software Engineer
Intel OTC

---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
#7 lore raw

PATCH 7/9 toaster: More linting of tests

In reply to Elliot Smith

Fix some more lint errors on the tests for toastergui.

---
 bitbake/lib/toaster/toastergui/tests.py | 35 +++++++++++++--------------------
 1 file changed, 14 insertions(+), 21 deletions(-)
bitbake/lib/toaster/toastergui/tests.py +14 -21
index 9769713..a646153 100644
@@ -433,6 +433,18 @@ class AllProjectsPageTests(TestCase):
 
     MACHINE_NAME = 'delorean'
 
+    def setUp(self):
+        """ Add default project manually """
+        project = Project.objects.create_project(CLI_BUILDS_PROJECT_NAME, None)
+        self.default_project = project
+        self.default_project.is_default = True
+        self.default_project.save()
+
+        # this project is only set for some of the tests
+        self.project = None
+
+        self.release = None
+
     def _add_build_to_default_project(self):
         """ Add a build to the default project (not used in all tests) """
         now = timezone.now()
@@ -458,16 +470,6 @@ class AllProjectsPageTests(TestCase):
                                                      value=self.MACHINE_NAME)
         project_var.save()
 
-    def setUp(self):
-        """ Add default project manually """
-        project = Project.objects.create_project(CLI_BUILDS_PROJECT_NAME, None)
-        self.default_project = project
-        self.default_project.is_default = True
-        self.default_project.save()
-
-        # this project is only set for some of the tests
-        self.project = None
-
     def test_default_project_hidden(self):
         """ The default project should be hidden if it has no builds """
         params = {"count": 10, "orderby": "updated:-", "page": 1}
@@ -573,17 +575,15 @@ class AllProjectsPageTests(TestCase):
         # link for default project
         row = soup.find('tr', attrs={'data-project': self.default_project.id})
         cell = row.find('td', attrs={'data-project-field': 'name'})
-        url = cell.find('a')['href']
         expected_url = reverse('projectbuilds', args=(self.default_project.id,))
-        self.assertEqual(url, expected_url,
+        self.assertEqual(cell.find('a')['href'], expected_url,
                          'link on default project name should point to builds')
 
         # link for other project
         row = soup.find('tr', attrs={'data-project': self.project.id})
         cell = row.find('td', attrs={'data-project-field': 'name'})
-        url = cell.find('a')['href']
         expected_url = reverse('project', args=(self.project.id,))
-        self.assertEqual(url, expected_url,
+        self.assertEqual(cell.find('a')['href'], expected_url,
                          'link on project name should point to configuration')
 
 class ProjectBuildsPageTests(TestCase):
@@ -640,13 +640,6 @@ class ProjectBuildsPageTests(TestCase):
             "outcome": Build.IN_PROGRESS
         }
 
-        self.default_project_build_success = {
-            "project": self.default_project,
-            "started_on": now,
-            "completed_on": now,
-            "outcome": Build.SUCCEEDED
-        }
-
     def _get_rows_for_project(self, project_id):
         """ Helper to retrieve HTML rows for a project """
         url = reverse("projectbuilds", args=(project_id,))
Signature · 14 lines
Elliot Smith
Software Engineer
Intel OTC

---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
#8 lore raw

PATCH 8/9 toaster: Clean up template code

In reply to Elliot Smith

Some errors in the HTML template didn't show up until they were rendering command line builds.

One useless conditional was lingering in another template.

Clean up these issues to display the latest builds section correctly.

---
 .../lib/toaster/toastergui/templates/builds.html    |  5 -----
 .../toaster/toastergui/templates/mrb_section.html   | 21 ++++++++++-----------
 2 files changed, 10 insertions(+), 16 deletions(-)
bitbake/lib/toaster/toastergui/templates/builds.html +0 -5
index 566c279..459fcb0 100644
@@ -29,8 +29,6 @@
 
   {% include "mrb_section.html" %}
 
-
-  {% if 1 %}
   <div class="page-header top-air">
      <h1>
       {% if request.GET.filter and objects.paginator.count > 0 or request.GET.search and objects.paginator.count > 0 %}
@@ -57,8 +55,6 @@
         </form>
       </div>
     </div>
-
-
   {% else %}
   {% include "basetable_top.html" %}
         <!-- Table data rows; the order needs to match the order of "tablecols" definitions; and the <td class value needs to match the tablecols clclass value for show/hide buttons to work -->
@@ -117,7 +113,6 @@
 
   {% include "basetable_bottom.html" %}
   {% endif %} {# objects.paginator.count #}
-{% endif %} {# empty #}
 </div><!-- end row-fluid-->
 
 {% endblock %}
bitbake/lib/toaster/toastergui/templates/mrb_section.html +10 -11
index 55687a1..bd8f991 100644
@@ -22,19 +22,18 @@
   {% endif %}
   <div id="latest-builds">
   {% for build in mru %}
-  <div data-latest-build-result="{{ build.id }}" class="alert build-result {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%}
-    {% if mrb_type != 'project' %}
-    project-name">
-       <span class="label {%if build.outcome == build.SUCCEEDED%}label-success{%elif build.outcome == build.FAILED%}label-important{%else%}label-info{%endif%}">
-         <a href={% project_url build.project %}>
-           {{build.project.name}}
-         </a>
-       </span>
-    {% endif %}
+  <div data-latest-build-result="{{ build.id }}" class="alert build-result {%if build.outcome == build.SUCCEEDED%}alert-success{%elif build.outcome == build.FAILED%}alert-error{%else%}alert-info{%endif%}{% if mrb_type != 'project' %} project-name{% endif %}">
+      {% if mrb_type != 'project' %}
+        <span class="label {%if build.outcome == build.SUCCEEDED%}label-success{%elif build.outcome == build.FAILED%}label-important{%else%}label-info{%endif%}">
+            <a href={% project_url build.project %}>
+                {{build.project.name}}
+            </a>
+        </span>
+      {% endif %}
         <div class="row-fluid">
-            <div class="span3 lead">
+          <div class="span3 lead">
     {%if build.outcome == build.SUCCEEDED or build.outcome == build.FAILED %}
-                <a href="{%url 'builddashboard' build.pk%}" class="{%if build.outcome == build.SUCCEEDED %}success{%else%}error{%endif%}">
+              <a href="{%url 'builddashboard' build.pk%}" class="{%if build.outcome == build.SUCCEEDED %}success{%else%}error{%endif%}">
     {% endif %}
             {% if build.target_set.all.count > 0 %}
                 <span data-toggle="tooltip"
Signature · 14 lines
Elliot Smith
Software Engineer
Intel OTC

---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
#9 lore raw

PATCH 9/9 toaster: Exclude "command line builds" project from projects typeahead

In reply to Elliot Smith

The "New build" drop-down provides autocomplete for Toaster projects.

However, it should not include the default project (for command line builds), as it should not be possible for the user to select this project as the container for a Toaster build.

[YOCTO #8231]

---
 bitbake/lib/toaster/toastergui/typeaheads.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
bitbake/lib/toaster/toastergui/typeaheads.py +2 -2
index 9db3182..dd4b7f5 100644
@@ -121,12 +121,12 @@ class RecipesTypeAhead(ToasterTypeAhead):
         return results
 
 class ProjectsTypeAhead(ToasterTypeAhead):
-    """ Typeahead for all the projects """
+    """ Typeahead for all the projects, except for command line builds """
     def __init__(self):
         super(ProjectsTypeAhead, self).__init__()
 
     def apply_search(self, search_term, prj, request):
-        projects = Project.objects.all().order_by("name")
+        projects = Project.objects.exclude(is_default=True).order_by("name")
 
         primary_results = projects.filter(name__istartswith=search_term)
         secondary_results = projects.filter(name__icontains=search_term).exclude(pk__in=primary_results)
Signature · 14 lines
Elliot Smith
Software Engineer
Intel OTC

---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
#10 lore raw
In reply to Elliot Smith
Quoted 23 lines · On 02/10/2015 18:46, "[email protected] on behalf of Elliot…

This implements changes across the UI to hide irrelevant options for command-line builds, and show additional help icons explaining why those options are hidden.

For details of the changes, see the document attached to bug 8231 at https://bugzilla.yoctoproject.org/attachment.cgi?id=2760.

Notes:

* One of the changes in the design document has not been implemented:
it's not possible to hide the "customise image recipe" links and buttons (page 9) as these are not in the templates yet.

* The visual design differs slightly from the design document, as it incorporates a new layout for the build dashboard which is not implemented yet.

* The "New build" button is displayed on the command line builds project page, as we came to the conclusion that the logic in the JavaScript (i.e. show the new build button if there is no build input form on the page) should apply in the case of the builds page for the "command line builds" project.

Quoted 5 lines

Changes since af1e3160922a30b8a538959335a5f8945f2f3549 (toaster-next) are in
git://git.yoctoproject.org/poky-contrib, elliot/toaster/cli_builds_ui-8231
http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=elliot/toaster/cl
i_builds_ui-8231

Hi Elliot,

This is looking pretty good. I have a couple of small comments though:

* Changing the project name only works on page refresh. When I click the 'save' button nothing happens, but when I refresh the page, the name has changed. I've checked, and I don't think this is happening on master.

* the 'new build' button in Command like builds project page shows the 'Command line builds' project selected. That project should never be selected in that new build form. Instead, the form in that project should behave as in the 'all builds' and 'all projects' pages (at the moment, no project selected, although we know that't not ideal either).

* in fact, if the only project that exists in the Toaster instance is the 'Command line builds' project, the 'new build' button should really not show at all. It's just noise: you cannot really interact with it in any way. Sorry: I forgot to think of this case when putting together the design

Any questions, let me know.

Thanks!

Belén

Quoted 50 lines

Related bug: https://bugzilla.yoctoproject.org/show_bug.cgi?id=8231

Elliot Smith (9):
toaster: Reorganise and lint tests
toaster: Replace "Run again" button with help text for cli builds
toaster: Show 'not applicable' for default project machine and release
toaster: Make the builds view the project page for "command line
builds"
toaster: Hide tabs and add info popups for command line builds
toaster: Show tooltip next to cli builds project name in all builds
toaster: More linting of tests
toaster: Clean up template code
toaster: Exclude "command line builds" project from projects typeahead

.../lib/toaster/toastergui/static/css/default.css  |   5 +
bitbake/lib/toaster/toastergui/templates/base.html |   5 +-
.../lib/toaster/toastergui/templates/builds.html   |  19 +-
.../toaster/toastergui/templates/mrb_section.html  |  66 +++--
.../lib/toaster/toastergui/templates/projects.html |  23 +-
.../toastergui/templates/projecttopbar.html        |  80 +++---
.../toastergui/templatetags/project_url_tag.py     |  34 +++
bitbake/lib/toaster/toastergui/tests.py            | 311
+++++++++++++++++----
bitbake/lib/toaster/toastergui/typeaheads.py       |   4 +-
9 files changed, 420 insertions(+), 127 deletions(-)

create mode 100644 bitbake/lib/toaster/toastergui/templatetags/project_url_tag.py

--
Elliot Smith
Software Engineer
Intel OTC

---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.

Signature · 4 lines
_______________________________________________
toaster mailing list
[email protected]
https://lists.yoctoproject.org/listinfo/toaster