All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] clean ups, buildinfohelper fix and delete project patches
@ 2016-09-06 15:13 Michael Wood
  2016-09-06 15:13 ` [PATCH 1/4] toaster: Clean up and convert to rest api project edit and get calls Michael Wood
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Michael Wood @ 2016-09-06 15:13 UTC (permalink / raw)
  To: toaster

Couple of clean ups, fix for local layers in buildinfohelper and api and implementation of delete a project.

Michael Wood (4):
  toaster: Clean up and convert to rest api project edit and get calls
  toaster: move MostRecentBuildsView to its own widget
  toaster: project page Implement front end feature to delete project
  toaster: buildinfohelper local layer don't construct path using git
    info

 bitbake/lib/bb/ui/buildinfohelper.py               |   8 +-
 bitbake/lib/toaster/toastergui/api.py              | 272 +++++++++++++--------
 .../lib/toaster/toastergui/static/js/libtoaster.js |   3 +-
 .../toaster/toastergui/static/js/projectpage.js    | 157 ++----------
 bitbake/lib/toaster/toastergui/templates/base.html |   1 +
 .../toastergui/templates/baseprojectpage.html      |   6 +
 .../lib/toaster/toastergui/templates/project.html  |  27 +-
 bitbake/lib/toaster/toastergui/urls.py             |   7 +-
 bitbake/lib/toaster/toastergui/views.py            | 131 +---------
 bitbake/lib/toaster/toastergui/widgets.py          | 141 +++++++++--
 10 files changed, 343 insertions(+), 410 deletions(-)

-- 
2.7.4



^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/4] toaster: Clean up and convert to rest api project edit and get calls
  2016-09-06 15:13 [PATCH 0/4] clean ups, buildinfohelper fix and delete project patches Michael Wood
@ 2016-09-06 15:13 ` Michael Wood
  2016-09-06 15:13 ` [PATCH 2/4] toaster: move MostRecentBuildsView to its own widget Michael Wood
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Michael Wood @ 2016-09-06 15:13 UTC (permalink / raw)
  To: toaster

Convert the project xhr calls into proper rest api and port the client
side calls to use the new API. Fix all the pyflakes identified issues
and clean up unused fields.

Also remove the api and client side code for changing release on the fly
as this is no longer supported.

[YOCTO #9519]

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 bitbake/lib/toaster/toastergui/api.py              | 161 +++++++++++++++++++++
 .../lib/toaster/toastergui/static/js/libtoaster.js |   3 +-
 .../toaster/toastergui/static/js/projectpage.js    | 147 +------------------
 bitbake/lib/toaster/toastergui/templates/base.html |   1 +
 bitbake/lib/toaster/toastergui/urls.py             |   4 +
 bitbake/lib/toaster/toastergui/views.py            | 131 +----------------
 6 files changed, 176 insertions(+), 271 deletions(-)

diff --git a/bitbake/lib/toaster/toastergui/api.py b/bitbake/lib/toaster/toastergui/api.py
index be18090..8876409 100644
--- a/bitbake/lib/toaster/toastergui/api.py
+++ b/bitbake/lib/toaster/toastergui/api.py
@@ -20,11 +20,13 @@
 
 import re
 import logging
+from collections import Counter
 
 from orm.models import Project, ProjectTarget, Build, Layer_Version
 from orm.models import LayerVersionDependency, LayerSource, ProjectLayer
 from orm.models import Recipe, CustomImageRecipe, CustomImagePackage
 from orm.models import Layer, Target, Package, Package_Dependency
+from orm.models import ProjectVariable
 from bldcontrol.models import BuildRequest
 from bldcontrol import bbcontroller
 
@@ -772,3 +774,162 @@ class XhrCustomRecipePackages(View):
         except CustomImageRecipe.DoesNotExist:
             return error_response("Tried to remove package that wasn't"
                                   " present")
+
+
+class XhrProject(View):
+    """ Create, delete or edit a project
+
+    Entry point: /xhr_project/<project_id>
+    """
+    def post(self, request, *args, **kwargs):
+        """
+          Edit project control
+
+          Args:
+              layerAdd = layer_version_id layer_version_id ...
+              layerDel = layer_version_id layer_version_id ...
+              projectName = new_project_name
+              machineName = new_machine_name
+
+          Returns:
+              {"error": "ok"}
+            or
+              {"error": <error message>}
+        """
+        try:
+            prj = Project.objects.get(pk=kwargs['project_id'])
+        except Project.DoesNotExist:
+            return error_response("No such project")
+
+        # Add layers
+        if 'layerAdd' in request.POST and len(request.POST['layerAdd']) > 0:
+            for layer_version_id in request.POST['layerAdd'].split(','):
+                try:
+                    lv = Layer_Version.objects.get(pk=int(layer_version_id))
+                    ProjectLayer.objects.get_or_create(project=prj,
+                                                       layercommit=lv)
+                except Layer_Version.DoesNotExist:
+                    return error_response("Layer version %s asked to add "
+                                          "doesn't exist" % layer_version_id)
+
+        # Remove layers
+        if 'layerDel' in request.POST and len(request.POST['layerDel']) > 0:
+            layer_version_ids = request.POST['layerDel'].split(',')
+            ProjectLayer.objects.filter(
+                project=prj,
+                layercommit_id__in=layer_version_ids).delete()
+
+        # Project name change
+        if 'projectName' in request.POST:
+            prj.name = request.POST['projectName']
+            prj.save()
+
+        # Machine name change
+        if 'machineName' in request.POST:
+            machinevar = prj.projectvariable_set.get(name="MACHINE")
+            machinevar.value = request.POST['machineName']
+            machinevar.save()
+
+        return JsonResponse({"error": "ok"})
+
+    def get(self, request, *args, **kwargs):
+        """
+        Returns:
+            json object representing the current project
+        or:
+            {"error": <error message>}
+        """
+
+        try:
+            project = Project.objects.get(pk=kwargs['project_id'])
+        except Project.DoesNotExist:
+            return error_response("Project %s does not exist" %
+                                  kwargs['project_id'])
+
+        # Create the frequently built targets list
+
+        freqtargets = Counter(Target.objects.filter(
+            Q(build__project=project),
+            ~Q(build__outcome=Build.IN_PROGRESS)
+        ).order_by("target").values_list("target", flat=True))
+
+        freqtargets = freqtargets.most_common(5)
+
+        # We now have the targets in order of frequency but if there are two
+        # with the same frequency then we need to make sure those are in
+        # alphabetical order without losing the frequency ordering
+
+        tmp = []
+        switch = None
+        for i, freqtartget in enumerate(freqtargets):
+            target, count = freqtartget
+            try:
+                target_next, count_next = freqtargets[i+1]
+                if count == count_next and target > target_next:
+                    switch = target
+                    continue
+            except IndexError:
+                pass
+
+            tmp.append(target)
+
+            if switch:
+                tmp.append(switch)
+                switch = None
+
+        freqtargets = tmp
+
+        layers = []
+        for layer in project.projectlayer_set.all():
+            layers.append({
+                "id": layer.layercommit.pk,
+                "name": layer.layercommit.layer.name,
+                "vcs_url": layer.layercommit.layer.vcs_url,
+                "local_source_dir": layer.layercommit.layer.local_source_dir,
+                "vcs_reference": layer.layercommit.get_vcs_reference(),
+                "url": layer.layercommit.layer.layer_index_url,
+                "layerdetailurl": layer.layercommit.get_detailspage_url(
+                    project.pk),
+                "layersource": layer.layercommit.layer_source
+            })
+
+        data = {
+            "name": project.name,
+            "layers": layers,
+            "freqtargets": freqtargets,
+        }
+
+        if project.release is not None:
+            data['release'] = {
+                "id": project.release.pk,
+                "name": project.release.name,
+                "description": project.release.description
+            }
+
+        try:
+            data["machine"] = {"name":
+                               project.projectvariable_set.get(
+                                   name="MACHINE").value}
+        except ProjectVariable.DoesNotExist:
+            data["machine"] = None
+        try:
+            data["distro"] = project.projectvariable_set.get(
+                name="DISTRO").value
+        except ProjectVariable.DoesNotExist:
+            data["distro"] = "-- not set yet"
+
+        data['error'] = "ok"
+
+        return JsonResponse(data)
+
+    def put(self, request, *args, **kwargs):
+        # TODO create new project api
+        return HttpResponse()
+
+    def delete(self, request, *args, **kwargs):
+        try:
+            Project.objects.get(kwargs['project_id']).delete()
+        except Project.DoesNotExist:
+            return error_response("Project %s does not exist" %
+                                  kwargs['project_id'])
+        return JsonResponse({"error": "ok"})
diff --git a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
index f56affd..b2099a6 100644
--- a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
+++ b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js
@@ -167,7 +167,6 @@ var libtoaster = (function () {
   function _getProjectInfo(url, onsuccess, onfail){
     $.ajax({
         type: "GET",
-        data : { format: "json" },
         url: url,
         headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
         success: function (_data) {
@@ -194,7 +193,7 @@ var libtoaster = (function () {
   function _editCurrentProject(data, onSuccess, onFail){
     $.ajax({
         type: "POST",
-        url: libtoaster.ctx.projectPageUrl + "?format=json",
+        url: libtoaster.ctx.xhrProjectUrl,
         data: data,
         headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
         success: function (data) {
diff --git a/bitbake/lib/toaster/toastergui/static/js/projectpage.js b/bitbake/lib/toaster/toastergui/static/js/projectpage.js
index b75b3e1..3bf3cba 100644
--- a/bitbake/lib/toaster/toastergui/static/js/projectpage.js
+++ b/bitbake/lib/toaster/toastergui/static/js/projectpage.js
@@ -27,11 +27,10 @@ function projectPageInit(ctx) {
 
   var urlParams = libtoaster.parseUrlParams();
 
-  libtoaster.getProjectInfo(libtoaster.ctx.projectPageUrl, function(prjInfo){
+  libtoaster.getProjectInfo(libtoaster.ctx.xhrProjectUrl, function(prjInfo){
     updateProjectLayers(prjInfo.layers);
     updateFreqBuildRecipes(prjInfo.freqtargets);
     updateProjectRelease(prjInfo.release);
-    updateProjectReleases(prjInfo.releases, prjInfo.release);
 
     /* If we're receiving a machine set from the url and it's different from
      * our current machine then activate set machine sequence.
@@ -287,7 +286,9 @@ function projectPageInit(ctx) {
     machineNameTitle.text(machineName);
   }
 
-  libtoaster.makeTypeahead(machineChangeInput, libtoaster.ctx.machinesTypeAheadUrl, { }, function(item){
+  libtoaster.makeTypeahead(machineChangeInput,
+                           libtoaster.ctx.machinesTypeAheadUrl,
+                           { }, function(item){
     currentMachineAddSelection = item.name;
     machineChangeBtn.removeAttr("disabled");
   });
@@ -324,146 +325,10 @@ function projectPageInit(ctx) {
     releaseTitle.text(release.description);
   }
 
-  function updateProjectReleases(releases, current){
-    for (var i in releases){
-      var releaseOption = $("<option></option>");
 
-      releaseOption.val(releases[i].id);
-      releaseOption.text(releases[i].description);
-      releaseOption.data('release', releases[i]);
-
-      if (releases[i].id == current.id)
-        releaseOption.attr("selected", "selected");
-
-      releaseForm.children("select").append(releaseOption);
-    }
-  }
-
-  releaseChangeFormToggle.click(function(){
-    releaseForm.slideDown();
-    releaseTitle.hide();
-    $(this).hide();
-  });
-
-  cancelReleaseChange.click(function(e){
+  $("#delete-project-confirmed").click(function(e){
     e.preventDefault();
-    releaseForm.slideUp(function(){
-      releaseTitle.show();
-      releaseChangeFormToggle.show();
-    });
-  });
-
-  function changeProjectRelease(release, layersToRm){
-    libtoaster.editCurrentProject({ projectVersion : release.id },
-      function(){
-        /* Success */
-        /* Update layers list with new layers */
-        layersInPrjList.addClass('muted');
-        libtoaster.getProjectInfo(libtoaster.ctx.projectPageUrl,
-            function(prjInfo){
-              layersInPrjList.children().remove();
-              updateProjectLayers(prjInfo.layers);
-              layersInPrjList.removeClass('muted');
-              releaseChangedNotification(release, prjInfo.layers, layersToRm);
-        });
-        updateProjectRelease(release);
-        cancelReleaseChange.click();
-    });
-  }
-
-  /* Create a notification to show the changes to the layer configuration
-   * caused by changing a release.
-   */
-
-  function releaseChangedNotification(release, layers, layersToRm){
-
-    var message;
-
-    if (layers.length === 0 && layersToRm.length === 0){
-      message = $('<span><span class="lead">You have changed the project release to: <strong><span id="notify-release-name"></span></strong>.');
-      message.find("#notify-release-name").text(release.description);
-      libtoaster.showChangeNotification(message);
-      return;
-    }
-
-    /* Create the whitespace separated list of layers removed */
-    var layersDelList = "";
-
-    layersToRm.map(function(layer, i){
-      layersDelList += layer.name;
-      if (layersToRm[i+1] !== undefined)
-        layersDelList += ', ';
-    });
-
-    message = $('<span><span class="lead">You have changed the project release to: <strong><span id="notify-release-name"></span></strong>. This has caused the following changes in your project layers:</span><ul id="notify-layers-changed-list"></ul></span>');
-
-    var changedList = message.find("#notify-layers-changed-list");
-
-    message.find("#notify-release-name").text(release.description);
-
-    /* Manually construct the list item for changed layers */
-    var li = '<li><strong>'+layers.length+'</strong> layers changed to the <strong>'+release.name+'</strong> release: ';
-    for (var i in layers){
-      li += '<a href='+layers[i].layerdetailurl+'>'+layers[i].name+'</a>';
-      if (i !== 0)
-        li += ', ';
-    }
-
-    changedList.append($(li));
-
-    /* Layers removed */
-    if (layersToRm && layersToRm.length > 0){
-      if (layersToRm.length == 1)
-        li = '<li><strong>1</strong> layer removed: '+layersToRm[0].name+'</li>';
-      else
-        li = '<li><strong>'+layersToRm.length+'</strong> layers deleted: '+layersDelList+'</li>';
-
-      changedList.append($(li));
-    }
-
-    libtoaster.showChangeNotification(message);
-  }
-
-  /* Show the modal dialog which gives the option to remove layers which
-   * aren't compatible with the proposed release
-   */
-  function showReleaseLayerChangeModal(release, layers){
-    var layersToRmList = releaseModal.find("#layers-to-remove-list");
-    layersToRmList.text("");
-
-    releaseModal.find(".proposed-release-change-name").text(release.description);
-    releaseModal.data("layers", layers);
-    releaseModal.data("release", release);
-
-    for (var i in layers){
-      layersToRmList.append($("<li></li>").text(layers[i].name));
-    }
-    releaseModal.modal('show');
-  }
-
-  $("#change-release-btn").click(function(e){
-    e.preventDefault();
-
-    var newRelease = releaseForm.find("option:selected").data('release');
-
-    $.getJSON(ctx.testReleaseChangeUrl,
-      { new_release_id: newRelease.id },
-      function(layers) {
-        if (layers.rows.length === 0){
-          /* No layers to change for this release */
-          changeProjectRelease(newRelease, []);
-        } else {
-          showReleaseLayerChangeModal(newRelease, layers.rows);
-        }
-    });
-  });
-
-  /* Release change modal accept */
-  $("#change-release-and-rm-layers").click(function(){
-    var layers = releaseModal.data("layers");
-    var release =  releaseModal.data("release");
-
-    changeProjectRelease(release, layers);
+  
   });
 
 }
diff --git a/bitbake/lib/toaster/toastergui/templates/base.html b/bitbake/lib/toaster/toastergui/templates/base.html
index 58491eb..c1b1417 100644
--- a/bitbake/lib/toaster/toastergui/templates/base.html
+++ b/bitbake/lib/toaster/toastergui/templates/base.html
@@ -44,6 +44,7 @@
         {% if project.id %}
         projectId : {{project.id}},
         projectPageUrl : {% url 'project' project.id as purl %}{{purl|json}},
+        xhrProjectUrl : {% url 'xhr_project' project.id as pxurl %}{{pxurl|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}},
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index 9509cd5..1232611 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -220,6 +220,10 @@ urlpatterns = patterns('toastergui.views',
             api.XhrBuildRequest.as_view(),
             name='xhr_buildrequest'),
 
+        url(r'xhr_project/(?P<project_id>\d+)$',
+            api.XhrProject.as_view(),
+            name='xhr_project'),
+
         url(r'^mostrecentbuilds$', api.MostRecentBuildsView.as_view(),
             name='most_recent_builds'),
 
diff --git a/bitbake/lib/toaster/toastergui/views.py b/bitbake/lib/toaster/toastergui/views.py
index 365a1e8..2efb0fd 100755
--- a/bitbake/lib/toaster/toastergui/views.py
+++ b/bitbake/lib/toaster/toastergui/views.py
@@ -1361,136 +1361,11 @@ if True:
 
         raise Exception("Invalid HTTP method for this page")
 
-
-
     # Shows the edit project page
-    @_template_renderer('project.html')
     def project(request, pid):
-        prj = Project.objects.get(id = pid)
-
-        try:
-            puser = User.objects.get(id = prj.user_id)
-        except User.DoesNotExist:
-            puser = None
-
-        # execute POST requests
-        if request.method == "POST":
-            # add layers
-            if 'layerAdd' in request.POST and len(request.POST['layerAdd']) > 0:
-                for lc in Layer_Version.objects.filter(pk__in=[i for i in request.POST['layerAdd'].split(",") if len(i) > 0]):
-                    ProjectLayer.objects.get_or_create(project = prj, layercommit = lc)
-
-            # remove layers
-            if 'layerDel' in request.POST and len(request.POST['layerDel']) > 0:
-                for t in request.POST['layerDel'].strip().split(" "):
-                    pt = ProjectLayer.objects.filter(project = prj, layercommit_id = int(t)).delete()
-
-            if 'projectName' in request.POST:
-                prj.name = request.POST['projectName']
-                prj.save();
-
-            if 'projectVersion' in request.POST:
-                # If the release is the current project then return now
-                if prj.release.pk == int(request.POST.get('projectVersion',-1)):
-                    return {}
-
-                prj.release = Release.objects.get(pk = request.POST['projectVersion'])
-                # we need to change the bitbake version
-                prj.bitbake_version = prj.release.bitbake_version
-                prj.save()
-                # we need to change the layers
-                for project in prj.projectlayer_set.all():
-                    # find and add a similarly-named layer on the new branch
-                    try:
-                        layer_versions = prj.get_all_compatible_layer_versions()
-                        layer_versions = layer_versions.filter(layer__name = project.layercommit.layer.name)
-                        ProjectLayer.objects.get_or_create(project = prj, layercommit = layer_versions.first())
-                    except IndexError:
-                        pass
-                    finally:
-                        # get rid of the old entry
-                        project.delete()
-
-            if 'machineName' in request.POST:
-                machinevar = prj.projectvariable_set.get(name="MACHINE")
-                machinevar.value=request.POST['machineName']
-                machinevar.save()
-
-
-        # we use implicit knowledge of the current user's project to filter layer information, e.g.
-        pid = prj.id
-
-        from collections import Counter
-
-        freqtargets = Counter(Target.objects.filter(
-            Q(build__project=prj),
-            ~Q(build__outcome=Build.IN_PROGRESS)
-        ).order_by("target").values_list("target", flat=True))
-
-        freqtargets = freqtargets.most_common(5)
-
-        # We now have the targets in order of frequency but if there are two
-        # with the same frequency then we need to make sure those are in
-        # alphabetical order without losing the frequency ordering
-
-        tmp = []
-        switch = None
-        for i, freqtartget in enumerate(freqtargets):
-            target, count = freqtartget
-            try:
-                target_next, count_next = freqtargets[i+1]
-                if count == count_next and target > target_next:
-                    switch = target
-                    continue
-            except IndexError:
-                pass
-
-            tmp.append(target)
-
-            if switch:
-                tmp.append(switch)
-                switch = None
-
-        freqtargets = tmp
-
-        layers = [{"id": x.layercommit.pk, "orderid": x.pk, "name" : x.layercommit.layer.name,
-                   "vcs_url": x.layercommit.layer.vcs_url, "local_source_dir": x.layercommit.layer.local_source_dir, "vcs_reference" : x.layercommit.get_vcs_reference(),
-                   "url": x.layercommit.layer.layer_index_url, "layerdetailurl": x.layercommit.get_detailspage_url(prj.pk),
-                   "branch" : {"name" : x.layercommit.get_vcs_reference(),
-                               "layersource" : x.layercommit.layer_source }
-                   } for x in prj.projectlayer_set.all().order_by("id")]
-
-        context = {
-            "project" : prj,
-            "lvs_nos" : Layer_Version.objects.all().count(),
-            "completedbuilds": Build.objects.exclude(outcome = Build.IN_PROGRESS).filter(project_id = pid),
-            "prj" : {"name": prj.name, },
-            "buildrequests" : prj.build_set.filter(outcome=Build.IN_PROGRESS),
-            "builds" : Build.get_recent(prj),
-            "layers" : layers,
-            "targets" : [{"target" : x.target, "task" : x.task, "pk": x.pk} for x in prj.projecttarget_set.all()],
-            "variables": [(x.name, x.value) for x in prj.projectvariable_set.all()],
-            "freqtargets": freqtargets,
-            "releases": [{"id": x.pk, "name": x.name, "description":x.description} for x in Release.objects.all()],
-            "project_html": 1,
-            "recipesTypeAheadUrl": reverse('xhr_recipestypeahead', args=(prj.pk,)),
-            "projectBuildsUrl": reverse('projectbuilds', args=(prj.pk,)),
-        }
-
-        if prj.release is not None:
-            context['release'] = { "id": prj.release.pk, "name": prj.release.name, "description": prj.release.description}
-
-
-        try:
-            context["machine"] = {"name": prj.projectvariable_set.get(name="MACHINE").value}
-        except ProjectVariable.DoesNotExist:
-            context["machine"] = None
-        try:
-            context["distro"] = prj.projectvariable_set.get(name="DISTRO").value
-        except ProjectVariable.DoesNotExist:
-            context["distro"] = "-- not set yet"
-
-        return context
+        project = Project.objects.get(pk=pid)
+        context = {"project": project}
+        return render(request, "project.html", context)
 
     def jsunittests(request):
         """ Provides a page for the js unit tests """
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/4] toaster: move MostRecentBuildsView to its own widget
  2016-09-06 15:13 [PATCH 0/4] clean ups, buildinfohelper fix and delete project patches Michael Wood
  2016-09-06 15:13 ` [PATCH 1/4] toaster: Clean up and convert to rest api project edit and get calls Michael Wood
@ 2016-09-06 15:13 ` Michael Wood
  2016-09-06 15:13 ` [PATCH 3/4] toaster: project page Implement front end feature to delete project Michael Wood
  2016-09-06 15:13 ` [PATCH 4/4] toaster: buildinfohelper local layer don't construct path using git info Michael Wood
  3 siblings, 0 replies; 5+ messages in thread
From: Michael Wood @ 2016-09-06 15:13 UTC (permalink / raw)
  To: toaster

This view is specific to the builds dashboard rather than gernic api so
like ToasterTable and ToasterTypeAhead we class it as a widget as it has
a single purpose. Also clean up some flake8 identified issues.

Original author of the code moved in this commit is Elliot Smith.

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 bitbake/lib/toaster/toastergui/api.py     | 108 -----------------------
 bitbake/lib/toaster/toastergui/urls.py    |   3 +-
 bitbake/lib/toaster/toastergui/widgets.py | 141 ++++++++++++++++++++++++++----
 3 files changed, 127 insertions(+), 125 deletions(-)

diff --git a/bitbake/lib/toaster/toastergui/api.py b/bitbake/lib/toaster/toastergui/api.py
index 8876409..5589118 100644
--- a/bitbake/lib/toaster/toastergui/api.py
+++ b/bitbake/lib/toaster/toastergui/api.py
@@ -33,10 +33,8 @@ from bldcontrol import bbcontroller
 from django.http import HttpResponse, JsonResponse
 from django.views.generic import View
 from django.core.urlresolvers import reverse
-from django.utils import timezone
 from django.db.models import Q, F
 from django.db import Error
-from toastergui.templatetags.projecttags import json, sectohms, get_tasks
 from toastergui.templatetags.projecttags import filtered_filesizeformat
 
 logger = logging.getLogger("toaster")
@@ -227,112 +225,6 @@ class XhrLayer(View):
         })
 
 
-class MostRecentBuildsView(View):
-    def _was_yesterday_or_earlier(self, completed_on):
-        now = timezone.now()
-        delta = now - completed_on
-
-        if delta.days >= 1:
-            return True
-
-        return False
-
-    def get(self, request, *args, **kwargs):
-        """
-        Returns a list of builds in JSON format.
-        """
-        project = None
-
-        project_id = request.GET.get('project_id', None)
-        if project_id:
-            try:
-                project = Project.objects.get(pk=project_id)
-            except:
-                # if project lookup fails, assume no project
-                pass
-
-        recent_build_objs = Build.get_recent(project)
-        recent_builds = []
-
-        for build_obj in recent_build_objs:
-            dashboard_url = reverse('builddashboard', args=(build_obj.pk,))
-            buildtime_url = reverse('buildtime', args=(build_obj.pk,))
-            rebuild_url = \
-                reverse('xhr_buildrequest', args=(build_obj.project.pk,))
-            cancel_url = \
-                reverse('xhr_buildrequest', args=(build_obj.project.pk,))
-
-            build = {}
-            build['id'] = build_obj.pk
-            build['dashboard_url'] = dashboard_url
-
-            buildrequest_id = None
-            if hasattr(build_obj, 'buildrequest'):
-                buildrequest_id = build_obj.buildrequest.pk
-            build['buildrequest_id'] = buildrequest_id
-
-            build['recipes_parsed_percentage'] = \
-                int((build_obj.recipes_parsed /
-                     build_obj.recipes_to_parse) * 100)
-
-            tasks_complete_percentage = 0
-            if build_obj.outcome in (Build.SUCCEEDED, Build.FAILED):
-                tasks_complete_percentage = 100
-            elif build_obj.outcome == Build.IN_PROGRESS:
-                tasks_complete_percentage = build_obj.completeper()
-            build['tasks_complete_percentage'] = tasks_complete_percentage
-
-            build['state'] = build_obj.get_state()
-
-            build['errors'] = build_obj.errors.count()
-            build['dashboard_errors_url'] = dashboard_url + '#errors'
-
-            build['warnings'] = build_obj.warnings.count()
-            build['dashboard_warnings_url'] = dashboard_url + '#warnings'
-
-            build['buildtime'] = sectohms(build_obj.timespent_seconds)
-            build['buildtime_url'] = buildtime_url
-
-            build['rebuild_url'] = rebuild_url
-            build['cancel_url'] = cancel_url
-
-            build['is_default_project_build'] = build_obj.project.is_default
-
-            build['build_targets_json'] = \
-                json(get_tasks(build_obj.target_set.all()))
-
-            # convert completed_on time to user's timezone
-            completed_on = timezone.localtime(build_obj.completed_on)
-
-            completed_on_template = '%H:%M'
-            if self._was_yesterday_or_earlier(completed_on):
-                completed_on_template = '%d/%m/%Y ' + completed_on_template
-            build['completed_on'] = completed_on.strftime(
-                completed_on_template)
-
-            targets = []
-            target_objs = build_obj.get_sorted_target_list()
-            for target_obj in target_objs:
-                if target_obj.task:
-                    targets.append(target_obj.target + ':' + target_obj.task)
-                else:
-                    targets.append(target_obj.target)
-            build['targets'] = ' '.join(targets)
-
-            # abbreviated form of the full target list
-            abbreviated_targets = ''
-            num_targets = len(targets)
-            if num_targets > 0:
-                abbreviated_targets = targets[0]
-            if num_targets > 1:
-                abbreviated_targets += (' +%s' % (num_targets - 1))
-            build['targets_abbreviated'] = abbreviated_targets
-
-            recent_builds.append(build)
-
-        return JsonResponse(recent_builds, safe=False)
-
-
 class XhrCustomRecipe(View):
     """ Create a custom image recipe """
 
diff --git a/bitbake/lib/toaster/toastergui/urls.py b/bitbake/lib/toaster/toastergui/urls.py
index 1232611..0002a5a 100644
--- a/bitbake/lib/toaster/toastergui/urls.py
+++ b/bitbake/lib/toaster/toastergui/urls.py
@@ -24,6 +24,7 @@ from toastergui import tables
 from toastergui import buildtables
 from toastergui import typeaheads
 from toastergui import api
+from toastergui import widgets
 
 urlpatterns = patterns('toastergui.views',
         # landing page
@@ -224,7 +225,7 @@ urlpatterns = patterns('toastergui.views',
             api.XhrProject.as_view(),
             name='xhr_project'),
 
-        url(r'^mostrecentbuilds$', api.MostRecentBuildsView.as_view(),
+        url(r'^mostrecentbuilds$', widgets.MostRecentBuildsView.as_view(),
             name='most_recent_builds'),
 
           # default redirection
diff --git a/bitbake/lib/toaster/toastergui/widgets.py b/bitbake/lib/toaster/toastergui/widgets.py
index 005a562..026903d 100644
--- a/bitbake/lib/toaster/toastergui/widgets.py
+++ b/bitbake/lib/toaster/toastergui/widgets.py
@@ -22,23 +22,24 @@
 from django.views.generic import View, TemplateView
 from django.views.decorators.cache import cache_control
 from django.shortcuts import HttpResponse
-from django.http import HttpResponseBadRequest
-from django.core import serializers
 from django.core.cache import cache
 from django.core.paginator import Paginator, EmptyPage
 from django.db.models import Q
-from orm.models import Project, ProjectLayer, Layer_Version
+from orm.models import Project, Build
 from django.template import Context, Template
 from django.template import VariableDoesNotExist
 from django.template import TemplateSyntaxError
 from django.core.serializers.json import DjangoJSONEncoder
 from django.core.exceptions import FieldError
-from django.conf.urls import url, patterns
+from django.utils import timezone
+from toastergui.templatetags.projecttags import sectohms, get_tasks
+from toastergui.templatetags.projecttags import json as template_json
+from django.http import JsonResponse
+from django.core.urlresolvers import reverse
 
 import types
 import json
 import collections
-import operator
 import re
 
 try:
@@ -55,6 +56,7 @@ from toastergui.tablefilter import TableFilterMap
 class NoFieldOrDataName(Exception):
     pass
 
+
 class ToasterTable(TemplateView):
     def __init__(self, *args, **kwargs):
         super(ToasterTable, self).__init__()
@@ -81,7 +83,7 @@ class ToasterTable(TemplateView):
     def get_context_data(self, **kwargs):
         context = super(ToasterTable, self).get_context_data(**kwargs)
         context['title'] = self.title
-        context['table_name'] =  type(self).__name__.lower()
+        context['table_name'] = type(self).__name__.lower()
         context['empty_state'] = self.empty_state
 
         return context
@@ -406,7 +408,6 @@ class ToasterTable(TemplateView):
         return data
 
 
-
 class ToasterTypeAhead(View):
     """ A typeahead mechanism to support the front end typeahead widgets """
     MAX_RESULTS = 6
@@ -427,34 +428,142 @@ class ToasterTypeAhead(View):
         error = "ok"
 
         search_term = request.GET.get("search", None)
-        if search_term == None:
+        if search_term is None:
             # We got no search value so return empty reponse
-            return response({'error' : error , 'results': []})
+            return response({'error': error, 'results': []})
 
         try:
             prj = Project.objects.get(pk=kwargs['pid'])
         except KeyError:
             prj = None
 
-        results = self.apply_search(search_term, prj, request)[:ToasterTypeAhead.MAX_RESULTS]
+        results = self.apply_search(search_term,
+                                    prj,
+                                    request)[:ToasterTypeAhead.MAX_RESULTS]
 
         if len(results) > 0:
             try:
                 self.validate_fields(results[0])
-            except MissingFieldsException as e:
+            except self.MissingFieldsException as e:
                 error = e
 
-        data = { 'results' : results,
-                'error' : error,
-               }
+        data = {'results': results,
+                'error': error}
 
         return response(data)
 
     def validate_fields(self, result):
-        if 'name' in result == False or 'detail' in result == False:
-            raise MissingFieldsException("name and detail are required fields")
+        if 'name' in result is False or 'detail' in result is False:
+            raise self.MissingFieldsException(
+                "name and detail are required fields")
 
     def apply_search(self, search_term, prj):
         """ Override this function to implement search. Return an array of
         dictionaries with a minium of a name and detail field"""
         pass
+
+
+class MostRecentBuildsView(View):
+    def _was_yesterday_or_earlier(self, completed_on):
+        now = timezone.now()
+        delta = now - completed_on
+
+        if delta.days >= 1:
+            return True
+
+        return False
+
+    def get(self, request, *args, **kwargs):
+        """
+        Returns a list of builds in JSON format.
+        """
+        project = None
+
+        project_id = request.GET.get('project_id', None)
+        if project_id:
+            try:
+                project = Project.objects.get(pk=project_id)
+            except:
+                # if project lookup fails, assume no project
+                pass
+
+        recent_build_objs = Build.get_recent(project)
+        recent_builds = []
+
+        for build_obj in recent_build_objs:
+            dashboard_url = reverse('builddashboard', args=(build_obj.pk,))
+            buildtime_url = reverse('buildtime', args=(build_obj.pk,))
+            rebuild_url = \
+                reverse('xhr_buildrequest', args=(build_obj.project.pk,))
+            cancel_url = \
+                reverse('xhr_buildrequest', args=(build_obj.project.pk,))
+
+            build = {}
+            build['id'] = build_obj.pk
+            build['dashboard_url'] = dashboard_url
+
+            buildrequest_id = None
+            if hasattr(build_obj, 'buildrequest'):
+                buildrequest_id = build_obj.buildrequest.pk
+            build['buildrequest_id'] = buildrequest_id
+
+            build['recipes_parsed_percentage'] = \
+                int((build_obj.recipes_parsed /
+                     build_obj.recipes_to_parse) * 100)
+
+            tasks_complete_percentage = 0
+            if build_obj.outcome in (Build.SUCCEEDED, Build.FAILED):
+                tasks_complete_percentage = 100
+            elif build_obj.outcome == Build.IN_PROGRESS:
+                tasks_complete_percentage = build_obj.completeper()
+            build['tasks_complete_percentage'] = tasks_complete_percentage
+
+            build['state'] = build_obj.get_state()
+
+            build['errors'] = build_obj.errors.count()
+            build['dashboard_errors_url'] = dashboard_url + '#errors'
+
+            build['warnings'] = build_obj.warnings.count()
+            build['dashboard_warnings_url'] = dashboard_url + '#warnings'
+
+            build['buildtime'] = sectohms(build_obj.timespent_seconds)
+            build['buildtime_url'] = buildtime_url
+
+            build['rebuild_url'] = rebuild_url
+            build['cancel_url'] = cancel_url
+
+            build['is_default_project_build'] = build_obj.project.is_default
+
+            build['build_targets_json'] = \
+                template_json(get_tasks(build_obj.target_set.all()))
+
+            # convert completed_on time to user's timezone
+            completed_on = timezone.localtime(build_obj.completed_on)
+
+            completed_on_template = '%H:%M'
+            if self._was_yesterday_or_earlier(completed_on):
+                completed_on_template = '%d/%m/%Y ' + completed_on_template
+            build['completed_on'] = completed_on.strftime(
+                completed_on_template)
+
+            targets = []
+            target_objs = build_obj.get_sorted_target_list()
+            for target_obj in target_objs:
+                if target_obj.task:
+                    targets.append(target_obj.target + ':' + target_obj.task)
+                else:
+                    targets.append(target_obj.target)
+            build['targets'] = ' '.join(targets)
+
+            # abbreviated form of the full target list
+            abbreviated_targets = ''
+            num_targets = len(targets)
+            if num_targets > 0:
+                abbreviated_targets = targets[0]
+            if num_targets > 1:
+                abbreviated_targets += (' +%s' % (num_targets - 1))
+            build['targets_abbreviated'] = abbreviated_targets
+
+            recent_builds.append(build)
+
+        return JsonResponse(recent_builds, safe=False)
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/4] toaster: project page Implement front end feature to delete project
  2016-09-06 15:13 [PATCH 0/4] clean ups, buildinfohelper fix and delete project patches Michael Wood
  2016-09-06 15:13 ` [PATCH 1/4] toaster: Clean up and convert to rest api project edit and get calls Michael Wood
  2016-09-06 15:13 ` [PATCH 2/4] toaster: move MostRecentBuildsView to its own widget Michael Wood
@ 2016-09-06 15:13 ` Michael Wood
  2016-09-06 15:13 ` [PATCH 4/4] toaster: buildinfohelper local layer don't construct path using git info Michael Wood
  3 siblings, 0 replies; 5+ messages in thread
From: Michael Wood @ 2016-09-06 15:13 UTC (permalink / raw)
  To: toaster

Add confirm modal and api calls to delete a project from the project
dashboard.

[YOCTO #6238]

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 bitbake/lib/toaster/toastergui/api.py              |  7 ++++--
 .../toaster/toastergui/static/js/projectpage.js    | 18 ++++++++++++++-
 .../toastergui/templates/baseprojectpage.html      |  6 +++++
 .../lib/toaster/toastergui/templates/project.html  | 27 +++++++++++-----------
 4 files changed, 41 insertions(+), 17 deletions(-)

diff --git a/bitbake/lib/toaster/toastergui/api.py b/bitbake/lib/toaster/toastergui/api.py
index 5589118..4fb078b 100644
--- a/bitbake/lib/toaster/toastergui/api.py
+++ b/bitbake/lib/toaster/toastergui/api.py
@@ -820,8 +820,11 @@ class XhrProject(View):
 
     def delete(self, request, *args, **kwargs):
         try:
-            Project.objects.get(kwargs['project_id']).delete()
+            Project.objects.get(pk=kwargs['project_id']).delete()
         except Project.DoesNotExist:
             return error_response("Project %s does not exist" %
                                   kwargs['project_id'])
-        return JsonResponse({"error": "ok"})
+        return JsonResponse({
+            "error": "ok",
+            "gotoUrl": reverse("all-projects", args=[])
+        })
diff --git a/bitbake/lib/toaster/toastergui/static/js/projectpage.js b/bitbake/lib/toaster/toastergui/static/js/projectpage.js
index 3bf3cba..d38389c 100644
--- a/bitbake/lib/toaster/toastergui/static/js/projectpage.js
+++ b/bitbake/lib/toaster/toastergui/static/js/projectpage.js
@@ -328,7 +328,23 @@ function projectPageInit(ctx) {
 
   $("#delete-project-confirmed").click(function(e){
     e.preventDefault();
-  
+
+    $.ajax({
+        type: 'DELETE',
+        url: libtoaster.ctx.xhrProjectUrl,
+        headers: { 'X-CSRFToken' : $.cookie('csrftoken')},
+        success: function (data) {
+          if (data.error !== "ok") {
+            console.warn(data.error);
+          } else {
+            window.location.replace(data.gotoUrl);
+          }
+        },
+        error: function (data) {
+          console.warn(data);
+          if (onfail) onfail(data);
+        }
+    });
   });
 
 }
diff --git a/bitbake/lib/toaster/toastergui/templates/baseprojectpage.html b/bitbake/lib/toaster/toastergui/templates/baseprojectpage.html
index b3b6f1c..8427d25 100644
--- a/bitbake/lib/toaster/toastergui/templates/baseprojectpage.html
+++ b/bitbake/lib/toaster/toastergui/templates/baseprojectpage.html
@@ -34,6 +34,12 @@ $(document).ready(function(){
       <li><a href="{% url 'projectlayers' project.id %}">Layers</a></li>
       <li class="nav-header">Extra configuration</li>
       <li><a href="{% url 'projectconf' project.id %}">BitBake variables</a></li>
+
+      <li class="nav-header">Actions</li>
+      <li>
+        <a href="#delete-project-modal" role="button" class="text-danger" data-toggle="modal" data-target="#delete-project-modal">
+          <i class="icon-trash text-danger"></i> Delete project</a>
+      </li>
     </ul>
   </div>
   <div class="col-md-10">
diff --git a/bitbake/lib/toaster/toastergui/templates/project.html b/bitbake/lib/toaster/toastergui/templates/project.html
index ff9eab7..d932c56 100644
--- a/bitbake/lib/toaster/toastergui/templates/project.html
+++ b/bitbake/lib/toaster/toastergui/templates/project.html
@@ -24,30 +24,29 @@
   });
 </script>
 
-{% comment %}
-<!-- Comment out the ability to change the project release, until we decide what to do this functionality -->
-<div id="change-release-modal" class="modal hide fade in" tabindex="-1" role="dialog" aria-labelledby="change-release-modal" aria-hidden="false">
+<div id="delete-project-modal" class="modal fade" tabindex="-1" role="dialog">
   <div class="modal-dialog">
     <div class="modal-content">
-
       <div class="modal-header">
-	<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
-	<h3>Changing Yocto Project release to <span class="proposed-release-change-name"></span></h3>
+        <h4>Are you sure you want to delete this project?</h4>
       </div>
       <div class="modal-body">
-	<p>The following added layers do not exist for <span class="proposed-release-change-name"></span>: </p>
-	<ul id="layers-to-remove-list">
-	</ul>
-	<p>If you change the Yocto Project release to <span class="proposed-release-change-name"></span>, the above layers will be deleted from your added layers.</p>
+        <p>Deleting the <strong>A Toaster project</strong> project will remove forever:</p>
+        <ul>
+          <li>Its configuration information</li>
+          <li>Its imported layers</li>
+          <li>Its custom images</li>
+          <li>All its build information</li>
+        </ul>
       </div>
       <div class="modal-footer">
-	<button id="change-release-and-rm-layers" data-dismiss="modal" type="submit" class="btn btn-primary">Change release and delete layers</button>
-	<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
+        <button type="button" class="btn btn-primary" id="delete-project-confirmed">Delete project</button>
+        <button type="button" class="btn btn-link" data-dismiss="modal">Cancel</button>
       </div>
     </div><!-- /.modal-content -->
   </div><!-- /.modal-dialog -->
-</div><!-- /.modal -->
-{% endcomment %}
+</div>
+
 
 <div class="row" id="project-page" style="display:none">
   <div class="col-md-6">
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 4/4] toaster: buildinfohelper local layer don't construct path using git info
  2016-09-06 15:13 [PATCH 0/4] clean ups, buildinfohelper fix and delete project patches Michael Wood
                   ` (2 preceding siblings ...)
  2016-09-06 15:13 ` [PATCH 3/4] toaster: project page Implement front end feature to delete project Michael Wood
@ 2016-09-06 15:13 ` Michael Wood
  3 siblings, 0 replies; 5+ messages in thread
From: Michael Wood @ 2016-09-06 15:13 UTC (permalink / raw)
  To: toaster

When the layer is local source don't try and work out the location of
the layer by using the git information (getGitCloneDirectory)

[YOCTO #10199]

Signed-off-by: Michael Wood <michael.g.wood@intel.com>
---
 bitbake/lib/bb/ui/buildinfohelper.py | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/bitbake/lib/bb/ui/buildinfohelper.py b/bitbake/lib/bb/ui/buildinfohelper.py
index 0f09b5c..6bf8278 100644
--- a/bitbake/lib/bb/ui/buildinfohelper.py
+++ b/bitbake/lib/bb/ui/buildinfohelper.py
@@ -449,7 +449,11 @@ class ORMWrapper(object):
             # note that this is different
             buildrequest = BuildRequest.objects.get(pk = br_id)
             for brl in buildrequest.brlayer_set.all():
-                localdirname = os.path.join(bc.getGitCloneDirectory(brl.giturl, brl.commit), brl.dirpath)
+                if brl.local_source_dir:
+                    localdirname = os.path.join(brl.local_source_dir,
+                                                brl.dirpath)
+                else:
+                    localdirname = os.path.join(bc.getGitCloneDirectory(brl.giturl, brl.commit), brl.dirpath)
                 # we get a relative path, unless running in HEAD mode where the path is absolute
                 if not localdirname.startswith("/"):
                     localdirname = os.path.join(bc.be.sourcedir, localdirname)
@@ -979,8 +983,6 @@ class BuildInfoHelper(object):
     def _get_layer_version_for_path(self, path):
         self._ensure_build()
 
-        assert path.startswith("/")
-
         def _slkey_interactive(layer_version):
             assert isinstance(layer_version, Layer_Version)
             return len(layer_version.local_path)
-- 
2.7.4



^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2016-09-06 15:14 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-09-06 15:13 [PATCH 0/4] clean ups, buildinfohelper fix and delete project patches Michael Wood
2016-09-06 15:13 ` [PATCH 1/4] toaster: Clean up and convert to rest api project edit and get calls Michael Wood
2016-09-06 15:13 ` [PATCH 2/4] toaster: move MostRecentBuildsView to its own widget Michael Wood
2016-09-06 15:13 ` [PATCH 3/4] toaster: project page Implement front end feature to delete project Michael Wood
2016-09-06 15:13 ` [PATCH 4/4] toaster: buildinfohelper local layer don't construct path using git info Michael Wood

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.