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