From cb1338dedc3873ac81488df9dd3aa6f048e6b02f Mon Sep 17 00:00:00 2001 From: Alexandru DAMIAN Date: Fri, 11 Oct 2013 13:46:23 +0100 Subject: bitbake: add toaster code to bitbake This patch adds the Toaster component to Bitbake. Toaster is a module designed to record the progress of a Bitbake build, and data about the resultant artifacts. It contains a web-based interface and a REST API allowing post-facto inspection of the build process and artifacts. Features present in this build: * toaster start script * relational data model * Django boilerplate code * the REST API * the Simple UI web interface This patch has all the development history squashed together. Code portions contributed by Calin Dragomir . Signed-off-by: Alexandru DAMIAN --- bitbake/lib/toaster/bldviewer/__init__.py | 0 bitbake/lib/toaster/bldviewer/api.py | 37 +++ bitbake/lib/toaster/bldviewer/templates/base.html | 30 +++ .../toaster/bldviewer/templates/basebuildpage.html | 17 ++ .../lib/toaster/bldviewer/templates/basetable.html | 46 ++++ bitbake/lib/toaster/bldviewer/templates/bfile.html | 24 ++ .../lib/toaster/bldviewer/templates/bpackage.html | 44 ++++ bitbake/lib/toaster/bldviewer/templates/build.html | 43 ++++ .../toaster/bldviewer/templates/configuration.html | 20 ++ bitbake/lib/toaster/bldviewer/templates/layer.html | 34 +++ .../lib/toaster/bldviewer/templates/package.html | 36 +++ .../lib/toaster/bldviewer/templates/recipe.html | 54 +++++ bitbake/lib/toaster/bldviewer/templates/task.html | 63 +++++ .../lib/toaster/bldviewer/templatetags/__init__.py | 0 .../toaster/bldviewer/templatetags/projecttags.py | 26 +++ bitbake/lib/toaster/bldviewer/urls.py | 32 +++ bitbake/lib/toaster/bldviewer/views.py | 260 +++++++++++++++++++++ 17 files changed, 766 insertions(+) create mode 100644 bitbake/lib/toaster/bldviewer/__init__.py create mode 100644 bitbake/lib/toaster/bldviewer/api.py create mode 100644 bitbake/lib/toaster/bldviewer/templates/base.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/basebuildpage.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/basetable.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/bfile.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/bpackage.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/build.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/configuration.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/layer.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/package.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/recipe.html create mode 100644 bitbake/lib/toaster/bldviewer/templates/task.html create mode 100644 bitbake/lib/toaster/bldviewer/templatetags/__init__.py create mode 100644 bitbake/lib/toaster/bldviewer/templatetags/projecttags.py create mode 100644 bitbake/lib/toaster/bldviewer/urls.py create mode 100644 bitbake/lib/toaster/bldviewer/views.py (limited to 'bitbake/lib/toaster/bldviewer') diff --git a/bitbake/lib/toaster/bldviewer/__init__.py b/bitbake/lib/toaster/bldviewer/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitbake/lib/toaster/bldviewer/api.py b/bitbake/lib/toaster/bldviewer/api.py new file mode 100644 index 0000000000..f761ba65a9 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/api.py @@ -0,0 +1,37 @@ +# +# BitBake Toaster Implementation +# +# Copyright (C) 2013 Intel Corporation +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +from django.conf.urls import patterns, include, url + + +urlpatterns = patterns('bldviewer.views', + url(r'^builds$', 'model_explorer', {'model_name':'build'}, name='builds'), + url(r'^targets$', 'model_explorer', {'model_name':'target'}, name='targets'), + url(r'^tasks$', 'model_explorer', {'model_name':'task'}, name='task'), + url(r'^task_dependencies$', 'model_explorer', {'model_name':'task_dependency'}, name='task_dependencies'), + url(r'^packages$', 'model_explorer', {'model_name':'build_package'}, name='build_packages'), + url(r'^package_dependencies$', 'model_explorer', {'model_name':'build_package_dependency'}, name='build_package_dependencies'), + url(r'^target_packages$', 'model_explorer', {'model_name':'target_package'}, name='target_packages'), + url(r'^package_files$', 'model_explorer', {'model_name':'build_file'}, name='build_files'), + url(r'^layers$', 'model_explorer', {'model_name':'layer'}, name='layer'), + url(r'^layerversions$', 'model_explorer', {'model_name':'layerversion'}, name='layerversion'), + url(r'^recipes$', 'model_explorer', {'model_name':'recipe'}, name='recipe'), + url(r'^recipe_dependencies$', 'model_explorer', {'model_name':'recipe_dependency'}, name='recipe_dependencies'), + url(r'^variables$', 'model_explorer', {'model_name':'variable'}, name='variables'), + url(r'^logmessages$', 'model_explorer', {'model_name':'logmessage'}, name='logmessages'), +) diff --git a/bitbake/lib/toaster/bldviewer/templates/base.html b/bitbake/lib/toaster/bldviewer/templates/base.html new file mode 100644 index 0000000000..101880d3ea --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/base.html @@ -0,0 +1,30 @@ + +{% load static %} + + + Toaster Simple Explorer + + + + + + +
+ + +
+{% block pagecontent %} +{% endblock %} +
+ +
+ + + diff --git a/bitbake/lib/toaster/bldviewer/templates/basebuildpage.html b/bitbake/lib/toaster/bldviewer/templates/basebuildpage.html new file mode 100644 index 0000000000..873f271bab --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/basebuildpage.html @@ -0,0 +1,17 @@ +{% extends "basetable.html" %} + +{% block pagename %} + +

Toaster - Build {% block pagetitle %} {% endblock %}

+{% endblock %} + diff --git a/bitbake/lib/toaster/bldviewer/templates/basetable.html b/bitbake/lib/toaster/bldviewer/templates/basetable.html new file mode 100644 index 0000000000..083bcb82e9 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/basetable.html @@ -0,0 +1,46 @@ +{% extends "base.html" %} + +{% block pagecontent %} + +
+ + {% block pagename %} + {% endblock %} +
Search: +
+ {% if hideshowcols %} +
Show/Hide columns: + {% for i in hideshowcols %} + {{i.name}} | + {% endfor %} +
+ {% endif %} +
+
+ +{% block pagetable %} +{% endblock %} +
+
+ +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/bfile.html b/bitbake/lib/toaster/bldviewer/templates/bfile.html new file mode 100644 index 0000000000..d90f4fbddb --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/bfile.html @@ -0,0 +1,24 @@ +{% extends "basebuildpage.html" %} + +{% block pagetitle %}Files for package {{files.0.bpackage.name}} {% endblock %} +{% block pagetable %} + {% if not files %} +

No files were recorded for this package!

+ {% else %} + + + Name + Size (Bytes) + + + {% for file in files %} + + + {{file.path}} + {{file.size}} + + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/bpackage.html b/bitbake/lib/toaster/bldviewer/templates/bpackage.html new file mode 100644 index 0000000000..2e254dbad6 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/bpackage.html @@ -0,0 +1,44 @@ +{% extends "basebuildpage.html" %} + +{% block pagetitle %}Packages{% endblock %} +{% block pagetable %} + {% if not packages %} +

No packages were recorded for this target!

+ {% else %} + + + Name + Version + Recipe + Summary + Section + Description + Size on host disk (KBytes) + License + Dependencies List (all) + + + {% for package in packages %} + + + {{package.name}} ({{package.filelist_bpackage.count}} files) + {{package.version}}-{{package.revision}} + {{package.recipe.name}}{{package.package_name}} + + {{package.summary}} + {{package.section}} + {{package.description}} + {{package.size}} + {{package.license}} + +
+ {% for bpd in package.bpackage_dependencies_package.all %} + {{bpd.dep_type}}: {{bpd.depends_on}}
+ {% endfor %} +
+ + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/build.html b/bitbake/lib/toaster/bldviewer/templates/build.html new file mode 100644 index 0000000000..ab6e19643b --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/build.html @@ -0,0 +1,43 @@ +{% extends "basetable.html" %} + +{% block pagename %} +

Toaster - Builds

+{% endblock %} + +{% block pagetable %} + + {% load projecttags %} + + Outcome + Started On + Completed On + Target + Machine + Time + Errors + Warnings + Output + Log + Bitbake Version + Build Name + + {% for build in builds %} + + {{build.get_outcome_display}} + {{build.started_on}} + {{build.completed_on}} + {% for t in build.target_set.all %}{{t.target}}{% if t.is_image %} (Img){% endif %}
{% endfor %} + {{build.machine}} + {% time_difference build.started_on build.completed_on %} + {{build.errors_no}}:{% if build.errors_no %}{% for error in logs %}{% if error.build == build %}{% if error.level == 2 %}

{{error.message}}

{% endif %}{% endif %}{% endfor %}{% else %}None{% endif %} + {{build.warnings_no}}:{% if build.warnings_no %}{% for warning in logs %}{% if warning.build == build %}{% if warning.level == 1 %}

{{warning.message}}

{% endif %}{% endif %}{% endfor %}{% else %}None{% endif %} + {% if build.outcome == 0 %}{% for t in build.target_set.all %}{% if t.is_image %}{{build.image_fstypes}}{% endif %}{% endfor %}{% endif %} + {{build.cooker_log_path}} + {{build.bitbake_version}} + {{build.build_name}} + + + {% endfor %} +{% endblock %} + + diff --git a/bitbake/lib/toaster/bldviewer/templates/configuration.html b/bitbake/lib/toaster/bldviewer/templates/configuration.html new file mode 100644 index 0000000000..052c37c4e8 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/configuration.html @@ -0,0 +1,20 @@ +{% extends "basebuildpage.html" %} + +{% block pagetitle %}Configuration{% endblock %} +{% block pagetable %} + + + Name + Value + Description + + + {% for variable in configuration %} + + + {{variable.variable_name}} + {{variable.variable_value}} + {% if variable.description %}{{variable.description}}{% endif %} + {% endfor %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/layer.html b/bitbake/lib/toaster/bldviewer/templates/layer.html new file mode 100644 index 0000000000..fa4fd9bde8 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/layer.html @@ -0,0 +1,34 @@ +{% extends "basetable.html" %} + +{% block pagename %} +

Toaster - Layers

+{% endblock %} + +{% block pagetable %} + {% load projecttags %} + + + Name + Local Path + Layer Index URL + Known Versions + + + {% for layer in layers %} + + + {{layer.name}} + {{layer.local_path}} + {{layer.layer_index_url}} + + {% for lv in layer.versions %} + + {% endfor %} +
+ ({{lv.priority}}){{lv.branch}}:{{lv.commit}} ({{lv.count}} recipes) +
+ + + {% endfor %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/package.html b/bitbake/lib/toaster/bldviewer/templates/package.html new file mode 100644 index 0000000000..642fcab9a1 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/package.html @@ -0,0 +1,36 @@ +{% extends "basebuildpage.html" %} + +{% block pagetable %} + {% if not packages %} +

No packages were recorded for this target!

+ {% else %} + + + Name + Version + Size (Bytes) + Recipe + Depends on + + + {% for package in packages %} + + + {{package.name}} + {{package.version}} + {{package.size}} + + {{package.recipe.name}}{{package.package_name}} + +
+ {% for d in package.depends_on %} + {{d.name}}
+ {% endfor %} +
+ + + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/recipe.html b/bitbake/lib/toaster/bldviewer/templates/recipe.html new file mode 100644 index 0000000000..a62437066d --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/recipe.html @@ -0,0 +1,54 @@ +{% extends "basetable.html" %} + +{% block pagename %} + +

Toaster - Recipes for a Layer

+{% endblock %} + +{% block pagetable %} + {% load projecttags %} + + + + Name + Version + Summary + Description + Section + License + License file + Homepage + Bugtracker + Author + File_path + Recipe Dependency + + + {% for recipe in recipes %} + + + {{recipe.name}} + {{recipe.version}} + {{recipe.summary}} + {{recipe.description}} + {{recipe.section}} + {{recipe.license}} + {{recipe.licensing_info}} + {{recipe.homepage}} + {{recipe.bugtracker}} + {{recipe.author}} + {{recipe.file_path}} + +
+ {% for rr in recipe.r_dependencies_recipe.all %} + {{rr.depends_on.name}}
+ {% endfor %} +
+ + + + {% endfor %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templates/task.html b/bitbake/lib/toaster/bldviewer/templates/task.html new file mode 100644 index 0000000000..e7253698cd --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templates/task.html @@ -0,0 +1,63 @@ +{% extends "basebuildpage.html" %} + +{% block pagetitle %}Tasks{% endblock %} +{% block pagetable %} + {% if not tasks %} +

No tasks were executed in this build!

+ {% else %} + + + Order + Task + Recipe Version + Task Type + Checksum + Outcome + Message + Logfile + Time + CPU usage + Disk I/O + Script type + File path + Depends + + + {% for task in tasks %} + + + {{task.order}} + + {{task.recipe.name}}.{{task.task_name}} + {{task.recipe.version}} + + {% if task.task_executed %} + Executed + {% else %} + Prebuilt + {% endif %} + + {{task.sstate_checksum}} + {{task.get_outcome_display}}{% if task.provider %}
(by {{task.provider.recipe.name}}.{{task.provider.task_name}}){% endif %} +

{{task.message}} + {{task.logfile}} + {{task.elapsed_time}} + {{task.cpu_usage}} + {{task.disk_io}} + {{task.get_script_type_display}} + {{task.recipe.file_path}} + +

+ {% for tt in task.task_dependencies_task.all %} + + {{tt.depends_on.recipe.name}}.{{tt.depends_on.task_name}}
+ {% endfor %} +
+ + + + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/bitbake/lib/toaster/bldviewer/templatetags/__init__.py b/bitbake/lib/toaster/bldviewer/templatetags/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitbake/lib/toaster/bldviewer/templatetags/projecttags.py b/bitbake/lib/toaster/bldviewer/templatetags/projecttags.py new file mode 100644 index 0000000000..0c0d804c0c --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/templatetags/projecttags.py @@ -0,0 +1,26 @@ +# +# BitBake Toaster Implementation +# +# Copyright (C) 2013 Intel Corporation +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +from datetime import datetime +from django import template + +register = template.Library() + +@register.simple_tag +def time_difference(start_time, end_time): + return end_time - start_time diff --git a/bitbake/lib/toaster/bldviewer/urls.py b/bitbake/lib/toaster/bldviewer/urls.py new file mode 100644 index 0000000000..becc679203 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/urls.py @@ -0,0 +1,32 @@ +# +# BitBake Toaster Implementation +# +# Copyright (C) 2013 Intel Corporation +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +from django.conf.urls import patterns, include, url +from django.views.generic.simple import redirect_to + +urlpatterns = patterns('bldviewer.views', + url(r'^builds/$', 'build', name='all-builds'), + url(r'^build/(?P\d+)/task/$', 'task', name='task'), + url(r'^build/(?P\d+)/packages/$', 'bpackage', name='bpackage'), + url(r'^build/(?P\d+)/package/(?P\d+)/files/$', 'bfile', name='bfile'), + url(r'^build/(?P\d+)/target/(?P\d+)/packages/$', 'tpackage', name='tpackage'), + url(r'^build/(?P\d+)/configuration/$', 'configuration', name='configuration'), + url(r'^layers/$', 'layer', name='all-layers'), + url(r'^layerversions/(?P\d+)/recipes/.*$', 'layer_versions_recipes', name='layer_versions_recipes'), + url(r'^$', redirect_to, {'url': 'builds/'}), +) diff --git a/bitbake/lib/toaster/bldviewer/views.py b/bitbake/lib/toaster/bldviewer/views.py new file mode 100644 index 0000000000..7be4d4b899 --- /dev/null +++ b/bitbake/lib/toaster/bldviewer/views.py @@ -0,0 +1,260 @@ +# +# BitBake Toaster Implementation +# +# Copyright (C) 2013 Intel Corporation +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import operator + +from django.db.models import Q +from django.shortcuts import render +from orm.models import Build, Target, Task, Layer, Layer_Version, Recipe, Target_Package, LogMessage, Variable +from orm.models import Task_Dependency, Recipe_Dependency, Build_Package, Build_File, Build_Package_Dependency +from django.views.decorators.cache import cache_control + +@cache_control(no_store=True) +def build(request): + template = 'build.html' + build_info = Build.objects.all() + + logs = LogMessage.objects.all() + + context = {'builds': build_info, 'logs': logs , + 'hideshowcols' : [ + {'name': 'Output', 'order':10}, + {'name': 'Log', 'order':11}, + ]} + + return render(request, template, context) + + +def _find_task_revdep(task): + tp = [] + for p in Task_Dependency.objects.filter(depends_on=task): + tp.append(p.task); + return tp + +def _find_task_provider(task): + task_revdeps = _find_task_revdep(task) + for tr in task_revdeps: + if tr.outcome != Task.OUTCOME_COVERED: + return tr + for tr in task_revdeps: + trc = _find_task_provider(tr) + if trc is not None: + return trc + return None + +def task(request, build_id): + template = 'task.html' + + tasks = Task.objects.filter(build=build_id) + + for t in tasks: + if t.outcome == Task.OUTCOME_COVERED: + t.provider = _find_task_provider(t) + + context = {'build': Build.objects.filter(pk=build_id)[0], 'tasks': tasks} + + return render(request, template, context) + +def configuration(request, build_id): + template = 'configuration.html' + variables = Variable.objects.filter(build=build_id) + context = {'build': Build.objects.filter(pk=build_id)[0], 'configuration' : variables} + return render(request, template, context) + +def bpackage(request, build_id): + template = 'bpackage.html' + packages = Build_Package.objects.filter(build = build_id) + context = {'build': Build.objects.filter(pk=build_id)[0], 'packages' : packages} + return render(request, template, context) + +def bfile(request, build_id, package_id): + template = 'bfile.html' + files = Build_File.objects.filter(bpackage = package_id) + context = {'build': Build.objects.filter(pk=build_id)[0], 'files' : files} + return render(request, template, context) + +def tpackage(request, build_id, target_id): + template = 'package.html' + + packages = Target_Package.objects.filter(target=target_id) + + context = {'build' : Build.objects.filter(pk=build_id)[0],'packages': packages} + + return render(request, template, context) + +def layer(request): + template = 'layer.html' + layer_info = Layer.objects.all() + + for li in layer_info: + li.versions = Layer_Version.objects.filter(layer = li) + for liv in li.versions: + liv.count = Recipe.objects.filter(layer_version__id = liv.id).count() + + context = {'layers': layer_info} + + return render(request, template, context) + + +def layer_versions_recipes(request, layerversion_id): + template = 'recipe.html' + recipes = Recipe.objects.filter(layer_version__id = layerversion_id) + + context = {'recipes': recipes, + 'layer_version' : Layer_Version.objects.filter( id = layerversion_id )[0] + } + + return render(request, template, context) + +#### API + +import json +from django.core import serializers +from django.http import HttpResponse, HttpResponseBadRequest + + +def model_explorer(request, model_name): + + DESCENDING = 'desc' + response_data = {} + model_mapping = { + 'build': Build, + 'target': Target, + 'target_package': Target_Package, + 'task': Task, + 'task_dependency': Task_Dependency, + 'package': Build_Package, + 'layer': Layer, + 'layerversion': Layer_Version, + 'recipe': Recipe, + 'recipe_dependency': Recipe_Dependency, + 'build_package': Build_Package, + 'build_package_dependency': Build_Package_Dependency, + 'build_file': Build_File, + 'variable': Variable, + 'logmessage': LogMessage, + } + + if model_name not in model_mapping.keys(): + return HttpResponseBadRequest() + + model = model_mapping[model_name] + + try: + limit = int(request.GET.get('limit', 0)) + except ValueError: + limit = 0 + + try: + offset = int(request.GET.get('offset', 0)) + except ValueError: + offset = 0 + + ordering_string, invalid = _validate_input(request.GET.get('orderby', ''), + model) + if invalid: + return HttpResponseBadRequest() + + filter_string, invalid = _validate_input(request.GET.get('filter', ''), + model) + if invalid: + return HttpResponseBadRequest() + + search_term = request.GET.get('search', '') + + if filter_string: + filter_terms = _get_filtering_terms(filter_string) + try: + queryset = model.objects.filter(**filter_terms) + except ValueError: + queryset = [] + else: + queryset = model.objects.all() + + if search_term: + queryset = _get_search_results(search_term, queryset, model) + + if ordering_string and queryset: + column, order = ordering_string.split(':') + if order.lower() == DESCENDING: + queryset = queryset.order_by('-' + column) + else: + queryset = queryset.order_by(column) + + if offset and limit: + queryset = queryset[offset:(offset+limit)] + elif offset: + queryset = queryset[offset:] + elif limit: + queryset = queryset[:limit] + + if queryset: + response_data['count'] = queryset.count() + else: + response_data['count'] = 0 + + response_data['list'] = serializers.serialize('json', queryset) + + return HttpResponse(json.dumps(response_data), + content_type='application/json') + +def _get_filtering_terms(filter_string): + + search_terms = filter_string.split(":") + keys = search_terms[0].split(',') + values = search_terms[1].split(',') + + return dict(zip(keys, values)) + +def _validate_input(input, model): + + invalid = 0 + + if input: + input_list = input.split(":") + + # Check we have only one colon + if len(input_list) != 2: + invalid = 1 + return None, invalid + + # Check we have an equal number of terms both sides of the colon + if len(input_list[0].split(',')) != len(input_list[1].split(',')): + invalid = 1 + return None, invalid + + # Check we are looking for a valid field + valid_fields = model._meta.get_all_field_names() + for field in input_list[0].split(','): + if field not in valid_fields: + invalid = 1 + return None, invalid + + return input, invalid + +def _get_search_results(search_term, queryset, model): + search_objects = [] + for st in search_term.split(" "): + q_map = map(lambda x: Q(**{x+'__icontains': st}), + model.search_allowed_fields) + + search_objects.append(reduce(operator.or_, q_map)) + search_object = reduce(operator.and_, search_objects) + queryset = queryset.filter(search_object) + + return queryset -- cgit v1.2.3-54-g00ecf