* [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