Openembedded Bitbake Development
 help / color / mirror / Atom feed
* [PATCH 1/9] toaster: remove strftime calls in filters
  2014-07-04 12:44 [PATCH 0/9] toaster pull request Alex DAMIAN
@ 2014-07-04 12:44 ` Alex DAMIAN
  2014-07-04 12:44 ` [PATCH 2/9] toaster: automatically discover urls.py files Alex DAMIAN
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-04 12:44 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

We remove the unneeded strftime calls in filters, as the
filters can deal directly with datetime types.

[YOCTO #6379]

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/toaster/toastergui/views.py | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 68e981a..1f3e11d 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -301,9 +301,9 @@ def builds(request):
                  'filter' : {'class' : 'started_on',
                              'label': 'Show:',
                              'options' : [
-                                         ("Today's builds" , 'started_on__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=timezone.now().strftime("%Y-%m-%d")).count()),
-                                         ("Yesterday's builds", 'started_on__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d")).count()),
-                                         ("This week's builds", 'started_on__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d")).count()),
+                                         ("Today's builds" , 'started_on__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=timezone.now()).count()),
+                                         ("Yesterday's builds", 'started_on__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=(timezone.now()-timedelta(hours=24))).count()),
+                                         ("This week's builds", 'started_on__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=(timezone.now()-timedelta(days=7))).count()),
                                          ]
                             }
                 },
@@ -315,9 +315,9 @@ def builds(request):
                  'filter' : {'class' : 'completed_on',
                              'label': 'Show:',
                              'options' : [
-                                         ("Today's builds", 'completed_on__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=timezone.now().strftime("%Y-%m-%d")).count()),
-                                         ("Yesterday's builds", 'completed_on__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d")).count()),
-                                         ("This week's builds", 'completed_on__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d")).count()),
+                                         ("Today's builds", 'completed_on__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=timezone.now()).count()),
+                                         ("Yesterday's builds", 'completed_on__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=(timezone.now()-timedelta(hours=24))).count()),
+                                         ("This week's builds", 'completed_on__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=(timezone.now()-timedelta(days=7))).count()),
                                          ]
                             }
                 },
-- 
1.9.1



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

* [PATCH 2/9] toaster: automatically discover urls.py files
  2014-07-04 12:44 [PATCH 0/9] toaster pull request Alex DAMIAN
  2014-07-04 12:44 ` [PATCH 1/9] toaster: remove strftime calls in filters Alex DAMIAN
@ 2014-07-04 12:44 ` Alex DAMIAN
  2014-07-04 12:44 ` [PATCH 3/9] toaster: automatically enable applications Alex DAMIAN
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-04 12:44 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

Added code to automatically discover and add url dispatchers
to the urlpattern list. This allows extension of Toaster
through adding applications that will be automatically
registered with the URL dispatcher.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/toaster/toastermain/urls.py | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/lib/toaster/toastermain/urls.py b/lib/toaster/toastermain/urls.py
index ede5e4f..0e7b5c2 100644
--- a/lib/toaster/toastermain/urls.py
+++ b/lib/toaster/toastermain/urls.py
@@ -27,12 +27,10 @@ from django.views.decorators.cache import never_cache
 # Uncomment the next two lines to enable the admin:
 # from django.contrib import admin
 # admin.autodiscover()
-
 urlpatterns = patterns('',
-    url(r'^simple/', include('bldviewer.urls')),
     url(r'^api/1.0/', include('bldviewer.api')),
-    url(r'^gui/', include('toastergui.urls')),
-    url(r'^$', never_cache(RedirectView.as_view(url='/gui/'))),
+    url(r'^$', never_cache(RedirectView.as_view(url='/toastergui/'))),
+
     # Examples:
     # url(r'^toaster/', include('toaster.foo.urls')),
 
@@ -42,3 +40,13 @@ urlpatterns = patterns('',
     # Uncomment the next line to enable the admin:
     # url(r'^admin/', include(admin.site.urls)),
 )
+
+# Automatically discover urls.py in various apps, beside our own
+# and map module directories to the patterns
+
+import os
+currentdir = os.path.dirname(__file__)
+for t in os.walk(os.path.dirname(currentdir)):
+    if "urls.py" in t[2] and t[0] != currentdir:
+        modulename = os.path.basename(t[0])
+        urlpatterns.append( url(r'^' + modulename + '/', include ( modulename + '.urls')))
-- 
1.9.1



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

* [PATCH 3/9] toaster: automatically enable applications
  2014-07-04 12:44 [PATCH 0/9] toaster pull request Alex DAMIAN
  2014-07-04 12:44 ` [PATCH 1/9] toaster: remove strftime calls in filters Alex DAMIAN
  2014-07-04 12:44 ` [PATCH 2/9] toaster: automatically discover urls.py files Alex DAMIAN
@ 2014-07-04 12:44 ` Alex DAMIAN
  2014-07-04 12:44 ` [PATCH 4/9] toasterui: Show in the log that ToasterUi is ready to receive events Alex DAMIAN
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-04 12:44 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

We automatically enable local applications in INSTALLED_APPS
based on detecting a models.py or views.py file.
This allows Toaster extensibility by adding applications,
without having to edit configuration files.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/toaster/toastermain/settings.py | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/lib/toaster/toastermain/settings.py b/lib/toaster/toastermain/settings.py
index 2ce10c4..97f2ff7 100644
--- a/lib/toaster/toastermain/settings.py
+++ b/lib/toaster/toastermain/settings.py
@@ -243,6 +243,15 @@ INSTALLED_APPS = (
     'bldcontrol',
 )
 
+# We automatically detect and install applications here if
+# they have a 'models.py' or 'views.py' file
+import os
+currentdir = os.path.dirname(__file__)
+for t in os.walk(os.path.dirname(currentdir)):
+    modulename = os.path.basename(t[0])
+    if ("views.py" in t[2] or "models.py" in t[2]) and not modulename in INSTALLED_APPS:
+        INSTALLED_APPS.append(modulename)
+
 # A sample logging configuration. The only tangible logging
 # performed by this configuration is to send an email to
 # the site admins on every HTTP 500 error when DEBUG=False.
-- 
1.9.1



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

* [PATCH 0/9] toaster pull request
@ 2014-07-04 12:44 Alex DAMIAN
  2014-07-04 12:44 ` [PATCH 1/9] toaster: remove strftime calls in filters Alex DAMIAN
                   ` (9 more replies)
  0 siblings, 10 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-04 12:44 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Dave Lerner, Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

Hello,

Can you please merge these changes into the master tree ?

This patchset consists of some bug fixes and new feature related to 
the project building functionality. The patchset has been reviewed
on the toaster mailing list.

Thank you,
Alex

The following changes since commit 56c294dc30b6c2575b1cf904e26b8b8bef7677c2:

  fetch2/svn: Add transportuser parameter (2014-07-03 14:09:17 +0100)

are available in the git repository at:

  git://git.yoctoproject.org/poky-contrib adamian/20140704-submission-bb
  http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=adamian/20140704-submission-bb

Alexandru DAMIAN (8):
  toaster: remove strftime calls in filters
  toaster: automatically discover urls.py files
  toaster: automatically enable applications
  toaster: add project pages
  toaster: add automated login in new project page
  toaster: whitespace fix
  toaster: new project page implementation
  toasterui: fix build - project identification

Dave Lerner (1):
  toasterui: Show in the log that ToasterUi is ready to receive events.

 bin/toaster                                        |   1 +
 lib/bb/ui/buildinfohelper.py                       |  17 +-
 lib/bb/ui/toasterui.py                             |  11 +-
 ...anch__add_field_project_short_description__a.py | 257 +++++++++++++++++++++
 lib/toaster/orm/models.py                          |   7 +
 lib/toaster/toastergui/templates/base.html         |  13 ++
 lib/toaster/toastergui/templates/newproject.html   |  43 ++++
 lib/toaster/toastergui/templates/project.html      |   6 +
 lib/toaster/toastergui/templatetags/projecttags.py |  18 +-
 lib/toaster/toastergui/urls.py                     |   5 +
 lib/toaster/toastergui/views.py                    | 106 +++++++--
 lib/toaster/toastermain/settings.py                |  28 ++-
 lib/toaster/toastermain/urls.py                    |  16 +-
 13 files changed, 491 insertions(+), 37 deletions(-)
 create mode 100644 lib/toaster/orm/migrations/0010_auto__add_field_project_branch__add_field_project_short_description__a.py
 create mode 100644 lib/toaster/toastergui/templates/newproject.html
 create mode 100644 lib/toaster/toastergui/templates/project.html

-- 
1.9.1



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

* [PATCH 4/9] toasterui: Show in the log that ToasterUi is ready to receive events.
  2014-07-04 12:44 [PATCH 0/9] toaster pull request Alex DAMIAN
                   ` (2 preceding siblings ...)
  2014-07-04 12:44 ` [PATCH 3/9] toaster: automatically enable applications Alex DAMIAN
@ 2014-07-04 12:44 ` Alex DAMIAN
  2014-07-04 12:44 ` [PATCH 5/9] toaster: add project pages Alex DAMIAN
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-04 12:44 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Dave Lerner, Alexandru DAMIAN

From: Dave Lerner <dave.lerner@windriver.com>

Issue: TA53702

It was observed that a sequence in a script such as
   bitbake --server-only ..
   bitbake --observe-only ..
   bitbake <some target>
could generate events from the server to the observer before
the observer was ready to read the events, and the early events
of builds were consistently dropped.  Adding a "ready" note in the
log allows the script to scan for that message before proceeding
to building a target.

Signed-off-by: Dave Lerner <dave.lerner@windriver.com>
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/bb/ui/toasterui.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lib/bb/ui/toasterui.py b/lib/bb/ui/toasterui.py
index b1c80cc..5f87a9d 100644
--- a/lib/bb/ui/toasterui.py
+++ b/lib/bb/ui/toasterui.py
@@ -91,6 +91,7 @@ def main(server, eventHandler, params ):
     errors = 0
     warnings = 0
     taskfailures = []
+    first = True
 
     buildinfohelper = BuildInfoHelper(server, build_history_enabled)
 
@@ -98,6 +99,9 @@ def main(server, eventHandler, params ):
     while True:
         try:
             event = eventHandler.waitEvent(0.25)
+            if first:
+                first = False
+                logger.info("ToasterUI waiting for events")
 
             if event is None:
                 if main.shutdown > 0:
-- 
1.9.1



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

* [PATCH 5/9] toaster: add project pages
  2014-07-04 12:44 [PATCH 0/9] toaster pull request Alex DAMIAN
                   ` (3 preceding siblings ...)
  2014-07-04 12:44 ` [PATCH 4/9] toasterui: Show in the log that ToasterUi is ready to receive events Alex DAMIAN
@ 2014-07-04 12:44 ` Alex DAMIAN
  2014-07-04 12:44 ` [PATCH 6/9] toaster: add automated login in new project page Alex DAMIAN
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-04 12:44 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

We add the new project and project page skeletons.

In the process, we add an identifier in the settings.py
to detect whenever Toaster is running in managed mode,
and a context processor to make this value available
to the template processor.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 bin/toaster                                      |  1 +
 lib/toaster/toastergui/templates/base.html       | 13 ++++++++
 lib/toaster/toastergui/templates/newproject.html | 41 ++++++++++++++++++++++++
 lib/toaster/toastergui/templates/project.html    |  6 ++++
 lib/toaster/toastergui/urls.py                   |  5 +++
 lib/toaster/toastergui/views.py                  | 38 ++++++++++++++++++++++
 lib/toaster/toastermain/settings.py              | 19 ++++++++---
 7 files changed, 119 insertions(+), 4 deletions(-)
 create mode 100644 lib/toaster/toastergui/templates/newproject.html
 create mode 100644 lib/toaster/toastergui/templates/project.html

diff --git a/bin/toaster b/bin/toaster
index 01ffc7a..90cd982 100755
--- a/bin/toaster
+++ b/bin/toaster
@@ -139,6 +139,7 @@ if [ -z "$ZSH_NAME" ] && [ `basename \"$0\"` = `basename \"$BASH_SOURCE\"` ]; th
         webserverKillAll
         RUNNING=0
     }
+    export TOASTER_MANAGED=1
     webserverStartAll || (echo "Fail to start the web server, stopping" 1>&2 && exit 1)
     xdg-open http://0.0.0.0:8000/ >/dev/null 2>&1 &
     trap trap_ctrlc SIGINT
diff --git a/lib/toaster/toastergui/templates/base.html b/lib/toaster/toastergui/templates/base.html
index 9ca9c9a..1407d64 100644
--- a/lib/toaster/toastergui/templates/base.html
+++ b/lib/toaster/toastergui/templates/base.html
@@ -58,6 +58,19 @@ function reload_params(params) {
     <div class="navbar-inner">
             <a class="brand logo" href="#"><img src="{% static 'img/logo.png' %}" class="" alt="Yocto logo project"/></a>
             <a class="brand" href="/">Toaster</a>
+				{%if MANAGED %}
+					  <div class="btn-group pull-right">
+						  <a class="btn" href="{% url 'newproject' %}">New project</a>
+						  <button class="btn dropdown-toggle" data-toggle="dropdown">
+							  <i class="icon-caret-down"></i>
+						  </button>
+						  <ul class="dropdown-menu">
+							  <li><a href="#">Clone project</a></li>
+							  <li><a href="#">Export project</a></li>
+							  <li><a href="#">Import project</a></li>
+						  </ul>
+					  </div>
+				{%endif%}
             <a class="pull-right manual" target="_blank" href="http://www.yoctoproject.org/documentation/toaster-manual">
                 <i class="icon-book"></i>
                 Toaster manual
diff --git a/lib/toaster/toastergui/templates/newproject.html b/lib/toaster/toastergui/templates/newproject.html
new file mode 100644
index 0000000..e5a6551
--- /dev/null
+++ b/lib/toaster/toastergui/templates/newproject.html
@@ -0,0 +1,41 @@
+{% extends "base.html" %}
+{% load projecttags %}
+{% load humanize %}
+{% block pagecontent %}
+<div class="row-fluid">
+				<div class="span6">
+				<div class="page-header">
+					<h1>Create a new project</h1>
+				</div>  
+					<form>
+						<fieldset>
+							<label>Project name <span class="muted">(required)</span></label>
+							<input type="text" class="input-xlarge" required name="projectname">
+							<label class="project-form">
+								Project owner
+								<i class="icon-question-sign get-help" title="The go-to person for this project"></i>
+							</label>
+				<form method="POST">
+							<input type="text">
+							<label class="project-form">Owner's email</label>
+							<input type="email" class="input-large" name="email">
+							<label class="project-form">
+								Yocto Project version
+								<i class="icon-question-sign get-help" title="This sets the branch for the Yocto Project core layers (meta, meta-yocto and meta-yocto-bsp), and for the layers you use from the OpenEmbedded Metadata Index"></i>
+							</label>
+							<select>
+								<option>Yocto Project 1.7 "D?"</option>
+								<option>Yocto Project 1.6 "Daisy"</option>
+								<option>Yocto Project 1.5 "Dora"</option>
+							</select>
+				</form>
+						</fieldset>
+									
+						<div class="form-actions">
+							<a href="project-with-targets.html" class="btn btn-primary btn-large">Create project</a>
+						</div>
+					</form> 
+				</div>
+			</div>
+		</div>  
+{% endblock %}
diff --git a/lib/toaster/toastergui/templates/project.html b/lib/toaster/toastergui/templates/project.html
new file mode 100644
index 0000000..71adb54
--- /dev/null
+++ b/lib/toaster/toastergui/templates/project.html
@@ -0,0 +1,6 @@
+{% extends "base.html" %}
+{% load projecttags %}
+{% load humanize %}
+{% block pagecontent %}
+
+{% endblock %}
diff --git a/lib/toaster/toastergui/urls.py b/lib/toaster/toastergui/urls.py
index 9b583f2..bba4fda 100644
--- a/lib/toaster/toastergui/urls.py
+++ b/lib/toaster/toastergui/urls.py
@@ -65,6 +65,11 @@ urlpatterns = patterns('toastergui.views',
         # urls not linked from the dashboard
         url(r'^layers/$', 'layer', name='all-layers'),
         url(r'^layerversions/(?P<layerversion_id>\d+)/recipes/.*$', 'layer_versions_recipes', name='layer_versions_recipes'),
+
+        # project URLs
+        url(r'^newproject/$', 'newproject', name='newproject'),
+        url(r'^project/$', 'project', name='project'),
+
         # default redirection
         url(r'^$', RedirectView.as_view( url= 'builds/')),
 )
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 1f3e11d..7dc0108 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -1758,3 +1758,41 @@ def image_information_dir(request, build_id, target_id, packagefile_id):
     # stubbed for now
     return redirect(builds)
 
+
+import toastermain.settings
+def managedcontextprocessor(request):
+    return { "MANAGED" : toastermain.settings.MANAGED }
+
+
+# we have a set of functions if we're in managed mode, or
+# a default "page not available" simple functions for interactive mode
+if toastermain.settings.MANAGED:
+
+    # new project
+    def newproject(request):
+        template = "newproject.html"
+        context = {}
+        if request.method == "GET":
+            # render new project page
+            return render(request, template, context)
+        elif request.method == "POST":
+            if request.method:
+                return redirect(project)
+            else:
+                return render(request, template, context)
+        raise Exception("Invalid HTTP method for this page")
+
+    # Shows the edit project page
+    def project(request):
+        template = "project.html"
+        context = {}
+        return render(request, template, context)
+
+
+else:
+    # these are pages that are NOT available in interactive mode
+    def newproject(request):
+        raise Exception("page not available in interactive mode")
+
+    def project(request):
+        raise Exception("page not available in interactive mode")
diff --git a/lib/toaster/toastermain/settings.py b/lib/toaster/toastermain/settings.py
index 97f2ff7..09ec2bd 100644
--- a/lib/toaster/toastermain/settings.py
+++ b/lib/toaster/toastermain/settings.py
@@ -75,6 +75,11 @@ if 'DATABASE_URL' in os.environ:
         raise Exception("FIXME: Please implement missing database url schema for url: %s" % dburl)
 
 
+if 'TOASTER_MANAGED' in os.environ and os.environ['TOASTER_MANAGED'] == "1":
+    MANAGED = True
+else:
+    MANAGED = False
+
 # Allows current database settings to be exported as a DATABASE_URL environment variable value
 
 def getDATABASE_URL():
@@ -221,12 +226,11 @@ TEMPLATE_CONTEXT_PROCESSORS = ('django.contrib.auth.context_processors.auth',
  'django.core.context_processors.static',
  'django.core.context_processors.tz',
  'django.contrib.messages.context_processors.messages',
- "django.core.context_processors.request")
+ "django.core.context_processors.request",
+ 'toastergui.views.managedcontextprocessor',
+ )
 
 INSTALLED_APPS = (
-    #'django.contrib.auth',
-    #'django.contrib.contenttypes',
-    #'django.contrib.sessions',
     #'django.contrib.sites',
     #'django.contrib.messages',
     'django.contrib.staticfiles',
@@ -243,6 +247,13 @@ INSTALLED_APPS = (
     'bldcontrol',
 )
 
+# if we run in managed mode, we need user support
+if MANAGED:
+    INSTALLED_APPS = ('django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',) + INSTALLED_APPS
+
+
 # We automatically detect and install applications here if
 # they have a 'models.py' or 'views.py' file
 import os
-- 
1.9.1



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

* [PATCH 6/9] toaster: add automated login in new project page
  2014-07-04 12:44 [PATCH 0/9] toaster pull request Alex DAMIAN
                   ` (4 preceding siblings ...)
  2014-07-04 12:44 ` [PATCH 5/9] toaster: add project pages Alex DAMIAN
@ 2014-07-04 12:44 ` Alex DAMIAN
  2014-07-04 12:44 ` [PATCH 7/9] toaster: whitespace fix Alex DAMIAN
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-04 12:44 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

Toaster uses the Django authentication system to assign
user accounts to the projects that are being created.

In the current implementation, the user accounts are
created/authenticated automatically, on the fly, based
on the fields specified in the create new project page.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/toaster/toastergui/templates/newproject.html | 35 +++++++++++++-----------
 lib/toaster/toastergui/views.py                  | 29 ++++++++++++++++++--
 2 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/lib/toaster/toastergui/templates/newproject.html b/lib/toaster/toastergui/templates/newproject.html
index e5a6551..ce01800 100644
--- a/lib/toaster/toastergui/templates/newproject.html
+++ b/lib/toaster/toastergui/templates/newproject.html
@@ -6,36 +6,39 @@
 				<div class="span6">
 				<div class="page-header">
 					<h1>Create a new project</h1>
-				</div>  
-					<form>
+				</div>
+				<div class="container-fluid">
+		{% for a in alerts %}
+			<div class="alert alert-error row-fluid" role="alert">{{a}}</div>
+		{% endfor %}
+				</div>
+					<form method="POST">{% csrf_token %}
 						<fieldset>
 							<label>Project name <span class="muted">(required)</span></label>
-							<input type="text" class="input-xlarge" required name="projectname">
+							<input type="text" class="input-xlarge" required name="projectname" value="{{projectname}}">
 							<label class="project-form">
 								Project owner
-								<i class="icon-question-sign get-help" title="The go-to person for this project"></i>
+					<i class="icon-question-sign get-help" title="The go-to person for this project"></i>
 							</label>
-				<form method="POST">
-							<input type="text">
+							<input type="text" name="username" value="{{username}}">
 							<label class="project-form">Owner's email</label>
-							<input type="email" class="input-large" name="email">
+							<input type="email" class="input-large" name="email" value="{{email}}">
 							<label class="project-form">
 								Yocto Project version
 								<i class="icon-question-sign get-help" title="This sets the branch for the Yocto Project core layers (meta, meta-yocto and meta-yocto-bsp), and for the layers you use from the OpenEmbedded Metadata Index"></i>
 							</label>
-							<select>
-								<option>Yocto Project 1.7 "D?"</option>
-								<option>Yocto Project 1.6 "Daisy"</option>
-								<option>Yocto Project 1.5 "Dora"</option>
+							<select name="projectversion">
+		<option value="1.7" {%if projectversion == "1.7" %}selected{%endif%}>Yocto Project 1.7 "D?"</option>
+		<option value="1.6" {%if projectversion == "1.6" %}selected{%endif%}>Yocto Project 1.6 "Daisy"</option>
+		<option value="1.5" {%if projectversion == "1.5" %}selected{%endif%}>Yocto Project 1.5 "Dora"</option>
 							</select>
-				</form>
 						</fieldset>
-									
+
 						<div class="form-actions">
-							<a href="project-with-targets.html" class="btn btn-primary btn-large">Create project</a>
+							<input type="submit" class="btn btn-primary btn-large" value="Create project"/>
 						</div>
-					</form> 
+					</form>
 				</div>
 			</div>
-		</div>  
+		</div>
 {% endblock %}
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 7dc0108..89c02d4 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -1768,17 +1768,42 @@ def managedcontextprocessor(request):
 # a default "page not available" simple functions for interactive mode
 if toastermain.settings.MANAGED:
 
+    from django.contrib.auth.models import User
+    from django.contrib.auth import authenticate, login
+    from django.contrib.auth.decorators import login_required
+
+
     # new project
     def newproject(request):
         template = "newproject.html"
-        context = {}
+        context = {
+            'email': request.user.email if request.user.is_authenticated() else '',
+            'username': request.user.username if request.user.is_authenticated() else '',
+        }
+
+
         if request.method == "GET":
             # render new project page
             return render(request, template, context)
         elif request.method == "POST":
-            if request.method:
+            mandatory_fields = ['projectname', 'email', 'username', 'projectversion']
+            if reduce( lambda x, y: x and y, map(lambda x: x in request.POST and len(request.POST[x]) > 0, mandatory_fields)):
+                if not request.user.is_authenticated():
+                    user = authenticate(username = request.POST['username'], password = 'nopass')
+                    if user is None:
+                        user = User.objects.create_user(username = request.POST['username'], email = request.POST['email'], password = "nopass")
+                        raise Exception("User cannot be authed, creating")
+                        user = authenticate(username = request.POST['username'], password = '')
+                    login(request, user)
+
                 return redirect(project)
             else:
+                alerts = []
+                # set alerts for missing fields
+                map(lambda x: alerts.append('Field '+ x + ' not filled in') if not x in request.POST or len(request.POST[x]) == 0 else None, mandatory_fields)
+                # fill in new page with already submitted values
+                map(lambda x: context.__setitem__(x, request.POST[x]), mandatory_fields)
+                context['alerts'] = alerts
                 return render(request, template, context)
         raise Exception("Invalid HTTP method for this page")
 
-- 
1.9.1



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

* [PATCH 7/9] toaster: whitespace fix
  2014-07-04 12:44 [PATCH 0/9] toaster pull request Alex DAMIAN
                   ` (5 preceding siblings ...)
  2014-07-04 12:44 ` [PATCH 6/9] toaster: add automated login in new project page Alex DAMIAN
@ 2014-07-04 12:44 ` Alex DAMIAN
  2014-07-04 12:44 ` [PATCH 8/9] toaster: new project page implementation Alex DAMIAN
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-04 12:44 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

This patch is just a whitespace (end-of-line) fix.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/toaster/toastergui/templatetags/projecttags.py | 18 +++++++++---------
 lib/toaster/toastergui/views.py                    | 14 +++++++-------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/lib/toaster/toastergui/templatetags/projecttags.py b/lib/toaster/toastergui/templatetags/projecttags.py
index be75b21..b953aa1 100644
--- a/lib/toaster/toastergui/templatetags/projecttags.py
+++ b/lib/toaster/toastergui/templatetags/projecttags.py
@@ -142,7 +142,7 @@ def variable_parent_name(value):
     """
     value=re.sub('_\$.*', '', value)
     return re.sub('_[a-z].*', '', value)
-  
+
 @register.filter
 def filter_setin_files(file_list,matchstr):
     """ filter/search the 'set in' file lists. Note
@@ -150,7 +150,7 @@ def filter_setin_files(file_list,matchstr):
         the <p> marks, but this is safe as the data
         is file paths
     """
-   
+
     # no filters, show last file (if any)
     if matchstr == ":":
         if file_list:
@@ -162,24 +162,24 @@ def filter_setin_files(file_list,matchstr):
     htmlstr=""
     # match only filters
     if search == '':
-        for i in range(len(file_list)):   
+        for i in range(len(file_list)):
             if re.search(filter, file_list[i].file_name):
                 if htmlstr.find(file_list[i].file_name + "<p>") < 0:
                     htmlstr += file_list[i].file_name + "<p>"
         return htmlstr
-       
+
     # match only search string, plus always last file
     if filter == "":
-        for i in range(len(file_list)-1):   
+        for i in range(len(file_list)-1):
             if re.search(search,file_list[i].file_name):
                 if htmlstr.find(file_list[i].file_name + "<p>") < 0:
                     htmlstr += file_list[i].file_name + "<p>"
         if htmlstr.find(file_list[len(file_list)-1].file_name) < 0:
             htmlstr += file_list[len(file_list)-1].file_name
         return htmlstr
-       
+
     # match filter or search string
-    for i in range(len(file_list)):   
+    for i in range(len(file_list)):
         if re.search(filter, file_list[i].file_name) or re.search(search,file_list[i].file_name):
             if htmlstr.find(file_list[i].file_name + "<p>") < 0:
                 htmlstr += file_list[i].file_name + "<p>"
@@ -218,7 +218,7 @@ def filtered_packageversion(version, revision):
         else ""
     """
     return "" if (not version or version == "") else version if (not revision or revision == "") else version + "-" + revision
-        
+
 @register.filter
 def filter_sizeovertotal(package_object, total_size):
     """ Return the % size of the package over the total size argument
@@ -227,7 +227,7 @@ def filter_sizeovertotal(package_object, total_size):
     size = package_object.installed_size
     if size == None or size == '':
         size = package_object.size
-  
+
     return '{:.1%}'.format(float(size)/float(total_size))
 
 from django.utils.safestring import mark_safe
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 89c02d4..a57f001 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -429,7 +429,7 @@ def builddashboard( request, build_id ):
                 ndx = 0;
             f = i.file_name[ ndx + 1: ]
             imageFiles.append({ 'path': f, 'size' : i.file_size })
-        if ( t.is_image and 
+        if ( t.is_image and
              (( len( imageFiles ) <= 0 ) or ( len( t.license_manifest_path ) <= 0 ))):
             targetHasNoImages = True
         elem[ 'imageFiles' ] = imageFiles
@@ -516,8 +516,8 @@ def task( request, build_id, task_id ):
     }
     if request.GET.get( 'show_matches', "" ):
         context[ 'showing_matches' ] = True
-        context[ 'matching_tasks' ] = Task.objects.filter( 
-            sstate_checksum=task.sstate_checksum ).filter( 
+        context[ 'matching_tasks' ] = Task.objects.filter(
+            sstate_checksum=task.sstate_checksum ).filter(
             build__completed_on__lt=task.build.completed_on).exclude(
             order__isnull=True).exclude(outcome=Task.OUTCOME_NA).order_by('-build__completed_on')
 
@@ -551,14 +551,14 @@ def target_common( request, build_id, target_id, variant ):
     mandatory_parameters = { 'count': 25,  'page' : 1, 'orderby':'name:+'};
     retval = _verify_parameters( request.GET, mandatory_parameters )
     if retval:
-        return _redirect_parameters( 
-                    variant, request.GET, mandatory_parameters, 
+        return _redirect_parameters(
+                    variant, request.GET, mandatory_parameters,
                     build_id = build_id, target_id = target_id )
     ( filter_string, search_term, ordering_string ) = _search_tuple( request, Package )
 
     # FUTURE:  get rid of nested sub-queries replacing with ManyToMany field
     queryset = Package.objects.filter(
-                    size__gte = 0, 
+                    size__gte = 0,
                     id__in = Target_Installed_Package.objects.filter(
                         target_id=target_id ).values( 'package_id' ))
     packages_sum =  queryset.aggregate( Sum( 'installed_size' ))
@@ -682,7 +682,7 @@ his package',
         'clclass'    : 'layer_directory',
         'hidden'     : 1,
         }
-    context = { 
+    context = {
         'objectname': variant,
         'build'                : Build.objects.filter( pk = build_id )[ 0 ],
         'target'               : Target.objects.filter( pk = target_id )[ 0 ],
-- 
1.9.1



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

* [PATCH 8/9] toaster: new project page implementation
  2014-07-04 12:44 [PATCH 0/9] toaster pull request Alex DAMIAN
                   ` (6 preceding siblings ...)
  2014-07-04 12:44 ` [PATCH 7/9] toaster: whitespace fix Alex DAMIAN
@ 2014-07-04 12:44 ` Alex DAMIAN
  2014-07-04 12:44 ` [PATCH 9/9] toasterui: fix build - project identification Alex DAMIAN
  2014-07-09 16:55 ` [PATCH 0/9] [V2] toaster pull request Alex DAMIAN
  9 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-04 12:44 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

We add the logic to create a new project. This page
also serves as user registration and silent login
for users.

Once the project is added, the main project page is displayed.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 ...anch__add_field_project_short_description__a.py | 257 +++++++++++++++++++++
 lib/toaster/orm/models.py                          |   7 +
 lib/toaster/toastergui/templates/newproject.html   |  13 +-
 lib/toaster/toastergui/urls.py                     |   2 +-
 lib/toaster/toastergui/views.py                    |  27 ++-
 5 files changed, 292 insertions(+), 14 deletions(-)
 create mode 100644 lib/toaster/orm/migrations/0010_auto__add_field_project_branch__add_field_project_short_description__a.py

diff --git a/lib/toaster/orm/migrations/0010_auto__add_field_project_branch__add_field_project_short_description__a.py b/lib/toaster/orm/migrations/0010_auto__add_field_project_branch__add_field_project_short_description__a.py
new file mode 100644
index 0000000..aa1ce1f
--- /dev/null
+++ b/lib/toaster/orm/migrations/0010_auto__add_field_project_branch__add_field_project_short_description__a.py
@@ -0,0 +1,257 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'Project.branch'
+        db.add_column(u'orm_project', 'branch',
+                      self.gf('django.db.models.fields.CharField')(default='master', max_length=50),
+                      keep_default=False)
+
+        # Adding field 'Project.short_description'
+        db.add_column(u'orm_project', 'short_description',
+                      self.gf('django.db.models.fields.CharField')(default='', max_length=50, blank=True),
+                      keep_default=False)
+
+        # Adding field 'Project.user_id'
+        db.add_column(u'orm_project', 'user_id',
+                      self.gf('django.db.models.fields.IntegerField')(null=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Project.branch'
+        db.delete_column(u'orm_project', 'branch')
+
+        # Deleting field 'Project.short_description'
+        db.delete_column(u'orm_project', 'short_description')
+
+        # Deleting field 'Project.user_id'
+        db.delete_column(u'orm_project', 'user_id')
+
+
+    models = {
+        u'orm.build': {
+            'Meta': {'object_name': 'Build'},
+            'bitbake_version': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'build_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'completed_on': ('django.db.models.fields.DateTimeField', [], {}),
+            'cooker_log_path': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+            'distro': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'distro_version': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'errors_no': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'machine': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'outcome': ('django.db.models.fields.IntegerField', [], {'default': '2'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']", 'null': 'True'}),
+            'started_on': ('django.db.models.fields.DateTimeField', [], {}),
+            'timespent': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'warnings_no': ('django.db.models.fields.IntegerField', [], {'default': '0'})
+        },
+        u'orm.helptext': {
+            'Meta': {'object_name': 'HelpText'},
+            'area': ('django.db.models.fields.IntegerField', [], {}),
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'helptext_build'", 'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'text': ('django.db.models.fields.TextField', [], {})
+        },
+        u'orm.layer': {
+            'Meta': {'object_name': 'Layer'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer_index_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+            'local_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.layer_version': {
+            'Meta': {'object_name': 'Layer_Version'},
+            'branch': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_build'", 'to': u"orm['orm.Build']"}),
+            'commit': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_layer'", 'to': u"orm['orm.Layer']"}),
+            'priority': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'orm.logmessage': {
+            'Meta': {'object_name': 'LogMessage'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'lineno': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+            'pathname': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'task': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Task']", 'null': 'True', 'blank': 'True'})
+        },
+        u'orm.package': {
+            'Meta': {'object_name': 'Package'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'installed_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'installed_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'license': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Recipe']", 'null': 'True'}),
+            'revision': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+            'section': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        u'orm.package_dependency': {
+            'Meta': {'object_name': 'Package_Dependency'},
+            'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_target'", 'to': u"orm['orm.Package']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_source'", 'to': u"orm['orm.Package']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']", 'null': 'True'})
+        },
+        u'orm.package_file': {
+            'Meta': {'object_name': 'Package_File'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildfilelist_package'", 'to': u"orm['orm.Package']"}),
+            'path': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'orm.project': {
+            'Meta': {'object_name': 'Project'},
+            'branch': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'short_description': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'user_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
+        },
+        u'orm.projectlayer': {
+            'Meta': {'object_name': 'ProjectLayer'},
+            'commit': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
+            'giturl': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"})
+        },
+        u'orm.projecttarget': {
+            'Meta': {'object_name': 'ProjectTarget'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+            'target': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.projectvariable': {
+            'Meta': {'object_name': 'ProjectVariable'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+            'value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        u'orm.recipe': {
+            'Meta': {'object_name': 'Recipe'},
+            'bugtracker': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'file_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            'homepage': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'recipe_layer_version'", 'to': u"orm['orm.Layer_Version']"}),
+            'license': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'section': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        u'orm.recipe_dependency': {
+            'Meta': {'object_name': 'Recipe_Dependency'},
+            'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_depends'", 'to': u"orm['orm.Recipe']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_recipe'", 'to': u"orm['orm.Recipe']"})
+        },
+        u'orm.target': {
+            'Meta': {'object_name': 'Target'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'is_image': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'license_manifest_path': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True'}),
+            'target': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.target_file': {
+            'Meta': {'object_name': 'Target_File'},
+            'directory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'directory_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+            'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'inodetype': ('django.db.models.fields.IntegerField', [], {}),
+            'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'path': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
+            'permission': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'size': ('django.db.models.fields.IntegerField', [], {}),
+            'sym_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'symlink_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.target_image_file': {
+            'Meta': {'object_name': 'Target_Image_File'},
+            'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '254'}),
+            'file_size': ('django.db.models.fields.IntegerField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.target_installed_package': {
+            'Meta': {'object_name': 'Target_Installed_Package'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildtargetlist_package'", 'to': u"orm['orm.Package']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.task': {
+            'Meta': {'ordering': "('order', 'recipe')", 'unique_together': "(('build', 'recipe', 'task_name'),)", 'object_name': 'Task'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_build'", 'to': u"orm['orm.Build']"}),
+            'cpu_usage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            'disk_io': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'elapsed_time': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'logfile': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'outcome': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
+            'path_to_sstate_obj': ('django.db.models.fields.FilePathField', [], {'max_length': '500', 'blank': 'True'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'build_recipe'", 'to': u"orm['orm.Recipe']"}),
+            'script_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'source_url': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'sstate_checksum': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'sstate_result': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'task_executed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'task_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'work_directory': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'})
+        },
+        u'orm.task_dependency': {
+            'Meta': {'object_name': 'Task_Dependency'},
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_depends'", 'to': u"orm['orm.Task']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'task': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_task'", 'to': u"orm['orm.Task']"})
+        },
+        u'orm.variable': {
+            'Meta': {'object_name': 'Variable'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variable_build'", 'to': u"orm['orm.Build']"}),
+            'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'human_readable_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'variable_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'variable_value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        u'orm.variablehistory': {
+            'Meta': {'object_name': 'VariableHistory'},
+            'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_number': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'operation': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'value': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'variable': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'vhistory'", 'to': u"orm['orm.Variable']"})
+        }
+    }
+
+    complete_apps = ['orm']
\ No newline at end of file
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index b67afe1..8f6aa37 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -25,8 +25,15 @@ from django.utils.encoding import python_2_unicode_compatible
 
 class Project(models.Model):
     name = models.CharField(max_length=100)
+    branch = models.CharField(max_length=50)
+    short_description = models.CharField(max_length=50, blank=True)
     created     = models.DateTimeField(auto_now_add = True)
     updated     = models.DateTimeField(auto_now = True)
+    # This is a horrible hack; since Toaster has no "User" model available when
+    # running in interactive mode, we can't reference the field here directly
+    # Instead, we keep a possible null reference to the User id, as not to force
+    # hard links to possibly missing models
+    user_id     = models.IntegerField(null = True)
 
 class Build(models.Model):
     SUCCEEDED = 0
diff --git a/lib/toaster/toastergui/templates/newproject.html b/lib/toaster/toastergui/templates/newproject.html
index ce01800..8f1867a 100644
--- a/lib/toaster/toastergui/templates/newproject.html
+++ b/lib/toaster/toastergui/templates/newproject.html
@@ -8,9 +8,9 @@
 					<h1>Create a new project</h1>
 				</div>
 				<div class="container-fluid">
-		{% for a in alerts %}
-			<div class="alert alert-error row-fluid" role="alert">{{a}}</div>
-		{% endfor %}
+		{% if alert %}
+			<div class="alert alert-error row-fluid" role="alert">{{alert}}</div>
+		{% endif %}
 				</div>
 					<form method="POST">{% csrf_token %}
 						<fieldset>
@@ -27,10 +27,9 @@
 								Yocto Project version
 								<i class="icon-question-sign get-help" title="This sets the branch for the Yocto Project core layers (meta, meta-yocto and meta-yocto-bsp), and for the layers you use from the OpenEmbedded Metadata Index"></i>
 							</label>
-							<select name="projectversion">
-		<option value="1.7" {%if projectversion == "1.7" %}selected{%endif%}>Yocto Project 1.7 "D?"</option>
-		<option value="1.6" {%if projectversion == "1.6" %}selected{%endif%}>Yocto Project 1.6 "Daisy"</option>
-		<option value="1.5" {%if projectversion == "1.5" %}selected{%endif%}>Yocto Project 1.5 "Dora"</option>
+							<select name="projectversion" id="projectversion">
+		<!-- TODO: XHR data from http://layers.openembedded.org/layerindex/branch/master/layers/ -->
+		<option value="master" {%if projectversion == "master" %}selected{%endif%}>master</option>
 							</select>
 						</fieldset>
 
diff --git a/lib/toaster/toastergui/urls.py b/lib/toaster/toastergui/urls.py
index bba4fda..0d7a4c3 100644
--- a/lib/toaster/toastergui/urls.py
+++ b/lib/toaster/toastergui/urls.py
@@ -68,7 +68,7 @@ urlpatterns = patterns('toastergui.views',
 
         # project URLs
         url(r'^newproject/$', 'newproject', name='newproject'),
-        url(r'^project/$', 'project', name='project'),
+        url(r'^project/(?P<pid>\d+)/$', 'project', name='project'),
 
         # default redirection
         url(r'^$', RedirectView.as_view( url= 'builds/')),
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index a57f001..1648ab1 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -22,11 +22,13 @@
 import operator,re
 
 from django.db.models import Q, Sum
+from django.db import IntegrityError
 from django.shortcuts import render, redirect
 from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable
 from orm.models import Task_Dependency, Recipe_Dependency, Package, Package_File, Package_Dependency
 from orm.models import Target_Installed_Package, Target_File, Target_Image_File
 from django.views.decorators.cache import cache_control
+from django.core.urlresolvers import reverse
 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 from django.http import HttpResponseBadRequest
 from django.utils import timezone
@@ -68,7 +70,6 @@ def _verify_parameters(g, mandatory_parameters):
 
 def _redirect_parameters(view, g, mandatory_parameters, *args, **kwargs):
     import urllib
-    from django.core.urlresolvers import reverse
     url = reverse(view, kwargs=kwargs)
     params = {}
     for i in g:
@@ -1772,6 +1773,7 @@ if toastermain.settings.MANAGED:
     from django.contrib.auth import authenticate, login
     from django.contrib.auth.decorators import login_required
 
+    from orm.models import Project
 
     # new project
     def newproject(request):
@@ -1787,16 +1789,29 @@ if toastermain.settings.MANAGED:
             return render(request, template, context)
         elif request.method == "POST":
             mandatory_fields = ['projectname', 'email', 'username', 'projectversion']
-            if reduce( lambda x, y: x and y, map(lambda x: x in request.POST and len(request.POST[x]) > 0, mandatory_fields)):
+            class BadParameterException(Exception): pass
+            try:
+                # make sure we have values for all mandatory_fields
+                if reduce( lambda x, y: x or y, map(lambda x: len(request.POST.get(x, '')) == 0, mandatory_fields)):
+                # set alert for missing fields
+                    raise BadParameterException("Fields missing: " +
+            ", ".join([x for x in mandatory_fields if len(request.POST.get(x, '')) == 0 ]))
+
                 if not request.user.is_authenticated():
                     user = authenticate(username = request.POST['username'], password = 'nopass')
                     if user is None:
                         user = User.objects.create_user(username = request.POST['username'], email = request.POST['email'], password = "nopass")
-                        raise Exception("User cannot be authed, creating")
-                        user = authenticate(username = request.POST['username'], password = '')
+
+                        user = authenticate(username = user.username, password = 'nopass')
                     login(request, user)
 
-                return redirect(project)
+                #  save the project
+                prj = Project.objects.create(name = request.POST['projectname'],
+                    branch = request.POST['projectversion'].split(" ")[0],
+                    short_description=request.POST['projectversion'].split(" ")[1:])
+                prj.user_id = request.user.pk
+                prj.save()
+                return redirect(reverse(project, args = (prj.pk,)))
             else:
                 alerts = []
                 # set alerts for missing fields
@@ -1808,7 +1823,7 @@ if toastermain.settings.MANAGED:
         raise Exception("Invalid HTTP method for this page")
 
     # Shows the edit project page
-    def project(request):
+    def project(request, pid):
         template = "project.html"
         context = {}
         return render(request, template, context)
-- 
1.9.1



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

* [PATCH 9/9] toasterui: fix build - project identification
  2014-07-04 12:44 [PATCH 0/9] toaster pull request Alex DAMIAN
                   ` (7 preceding siblings ...)
  2014-07-04 12:44 ` [PATCH 8/9] toaster: new project page implementation Alex DAMIAN
@ 2014-07-04 12:44 ` Alex DAMIAN
  2014-07-09 16:55 ` [PATCH 0/9] [V2] toaster pull request Alex DAMIAN
  9 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-04 12:44 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

This patches fixes the build - project identification when
running under managed mode. The build is assigned to the
project from which it was triggered, and to the
build request, as to simplify relationships queries
 in the database.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/bb/ui/buildinfohelper.py | 17 +++++++++++++++--
 lib/bb/ui/toasterui.py       |  7 +++----
 2 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/lib/bb/ui/buildinfohelper.py b/lib/bb/ui/buildinfohelper.py
index 77be7c7..29cfc81 100644
--- a/lib/bb/ui/buildinfohelper.py
+++ b/lib/bb/ui/buildinfohelper.py
@@ -46,7 +46,7 @@ class ORMWrapper(object):
         pass
 
 
-    def create_build_object(self, build_info):
+    def create_build_object(self, build_info, brbe):
         assert 'machine' in build_info
         assert 'distro' in build_info
         assert 'distro_version' in build_info
@@ -65,6 +65,13 @@ class ORMWrapper(object):
                                     build_name=build_info['build_name'],
                                     bitbake_version=build_info['bitbake_version'])
 
+        if brbe is not None:
+            from bldcontrol.models import BuildEnvironment, BuildRequest
+            br, be = brbe.split(":")
+            buildrequest = BuildRequest.objects.get(pk = br)
+            build.project = buildrequest.project
+            build.save()
+
         return build
 
     def create_target_objects(self, target_info):
@@ -600,7 +607,10 @@ class BuildInfoHelper(object):
         assert '_pkgs' in vars(event)
         build_information = self._get_build_information()
 
-        build_obj = self.orm_wrapper.create_build_object(build_information)
+        brbe = self.server.runCommand(["getVariable", "TOASTER_BRBE"])[0]
+
+        build_obj = self.orm_wrapper.create_build_object(build_information, brbe)
+
         self.internal_state['build'] = build_obj
 
         # save layer version information for this build
@@ -619,6 +629,9 @@ class BuildInfoHelper(object):
         # Save build configuration
         self.orm_wrapper.save_build_variables(build_obj, self.server.runCommand(["getAllKeysWithFlags", ["doc", "func"]])[0])
 
+        return brbe
+
+
     def update_target_image_file(self, event):
         image_fstypes = self.server.runCommand(["getVariable", "IMAGE_FSTYPES"])[0]
         for t in self.internal_state['targets']:
diff --git a/lib/bb/ui/toasterui.py b/lib/bb/ui/toasterui.py
index 5f87a9d..2f628e9 100644
--- a/lib/bb/ui/toasterui.py
+++ b/lib/bb/ui/toasterui.py
@@ -94,7 +94,7 @@ def main(server, eventHandler, params ):
     first = True
 
     buildinfohelper = BuildInfoHelper(server, build_history_enabled)
-
+    brbe = None
 
     while True:
         try:
@@ -111,7 +111,7 @@ def main(server, eventHandler, params ):
             helper.eventHandler(event)
 
             if isinstance(event, bb.event.BuildStarted):
-                buildinfohelper.store_started_build(event)
+                brbe = buildinfohelper.store_started_build(event)
 
             if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)):
                 buildinfohelper.update_and_store_task(event)
@@ -231,10 +231,9 @@ def main(server, eventHandler, params ):
                 buildinfohelper.update_build_information(event, errors, warnings, taskfailures)
 
 
-                brbe = server.runCommand(["getVariable", "TOASTER_BRBE"])[0]
-                br_id, be_id = brbe.split(":")
                 # we start a new build info
                 if brbe is not None:
+                    br_id, be_id = brbe.split(":")
                     buildinfohelper.store_build_done(br_id, be_id)
 
                     print "we are under BuildEnvironment management - after the build, we exit"
-- 
1.9.1



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

* [PATCH 0/9] [V2] toaster pull request
  2014-07-04 12:44 [PATCH 0/9] toaster pull request Alex DAMIAN
                   ` (8 preceding siblings ...)
  2014-07-04 12:44 ` [PATCH 9/9] toasterui: fix build - project identification Alex DAMIAN
@ 2014-07-09 16:55 ` Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 1/9] toaster: remove strftime calls in filters Alex DAMIAN
                     ` (8 more replies)
  9 siblings, 9 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-09 16:55 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Dave Lerner, Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

Hello,

Can you please merge these changes into the master tree ?

This patchset consists of some bug fixes and new feature related to
the project building functionality. The patchset has been reviewed
on the toaster mailing list. This is version 2, with bug fixes.

Thank you,
Alex

The following changes since commit 56c294dc30b6c2575b1cf904e26b8b8bef7677c2:

  fetch2/svn: Add transportuser parameter (2014-07-03 14:09:17 +0100)

are available in the git repository at:

  git://git.yoctoproject.org/poky-contrib adamian/20140704-submission-bb
  http://git.yoctoproject.org/cgit.cgi/poky-contrib/log/?h=adamian/20140704-submission-bb

Alexandru DAMIAN (8):
  toaster: remove strftime calls in filters
  toaster: automatically discover urls.py files
  toaster: automatically enable applications
  toaster: add project pages
  toaster: add automated login in new project page
  toaster: whitespace fix
  toaster: new project page implementation
  toasterui: fix build - project identification

Dave Lerner (1):
  toasterui: Show in the log that ToasterUi is ready to receive events.

 bin/toaster                                        |   1 +
 lib/bb/ui/buildinfohelper.py                       |  17 +-
 lib/bb/ui/toasterui.py                             |  11 +-
 ...anch__add_field_project_short_description__a.py | 257 +++++++++++++++++++++
 lib/toaster/orm/models.py                          |  38 +++
 lib/toaster/toastergui/templates/base.html         |  13 ++
 lib/toaster/toastergui/templates/newproject.html   |  43 ++++
 lib/toaster/toastergui/templates/project.html      |   6 +
 lib/toaster/toastergui/templatetags/projecttags.py |  18 +-
 lib/toaster/toastergui/urls.py                     |   5 +
 lib/toaster/toastergui/views.py                    | 115 +++++++--
 lib/toaster/toastermain/settings.py                |  28 ++-
 lib/toaster/toastermain/urls.py                    |  16 +-
 13 files changed, 531 insertions(+), 37 deletions(-)
 create mode 100644 lib/toaster/orm/migrations/0010_auto__add_field_project_branch__add_field_project_short_description__a.py
 create mode 100644 lib/toaster/toastergui/templates/newproject.html
 create mode 100644 lib/toaster/toastergui/templates/project.html

-- 
1.9.1



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

* [PATCH 1/9] toaster: remove strftime calls in filters
  2014-07-09 16:55 ` [PATCH 0/9] [V2] toaster pull request Alex DAMIAN
@ 2014-07-09 16:55   ` Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 2/9] toaster: automatically discover urls.py files Alex DAMIAN
                     ` (7 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-09 16:55 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

We remove the unneeded strftime calls in filters, as the
filters can deal directly with datetime types.

[YOCTO #6379]

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/toaster/toastergui/views.py | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 68e981a..1f3e11d 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -301,9 +301,9 @@ def builds(request):
                  'filter' : {'class' : 'started_on',
                              'label': 'Show:',
                              'options' : [
-                                         ("Today's builds" , 'started_on__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=timezone.now().strftime("%Y-%m-%d")).count()),
-                                         ("Yesterday's builds", 'started_on__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d")).count()),
-                                         ("This week's builds", 'started_on__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d")).count()),
+                                         ("Today's builds" , 'started_on__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=timezone.now()).count()),
+                                         ("Yesterday's builds", 'started_on__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=(timezone.now()-timedelta(hours=24))).count()),
+                                         ("This week's builds", 'started_on__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(started_on__gte=(timezone.now()-timedelta(days=7))).count()),
                                          ]
                             }
                 },
@@ -315,9 +315,9 @@ def builds(request):
                  'filter' : {'class' : 'completed_on',
                              'label': 'Show:',
                              'options' : [
-                                         ("Today's builds", 'completed_on__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=timezone.now().strftime("%Y-%m-%d")).count()),
-                                         ("Yesterday's builds", 'completed_on__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d")).count()),
-                                         ("This week's builds", 'completed_on__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d")).count()),
+                                         ("Today's builds", 'completed_on__gte:'+timezone.now().strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=timezone.now()).count()),
+                                         ("Yesterday's builds", 'completed_on__gte:'+(timezone.now()-timedelta(hours=24)).strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=(timezone.now()-timedelta(hours=24))).count()),
+                                         ("This week's builds", 'completed_on__gte:'+(timezone.now()-timedelta(days=7)).strftime("%Y-%m-%d"), queryset_with_search.filter(completed_on__gte=(timezone.now()-timedelta(days=7))).count()),
                                          ]
                             }
                 },
-- 
1.9.1



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

* [PATCH 2/9] toaster: automatically discover urls.py files
  2014-07-09 16:55 ` [PATCH 0/9] [V2] toaster pull request Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 1/9] toaster: remove strftime calls in filters Alex DAMIAN
@ 2014-07-09 16:55   ` Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 3/9] toaster: automatically enable applications Alex DAMIAN
                     ` (6 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-09 16:55 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

Added code to automatically discover and add url dispatchers
to the urlpattern list. This allows extension of Toaster
through adding applications that will be automatically
registered with the URL dispatcher.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/toaster/toastermain/urls.py | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/lib/toaster/toastermain/urls.py b/lib/toaster/toastermain/urls.py
index ede5e4f..0e7b5c2 100644
--- a/lib/toaster/toastermain/urls.py
+++ b/lib/toaster/toastermain/urls.py
@@ -27,12 +27,10 @@ from django.views.decorators.cache import never_cache
 # Uncomment the next two lines to enable the admin:
 # from django.contrib import admin
 # admin.autodiscover()
-
 urlpatterns = patterns('',
-    url(r'^simple/', include('bldviewer.urls')),
     url(r'^api/1.0/', include('bldviewer.api')),
-    url(r'^gui/', include('toastergui.urls')),
-    url(r'^$', never_cache(RedirectView.as_view(url='/gui/'))),
+    url(r'^$', never_cache(RedirectView.as_view(url='/toastergui/'))),
+
     # Examples:
     # url(r'^toaster/', include('toaster.foo.urls')),
 
@@ -42,3 +40,13 @@ urlpatterns = patterns('',
     # Uncomment the next line to enable the admin:
     # url(r'^admin/', include(admin.site.urls)),
 )
+
+# Automatically discover urls.py in various apps, beside our own
+# and map module directories to the patterns
+
+import os
+currentdir = os.path.dirname(__file__)
+for t in os.walk(os.path.dirname(currentdir)):
+    if "urls.py" in t[2] and t[0] != currentdir:
+        modulename = os.path.basename(t[0])
+        urlpatterns.append( url(r'^' + modulename + '/', include ( modulename + '.urls')))
-- 
1.9.1



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

* [PATCH 3/9] toaster: automatically enable applications
  2014-07-09 16:55 ` [PATCH 0/9] [V2] toaster pull request Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 1/9] toaster: remove strftime calls in filters Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 2/9] toaster: automatically discover urls.py files Alex DAMIAN
@ 2014-07-09 16:55   ` Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 4/9] toasterui: Show in the log that ToasterUi is ready to receive events Alex DAMIAN
                     ` (5 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-09 16:55 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

We automatically enable local applications in INSTALLED_APPS
based on detecting a models.py or views.py file.
This allows Toaster extensibility by adding applications,
without having to edit configuration files.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/toaster/toastermain/settings.py | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/lib/toaster/toastermain/settings.py b/lib/toaster/toastermain/settings.py
index 2ce10c4..97f2ff7 100644
--- a/lib/toaster/toastermain/settings.py
+++ b/lib/toaster/toastermain/settings.py
@@ -243,6 +243,15 @@ INSTALLED_APPS = (
     'bldcontrol',
 )
 
+# We automatically detect and install applications here if
+# they have a 'models.py' or 'views.py' file
+import os
+currentdir = os.path.dirname(__file__)
+for t in os.walk(os.path.dirname(currentdir)):
+    modulename = os.path.basename(t[0])
+    if ("views.py" in t[2] or "models.py" in t[2]) and not modulename in INSTALLED_APPS:
+        INSTALLED_APPS.append(modulename)
+
 # A sample logging configuration. The only tangible logging
 # performed by this configuration is to send an email to
 # the site admins on every HTTP 500 error when DEBUG=False.
-- 
1.9.1



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

* [PATCH 4/9] toasterui: Show in the log that ToasterUi is ready to receive events.
  2014-07-09 16:55 ` [PATCH 0/9] [V2] toaster pull request Alex DAMIAN
                     ` (2 preceding siblings ...)
  2014-07-09 16:55   ` [PATCH 3/9] toaster: automatically enable applications Alex DAMIAN
@ 2014-07-09 16:55   ` Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 5/9] toaster: add project pages Alex DAMIAN
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-09 16:55 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Dave Lerner, Alexandru DAMIAN

From: Dave Lerner <dave.lerner@windriver.com>

Issue: TA53702

It was observed that a sequence in a script such as
   bitbake --server-only ..
   bitbake --observe-only ..
   bitbake <some target>
could generate events from the server to the observer before
the observer was ready to read the events, and the early events
of builds were consistently dropped.  Adding a "ready" note in the
log allows the script to scan for that message before proceeding
to building a target.

Signed-off-by: Dave Lerner <dave.lerner@windriver.com>
Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/bb/ui/toasterui.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lib/bb/ui/toasterui.py b/lib/bb/ui/toasterui.py
index b1c80cc..5f87a9d 100644
--- a/lib/bb/ui/toasterui.py
+++ b/lib/bb/ui/toasterui.py
@@ -91,6 +91,7 @@ def main(server, eventHandler, params ):
     errors = 0
     warnings = 0
     taskfailures = []
+    first = True
 
     buildinfohelper = BuildInfoHelper(server, build_history_enabled)
 
@@ -98,6 +99,9 @@ def main(server, eventHandler, params ):
     while True:
         try:
             event = eventHandler.waitEvent(0.25)
+            if first:
+                first = False
+                logger.info("ToasterUI waiting for events")
 
             if event is None:
                 if main.shutdown > 0:
-- 
1.9.1



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

* [PATCH 5/9] toaster: add project pages
  2014-07-09 16:55 ` [PATCH 0/9] [V2] toaster pull request Alex DAMIAN
                     ` (3 preceding siblings ...)
  2014-07-09 16:55   ` [PATCH 4/9] toasterui: Show in the log that ToasterUi is ready to receive events Alex DAMIAN
@ 2014-07-09 16:55   ` Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 6/9] toaster: add automated login in new project page Alex DAMIAN
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-09 16:55 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

We add the new project and project page skeletons.

In the process, we add an identifier in the settings.py
to detect whenever Toaster is running in managed mode,
and a context processor to make this value available
to the template processor.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 bin/toaster                                      |  1 +
 lib/toaster/toastergui/templates/base.html       | 13 ++++++++
 lib/toaster/toastergui/templates/newproject.html | 41 ++++++++++++++++++++++++
 lib/toaster/toastergui/templates/project.html    |  6 ++++
 lib/toaster/toastergui/urls.py                   |  5 +++
 lib/toaster/toastergui/views.py                  | 38 ++++++++++++++++++++++
 lib/toaster/toastermain/settings.py              | 19 ++++++++---
 7 files changed, 119 insertions(+), 4 deletions(-)
 create mode 100644 lib/toaster/toastergui/templates/newproject.html
 create mode 100644 lib/toaster/toastergui/templates/project.html

diff --git a/bin/toaster b/bin/toaster
index 01ffc7a..90cd982 100755
--- a/bin/toaster
+++ b/bin/toaster
@@ -139,6 +139,7 @@ if [ -z "$ZSH_NAME" ] && [ `basename \"$0\"` = `basename \"$BASH_SOURCE\"` ]; th
         webserverKillAll
         RUNNING=0
     }
+    export TOASTER_MANAGED=1
     webserverStartAll || (echo "Fail to start the web server, stopping" 1>&2 && exit 1)
     xdg-open http://0.0.0.0:8000/ >/dev/null 2>&1 &
     trap trap_ctrlc SIGINT
diff --git a/lib/toaster/toastergui/templates/base.html b/lib/toaster/toastergui/templates/base.html
index 9ca9c9a..1407d64 100644
--- a/lib/toaster/toastergui/templates/base.html
+++ b/lib/toaster/toastergui/templates/base.html
@@ -58,6 +58,19 @@ function reload_params(params) {
     <div class="navbar-inner">
             <a class="brand logo" href="#"><img src="{% static 'img/logo.png' %}" class="" alt="Yocto logo project"/></a>
             <a class="brand" href="/">Toaster</a>
+				{%if MANAGED %}
+					  <div class="btn-group pull-right">
+						  <a class="btn" href="{% url 'newproject' %}">New project</a>
+						  <button class="btn dropdown-toggle" data-toggle="dropdown">
+							  <i class="icon-caret-down"></i>
+						  </button>
+						  <ul class="dropdown-menu">
+							  <li><a href="#">Clone project</a></li>
+							  <li><a href="#">Export project</a></li>
+							  <li><a href="#">Import project</a></li>
+						  </ul>
+					  </div>
+				{%endif%}
             <a class="pull-right manual" target="_blank" href="http://www.yoctoproject.org/documentation/toaster-manual">
                 <i class="icon-book"></i>
                 Toaster manual
diff --git a/lib/toaster/toastergui/templates/newproject.html b/lib/toaster/toastergui/templates/newproject.html
new file mode 100644
index 0000000..e5a6551
--- /dev/null
+++ b/lib/toaster/toastergui/templates/newproject.html
@@ -0,0 +1,41 @@
+{% extends "base.html" %}
+{% load projecttags %}
+{% load humanize %}
+{% block pagecontent %}
+<div class="row-fluid">
+				<div class="span6">
+				<div class="page-header">
+					<h1>Create a new project</h1>
+				</div>  
+					<form>
+						<fieldset>
+							<label>Project name <span class="muted">(required)</span></label>
+							<input type="text" class="input-xlarge" required name="projectname">
+							<label class="project-form">
+								Project owner
+								<i class="icon-question-sign get-help" title="The go-to person for this project"></i>
+							</label>
+				<form method="POST">
+							<input type="text">
+							<label class="project-form">Owner's email</label>
+							<input type="email" class="input-large" name="email">
+							<label class="project-form">
+								Yocto Project version
+								<i class="icon-question-sign get-help" title="This sets the branch for the Yocto Project core layers (meta, meta-yocto and meta-yocto-bsp), and for the layers you use from the OpenEmbedded Metadata Index"></i>
+							</label>
+							<select>
+								<option>Yocto Project 1.7 "D?"</option>
+								<option>Yocto Project 1.6 "Daisy"</option>
+								<option>Yocto Project 1.5 "Dora"</option>
+							</select>
+				</form>
+						</fieldset>
+									
+						<div class="form-actions">
+							<a href="project-with-targets.html" class="btn btn-primary btn-large">Create project</a>
+						</div>
+					</form> 
+				</div>
+			</div>
+		</div>  
+{% endblock %}
diff --git a/lib/toaster/toastergui/templates/project.html b/lib/toaster/toastergui/templates/project.html
new file mode 100644
index 0000000..71adb54
--- /dev/null
+++ b/lib/toaster/toastergui/templates/project.html
@@ -0,0 +1,6 @@
+{% extends "base.html" %}
+{% load projecttags %}
+{% load humanize %}
+{% block pagecontent %}
+
+{% endblock %}
diff --git a/lib/toaster/toastergui/urls.py b/lib/toaster/toastergui/urls.py
index 9b583f2..bba4fda 100644
--- a/lib/toaster/toastergui/urls.py
+++ b/lib/toaster/toastergui/urls.py
@@ -65,6 +65,11 @@ urlpatterns = patterns('toastergui.views',
         # urls not linked from the dashboard
         url(r'^layers/$', 'layer', name='all-layers'),
         url(r'^layerversions/(?P<layerversion_id>\d+)/recipes/.*$', 'layer_versions_recipes', name='layer_versions_recipes'),
+
+        # project URLs
+        url(r'^newproject/$', 'newproject', name='newproject'),
+        url(r'^project/$', 'project', name='project'),
+
         # default redirection
         url(r'^$', RedirectView.as_view( url= 'builds/')),
 )
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 1f3e11d..7dc0108 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -1758,3 +1758,41 @@ def image_information_dir(request, build_id, target_id, packagefile_id):
     # stubbed for now
     return redirect(builds)
 
+
+import toastermain.settings
+def managedcontextprocessor(request):
+    return { "MANAGED" : toastermain.settings.MANAGED }
+
+
+# we have a set of functions if we're in managed mode, or
+# a default "page not available" simple functions for interactive mode
+if toastermain.settings.MANAGED:
+
+    # new project
+    def newproject(request):
+        template = "newproject.html"
+        context = {}
+        if request.method == "GET":
+            # render new project page
+            return render(request, template, context)
+        elif request.method == "POST":
+            if request.method:
+                return redirect(project)
+            else:
+                return render(request, template, context)
+        raise Exception("Invalid HTTP method for this page")
+
+    # Shows the edit project page
+    def project(request):
+        template = "project.html"
+        context = {}
+        return render(request, template, context)
+
+
+else:
+    # these are pages that are NOT available in interactive mode
+    def newproject(request):
+        raise Exception("page not available in interactive mode")
+
+    def project(request):
+        raise Exception("page not available in interactive mode")
diff --git a/lib/toaster/toastermain/settings.py b/lib/toaster/toastermain/settings.py
index 97f2ff7..09ec2bd 100644
--- a/lib/toaster/toastermain/settings.py
+++ b/lib/toaster/toastermain/settings.py
@@ -75,6 +75,11 @@ if 'DATABASE_URL' in os.environ:
         raise Exception("FIXME: Please implement missing database url schema for url: %s" % dburl)
 
 
+if 'TOASTER_MANAGED' in os.environ and os.environ['TOASTER_MANAGED'] == "1":
+    MANAGED = True
+else:
+    MANAGED = False
+
 # Allows current database settings to be exported as a DATABASE_URL environment variable value
 
 def getDATABASE_URL():
@@ -221,12 +226,11 @@ TEMPLATE_CONTEXT_PROCESSORS = ('django.contrib.auth.context_processors.auth',
  'django.core.context_processors.static',
  'django.core.context_processors.tz',
  'django.contrib.messages.context_processors.messages',
- "django.core.context_processors.request")
+ "django.core.context_processors.request",
+ 'toastergui.views.managedcontextprocessor',
+ )
 
 INSTALLED_APPS = (
-    #'django.contrib.auth',
-    #'django.contrib.contenttypes',
-    #'django.contrib.sessions',
     #'django.contrib.sites',
     #'django.contrib.messages',
     'django.contrib.staticfiles',
@@ -243,6 +247,13 @@ INSTALLED_APPS = (
     'bldcontrol',
 )
 
+# if we run in managed mode, we need user support
+if MANAGED:
+    INSTALLED_APPS = ('django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',) + INSTALLED_APPS
+
+
 # We automatically detect and install applications here if
 # they have a 'models.py' or 'views.py' file
 import os
-- 
1.9.1



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

* [PATCH 6/9] toaster: add automated login in new project page
  2014-07-09 16:55 ` [PATCH 0/9] [V2] toaster pull request Alex DAMIAN
                     ` (4 preceding siblings ...)
  2014-07-09 16:55   ` [PATCH 5/9] toaster: add project pages Alex DAMIAN
@ 2014-07-09 16:55   ` Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 7/9] toaster: whitespace fix Alex DAMIAN
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-09 16:55 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

Toaster uses the Django authentication system to assign
user accounts to the projects that are being created.

In the current implementation, the user accounts are
created/authenticated automatically, on the fly, based
on the fields specified in the create new project page.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/toaster/toastergui/templates/newproject.html | 35 +++++++++++++-----------
 lib/toaster/toastergui/views.py                  | 29 ++++++++++++++++++--
 2 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/lib/toaster/toastergui/templates/newproject.html b/lib/toaster/toastergui/templates/newproject.html
index e5a6551..ce01800 100644
--- a/lib/toaster/toastergui/templates/newproject.html
+++ b/lib/toaster/toastergui/templates/newproject.html
@@ -6,36 +6,39 @@
 				<div class="span6">
 				<div class="page-header">
 					<h1>Create a new project</h1>
-				</div>  
-					<form>
+				</div>
+				<div class="container-fluid">
+		{% for a in alerts %}
+			<div class="alert alert-error row-fluid" role="alert">{{a}}</div>
+		{% endfor %}
+				</div>
+					<form method="POST">{% csrf_token %}
 						<fieldset>
 							<label>Project name <span class="muted">(required)</span></label>
-							<input type="text" class="input-xlarge" required name="projectname">
+							<input type="text" class="input-xlarge" required name="projectname" value="{{projectname}}">
 							<label class="project-form">
 								Project owner
-								<i class="icon-question-sign get-help" title="The go-to person for this project"></i>
+					<i class="icon-question-sign get-help" title="The go-to person for this project"></i>
 							</label>
-				<form method="POST">
-							<input type="text">
+							<input type="text" name="username" value="{{username}}">
 							<label class="project-form">Owner's email</label>
-							<input type="email" class="input-large" name="email">
+							<input type="email" class="input-large" name="email" value="{{email}}">
 							<label class="project-form">
 								Yocto Project version
 								<i class="icon-question-sign get-help" title="This sets the branch for the Yocto Project core layers (meta, meta-yocto and meta-yocto-bsp), and for the layers you use from the OpenEmbedded Metadata Index"></i>
 							</label>
-							<select>
-								<option>Yocto Project 1.7 "D?"</option>
-								<option>Yocto Project 1.6 "Daisy"</option>
-								<option>Yocto Project 1.5 "Dora"</option>
+							<select name="projectversion">
+		<option value="1.7" {%if projectversion == "1.7" %}selected{%endif%}>Yocto Project 1.7 "D?"</option>
+		<option value="1.6" {%if projectversion == "1.6" %}selected{%endif%}>Yocto Project 1.6 "Daisy"</option>
+		<option value="1.5" {%if projectversion == "1.5" %}selected{%endif%}>Yocto Project 1.5 "Dora"</option>
 							</select>
-				</form>
 						</fieldset>
-									
+
 						<div class="form-actions">
-							<a href="project-with-targets.html" class="btn btn-primary btn-large">Create project</a>
+							<input type="submit" class="btn btn-primary btn-large" value="Create project"/>
 						</div>
-					</form> 
+					</form>
 				</div>
 			</div>
-		</div>  
+		</div>
 {% endblock %}
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 7dc0108..89c02d4 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -1768,17 +1768,42 @@ def managedcontextprocessor(request):
 # a default "page not available" simple functions for interactive mode
 if toastermain.settings.MANAGED:
 
+    from django.contrib.auth.models import User
+    from django.contrib.auth import authenticate, login
+    from django.contrib.auth.decorators import login_required
+
+
     # new project
     def newproject(request):
         template = "newproject.html"
-        context = {}
+        context = {
+            'email': request.user.email if request.user.is_authenticated() else '',
+            'username': request.user.username if request.user.is_authenticated() else '',
+        }
+
+
         if request.method == "GET":
             # render new project page
             return render(request, template, context)
         elif request.method == "POST":
-            if request.method:
+            mandatory_fields = ['projectname', 'email', 'username', 'projectversion']
+            if reduce( lambda x, y: x and y, map(lambda x: x in request.POST and len(request.POST[x]) > 0, mandatory_fields)):
+                if not request.user.is_authenticated():
+                    user = authenticate(username = request.POST['username'], password = 'nopass')
+                    if user is None:
+                        user = User.objects.create_user(username = request.POST['username'], email = request.POST['email'], password = "nopass")
+                        raise Exception("User cannot be authed, creating")
+                        user = authenticate(username = request.POST['username'], password = '')
+                    login(request, user)
+
                 return redirect(project)
             else:
+                alerts = []
+                # set alerts for missing fields
+                map(lambda x: alerts.append('Field '+ x + ' not filled in') if not x in request.POST or len(request.POST[x]) == 0 else None, mandatory_fields)
+                # fill in new page with already submitted values
+                map(lambda x: context.__setitem__(x, request.POST[x]), mandatory_fields)
+                context['alerts'] = alerts
                 return render(request, template, context)
         raise Exception("Invalid HTTP method for this page")
 
-- 
1.9.1



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

* [PATCH 7/9] toaster: whitespace fix
  2014-07-09 16:55 ` [PATCH 0/9] [V2] toaster pull request Alex DAMIAN
                     ` (5 preceding siblings ...)
  2014-07-09 16:55   ` [PATCH 6/9] toaster: add automated login in new project page Alex DAMIAN
@ 2014-07-09 16:55   ` Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 8/9] toaster: new project page implementation Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 9/9] toasterui: fix build - project identification Alex DAMIAN
  8 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-09 16:55 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

This patch is just a whitespace (end-of-line) fix.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/toaster/toastergui/templatetags/projecttags.py | 18 +++++++++---------
 lib/toaster/toastergui/views.py                    | 14 +++++++-------
 2 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/lib/toaster/toastergui/templatetags/projecttags.py b/lib/toaster/toastergui/templatetags/projecttags.py
index be75b21..b953aa1 100644
--- a/lib/toaster/toastergui/templatetags/projecttags.py
+++ b/lib/toaster/toastergui/templatetags/projecttags.py
@@ -142,7 +142,7 @@ def variable_parent_name(value):
     """
     value=re.sub('_\$.*', '', value)
     return re.sub('_[a-z].*', '', value)
-  
+
 @register.filter
 def filter_setin_files(file_list,matchstr):
     """ filter/search the 'set in' file lists. Note
@@ -150,7 +150,7 @@ def filter_setin_files(file_list,matchstr):
         the <p> marks, but this is safe as the data
         is file paths
     """
-   
+
     # no filters, show last file (if any)
     if matchstr == ":":
         if file_list:
@@ -162,24 +162,24 @@ def filter_setin_files(file_list,matchstr):
     htmlstr=""
     # match only filters
     if search == '':
-        for i in range(len(file_list)):   
+        for i in range(len(file_list)):
             if re.search(filter, file_list[i].file_name):
                 if htmlstr.find(file_list[i].file_name + "<p>") < 0:
                     htmlstr += file_list[i].file_name + "<p>"
         return htmlstr
-       
+
     # match only search string, plus always last file
     if filter == "":
-        for i in range(len(file_list)-1):   
+        for i in range(len(file_list)-1):
             if re.search(search,file_list[i].file_name):
                 if htmlstr.find(file_list[i].file_name + "<p>") < 0:
                     htmlstr += file_list[i].file_name + "<p>"
         if htmlstr.find(file_list[len(file_list)-1].file_name) < 0:
             htmlstr += file_list[len(file_list)-1].file_name
         return htmlstr
-       
+
     # match filter or search string
-    for i in range(len(file_list)):   
+    for i in range(len(file_list)):
         if re.search(filter, file_list[i].file_name) or re.search(search,file_list[i].file_name):
             if htmlstr.find(file_list[i].file_name + "<p>") < 0:
                 htmlstr += file_list[i].file_name + "<p>"
@@ -218,7 +218,7 @@ def filtered_packageversion(version, revision):
         else ""
     """
     return "" if (not version or version == "") else version if (not revision or revision == "") else version + "-" + revision
-        
+
 @register.filter
 def filter_sizeovertotal(package_object, total_size):
     """ Return the % size of the package over the total size argument
@@ -227,7 +227,7 @@ def filter_sizeovertotal(package_object, total_size):
     size = package_object.installed_size
     if size == None or size == '':
         size = package_object.size
-  
+
     return '{:.1%}'.format(float(size)/float(total_size))
 
 from django.utils.safestring import mark_safe
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index 89c02d4..a57f001 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -429,7 +429,7 @@ def builddashboard( request, build_id ):
                 ndx = 0;
             f = i.file_name[ ndx + 1: ]
             imageFiles.append({ 'path': f, 'size' : i.file_size })
-        if ( t.is_image and 
+        if ( t.is_image and
              (( len( imageFiles ) <= 0 ) or ( len( t.license_manifest_path ) <= 0 ))):
             targetHasNoImages = True
         elem[ 'imageFiles' ] = imageFiles
@@ -516,8 +516,8 @@ def task( request, build_id, task_id ):
     }
     if request.GET.get( 'show_matches', "" ):
         context[ 'showing_matches' ] = True
-        context[ 'matching_tasks' ] = Task.objects.filter( 
-            sstate_checksum=task.sstate_checksum ).filter( 
+        context[ 'matching_tasks' ] = Task.objects.filter(
+            sstate_checksum=task.sstate_checksum ).filter(
             build__completed_on__lt=task.build.completed_on).exclude(
             order__isnull=True).exclude(outcome=Task.OUTCOME_NA).order_by('-build__completed_on')
 
@@ -551,14 +551,14 @@ def target_common( request, build_id, target_id, variant ):
     mandatory_parameters = { 'count': 25,  'page' : 1, 'orderby':'name:+'};
     retval = _verify_parameters( request.GET, mandatory_parameters )
     if retval:
-        return _redirect_parameters( 
-                    variant, request.GET, mandatory_parameters, 
+        return _redirect_parameters(
+                    variant, request.GET, mandatory_parameters,
                     build_id = build_id, target_id = target_id )
     ( filter_string, search_term, ordering_string ) = _search_tuple( request, Package )
 
     # FUTURE:  get rid of nested sub-queries replacing with ManyToMany field
     queryset = Package.objects.filter(
-                    size__gte = 0, 
+                    size__gte = 0,
                     id__in = Target_Installed_Package.objects.filter(
                         target_id=target_id ).values( 'package_id' ))
     packages_sum =  queryset.aggregate( Sum( 'installed_size' ))
@@ -682,7 +682,7 @@ his package',
         'clclass'    : 'layer_directory',
         'hidden'     : 1,
         }
-    context = { 
+    context = {
         'objectname': variant,
         'build'                : Build.objects.filter( pk = build_id )[ 0 ],
         'target'               : Target.objects.filter( pk = target_id )[ 0 ],
-- 
1.9.1



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

* [PATCH 8/9] toaster: new project page implementation
  2014-07-09 16:55 ` [PATCH 0/9] [V2] toaster pull request Alex DAMIAN
                     ` (6 preceding siblings ...)
  2014-07-09 16:55   ` [PATCH 7/9] toaster: whitespace fix Alex DAMIAN
@ 2014-07-09 16:55   ` Alex DAMIAN
  2014-07-09 16:55   ` [PATCH 9/9] toasterui: fix build - project identification Alex DAMIAN
  8 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-09 16:55 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

We add the logic to create a new project. This page
also serves as user registration and silent login
for users.

Once the project is added, the main project page is displayed.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 ...anch__add_field_project_short_description__a.py | 257 +++++++++++++++++++++
 lib/toaster/orm/models.py                          |  38 +++
 lib/toaster/toastergui/templates/newproject.html   |  13 +-
 lib/toaster/toastergui/urls.py                     |   2 +-
 lib/toaster/toastergui/views.py                    |  48 +++-
 5 files changed, 338 insertions(+), 20 deletions(-)
 create mode 100644 lib/toaster/orm/migrations/0010_auto__add_field_project_branch__add_field_project_short_description__a.py

diff --git a/lib/toaster/orm/migrations/0010_auto__add_field_project_branch__add_field_project_short_description__a.py b/lib/toaster/orm/migrations/0010_auto__add_field_project_branch__add_field_project_short_description__a.py
new file mode 100644
index 0000000..aa1ce1f
--- /dev/null
+++ b/lib/toaster/orm/migrations/0010_auto__add_field_project_branch__add_field_project_short_description__a.py
@@ -0,0 +1,257 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'Project.branch'
+        db.add_column(u'orm_project', 'branch',
+                      self.gf('django.db.models.fields.CharField')(default='master', max_length=50),
+                      keep_default=False)
+
+        # Adding field 'Project.short_description'
+        db.add_column(u'orm_project', 'short_description',
+                      self.gf('django.db.models.fields.CharField')(default='', max_length=50, blank=True),
+                      keep_default=False)
+
+        # Adding field 'Project.user_id'
+        db.add_column(u'orm_project', 'user_id',
+                      self.gf('django.db.models.fields.IntegerField')(null=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Project.branch'
+        db.delete_column(u'orm_project', 'branch')
+
+        # Deleting field 'Project.short_description'
+        db.delete_column(u'orm_project', 'short_description')
+
+        # Deleting field 'Project.user_id'
+        db.delete_column(u'orm_project', 'user_id')
+
+
+    models = {
+        u'orm.build': {
+            'Meta': {'object_name': 'Build'},
+            'bitbake_version': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'build_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'completed_on': ('django.db.models.fields.DateTimeField', [], {}),
+            'cooker_log_path': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
+            'distro': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'distro_version': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'errors_no': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'machine': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'outcome': ('django.db.models.fields.IntegerField', [], {'default': '2'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']", 'null': 'True'}),
+            'started_on': ('django.db.models.fields.DateTimeField', [], {}),
+            'timespent': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'warnings_no': ('django.db.models.fields.IntegerField', [], {'default': '0'})
+        },
+        u'orm.helptext': {
+            'Meta': {'object_name': 'HelpText'},
+            'area': ('django.db.models.fields.IntegerField', [], {}),
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'helptext_build'", 'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'text': ('django.db.models.fields.TextField', [], {})
+        },
+        u'orm.layer': {
+            'Meta': {'object_name': 'Layer'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer_index_url': ('django.db.models.fields.URLField', [], {'max_length': '200'}),
+            'local_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.layer_version': {
+            'Meta': {'object_name': 'Layer_Version'},
+            'branch': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_build'", 'to': u"orm['orm.Build']"}),
+            'commit': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'layer_version_layer'", 'to': u"orm['orm.Layer']"}),
+            'priority': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'orm.logmessage': {
+            'Meta': {'object_name': 'LogMessage'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'lineno': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+            'pathname': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'task': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Task']", 'null': 'True', 'blank': 'True'})
+        },
+        u'orm.package': {
+            'Meta': {'object_name': 'Package'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'installed_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
+            'installed_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'license': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Recipe']", 'null': 'True'}),
+            'revision': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}),
+            'section': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        u'orm.package_dependency': {
+            'Meta': {'object_name': 'Package_Dependency'},
+            'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_target'", 'to': u"orm['orm.Package']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_dependencies_source'", 'to': u"orm['orm.Package']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']", 'null': 'True'})
+        },
+        u'orm.package_file': {
+            'Meta': {'object_name': 'Package_File'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildfilelist_package'", 'to': u"orm['orm.Package']"}),
+            'path': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'size': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'orm.project': {
+            'Meta': {'object_name': 'Project'},
+            'branch': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'short_description': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'user_id': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
+        },
+        u'orm.projectlayer': {
+            'Meta': {'object_name': 'ProjectLayer'},
+            'commit': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
+            'giturl': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"})
+        },
+        u'orm.projecttarget': {
+            'Meta': {'object_name': 'ProjectTarget'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+            'target': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.projectvariable': {
+            'Meta': {'object_name': 'ProjectVariable'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Project']"}),
+            'value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        u'orm.recipe': {
+            'Meta': {'object_name': 'Recipe'},
+            'bugtracker': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'file_path': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            'homepage': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'layer_version': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'recipe_layer_version'", 'to': u"orm['orm.Layer_Version']"}),
+            'license': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'section': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'summary': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'version': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'})
+        },
+        u'orm.recipe_dependency': {
+            'Meta': {'object_name': 'Recipe_Dependency'},
+            'dep_type': ('django.db.models.fields.IntegerField', [], {}),
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_depends'", 'to': u"orm['orm.Recipe']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'r_dependencies_recipe'", 'to': u"orm['orm.Recipe']"})
+        },
+        u'orm.target': {
+            'Meta': {'object_name': 'Target'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Build']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image_size': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'is_image': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'license_manifest_path': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True'}),
+            'target': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'orm.target_file': {
+            'Meta': {'object_name': 'Target_File'},
+            'directory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'directory_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+            'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'inodetype': ('django.db.models.fields.IntegerField', [], {}),
+            'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'path': ('django.db.models.fields.FilePathField', [], {'max_length': '100'}),
+            'permission': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+            'size': ('django.db.models.fields.IntegerField', [], {}),
+            'sym_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'symlink_set'", 'null': 'True', 'to': u"orm['orm.Target_File']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.target_image_file': {
+            'Meta': {'object_name': 'Target_Image_File'},
+            'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '254'}),
+            'file_size': ('django.db.models.fields.IntegerField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.target_installed_package': {
+            'Meta': {'object_name': 'Target_Installed_Package'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'package': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'buildtargetlist_package'", 'to': u"orm['orm.Package']"}),
+            'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['orm.Target']"})
+        },
+        u'orm.task': {
+            'Meta': {'ordering': "('order', 'recipe')", 'unique_together': "(('build', 'recipe', 'task_name'),)", 'object_name': 'Task'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_build'", 'to': u"orm['orm.Build']"}),
+            'cpu_usage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            'disk_io': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'elapsed_time': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_number': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'logfile': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'message': ('django.db.models.fields.CharField', [], {'max_length': '240'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'outcome': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
+            'path_to_sstate_obj': ('django.db.models.fields.FilePathField', [], {'max_length': '500', 'blank': 'True'}),
+            'recipe': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'build_recipe'", 'to': u"orm['orm.Recipe']"}),
+            'script_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'source_url': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'}),
+            'sstate_checksum': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+            'sstate_result': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'task_executed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'task_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'work_directory': ('django.db.models.fields.FilePathField', [], {'max_length': '255', 'blank': 'True'})
+        },
+        u'orm.task_dependency': {
+            'Meta': {'object_name': 'Task_Dependency'},
+            'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_depends'", 'to': u"orm['orm.Task']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'task': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'task_dependencies_task'", 'to': u"orm['orm.Task']"})
+        },
+        u'orm.variable': {
+            'Meta': {'object_name': 'Variable'},
+            'build': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'variable_build'", 'to': u"orm['orm.Build']"}),
+            'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'human_readable_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'variable_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'variable_value': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+        },
+        u'orm.variablehistory': {
+            'Meta': {'object_name': 'VariableHistory'},
+            'file_name': ('django.db.models.fields.FilePathField', [], {'max_length': '255'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'line_number': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+            'operation': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+            'value': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'variable': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'vhistory'", 'to': u"orm['orm.Variable']"})
+        }
+    }
+
+    complete_apps = ['orm']
\ No newline at end of file
diff --git a/lib/toaster/orm/models.py b/lib/toaster/orm/models.py
index b67afe1..f406429 100644
--- a/lib/toaster/orm/models.py
+++ b/lib/toaster/orm/models.py
@@ -23,10 +23,48 @@ from django.db import models
 from django.db.models import F
 from django.utils.encoding import python_2_unicode_compatible
 
+class ProjectManager(models.Manager):
+    def create_project(self, name, branch, short_description):
+        prj = self.model(name = name, branch = branch, short_description = short_description)
+        prj.save()
+
+        # create default variables
+        ProjectVariable.objects.create(project = prj, name = "MACHINE", value = "qemux86")
+        ProjectVariable.objects.create(project = prj, name = "DISTRO", value = "poky")
+
+        # create default layers
+        ProjectLayer.objects.create(project = prj,
+            name = "meta",
+            giturl = "git://git.yoctoproject.org/poky",
+            commit = branch,
+            treepath = "meta")
+
+        ProjectLayer.objects.create(project = prj,
+            name = "meta-yocto",
+            giturl = "git://git.yoctoproject.org/poky",
+            commit = branch,
+            treepath = "meta-yocto")
+
+        return prj
+
+    def create(self, *args, **kwargs):
+        raise Exception("Invalid call to Project.objects.create. Use Project.objects.create_project() to create a project")
+
+    def get_or_create(self, *args, **kwargs):
+        raise Exception("Invalid call to Project.objects.get_or_create. Use Project.objects.create_project() to create a project")
+
 class Project(models.Model):
     name = models.CharField(max_length=100)
+    branch = models.CharField(max_length=50)
+    short_description = models.CharField(max_length=50, blank=True)
     created     = models.DateTimeField(auto_now_add = True)
     updated     = models.DateTimeField(auto_now = True)
+    # This is a horrible hack; since Toaster has no "User" model available when
+    # running in interactive mode, we can't reference the field here directly
+    # Instead, we keep a possible null reference to the User id, as not to force
+    # hard links to possibly missing models
+    user_id     = models.IntegerField(null = True)
+    objects     = ProjectManager()
 
 class Build(models.Model):
     SUCCEEDED = 0
diff --git a/lib/toaster/toastergui/templates/newproject.html b/lib/toaster/toastergui/templates/newproject.html
index ce01800..8f1867a 100644
--- a/lib/toaster/toastergui/templates/newproject.html
+++ b/lib/toaster/toastergui/templates/newproject.html
@@ -8,9 +8,9 @@
 					<h1>Create a new project</h1>
 				</div>
 				<div class="container-fluid">
-		{% for a in alerts %}
-			<div class="alert alert-error row-fluid" role="alert">{{a}}</div>
-		{% endfor %}
+		{% if alert %}
+			<div class="alert alert-error row-fluid" role="alert">{{alert}}</div>
+		{% endif %}
 				</div>
 					<form method="POST">{% csrf_token %}
 						<fieldset>
@@ -27,10 +27,9 @@
 								Yocto Project version
 								<i class="icon-question-sign get-help" title="This sets the branch for the Yocto Project core layers (meta, meta-yocto and meta-yocto-bsp), and for the layers you use from the OpenEmbedded Metadata Index"></i>
 							</label>
-							<select name="projectversion">
-		<option value="1.7" {%if projectversion == "1.7" %}selected{%endif%}>Yocto Project 1.7 "D?"</option>
-		<option value="1.6" {%if projectversion == "1.6" %}selected{%endif%}>Yocto Project 1.6 "Daisy"</option>
-		<option value="1.5" {%if projectversion == "1.5" %}selected{%endif%}>Yocto Project 1.5 "Dora"</option>
+							<select name="projectversion" id="projectversion">
+		<!-- TODO: XHR data from http://layers.openembedded.org/layerindex/branch/master/layers/ -->
+		<option value="master" {%if projectversion == "master" %}selected{%endif%}>master</option>
 							</select>
 						</fieldset>
 
diff --git a/lib/toaster/toastergui/urls.py b/lib/toaster/toastergui/urls.py
index bba4fda..0d7a4c3 100644
--- a/lib/toaster/toastergui/urls.py
+++ b/lib/toaster/toastergui/urls.py
@@ -68,7 +68,7 @@ urlpatterns = patterns('toastergui.views',
 
         # project URLs
         url(r'^newproject/$', 'newproject', name='newproject'),
-        url(r'^project/$', 'project', name='project'),
+        url(r'^project/(?P<pid>\d+)/$', 'project', name='project'),
 
         # default redirection
         url(r'^$', RedirectView.as_view( url= 'builds/')),
diff --git a/lib/toaster/toastergui/views.py b/lib/toaster/toastergui/views.py
index a57f001..8fbe8a3 100755
--- a/lib/toaster/toastergui/views.py
+++ b/lib/toaster/toastergui/views.py
@@ -22,11 +22,13 @@
 import operator,re
 
 from django.db.models import Q, Sum
+from django.db import IntegrityError
 from django.shortcuts import render, redirect
 from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, LogMessage, Variable
 from orm.models import Task_Dependency, Recipe_Dependency, Package, Package_File, Package_Dependency
 from orm.models import Target_Installed_Package, Target_File, Target_Image_File
 from django.views.decorators.cache import cache_control
+from django.core.urlresolvers import reverse
 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 from django.http import HttpResponseBadRequest
 from django.utils import timezone
@@ -68,7 +70,6 @@ def _verify_parameters(g, mandatory_parameters):
 
 def _redirect_parameters(view, g, mandatory_parameters, *args, **kwargs):
     import urllib
-    from django.core.urlresolvers import reverse
     url = reverse(view, kwargs=kwargs)
     params = {}
     for i in g:
@@ -1772,6 +1773,16 @@ if toastermain.settings.MANAGED:
     from django.contrib.auth import authenticate, login
     from django.contrib.auth.decorators import login_required
 
+    import traceback
+
+    class BadParameterException(Exception): pass        # error thrown on invalid POST requests
+
+    # the context processor that supplies data used across all the pages
+    def managedcontextprocessor(request):
+        return {
+            "projects": Project.objects.all(),
+            "MANAGED" : toastermain.settings.MANAGED
+        }
 
     # new project
     def newproject(request):
@@ -1787,28 +1798,41 @@ if toastermain.settings.MANAGED:
             return render(request, template, context)
         elif request.method == "POST":
             mandatory_fields = ['projectname', 'email', 'username', 'projectversion']
-            if reduce( lambda x, y: x and y, map(lambda x: x in request.POST and len(request.POST[x]) > 0, mandatory_fields)):
+            try:
+                # make sure we have values for all mandatory_fields
+                if reduce( lambda x, y: x or y, map(lambda x: len(request.POST.get(x, '')) == 0, mandatory_fields)):
+                # set alert for missing fields
+                    raise BadParameterException("Fields missing: " +
+            ", ".join([x for x in mandatory_fields if len(request.POST.get(x, '')) == 0 ]))
+
                 if not request.user.is_authenticated():
                     user = authenticate(username = request.POST['username'], password = 'nopass')
                     if user is None:
                         user = User.objects.create_user(username = request.POST['username'], email = request.POST['email'], password = "nopass")
-                        raise Exception("User cannot be authed, creating")
-                        user = authenticate(username = request.POST['username'], password = '')
+
+                        user = authenticate(username = user.username, password = 'nopass')
                     login(request, user)
 
-                return redirect(project)
-            else:
-                alerts = []
-                # set alerts for missing fields
-                map(lambda x: alerts.append('Field '+ x + ' not filled in') if not x in request.POST or len(request.POST[x]) == 0 else None, mandatory_fields)
-                # fill in new page with already submitted values
+                #  save the project
+                prj = Project.objects.create_project(name = request.POST['projectname'],
+                    branch = request.POST['projectversion'].split(" ")[0],
+                    short_description=request.POST['projectversion'].split(" ")[1:])
+                prj.user_id = request.user.pk
+                prj.save()
+                return redirect(reverse(project, args = (prj.pk,)))
+
+            except (IntegrityError, BadParameterException) as e:
+                # fill in page with previously submitted values
                 map(lambda x: context.__setitem__(x, request.POST[x]), mandatory_fields)
-                context['alerts'] = alerts
+                if isinstance(e, IntegrityError) and "username" in str(e):
+                    context['alert'] = "Your chosen username is already used"
+                else:
+                    context['alert'] = str(e)
                 return render(request, template, context)
         raise Exception("Invalid HTTP method for this page")
 
     # Shows the edit project page
-    def project(request):
+    def project(request, pid):
         template = "project.html"
         context = {}
         return render(request, template, context)
-- 
1.9.1



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

* [PATCH 9/9] toasterui: fix build - project identification
  2014-07-09 16:55 ` [PATCH 0/9] [V2] toaster pull request Alex DAMIAN
                     ` (7 preceding siblings ...)
  2014-07-09 16:55   ` [PATCH 8/9] toaster: new project page implementation Alex DAMIAN
@ 2014-07-09 16:55   ` Alex DAMIAN
  8 siblings, 0 replies; 20+ messages in thread
From: Alex DAMIAN @ 2014-07-09 16:55 UTC (permalink / raw)
  To: bitbake-devel; +Cc: Alexandru DAMIAN

From: Alexandru DAMIAN <alexandru.damian@intel.com>

This patches fixes the build - project identification when
running under managed mode. The build is assigned to the
project from which it was triggered, and to the
build request, as to simplify relationships queries
 in the database.

Signed-off-by: Alexandru DAMIAN <alexandru.damian@intel.com>
---
 lib/bb/ui/buildinfohelper.py | 17 +++++++++++++++--
 lib/bb/ui/toasterui.py       |  7 +++----
 2 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/lib/bb/ui/buildinfohelper.py b/lib/bb/ui/buildinfohelper.py
index 77be7c7..29cfc81 100644
--- a/lib/bb/ui/buildinfohelper.py
+++ b/lib/bb/ui/buildinfohelper.py
@@ -46,7 +46,7 @@ class ORMWrapper(object):
         pass
 
 
-    def create_build_object(self, build_info):
+    def create_build_object(self, build_info, brbe):
         assert 'machine' in build_info
         assert 'distro' in build_info
         assert 'distro_version' in build_info
@@ -65,6 +65,13 @@ class ORMWrapper(object):
                                     build_name=build_info['build_name'],
                                     bitbake_version=build_info['bitbake_version'])
 
+        if brbe is not None:
+            from bldcontrol.models import BuildEnvironment, BuildRequest
+            br, be = brbe.split(":")
+            buildrequest = BuildRequest.objects.get(pk = br)
+            build.project = buildrequest.project
+            build.save()
+
         return build
 
     def create_target_objects(self, target_info):
@@ -600,7 +607,10 @@ class BuildInfoHelper(object):
         assert '_pkgs' in vars(event)
         build_information = self._get_build_information()
 
-        build_obj = self.orm_wrapper.create_build_object(build_information)
+        brbe = self.server.runCommand(["getVariable", "TOASTER_BRBE"])[0]
+
+        build_obj = self.orm_wrapper.create_build_object(build_information, brbe)
+
         self.internal_state['build'] = build_obj
 
         # save layer version information for this build
@@ -619,6 +629,9 @@ class BuildInfoHelper(object):
         # Save build configuration
         self.orm_wrapper.save_build_variables(build_obj, self.server.runCommand(["getAllKeysWithFlags", ["doc", "func"]])[0])
 
+        return brbe
+
+
     def update_target_image_file(self, event):
         image_fstypes = self.server.runCommand(["getVariable", "IMAGE_FSTYPES"])[0]
         for t in self.internal_state['targets']:
diff --git a/lib/bb/ui/toasterui.py b/lib/bb/ui/toasterui.py
index 5f87a9d..2f628e9 100644
--- a/lib/bb/ui/toasterui.py
+++ b/lib/bb/ui/toasterui.py
@@ -94,7 +94,7 @@ def main(server, eventHandler, params ):
     first = True
 
     buildinfohelper = BuildInfoHelper(server, build_history_enabled)
-
+    brbe = None
 
     while True:
         try:
@@ -111,7 +111,7 @@ def main(server, eventHandler, params ):
             helper.eventHandler(event)
 
             if isinstance(event, bb.event.BuildStarted):
-                buildinfohelper.store_started_build(event)
+                brbe = buildinfohelper.store_started_build(event)
 
             if isinstance(event, (bb.build.TaskStarted, bb.build.TaskSucceeded, bb.build.TaskFailedSilent)):
                 buildinfohelper.update_and_store_task(event)
@@ -231,10 +231,9 @@ def main(server, eventHandler, params ):
                 buildinfohelper.update_build_information(event, errors, warnings, taskfailures)
 
 
-                brbe = server.runCommand(["getVariable", "TOASTER_BRBE"])[0]
-                br_id, be_id = brbe.split(":")
                 # we start a new build info
                 if brbe is not None:
+                    br_id, be_id = brbe.split(":")
                     buildinfohelper.store_build_done(br_id, be_id)
 
                     print "we are under BuildEnvironment management - after the build, we exit"
-- 
1.9.1



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

end of thread, other threads:[~2014-07-09 16:55 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-07-04 12:44 [PATCH 0/9] toaster pull request Alex DAMIAN
2014-07-04 12:44 ` [PATCH 1/9] toaster: remove strftime calls in filters Alex DAMIAN
2014-07-04 12:44 ` [PATCH 2/9] toaster: automatically discover urls.py files Alex DAMIAN
2014-07-04 12:44 ` [PATCH 3/9] toaster: automatically enable applications Alex DAMIAN
2014-07-04 12:44 ` [PATCH 4/9] toasterui: Show in the log that ToasterUi is ready to receive events Alex DAMIAN
2014-07-04 12:44 ` [PATCH 5/9] toaster: add project pages Alex DAMIAN
2014-07-04 12:44 ` [PATCH 6/9] toaster: add automated login in new project page Alex DAMIAN
2014-07-04 12:44 ` [PATCH 7/9] toaster: whitespace fix Alex DAMIAN
2014-07-04 12:44 ` [PATCH 8/9] toaster: new project page implementation Alex DAMIAN
2014-07-04 12:44 ` [PATCH 9/9] toasterui: fix build - project identification Alex DAMIAN
2014-07-09 16:55 ` [PATCH 0/9] [V2] toaster pull request Alex DAMIAN
2014-07-09 16:55   ` [PATCH 1/9] toaster: remove strftime calls in filters Alex DAMIAN
2014-07-09 16:55   ` [PATCH 2/9] toaster: automatically discover urls.py files Alex DAMIAN
2014-07-09 16:55   ` [PATCH 3/9] toaster: automatically enable applications Alex DAMIAN
2014-07-09 16:55   ` [PATCH 4/9] toasterui: Show in the log that ToasterUi is ready to receive events Alex DAMIAN
2014-07-09 16:55   ` [PATCH 5/9] toaster: add project pages Alex DAMIAN
2014-07-09 16:55   ` [PATCH 6/9] toaster: add automated login in new project page Alex DAMIAN
2014-07-09 16:55   ` [PATCH 7/9] toaster: whitespace fix Alex DAMIAN
2014-07-09 16:55   ` [PATCH 8/9] toaster: new project page implementation Alex DAMIAN
2014-07-09 16:55   ` [PATCH 9/9] toasterui: fix build - project identification Alex DAMIAN

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox