<commit_before>from datetime import datetime
import tempfile
import os

from django.http import HttpResponse, Http404, HttpResponseForbidden
from django import forms
from django.conf import settings
from django.shortcuts import render_to_response, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.template import RequestContext

from gerobak import utils, apt
from gerobak.apps.profile.models import Profile

class AddProfileForm(forms.Form):
    name = forms.CharField(max_length=50)
    desc = forms.CharField(max_length=250, label="Description")
    arch = forms.ChoiceField(choices=settings.GEROBAK_ARCHS,
                             initial=settings.GEROBAK_DEFAULT_ARCH,
                             label="Architecture")

class UploadStatusForm(forms.Form):
    file = forms.FileField()

class SourcesForm(forms.Form):
    sources = forms.CharField(widget=forms.Textarea)

class InstallForm(forms.Form):
    packages = forms.CharField()

class SearchForm(forms.Form):
    packages = forms.CharField()

def _post(func):
    def _func(request, pid, *args, **kwargs):
        if request.method != 'POST':
            return redirect(show, pid)
        return func(request, pid, *args, **kwargs)
    return _func

def _profile(func):
    def _func(request, pid, *args, **kwargs):
        profile = get_object_or_404(Profile, pk=pid)
        if profile.user != request.user:
            # TODO
            return HttpResponseForbidden()
        return func(request, profile, *args, **kwargs)
    return _func

def _updated(func):
    def _func(request, profile, *args, **kwargs):
        if profile.status_updated is None or \
           profile.sources_updated is None or \
           profile.repo_updated is None:

            request.user.message_set.create(message="update needed.")
            return redirect(show, profile.id)

        return func(request, profile, *args, **kwargs)
    return _func

def _pre_updated(func):
    def _func(request, profile, *args, **kwargs):
        if profile.status_updated is None or \
           profile.sources_updated is None:

            request.user.message_set.create(message="status or sources is missing")
            return redirect(show, profile.id)

        return func(request, profile, *args, **kwargs)
    return _func

def _decompress(realname, path):
    # FIXME uncompressed file size should be checked and limited

    fid, tmp = tempfile.mkstemp()
    fo = open(tmp, 'w')

    if realname.endswith('.gz'):
        import gzip
        fi = gzip.open(path, 'rb')

    elif realname.endswith('.bz2'):
        import bz2
        fi = bz2.BZ2File(path, 'r')

    fo.write(fi.read())

    fi.close()
    fo.close()

    return tmp

def handle_uploaded_status(profile, file):
    # FIXME file size should be checked and limited

    path = utils.get_path(profile.id)
    status = file.temporary_file_path()

    tmp = None
    if file.name.endswith('.gz') or file.name.endswith('.bz2'):
        tmp = _decompress(file.name, file.temporary_file_path())
        utils.update_status(path, tmp)
        os.unlink(tmp)

    else:
        utils.update_status(path, status)

@login_required
@_post
@_profile
@_updated
def install(request, profile):
    form = InstallForm(request.POST)
    if form.is_valid():
        packages = form.cleaned_data['packages']
        path = utils.get_path(profile.id)
        pkgs, ret, out, err = apt.install(path, packages)

        pkgs = ' '.join(pkgs)

        return render_to_response('profile/install.html',
                                  {'pkgs': pkgs,
                                   'ret': ret,
                                   'out': out,
                                   'err': err})
    else:
        print "invalid"
        print form.as_p()
    return HttpResponse('install pid=%d' % profile.id)

@login_required
@_profile
@_updated
def search(request, profile):
    form = SearchForm(request.POST)
    if form.is_valid():
        packages = form.cleaned_data['packages']
        path = utils.get_path(profile.id)
        pkgs, ret, out, err = apt.search(path, packages)

        pkgs = ' '.join(pkgs)

        return render_to_response('profile/search.html',
                                  {'pkgs': pkgs,
                                   'ret': ret,
                                   'out': out,
                                   'err': err})
    else:
        print "invalid"
        print form.as_p()
    return HttpResponse('search pid=%d' % profile.id)

@login_required
@_post
@_profile
@_pre_updated
def update(request, profile):
    path = utils.get_path(profile.id)
    ret, out, err = apt.update(path)

    profile.repo_updated = datetime.now()
    profile.save()

    return render_to_response('profile/update.html',
                              {'ret': ret,
                               'out': out,
                               'err': err})

@login_required
@_post
@_profile
def sources(request, profile):
    form = SourcesForm(request.POST)
    if form.is_valid():
        sources = form.cleaned_data['sources']
        path = utils.get_path(profile.id)
        utils.update_sources(path, sources)
        profile.sources_updated = datetime.now()
        profile.repo_updated = None
        profile.save()
        return redirect(show, profile.id)
    else:
        print "invalid"
        print form.as_p()
    return HttpResponse('sources pid=%d' % profile.id)

@login_required
@_post
@_profile
def status(request, profile):
    form = UploadStatusForm(request.POST, request.FILES)
    print request.FILES.keys()
    if form.is_valid():
        path = handle_uploaded_status(profile, request.FILES['file'])
        profile.repo_updated = None
        profile.status_updated = datetime.now()

        size, hash = utils.get_status_info(path)
        profile.status_size = size
        profile.status_hash = hash

        profile.save()
        return redirect(show, profile.id)
    else:
        print "invalid"
        print form.as_p()
    return HttpResponse('status pid=%d' % profile.id)

@login_required
@_profile
def show(request, profile):
    upload_form = UploadStatusForm()

    path = utils.get_path(profile.id)
    sources = utils.get_repo(path)
    sources_form = SourcesForm({'sources': sources})

    install_form = InstallForm()
    search_form = SearchForm()

    return render_to_response('profile/show.html',
                              {'profile': profile,
                               'upload': upload_form,
                               'sources': sources_form,
                               'install': install_form,
                               'search': search_form},
                              context_instance=RequestContext(request))

@login_required
def index(request):
    form = AddProfileForm()
    if request.method == 'POST':
        form = AddProfileForm(request.POST)
        if form.is_valid():
            profile = Profile()
            profile.user = request.user
            profile.name = form.cleaned_data['name']
            profile.desc = form.cleaned_data['desc']
            profile.arch = form.cleaned_data['arch']
            profile.save()

            path = utils.get_path(profile.id)
            utils.prepare_profile_dir(path)

            return redirect(index)

    profiles = Profile.objects.filter(user=request.user)
    return render_to_response('profile/index.html',
                              {'form': form,
                               'profiles': profiles},
                              context_instance=RequestContext(request))

<commit_msg>Package search and show<commit_after>from datetime import datetime
import tempfile
import os

try:
    import simplejson as json
except ImportError:
    import json

from django.http import HttpResponse, Http404, HttpResponseForbidden
from django import forms
from django.conf import settings
from django.shortcuts import render_to_response, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.template import RequestContext

from gerobak import utils, apt
from gerobak.apps.profile.models import Profile
from gerobak.apps.profile.utils import parse_apt_install, \
                                       parse_apt_search, \
                                       parse_apt_show

class AddProfileForm(forms.Form):
    name = forms.CharField(max_length=50)
    desc = forms.CharField(max_length=250, label="Description")
    arch = forms.ChoiceField(choices=settings.GEROBAK_ARCHS,
                             initial=settings.GEROBAK_DEFAULT_ARCH,
                             label="Architecture")

class UploadStatusForm(forms.Form):
    file = forms.FileField()

class SourcesForm(forms.Form):
    sources = forms.CharField(widget=forms.Textarea)

class InstallForm(forms.Form):
    packages = forms.CharField()

class SearchForm(forms.Form):
    packages = forms.CharField()

def _post(func):
    def _func(request, pid, *args, **kwargs):
        if request.method != 'POST':
            return redirect(show, pid)
        return func(request, pid, *args, **kwargs)
    return _func

def _profile(func):
    def _func(request, pid, *args, **kwargs):
        profile = get_object_or_404(Profile, pk=pid)
        if profile.user != request.user:
            # TODO
            return HttpResponseForbidden()
        return func(request, profile, *args, **kwargs)
    return _func

def _updated(func):
    def _func(request, profile, *args, **kwargs):
        if profile.status_updated is None or \
           profile.sources_updated is None or \
           profile.repo_updated is None:

            request.user.message_set.create(message="update needed.")
            return redirect(show, profile.id)

        return func(request, profile, *args, **kwargs)
    return _func

def _pre_updated(func):
    def _func(request, profile, *args, **kwargs):
        if profile.status_updated is None or \
           profile.sources_updated is None:

            request.user.message_set.create(message="status or sources is missing")
            return redirect(show, profile.id)

        return func(request, profile, *args, **kwargs)
    return _func

def _decompress(realname, path):
    # FIXME uncompressed file size should be checked and limited

    fid, tmp = tempfile.mkstemp()
    fo = open(tmp, 'w')

    if realname.endswith('.gz'):
        import gzip
        fi = gzip.open(path, 'rb')

    elif realname.endswith('.bz2'):
        import bz2
        fi = bz2.BZ2File(path, 'r')

    fo.write(fi.read())

    fi.close()
    fo.close()

    return tmp

def handle_uploaded_status(profile, file):
    # FIXME file size should be checked and limited

    path = utils.get_path(profile.id)
    status = file.temporary_file_path()

    tmp = None
    if file.name.endswith('.gz') or file.name.endswith('.bz2'):
        tmp = _decompress(file.name, file.temporary_file_path())
        utils.update_status(path, tmp)
        os.unlink(tmp)

    else:
        utils.update_status(path, status)

    return path

@login_required
@_post
@_profile
@_updated
def install(request, profile):
    form = InstallForm(request.POST)
    if form.is_valid():
        packages = form.cleaned_data['packages']
        path = utils.get_path(profile.id)
        pkgs, ret, out, err = apt.install(path, packages)

        pkgs = ' '.join(pkgs)

        return render_to_response('profile/install.html',
                                  {'pkgs': pkgs,
                                   'ret': ret,
                                   'out': out,
                                   'err': err})
    else:
        print "invalid"
        print form.as_p()
    return HttpResponse('install pid=%d' % profile.id)

@login_required
@_profile
@_updated
def info(request, profile, pkg):
    format = request.GET.get('format', 'html')
    if not format in ['html', 'json']:
        format = 'html'

    path = utils.get_path(profile.id)
    pkg, code, out, err = apt.show(path, pkg)

    if code == 100: # Not found
        raise Http404()

    info, keys = parse_apt_show(out)

    sdesc = info['Description'].split("\n")[0]
    desc = "\n".join(info['Description'].split("\n")[1:])

    data = []
    for key in keys:
        data.append((key, info[key]))

    if format == 'html':
        return render_to_response('profile/info.html',
                                  {'pkg': pkg,
                                   'desc': desc,
                                   'sdesc': sdesc,
                                   'data': data})

    elif format == 'json':
        data = {'stat': 'ok',
                'type': 'pkgshow',
                'data': {'package': pkg,
                         'desc': desc,
                         'sdesc': sdesc,
                         'data': data}}
        jdata = json.dumps(data)
        print jdata
        return render_to_response('profile/json', {'json': jdata},
                                  mimetype='application/json')

@login_required
@_profile
@_updated
def search(request, profile):
    format = request.GET.get('format', 'html')
    if not format in ['html', 'json']:
        format = 'html'

    form = SearchForm(request.POST)
    if form.is_valid():
        packages = form.cleaned_data['packages']
        path = utils.get_path(profile.id)
        pkgs, ret, out, err = apt.search(path, packages)

        pkgs = ' '.join(pkgs)

        return render_to_response('profile/search.%s' % format,
                                  {'pkgs': pkgs,
                                   'ret': ret,
                                   'items': parse_apt_search(out)})
    else:
        print "invalid"
        print form.as_p()
    return HttpResponse('search pid=%d' % profile.id)

@login_required
@_post
@_profile
@_pre_updated
def update(request, profile):
    path = utils.get_path(profile.id)
    ret, out, err = apt.update(path)

    profile.repo_updated = datetime.now()
    profile.save()

    return render_to_response('profile/update.html',
                              {'ret': ret,
                               'out': out,
                               'err': err})

@login_required
@_post
@_profile
def sources(request, profile):
    form = SourcesForm(request.POST)
    if form.is_valid():
        sources = form.cleaned_data['sources']
        path = utils.get_path(profile.id)
        utils.update_sources(path, sources)
        profile.sources_updated = datetime.now()
        profile.repo_updated = None
        profile.save()
        return redirect(show, profile.id)
    else:
        print "invalid"
        print form.as_p()
    return HttpResponse('sources pid=%d' % profile.id)

@login_required
@_post
@_profile
def status(request, profile):
    form = UploadStatusForm(request.POST, request.FILES)
    print request.FILES.keys()
    if form.is_valid():
        path = handle_uploaded_status(profile, request.FILES['file'])
        profile.repo_updated = None
        profile.status_updated = datetime.now()

        size, hash = utils.get_status_info(path)
        profile.status_size = size
        profile.status_hash = hash

        profile.save()
        return redirect(show, profile.id)
    else:
        print "invalid"
        print form.as_p()
    return HttpResponse('status pid=%d' % profile.id)

@login_required
@_profile
def show(request, profile):
    upload_form = UploadStatusForm()

    path = utils.get_path(profile.id)
    sources = utils.get_repo(path)
    sources_form = SourcesForm({'sources': sources})

    install_form = InstallForm()
    search_form = SearchForm()

    return render_to_response('profile/show.html',
                              {'profile': profile,
                               'upload': upload_form,
                               'sources': sources_form,
                               'install': install_form,
                               'search': search_form},
                              context_instance=RequestContext(request))

@login_required
def index(request):
    form = AddProfileForm()
    if request.method == 'POST':
        form = AddProfileForm(request.POST)
        if form.is_valid():
            profile = Profile()
            profile.user = request.user
            profile.name = form.cleaned_data['name']
            profile.desc = form.cleaned_data['desc']
            profile.arch = form.cleaned_data['arch']
            profile.save()

            path = utils.get_path(profile.id)
            utils.prepare_profile_dir(path)

            return redirect(index)

    profiles = Profile.objects.filter(user=request.user)
    return render_to_response('profile/index.html',
                              {'form': form,
                               'profiles': profiles},
                              context_instance=RequestContext(request))

<|endoftext|>


<commit_before># Copyright 2011 Omniscale GmbH & Co. KG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import with_statement

import struct
import sys
import zlib

from marshal import dumps

from imposm.parser.pbf import OSMPBF

SUPPORTED_FEATURES = set(['OsmSchema-V0.6', 'DenseNodes'])

_MEMBERTYPE = {0 : 'node',
               1 : 'way',
               2 : 'relation'}

class PBFParser(object):
    """
    OSM PBF parser.

    :param xxx_callback:
        callback functions for coords, nodes, ways and relations.
        Each callback function gets called with a list of multiple elements.

    :param xxx_filter:
        functions that can manipulate the tag dictionary.
        Nodes and relations without tags will not passed to the callback.

    :param marshal:
        return the data as a marshaled string
    """
    def __init__(self, nodes_callback=None, ways_callback=None,
        relations_callback=None, coords_callback=None, nodes_tag_filter=None,
        ways_tag_filter=None, relations_tag_filter=None, marshal=False):
        self.nodes_callback = nodes_callback
        self.ways_callback = ways_callback
        self.relations_callback = relations_callback
        self.coords_callback = coords_callback
        self.nodes_tag_filter = nodes_tag_filter
        self.ways_tag_filter = ways_tag_filter
        self.relations_tag_filter = relations_tag_filter
        self.marshal = marshal

    def parse(self, filename, offset, size):
        """
        Parse primitive block from `filename`.

        :param filename: path to PBF file
        :param offset: byte offset of the primitive block to parse
        :param size: size in bytes of the primitive block to parse
        """
        reader = PrimitiveBlockParser(filename, offset, size)

        if self.nodes_callback or self.coords_callback:
            self.handle_nodes(reader)
        if self.ways_callback:
            self.handle_ways(reader)
        if self.relations_callback:
            self.handle_relations(reader)

    def handle_nodes(self, reader):
        nodes = []
        coords = []
        nodes_callback = self.nodes_callback
        coords_callback = self.coords_callback
        for node in reader.nodes():
            if nodes_callback:
                if self.nodes_tag_filter:
                    self.nodes_tag_filter(node[1])
                if node[1]:
                    if self.marshal:
                        nodes.append((node[0], dumps((node[1], node[2]), 2)))
                    else:
                        nodes.append((node[0], node[1], node[2]))
                if len(nodes) >= 256:
                    nodes_callback(nodes)
                    nodes = []
            if coords_callback:
                coords.append((node[0], node[2][0], node[2][1]))
                if len(coords) >= 512:
                    coords_callback(coords)
                    coords = []
        if nodes_callback:
            nodes_callback(nodes)
        if coords_callback:
            coords_callback(coords)

    def handle_ways(self, reader):
        ways = []
        for way in reader.ways():
            if self.ways_tag_filter:
                self.ways_tag_filter(way[1])
            # always return ways, might be needed for relations
            if self.marshal:
                ways.append((way[0], dumps((way[1], way[2]), 2)))
            else:
                ways.append((way[0], way[1], way[2]))
            if len(ways) >= 256:
                self.ways_callback(ways)
                ways = []
        self.ways_callback(ways)

    def handle_relations(self, reader):
        relations = []
        for relation in reader.relations():
            if self.relations_tag_filter:
                self.relations_tag_filter(relation[1])
                if not relation[1]:
                    continue
            if self.marshal:
                relations.append((relation[0], dumps((relation[1], relation[2]), 2)))
            else:
                relations.append((relation[0], relation[1], relation[2]))
            if len(relations) >= 256:
                self.relations_callback(relations)
                relations = []
        self.relations_callback(relations)

def decoded_stringtable(stringtable):
    result = []
    for s in stringtable:
        result.append(s.decode('utf-8'))
    return result

class PrimitiveBlockParser(object):
    """
    Low level PBF primitive block parser.

    Parses a single primitive block and handles OSM PBF internals like
    dense nodes, delta encoding, stringtables, etc.

    :param filename: path to PBF file
    :param offset: byte offset of the primitive block to parse
    :param size: size in bytes of the primitive block to parse

    """
    def __init__(self, filename, blob_pos, blob_size):
        self.pos = filename, blob_pos, blob_size
        data = read_blob_data(filename, blob_pos, blob_size)
        self.primitive_block = OSMPBF.PrimitiveBlock()
        self.primitive_block.ParseFromString(data)
        self.primitivegroup = self.primitive_block.primitivegroup
        self.stringtable = decoded_stringtable(self.primitive_block.stringtable.s)

    def __repr__(self):
        return '<PrimitiveBlockParser %r>' % (self.pos, )

    def _get_tags(self, element, pos):
        tags = {}
        key = None
        value = None
        keyflag = False
        if pos >= len(element):
            return {}, pos
        while True:
            key_val = element[pos]
            pos += 1
            if key_val == 0:
                break
            if not keyflag:
                key = key_val
                keyflag = True
            else:
                value = key_val
                tags[self.stringtable[key]] = self.stringtable[value]
                keyflag = False
        return tags, pos

    def nodes(self):
        """
        Return an iterator for all *nodes* in this primitive block.

        :rtype: iterator of ``(osm_id, tags, (lon, lat))`` tuples
        """
        for group in self.primitivegroup:
            dense = group.dense
            if dense:
                granularity = self.primitive_block.granularity
                lat_offset = self.primitive_block.lat_offset or 0
                lon_offset = self.primitive_block.lon_offset or 0
                coord_scale = 0.000000001
                get_tags = self._get_tags
                ids = dense.id
                lats = dense.lat
                lons = dense.lon
                keys_vals = dense.keys_vals
                last_id = last_lat = last_lon = last_keysvals_pos = 0
                for i in xrange(len(ids)):
                    last_id += ids[i]
                    last_lat += coord_scale * (lat_offset + (granularity * lats[i]))
                    last_lon += coord_scale * (lon_offset + (granularity * lons[i]))
                    tags, last_keysvals_pos = get_tags(keys_vals, last_keysvals_pos)
                    yield (last_id, tags, (last_lon, last_lat))
            nodes = group.nodes
            if nodes:
                for node in nodes:
                    keys, vals = node.keys, node.vals
                    tags = []
                    for i in xrange(len(keys)):
                        tags.append((self.stringtable[keys[i]], self.stringtable[vals[i]]))
                    yield (node.id, tags, (node.lon, node.lat))

    def ways(self):
        """
        Return an iterator for all *ways* in this primitive block.

        :rtype: iterator of ``(osm_id, tags, [ref1, ref2, ...])`` tuples
        """
        for group in self.primitivegroup:
            ways = group.ways
            if ways:
                for way in ways:
                    keys = way.keys
                    vals = way.vals
                    delta_refs = way.refs

                    tags = {}
                    for i in xrange(len(keys)):
                        tags[self.stringtable[keys[i]]] = self.stringtable[vals[i]]
                    refs = []
                    ref = 0
                    for delta in delta_refs:
                        ref += delta
                        refs.append(ref)
                    yield (way.id, tags, refs)

    def relations(self):
        """
        Return an iterator for all *relations* in this primitive block.

        :rtype: iterator of ``(osm_id, tags, [(ref1, type, role), ...])`` tuples

        """
        for group in self.primitivegroup:
            relations = group.relations
            if relations:
                for relation in relations:
                    members = []
                    memids = relation.memids
                    rel_types = relation.types
                    roles_sids = relation.roles_sid
                    keys = relation.keys
                    vals = relation.vals
                    memid = 0
                    for i in xrange(len(rel_types)):
                        memid += memids[i]
                        members.append((memid, _MEMBERTYPE[rel_types[i]], self.stringtable[roles_sids[i]]))
                    tags = {}
                    for i in xrange(len(keys)):
                        tags[self.stringtable[keys[i]]] = self.stringtable[vals[i]]
                    yield (relation.id, tags, members)

class PBFHeader(object):
    def __init__(self, filename, blob_pos, blob_size):
        data = read_blob_data(filename, blob_pos, blob_size)
        self.header_block = OSMPBF.HeaderBlock()
        self.header_block.ParseFromString(data)

    def required_features(self):
        return set(self.header_block.required_features)

def read_blob_data(filename, blob_pos, blob_size):
    """
    Returns the (unzipped) blob data from filename and position.
    """
    with open(filename, 'rb') as f:
        f.seek(blob_pos)
        blob_data = f.read(blob_size)

    blob = OSMPBF.Blob()
    blob.ParseFromString(blob_data)
    raw_data = blob.raw
    if raw_data:
        return raw_data
    return zlib.decompress(blob.zlib_data)

import time

class PBFFile(object):
    """
    OSM PBF file reader.

    Parses the low-level file structure with header sizes,
    offsets and blob headers.

    :param filename: path to the PBF file
    """
    def __init__(self, filename):
        self.filename = filename
        self.file = open(filename, 'rb')
        self.next_blob_pos = self.prev_blob_pos = 0
        header_offsets = self._skip_header()
        self.header = PBFHeader(self.filename, header_offsets['blob_pos'], header_offsets['blob_size'])
        self.check_features()

    def check_features(self):
        missing_features = self.header.required_features().difference(SUPPORTED_FEATURES)
        if missing_features:
            raise NotImplementedError(
                '%s requires features not implemented by this parser: %s' %
                (self.filename, ', '.join(missing_features))
            )

    def _skip_header(self):
        return self.blob_offsets().next()

    def seek(self, pos):
        self.next_blob_pos = pos

    def rewind(self):
        self.next_blob_pos = self.prev_blob_pos

    def blob_offsets(self):
        """
        Returns an iterator of the blob offsets in this file.

        Each offsets is stored in a dictionary with:

        - `filename` the path of this PBF file.
        - `blob_pos` the byte offset
        - `blob_size` the size of this blob in bytes
        """
        while True:
            self.file.seek(self.next_blob_pos)

            blob_header_size = self._blob_header_size()
            if not blob_header_size: break

            blob_size = self._blob_size(self.file.read(blob_header_size))
            blob_pos = self.next_blob_pos + 4 + blob_header_size
            blob_header_pos=self.next_blob_pos,
            prev_blob_header_pos = self.prev_blob_pos
            self.prev_blob_pos = self.next_blob_pos
            self.next_blob_pos = blob_pos + blob_size
            yield dict(blob_pos=blob_pos, blob_size=blob_size,
                blob_header_pos=blob_header_pos,
                prev_blob_header_pos=prev_blob_header_pos,
                filename=self.filename)

    def primitive_block_parsers(self):
        """
        Returns an iterator of PrimitiveBlockParser.
        """
        for pos in self.blob_offsets():
            yield PrimitiveBlockParser(self.filename, pos['blob_pos'], pos['blob_size'])

    def _blob_size(self, data):
        blob_header = OSMPBF.BlobHeader()
        blob_header.ParseFromString(data)
        return blob_header.datasize

    def _blob_header_size(self):
        bytes = self.file.read(4)
        if bytes:
            return struct.unpack('!i', bytes)[0]
        return None

def read_pbf(filename):
    pbf = PBFFile(filename)
    for block in pbf.primitive_block_parsers():
        for node in block.nodes():
            pass
        for way in block.ways():
            pass
        for relation in block.relations():
            pass

if __name__ == '__main__':
    from timeit import Timer
    n = 1
    r = 1
    print "reading %s, number of calls: %d, repeat: %d" %(sys.argv[1],n,r)
    t = Timer("read_pbf(sys.argv[1])", "from __main__ import read_pbf")
    times = t.repeat(r,n)
    avrg_times = []
    for time in times:
        avrg_times.append(time/n)
    print "avrg time/call: %f" %(min(avrg_times))
<commit_msg>Allow falling back on the default granularity.<commit_after># Copyright 2011 Omniscale GmbH & Co. KG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import with_statement

import struct
import sys
import zlib

from marshal import dumps

from imposm.parser.pbf import OSMPBF

SUPPORTED_FEATURES = set(['OsmSchema-V0.6', 'DenseNodes'])

_MEMBERTYPE = {0 : 'node',
               1 : 'way',
               2 : 'relation'}

class PBFParser(object):
    """
    OSM PBF parser.

    :param xxx_callback:
        callback functions for coords, nodes, ways and relations.
        Each callback function gets called with a list of multiple elements.

    :param xxx_filter:
        functions that can manipulate the tag dictionary.
        Nodes and relations without tags will not passed to the callback.

    :param marshal:
        return the data as a marshaled string
    """
    def __init__(self, nodes_callback=None, ways_callback=None,
        relations_callback=None, coords_callback=None, nodes_tag_filter=None,
        ways_tag_filter=None, relations_tag_filter=None, marshal=False):
        self.nodes_callback = nodes_callback
        self.ways_callback = ways_callback
        self.relations_callback = relations_callback
        self.coords_callback = coords_callback
        self.nodes_tag_filter = nodes_tag_filter
        self.ways_tag_filter = ways_tag_filter
        self.relations_tag_filter = relations_tag_filter
        self.marshal = marshal

    def parse(self, filename, offset, size):
        """
        Parse primitive block from `filename`.

        :param filename: path to PBF file
        :param offset: byte offset of the primitive block to parse
        :param size: size in bytes of the primitive block to parse
        """
        reader = PrimitiveBlockParser(filename, offset, size)

        if self.nodes_callback or self.coords_callback:
            self.handle_nodes(reader)
        if self.ways_callback:
            self.handle_ways(reader)
        if self.relations_callback:
            self.handle_relations(reader)

    def handle_nodes(self, reader):
        nodes = []
        coords = []
        nodes_callback = self.nodes_callback
        coords_callback = self.coords_callback
        for node in reader.nodes():
            if nodes_callback:
                if self.nodes_tag_filter:
                    self.nodes_tag_filter(node[1])
                if node[1]:
                    if self.marshal:
                        nodes.append((node[0], dumps((node[1], node[2]), 2)))
                    else:
                        nodes.append((node[0], node[1], node[2]))
                if len(nodes) >= 256:
                    nodes_callback(nodes)
                    nodes = []
            if coords_callback:
                coords.append((node[0], node[2][0], node[2][1]))
                if len(coords) >= 512:
                    coords_callback(coords)
                    coords = []
        if nodes_callback:
            nodes_callback(nodes)
        if coords_callback:
            coords_callback(coords)

    def handle_ways(self, reader):
        ways = []
        for way in reader.ways():
            if self.ways_tag_filter:
                self.ways_tag_filter(way[1])
            # always return ways, might be needed for relations
            if self.marshal:
                ways.append((way[0], dumps((way[1], way[2]), 2)))
            else:
                ways.append((way[0], way[1], way[2]))
            if len(ways) >= 256:
                self.ways_callback(ways)
                ways = []
        self.ways_callback(ways)

    def handle_relations(self, reader):
        relations = []
        for relation in reader.relations():
            if self.relations_tag_filter:
                self.relations_tag_filter(relation[1])
                if not relation[1]:
                    continue
            if self.marshal:
                relations.append((relation[0], dumps((relation[1], relation[2]), 2)))
            else:
                relations.append((relation[0], relation[1], relation[2]))
            if len(relations) >= 256:
                self.relations_callback(relations)
                relations = []
        self.relations_callback(relations)

def decoded_stringtable(stringtable):
    result = []
    for s in stringtable:
        result.append(s.decode('utf-8'))
    return result

class PrimitiveBlockParser(object):
    """
    Low level PBF primitive block parser.

    Parses a single primitive block and handles OSM PBF internals like
    dense nodes, delta encoding, stringtables, etc.

    :param filename: path to PBF file
    :param offset: byte offset of the primitive block to parse
    :param size: size in bytes of the primitive block to parse

    """
    def __init__(self, filename, blob_pos, blob_size):
        self.pos = filename, blob_pos, blob_size
        data = read_blob_data(filename, blob_pos, blob_size)
        self.primitive_block = OSMPBF.PrimitiveBlock()
        self.primitive_block.ParseFromString(data)
        self.primitivegroup = self.primitive_block.primitivegroup
        self.stringtable = decoded_stringtable(self.primitive_block.stringtable.s)

    def __repr__(self):
        return '<PrimitiveBlockParser %r>' % (self.pos, )

    def _get_tags(self, element, pos):
        tags = {}
        key = None
        value = None
        keyflag = False
        if pos >= len(element):
            return {}, pos
        while True:
            key_val = element[pos]
            pos += 1
            if key_val == 0:
                break
            if not keyflag:
                key = key_val
                keyflag = True
            else:
                value = key_val
                tags[self.stringtable[key]] = self.stringtable[value]
                keyflag = False
        return tags, pos

    def nodes(self):
        """
        Return an iterator for all *nodes* in this primitive block.

        :rtype: iterator of ``(osm_id, tags, (lon, lat))`` tuples
        """
        for group in self.primitivegroup:
            dense = group.dense
            if dense:
                granularity = self.primitive_block.granularity or 100
                lat_offset = self.primitive_block.lat_offset or 0
                lon_offset = self.primitive_block.lon_offset or 0
                coord_scale = 0.000000001
                get_tags = self._get_tags
                ids = dense.id
                lats = dense.lat
                lons = dense.lon
                keys_vals = dense.keys_vals
                last_id = last_lat = last_lon = last_keysvals_pos = 0
                for i in xrange(len(ids)):
                    last_id += ids[i]
                    last_lat += coord_scale * (lat_offset + (granularity * lats[i]))
                    last_lon += coord_scale * (lon_offset + (granularity * lons[i]))
                    tags, last_keysvals_pos = get_tags(keys_vals, last_keysvals_pos)
                    yield (last_id, tags, (last_lon, last_lat))
            nodes = group.nodes
            if nodes:
                for node in nodes:
                    keys, vals = node.keys, node.vals
                    tags = []
                    for i in xrange(len(keys)):
                        tags.append((self.stringtable[keys[i]], self.stringtable[vals[i]]))
                    yield (node.id, tags, (node.lon, node.lat))

    def ways(self):
        """
        Return an iterator for all *ways* in this primitive block.

        :rtype: iterator of ``(osm_id, tags, [ref1, ref2, ...])`` tuples
        """
        for group in self.primitivegroup:
            ways = group.ways
            if ways:
                for way in ways:
                    keys = way.keys
                    vals = way.vals
                    delta_refs = way.refs

                    tags = {}
                    for i in xrange(len(keys)):
                        tags[self.stringtable[keys[i]]] = self.stringtable[vals[i]]
                    refs = []
                    ref = 0
                    for delta in delta_refs:
                        ref += delta
                        refs.append(ref)
                    yield (way.id, tags, refs)

    def relations(self):
        """
        Return an iterator for all *relations* in this primitive block.

        :rtype: iterator of ``(osm_id, tags, [(ref1, type, role), ...])`` tuples

        """
        for group in self.primitivegroup:
            relations = group.relations
            if relations:
                for relation in relations:
                    members = []
                    memids = relation.memids
                    rel_types = relation.types
                    roles_sids = relation.roles_sid
                    keys = relation.keys
                    vals = relation.vals
                    memid = 0
                    for i in xrange(len(rel_types)):
                        memid += memids[i]
                        members.append((memid, _MEMBERTYPE[rel_types[i]], self.stringtable[roles_sids[i]]))
                    tags = {}
                    for i in xrange(len(keys)):
                        tags[self.stringtable[keys[i]]] = self.stringtable[vals[i]]
                    yield (relation.id, tags, members)

class PBFHeader(object):
    def __init__(self, filename, blob_pos, blob_size):
        data = read_blob_data(filename, blob_pos, blob_size)
        self.header_block = OSMPBF.HeaderBlock()
        self.header_block.ParseFromString(data)

    def required_features(self):
        return set(self.header_block.required_features)

def read_blob_data(filename, blob_pos, blob_size):
    """
    Returns the (unzipped) blob data from filename and position.
    """
    with open(filename, 'rb') as f:
        f.seek(blob_pos)
        blob_data = f.read(blob_size)

    blob = OSMPBF.Blob()
    blob.ParseFromString(blob_data)
    raw_data = blob.raw
    if raw_data:
        return raw_data
    return zlib.decompress(blob.zlib_data)

import time

class PBFFile(object):
    """
    OSM PBF file reader.

    Parses the low-level file structure with header sizes,
    offsets and blob headers.

    :param filename: path to the PBF file
    """
    def __init__(self, filename):
        self.filename = filename
        self.file = open(filename, 'rb')
        self.next_blob_pos = self.prev_blob_pos = 0
        header_offsets = self._skip_header()
        self.header = PBFHeader(self.filename, header_offsets['blob_pos'], header_offsets['blob_size'])
        self.check_features()

    def check_features(self):
        missing_features = self.header.required_features().difference(SUPPORTED_FEATURES)
        if missing_features:
            raise NotImplementedError(
                '%s requires features not implemented by this parser: %s' %
                (self.filename, ', '.join(missing_features))
            )

    def _skip_header(self):
        return self.blob_offsets().next()

    def seek(self, pos):
        self.next_blob_pos = pos

    def rewind(self):
        self.next_blob_pos = self.prev_blob_pos

    def blob_offsets(self):
        """
        Returns an iterator of the blob offsets in this file.

        Each offsets is stored in a dictionary with:

        - `filename` the path of this PBF file.
        - `blob_pos` the byte offset
        - `blob_size` the size of this blob in bytes
        """
        while True:
            self.file.seek(self.next_blob_pos)

            blob_header_size = self._blob_header_size()
            if not blob_header_size: break

            blob_size = self._blob_size(self.file.read(blob_header_size))
            blob_pos = self.next_blob_pos + 4 + blob_header_size
            blob_header_pos=self.next_blob_pos,
            prev_blob_header_pos = self.prev_blob_pos
            self.prev_blob_pos = self.next_blob_pos
            self.next_blob_pos = blob_pos + blob_size
            yield dict(blob_pos=blob_pos, blob_size=blob_size,
                blob_header_pos=blob_header_pos,
                prev_blob_header_pos=prev_blob_header_pos,
                filename=self.filename)

    def primitive_block_parsers(self):
        """
        Returns an iterator of PrimitiveBlockParser.
        """
        for pos in self.blob_offsets():
            yield PrimitiveBlockParser(self.filename, pos['blob_pos'], pos['blob_size'])

    def _blob_size(self, data):
        blob_header = OSMPBF.BlobHeader()
        blob_header.ParseFromString(data)
        return blob_header.datasize

    def _blob_header_size(self):
        bytes = self.file.read(4)
        if bytes:
            return struct.unpack('!i', bytes)[0]
        return None

def read_pbf(filename):
    pbf = PBFFile(filename)
    for block in pbf.primitive_block_parsers():
        for node in block.nodes():
            pass
        for way in block.ways():
            pass
        for relation in block.relations():
            pass

if __name__ == '__main__':
    from timeit import Timer
    n = 1
    r = 1
    print "reading %s, number of calls: %d, repeat: %d" %(sys.argv[1],n,r)
    t = Timer("read_pbf(sys.argv[1])", "from __main__ import read_pbf")
    times = t.repeat(r,n)
    avrg_times = []
    for time in times:
        avrg_times.append(time/n)
    print "avrg time/call: %f" %(min(avrg_times))
<|endoftext|>


<commit_before>import argparse
import asyncio
import logging
from inspect import iscoroutine

from lightbus.commands.utilities import BusImportMixin, LogLevelMixin
from lightbus.exceptions import NoBusFoundInBusModule
from lightbus.plugins import plugin_hook
from lightbus.utilities.async_tools import block

logger = logging.getLogger(__name__)

class Command(LogLevelMixin, BusImportMixin, object):

    def setup(self, parser, subparsers):
        parser_run = subparsers.add_parser(
            "run", help="Run Lightbus", formatter_class=argparse.ArgumentDefaultsHelpFormatter
        )
        self.setup_import_parameter(parser_run)

        parser_run_action_group = parser_run.add_mutually_exclusive_group()
        parser_run_action_group.add_argument(
            "--events-only",
            "-E",
            help="Only listen for and handle events, do not respond to RPC calls",
            action="store_true",
        )
        parser_run_action_group.add_argument(
            "--schema",
            "-m",
            help=(
                "Manually load the schema from the given file or directory. "
                "This will normally be provided by the schema transport, "
                "but manual loading may be useful during development or testing."
            ),
            metavar="FILE_OR_DIRECTORY",
        )
        parser_run.set_defaults(func=self.handle)

    def handle(self, args, config):
        try:
            self._handle(args, config)
        except Exception as e:
            block(plugin_hook("exception", e=e), timeout=5)
            raise

    def _handle(self, args, config):
        self.setup_logging(override=getattr(args, "log_level", None), config=config)

        bus_module, bus = self.import_bus(args)

        # TODO: Move to lightbus.create()?
        if args.schema:
            if args.schema == "-":
                # if '-' read from stdin
                source = None
            else:
                source = args.schema
            bus.schema.load_local(source)

        before_server_start = getattr(bus_module, "before_server_start", None)
        if before_server_start:
            logger.debug("Calling {}.before_server_start() callback".format(bus_module.__name__))
            co = before_server_start(bus)
            if iscoroutine(co):
                block(co, timeout=10)

        block(plugin_hook("receive_args", args=args), timeout=5)

        if args.events_only:
            bus.run_forever(consume_rpcs=False)
        else:
            bus.run_forever()
<commit_msg>Fixing run command for run_forever() being removed from BusPath<commit_after>import argparse
import asyncio
import logging
from inspect import iscoroutine

from lightbus.commands.utilities import BusImportMixin, LogLevelMixin
from lightbus.exceptions import NoBusFoundInBusModule
from lightbus.plugins import plugin_hook
from lightbus.utilities.async_tools import block

logger = logging.getLogger(__name__)

class Command(LogLevelMixin, BusImportMixin, object):

    def setup(self, parser, subparsers):
        parser_run = subparsers.add_parser(
            "run", help="Run Lightbus", formatter_class=argparse.ArgumentDefaultsHelpFormatter
        )
        self.setup_import_parameter(parser_run)

        parser_run_action_group = parser_run.add_mutually_exclusive_group()
        parser_run_action_group.add_argument(
            "--events-only",
            "-E",
            help="Only listen for and handle events, do not respond to RPC calls",
            action="store_true",
        )
        parser_run_action_group.add_argument(
            "--schema",
            "-m",
            help=(
                "Manually load the schema from the given file or directory. "
                "This will normally be provided by the schema transport, "
                "but manual loading may be useful during development or testing."
            ),
            metavar="FILE_OR_DIRECTORY",
        )
        parser_run.set_defaults(func=self.handle)

    def handle(self, args, config):
        try:
            self._handle(args, config)
        except Exception as e:
            block(plugin_hook("exception", e=e), timeout=5)
            raise

    def _handle(self, args, config):
        self.setup_logging(override=getattr(args, "log_level", None), config=config)

        bus_module, bus = self.import_bus(args)

        # TODO: Move to lightbus.create()?
        if args.schema:
            if args.schema == "-":
                # if '-' read from stdin
                source = None
            else:
                source = args.schema
            bus.schema.load_local(source)

        before_server_start = getattr(bus_module, "before_server_start", None)
        if before_server_start:
            logger.debug("Calling {}.before_server_start() callback".format(bus_module.__name__))
            co = before_server_start(bus)
            if iscoroutine(co):
                block(co, timeout=10)

        block(plugin_hook("receive_args", args=args), timeout=5)

        if args.events_only:
            bus.client.run_forever(consume_rpcs=False)
        else:
            bus.client.run_forever()
<|endoftext|>


<commit_before>#!/usr/bin/env python3
"""
Dump SLURM script that executes the selected experiment with all projects.

This basically provides the same as pprof run, except that it just
dumps a slurm batch script that executes everything as an array job
on a configurable SLURM cluster.
"""
import os
from plumbum import cli
from pprof.settings import CFG
from pprof.experiments import *  # pylint: disable=W0401,W0614
from pprof.projects import *  # pylint: disable=W0401,W0614
from pprof import experiment, project
from pprof.utils import slurm

class Slurm(cli.Application):
    """ Generate a SLURM script. """

    def __init__(self, executable):
        super(Slurm, self).__init__(executable)
        self._experiment = None
        self._project_names = None
        self._group_name = None
        self._description = None

    @cli.switch(["-E", "--experiment"],
                str,
                mandatory=True,
                help="Specify experiments to run")
    def experiment(self, cfg_experiment):
        """Specify experiments to run"""
        self._experiment = cfg_experiment

    @cli.switch(["-P", "--project"],
                str,
                list=True,
                requires=["--experiment"],
                help="Specify projects to run")
    def projects(self, projects):
        """Specify projects to run"""
        self._project_names = projects

    @cli.switch(["-D", "--description"],
                str,
                help="A description for this experiment run")
    def experiment_tag(self, description):
        """A description for this experiment run"""
        self._description = description

    @cli.switch(["-G", "--group"],
                str,
                list=True,
                requires=["--experiment"],
                help="Run a group of projects under the given experiments")
    def group(self, groups):
        """Run a group of projects under the given experiments"""
        self._group_names = groups

    def __go__(self, project_names, group_name, exp_name):
        prj_registry = project.ProjectRegistry
        projects = prj_registry.projects
        project_names = self._project_names
        group_names = self._group_names
        if project_names is not None:
            allkeys = set(list(projects.keys()))
            usrkeys = set(project_names)
            projects = {x: projects[x] for x in allkeys & usrkeys}

        if group_names is not None:
            groupkeys = set(group_names)
            projects = {
                name: cls
                for name, cls in projects.items() if cls.GROUP in groupkeys
            }

        projects = {x: projects[x]
                    for x in projects
                    if projects[x].DOMAIN != "debug"}

        prj_keys = sorted(projects.keys())
        print("{} Projects".format(len(prj_keys)))

        slurm.prepare_slurm_script(exp_name, prj_keys)

    def main(self):
        """Main entry point of pprof run."""
        exp_registry = experiment.ExperimentRegistry
        project_names = self._project_names
        group_name = self._group_name
        exp_name = self._experiment

        if self._description:
            CFG["experiment_description"] = self._description

        CFG["slurm"]["logs"] = os.path.abspath(os.path.join(CFG[
            'build_dir'].value(), CFG['slurm']['logs'].value()))
        CFG["build_dir"] = CFG["slurm"]["node_dir"].value()

        print("Experiment: " + exp_name)
        if exp_name in exp_registry.experiments:
            self.__go__(project_names, group_name, exp_name)
        else:
            from logging import error
            error("Could not find {} in the experiment registry.", exp_name)
<commit_msg>slurm: initialize group_names to default None<commit_after>#!/usr/bin/env python3
"""
Dump SLURM script that executes the selected experiment with all projects.

This basically provides the same as pprof run, except that it just
dumps a slurm batch script that executes everything as an array job
on a configurable SLURM cluster.
"""
import os
from plumbum import cli
from pprof.settings import CFG
from pprof.experiments import *  # pylint: disable=W0401,W0614
from pprof.projects import *  # pylint: disable=W0401,W0614
from pprof import experiment, project
from pprof.utils import slurm

class Slurm(cli.Application):
    """ Generate a SLURM script. """

    def __init__(self, executable):
        super(Slurm, self).__init__(executable)
        self._experiment = None
        self._project_names = None
        self._group_names = None
        self._description = None

    @cli.switch(["-E", "--experiment"],
                str,
                mandatory=True,
                help="Specify experiments to run")
    def experiment(self, cfg_experiment):
        """Specify experiments to run"""
        self._experiment = cfg_experiment

    @cli.switch(["-P", "--project"],
                str,
                list=True,
                requires=["--experiment"],
                help="Specify projects to run")
    def projects(self, projects):
        """Specify projects to run"""
        self._project_names = projects

    @cli.switch(["-D", "--description"],
                str,
                help="A description for this experiment run")
    def experiment_tag(self, description):
        """A description for this experiment run"""
        self._description = description

    @cli.switch(["-G", "--group"],
                str,
                list=True,
                requires=["--experiment"],
                help="Run a group of projects under the given experiments")
    def group(self, groups):
        """Run a group of projects under the given experiments"""
        self._group_names = groups

    def __go__(self, project_names, exp_name):
        prj_registry = project.ProjectRegistry
        projects = prj_registry.projects
        project_names = self._project_names
        if project_names is not None:
            allkeys = set(list(projects.keys()))
            usrkeys = set(project_names)
            projects = {x: projects[x] for x in allkeys & usrkeys}

        group_names = self._group_names
        if group_names is not None:
            groupkeys = set(group_names)
            projects = {
                name: cls
                for name, cls in projects.items() if cls.GROUP in groupkeys
            }

        projects = {x: projects[x]
                    for x in projects
                    if projects[x].DOMAIN != "debug"}

        prj_keys = sorted(projects.keys())
        print("{} Projects".format(len(prj_keys)))

        slurm.prepare_slurm_script(exp_name, prj_keys)

    def main(self):
        """Main entry point of pprof run."""
        exp_registry = experiment.ExperimentRegistry
        project_names = self._project_names
        exp_name = self._experiment

        if self._description:
            CFG["experiment_description"] = self._description

        CFG["slurm"]["logs"] = os.path.abspath(os.path.join(CFG[
            'build_dir'].value(), CFG['slurm']['logs'].value()))
        CFG["build_dir"] = CFG["slurm"]["node_dir"].value()

        print("Experiment: " + exp_name)
        if exp_name in exp_registry.experiments:
            self.__go__(project_names, exp_name)
        else:
            from logging import error
            error("Could not find {} in the experiment registry.", exp_name)
<|endoftext|>


<commit_before>from collections import defaultdict
from urlparse import urlparse

import textwrap
import oauth2

from launch_params import LaunchParamsMixin
from request_validator import RequestValidatorMixin
from utils import InvalidLTIConfigError

class ToolConsumer(LaunchParamsMixin, RequestValidatorMixin, object):
    def __init__(self, consumer_key, consumer_secret, params =
            defaultdict(lambda: None)):
        '''
        Create new ToolConsumer.
        '''
        super(ToolConsumer, self).__init__()

        # Prevent key errors
        params_dict = defaultdict(lambda: None)
        params_dict.update(params)

        self.consumer_key = consumer_key
        self.consumer_secret = consumer_secret
        self.non_spec_params = defaultdict(lambda: None)

        self.launch_url = params_dict['launch_url']
        self.process_params(params_dict)

    def set_config(self, config):
        '''
        Set launch data from a ToolConfig.
        '''
        # TODO: Implement ToolConfig
        pass

    def has_required_params(self):
        '''
        Check if required parameters for a tool launch are set.
        '''
        return self.consumer_key and\
                self.consumer_secret and\
                self.launch_params['resource_link_id'] and\
                self.launch_url

    def generate_launch_data(self):
        # Validate params
        if not self.has_required_params():
            raise InvalidLTIConfigError(textwrap.dedent('ToolConsumer does not have all required attributes'))

        params = self.to_params()
        params['lti_version'] = 'LTI-1.0'
        params['lti_message_type'] = 'basic-lti-launch-request'

        # Parse URL to get host
        uri = urlparse(self.launch_url)

        if uri.port == None:
            host = uri.host
        else:
            host = uri.host + ':' + uri.port

        # Get new OAuth consumer
        consumer = oauth2.Consumer(key = self.options['consumer_key'],\
                secret = self.options['consumer_secret'])

        path = uri.path
        path = '/' if path == '' else path

        options = {
                'oauth_nonce': self.nonce,
                'oauth_timestamp': self.timestamp,
                'oauth_scheme': 'body'
                }
        request = oauth2.Request(method = 'POST',
                url = self.launch_url,
                parameters = options)

        signature_method = oauth2.SignatureMethod_HMAC_SHA1()
        request.sign_request(signature_method, consumer, None)

        # Request was made by an HTML form in the user's browser. Revert the
        # escapage and return the hash of post parameters ready for embedding
        # in an html view.
        dic = {}
        for param in request.body.split('&'):
            for key, val in param.split('='):
                dic[key] = val
        return dic
<commit_msg>Adds ToolConfig support to ToolConsumer<commit_after>from collections import defaultdict
from urlparse import urlparse

import textwrap
import oauth2

from launch_params import LaunchParamsMixin
from request_validator import RequestValidatorMixin
from utils import InvalidLTIConfigError

class ToolConsumer(LaunchParamsMixin, RequestValidatorMixin, object):
    def __init__(self, consumer_key, consumer_secret, params =
            defaultdict(lambda: None)):
        '''
        Create new ToolConsumer.
        '''
        super(ToolConsumer, self).__init__()

        # Prevent key errors
        params_dict = defaultdict(lambda: None)
        params_dict.update(params)

        self.consumer_key = consumer_key
        self.consumer_secret = consumer_secret
        self.non_spec_params = defaultdict(lambda: None)

        self.launch_url = params_dict['launch_url']
        self.process_params(params_dict)

    def set_config(self, config):
        '''
        Set launch data from a ToolConfig.
        '''
        if self.launch_url == None:
            self.launch_url = config.launch_url
            self.custom_params.update(config.customparams)

    def has_required_params(self):
        '''
        Check if required parameters for a tool launch are set.
        '''
        return self.consumer_key and\
                self.consumer_secret and\
                self.launch_params['resource_link_id'] and\
                self.launch_url

    def generate_launch_data(self):
        # Validate params
        if not self.has_required_params():
            raise InvalidLTIConfigError(textwrap.dedent('ToolConsumer does not have all required attributes'))

        params = self.to_params()
        params['lti_version'] = 'LTI-1.0'
        params['lti_message_type'] = 'basic-lti-launch-request'

        # Parse URL to get host
        uri = urlparse(self.launch_url)

        if uri.port == None:
            host = uri.host
        else:
            host = uri.host + ':' + uri.port

        # Get new OAuth consumer
        consumer = oauth2.Consumer(key = self.options['consumer_key'],\
                secret = self.options['consumer_secret'])

        path = uri.path
        path = '/' if path == '' else path

        options = {
                'oauth_nonce': self.nonce,
                'oauth_timestamp': self.timestamp,
                'oauth_scheme': 'body'
                }
        request = oauth2.Request(method = 'POST',
                url = self.launch_url,
                parameters = options)

        signature_method = oauth2.SignatureMethod_HMAC_SHA1()
        request.sign_request(signature_method, consumer, None)

        # Request was made by an HTML form in the user's browser. Revert the
        # escapage and return the hash of post parameters ready for embedding
        # in an html view.
        dic = {}
        for param in request.body.split('&'):
            for key, val in param.split('='):
                dic[key] = val
        return dic
<|endoftext|>


<commit_before>#encoding=utf-8
from . import statements
from . import grouping
from . import expressions
import json

class Select:
    """SELECT Query Builder"""
    def __init__(self, *fields):
        """
        Inicializa la consulta SELECT opcionalmente los argumentos pasados serna considerados campos para la proyección.
        """
        super(self.__class__, self).__init__()
        self.raw_fields = fields
        self.raw_fields = []
        self.raw_fields_group = []
        self.fields = []
        self.group_fields = []
        self.raw_tables = []
        self.raw_order_by = []
        self.order_by_fields = []
        self.tables = []
        self.where_criteria = grouping.BaseGrouper()

    def __prepareData__(self):
        """
        Helper para preparar los datos para la produccion del SQL final.
        """
        if isinstance(self.raw_fields, str):
            self.raw_fields = self.raw_fields.split(',')

        for x in self.raw_fields:
            self.fields.append(expressions.AliasExpression(x).result())

        if len(self.fields) == 0:
            self.fields.append('*')

        for x in self.raw_tables:
            self.tables.append(expressions.AliasExpression(x).result())

        if isinstance(self.raw_fields_group, str):
            self.raw_fields_group = self.raw_fields_group.split(',')

        for x in self.raw_fields_group:
            self.group_fields.append(expressions.AliasExpression(x).result())

        if isinstance(self.raw_order_by, str):
            self.raw_order_by = self.raw_order_by.split(',')

        for x in self.raw_order_by:
            self.order_by_fields.append(expressions.OrderByExpression(*x).result())

    def from_(self, table, alias=None):
        """
        Establece el origen de datos (y un alias opcionalmente).
        """
        if isinstance(table, str):
            table = [[table, alias]]
        self.raw_tables = table
        return self

    def where(self, field, value = None, operator = None):
        """
        Establece condiciones para la consulta unidas por AND
        """
        if field is None:
            return self
        conjunction = None
        if value is None and isinstance(field, dict):
            for f,vo in field.items():
                if self.where_criteria.size() > 0:
                    conjunction = 'AND'
                try:
                	operator, v = vo
                except Exception as e:
                	v = vo
                	operator = None
                self.where_criteria.append(expressions.ConditionExpression(f, v, operator=operator, conjunction=conjunction))

        else:
            if self.where_criteria.size() > 0:
                    conjunction = 'AND'
            self.where_criteria.append(expressions.ConditionExpression(field, value, operator=operator, conjunction=conjunction))
        return self

    def where_or(self, field, value = None, operator = None):
        """
        Establece condiciones para la consulta unidas por OR
        """
        if field is None:
            return self
        conjunction = None
        if value is None and isinstance(field, dict):
            for f,v in field.items():
                if self.where_criteria.size() > 0:
                    conjunction = 'OR'

                self.where_criteria.append(expressions.ConditionExpression(f, v, operator=operator, conjunction=conjunction))

        else:
            if self.where_criteria.size() > 0:
                    conjunction = 'OR'
            self.where_criteria.append(expressions.ConditionExpression(field, value, operator=operator, conjunction=conjunction))
        return self

    def group_by(self, *args):
        """
        Indica los campos para agrupación
        """
        if len(args) == 1:
            self.raw_fields_group = args[0].split(',')
        else:
            self.raw_fields_group = list(args)
        return self

    def order_by(self, field, orientation='ASC'):
        """
        Indica los campos y el criterio de ordenamiento
        """
        if isinstance(field, list):
            self.raw_order_by.append(field)
        else:
            self.raw_order_by.append([field, orientation])

        return self

    def result(self, *args, **kwargs):
        """
        Construye la consulta SQL
        """
        prettify = kwargs.get('pretty', False)
        self.__prepareData__()
        sql = 'SELECT '
        sql +=  ', '.join(self.fields)

        if len(self.tables) > 0:
            if prettify:
                sql += '\n'
            else:
                sql += ' '
            sql +=  'FROM '
            sql +=  ', '.join(self.tables)
        if self.where_criteria.size() > 0:
            if prettify:
                sql += '\n'
            else:
                sql += ' '
            sql +=  'WHERE '
            sql +=  self.where_criteria.result()
        if len(self.group_fields) > 0:
            if prettify:
                sql += '\n'
            else:
                sql += ' '
            sql +=  'GROUP BY '
            sql +=  ', '.join(self.group_fields)
        if len(self.order_by_fields) > 0:
            if prettify:
                sql += '\n'
            else:
                sql += ' '
            sql +=  'ORDER BY '
            sql +=  ', '.join(self.order_by_fields)
            if prettify:
                sql += '\n'
            else:
                sql += ' '

        return sql

class Delete(object):
    """
    DELETE Query Builder
    """

    def __init__(self, type):
        """
        Inicializa la consulta, type = recurso (Vertex|Edge)
        """
        super(Delete, self).__init__()
        self._class = None
        self._cluster = None
        self._type = None
        self.data = {}
        self.where_criteria = grouping.BaseGrouper()
        self._type = type

    def class_(self, _class):
        """
        Especifica la clase para eliminar
        """
        self._class = _class
        return self

    def where(self, field, value = None, operator = None):
        """
        Establece condiciones para la consulta unidas por AND
        """
        if field is None:
            return self
        conjunction = None
        if value is None and isinstance(field, dict):
            for f,v in field.items():
                if self.where_criteria.size() > 0:
                    conjunction = 'AND'
                self.where_criteria.append(expressions.ConditionExpression(f, v, operator=operator, conjunction=conjunction))

        else:
            if self.where_criteria.size() > 0:
                    conjunction = 'AND'
            self.where_criteria.append(expressions.ConditionExpression(field, value, operator=operator, conjunction=conjunction))
        return self

    def where_or(self, field, value = None, operator = None):
        """
        Establece condiciones para la consulta unidas por OR
        """
        if field is None:
            return self
        conjunction = None
        if value is None and isinstance(field, dict):
            for f,v in field.items():
                if self.where_criteria.size() > 0:
                    conjunction = 'OR'
                self.where_criteria.append(expressions.ConditionExpression(f, v, operator=operator, conjunction=conjunction))

        else:
            if self.where_criteria.size() > 0:
                    conjunction = 'OR'
            self.where_criteria.append(expressions.ConditionExpression(field, value, operator=operator, conjunction=conjunction))
        return self

    def result(self, *args, **kwargs):
        """
        Construye la consulta SQL
        """
        prettify = kwargs.get('pretty', False)

        sql = 'DELETE %s %s' % (self._type, self._class)

        if prettify:
            sql += '\n'
        else:
            sql += ' '

        if self.where_criteria.size() > 0:
            sql +=  'WHERE '
            sql +=  self.where_criteria.result()
            if prettify:
                sql += '\n'
            else:
                sql += ' '

        return sql

class Create(object):
    "CREATE query builder"

    def __init__(self, type):
        """
        Inicializa la consulta, type = recurso (Vertex|Edge)
        """
        super(Create, self).__init__()
        self._class = None
        self._cluster = None
        self._type = None
        self._from = None
        self._to = None
        self.data = {}
        self._type = type

    def class_(self, _class):
        """
        Especifica la clase para crear
        """
        self._class = _class
        return self

    def cluster(self, cluster):
        """
        Especifica el cluster donde se almacenara el nuevo recurso
        """
        self._cluster = cluster
        return self

    def from_(self, From):
        """
        [Edge-only] especifica el origen del lado
        """
        if self._type.lower() != 'edge':
            raise ValueError('Cannot set From/To to non-edge objects')
        self._from = From
        return self

    def to(self, to):
        """
        [Edge-only] especifica el destino del lado
        """
        if self._type.lower() != 'edge':
            raise ValueError('Cannot set From/To to non-edge objects')
        self._to = to
        return self

    def set(self, field, value = None):
        """
        [Edge|Vertex] establece datos del recurso
        """
        if value is None and isinstance(field, dict):
            self.content(field)
        if field and value:
            self.data[field] = value
        return self

    def content(self, obj):
        """
        [Edge|Vertex] establece datos del recurso
        """
        self.data.update(obj)
        return self

    def result(self, *args, **kwargs):
        """
        Construye la consulta SQL
        """
        prettify = kwargs.get('pretty', False)

        sql = 'CREATE %s %s' % (self._type, self._class)

        if prettify:
            sql += '\n'
        else:
            sql += ' '

        if self._type.lower() == 'edge':
            sql += " FROM %s TO %s " % (self._from, self._to)

        if self._cluster:
            sql += 'CLUSTER %s' % self._cluster
            if prettify:
                sql += '\n'
            else:
                sql += ' '

        if self.data:
            sql += 'CONTENT ' + json.dumps(self.data)
        print (sql)
        return sql

class Update(object):
    """
    UPDATE Query Builder
    """
    def __init__(self, _class):
        """
        Inicializa la clase, _class = origen donde actualizar
        """
        super(Update, self).__init__()
        self._class = None
        self._cluster = None
        self.data = {}
        self.where_criteria = grouping.BaseGrouper()
        self._class = _class

    def set(self, field, value = None):
        """
        [Edge|Vertex] establece datos del recurso
        """
        if value is None and isinstance(field, dict):
            self.content(field)
        if field and value:
            self.data[field] = value
        return self

    def content(self, obj):
        """
        [Edge|Vertex] establece datos del recurso
        """
        self.data.update(obj)
        return self

    def where(self, field, value = None, operator = None):
        """
        Establece condiciones para la consulta unidas por AND
        """
        if field is None:
            return self
        conjunction = None
        if value is None and isinstance(field, dict):
            for f,v in field.items():
                if self.where_criteria.size() > 0:
                    conjunction = 'AND'
                self.where_criteria.append(expressions.ConditionExpression(f, v, operator=operator, conjunction=conjunction))

        else:
            if self.where_criteria.size() > 0:
                    conjunction = 'AND'
            self.where_criteria.append(expressions.ConditionExpression(field, value, operator=operator, conjunction=conjunction))
        return self

    def where_or(self, field, value = None, operator = None):
        """
        Establece condiciones para la consulta unidas por OR
        """
        if field is None:
            return self
        conjunction = None
        if value is None and isinstance(field, dict):
            for f,v in field.items():
                if self.where_criteria.size() > 0:
                    conjunction = 'OR'
                self.where_criteria.append(expressions.ConditionExpression(f, v, operator=operator, conjunction=conjunction))

        else:
            if self.where_criteria.size() > 0:
                    conjunction = 'OR'
            self.where_criteria.append(expressions.ConditionExpression(field, value, operator=operator, conjunction=conjunction))
        return self

    def result(self, *args, **kwargs):
        """
        Construye la consulta SQL
        """
        prettify = kwargs.get('pretty', False)

        sql = 'UPDATE %s' % self._class

        if prettify:
            sql += '\n'
        else:
            sql += ' '

        if self.data:
            sql += 'MERGE ' + json.dumps(self.data)
            if prettify:
                sql += '\n'
            else:
                sql += ' '

        if self.where_criteria.size() > 0:
            sql +=  'WHERE '
            sql +=  self.where_criteria.result()
            if prettify:
                sql += '\n'
            else:
                sql += ' '

        print (sql)
        return sql
<commit_msg>cambios<commit_after>#encoding=utf-8
from . import statements
from . import grouping
from . import expressions
import json

class Select:
    """SELECT Query Builder"""
    def __init__(self, *fields):
        """
        Inicializa la consulta SELECT opcionalmente los argumentos pasados serna considerados campos para la proyección.
        """
        super(self.__class__, self).__init__()
        self.raw_fields = fields
        self.raw_fields = []
        self.raw_fields_group = []
        self.fields = []
        self.group_fields = []
        self.raw_tables = []
        self.raw_order_by = []
        self.order_by_fields = []
        self.tables = []
        self.where_criteria = grouping.BaseGrouper()

    def __prepareData__(self):
        """
        Helper para preparar los datos para la produccion del SQL final.
        """
        if isinstance(self.raw_fields, str):
            self.raw_fields = self.raw_fields.split(',')

        for x in self.raw_fields:
            self.fields.append(expressions.AliasExpression(x).result())

        if len(self.fields) == 0:
            self.fields.append('*')

        for x in self.raw_tables:
            self.tables.append(expressions.AliasExpression(x).result())

        if isinstance(self.raw_fields_group, str):
            self.raw_fields_group = self.raw_fields_group.split(',')

        for x in self.raw_fields_group:
            self.group_fields.append(expressions.AliasExpression(x).result())

        if isinstance(self.raw_order_by, str):
            self.raw_order_by = self.raw_order_by.split(',')

        for x in self.raw_order_by:
            self.order_by_fields.append(expressions.OrderByExpression(*x).result())

    def from_(self, table, alias=None):
        """
        Establece el origen de datos (y un alias opcionalmente).
        """
        if isinstance(table, str):
            table = [[table, alias]]
        self.raw_tables = table
        return self

    def where(self, field, value = None, operator = None):
        """
        Establece condiciones para la consulta unidas por AND
        """
        if field is None:
            return self
        conjunction = None
        if value is None and isinstance(field, dict):
            for f,vo in field.items():
                if self.where_criteria.size() > 0:
                    conjunction = 'AND'
                try:
                    operator, v = vo
                except Exception as e:
                    v = vo
                    operator = None
                self.where_criteria.append(expressions.ConditionExpression(f, v, operator=operator, conjunction=conjunction))

        else:
            if self.where_criteria.size() > 0:
                    conjunction = 'AND'
            self.where_criteria.append(expressions.ConditionExpression(field, value, operator=operator, conjunction=conjunction))
        return self

    def where_or(self, field, value = None, operator = None):
        """
        Establece condiciones para la consulta unidas por OR
        """
        if field is None:
            return self
        conjunction = None
        if value is None and isinstance(field, dict):
            for f,v in field.items():
                if self.where_criteria.size() > 0:
                    conjunction = 'OR'

                self.where_criteria.append(expressions.ConditionExpression(f, v, operator=operator, conjunction=conjunction))

        else:
            if self.where_criteria.size() > 0:
                    conjunction = 'OR'
            self.where_criteria.append(expressions.ConditionExpression(field, value, operator=operator, conjunction=conjunction))
        return self

    def group_by(self, *args):
        """
        Indica los campos para agrupación
        """
        if len(args) == 1:
            self.raw_fields_group = args[0].split(',')
        else:
            self.raw_fields_group = list(args)
        return self

    def order_by(self, field, orientation='ASC'):
        """
        Indica los campos y el criterio de ordenamiento
        """
        if isinstance(field, list):
            self.raw_order_by.append(field)
        else:
            self.raw_order_by.append([field, orientation])

        return self

    def result(self, *args, **kwargs):
        """
        Construye la consulta SQL
        """
        prettify = kwargs.get('pretty', False)
        self.__prepareData__()
        sql = 'SELECT '
        sql +=  ', '.join(self.fields)

        if len(self.tables) > 0:
            if prettify:
                sql += '\n'
            else:
                sql += ' '
            sql +=  'FROM '
            sql +=  ', '.join(self.tables)
        if self.where_criteria.size() > 0:
            if prettify:
                sql += '\n'
            else:
                sql += ' '
            sql +=  'WHERE '
            sql +=  self.where_criteria.result()
        if len(self.group_fields) > 0:
            if prettify:
                sql += '\n'
            else:
                sql += ' '
            sql +=  'GROUP BY '
            sql +=  ', '.join(self.group_fields)
        if len(self.order_by_fields) > 0:
            if prettify:
                sql += '\n'
            else:
                sql += ' '
            sql +=  'ORDER BY '
            sql +=  ', '.join(self.order_by_fields)
            if prettify:
                sql += '\n'
            else:
                sql += ' '

        return sql

class Delete(object):
    """
    DELETE Query Builder
    """

    def __init__(self, type):
        """
        Inicializa la consulta, type = recurso (Vertex|Edge)
        """
        super(Delete, self).__init__()
        self._class = None
        self._cluster = None
        self._type = None
        self.data = {}
        self.where_criteria = grouping.BaseGrouper()
        self._type = type

    def class_(self, _class):
        """
        Especifica la clase para eliminar
        """
        self._class = _class
        return self

    def where(self, field, value = None, operator = None):
        """
        Establece condiciones para la consulta unidas por AND
        """
        if field is None:
            return self
        conjunction = None
        if value is None and isinstance(field, dict):
            for f,v in field.items():
                if self.where_criteria.size() > 0:
                    conjunction = 'AND'
                self.where_criteria.append(expressions.ConditionExpression(f, v, operator=operator, conjunction=conjunction))

        else:
            if self.where_criteria.size() > 0:
                    conjunction = 'AND'
            self.where_criteria.append(expressions.ConditionExpression(field, value, operator=operator, conjunction=conjunction))
        return self

    def where_or(self, field, value = None, operator = None):
        """
        Establece condiciones para la consulta unidas por OR
        """
        if field is None:
            return self
        conjunction = None
        if value is None and isinstance(field, dict):
            for f,v in field.items():
                if self.where_criteria.size() > 0:
                    conjunction = 'OR'
                self.where_criteria.append(expressions.ConditionExpression(f, v, operator=operator, conjunction=conjunction))

        else:
            if self.where_criteria.size() > 0:
                    conjunction = 'OR'
            self.where_criteria.append(expressions.ConditionExpression(field, value, operator=operator, conjunction=conjunction))
        return self

    def result(self, *args, **kwargs):
        """
        Construye la consulta SQL
        """
        prettify = kwargs.get('pretty', False)

        sql = 'DELETE %s %s' % (self._type, self._class)

        if prettify:
            sql += '\n'
        else:
            sql += ' '

        if self.where_criteria.size() > 0:
            sql +=  'WHERE '
            sql +=  self.where_criteria.result()
            if prettify:
                sql += '\n'
            else:
                sql += ' '

        return sql

class Create(object):
    "CREATE query builder"

    def __init__(self, type):
        """
        Inicializa la consulta, type = recurso (Vertex|Edge)
        """
        super(Create, self).__init__()
        self._class = None
        self._cluster = None
        self._type = None
        self._from = None
        self._to = None
        self.data = {}
        self._type = type

    def class_(self, _class):
        """
        Especifica la clase para crear
        """
        self._class = _class
        return self

    def cluster(self, cluster):
        """
        Especifica el cluster donde se almacenara el nuevo recurso
        """
        self._cluster = cluster
        return self

    def from_(self, From):
        """
        [Edge-only] especifica el origen del lado
        """
        if self._type.lower() != 'edge':
            raise ValueError('Cannot set From/To to non-edge objects')
        self._from = From
        return self

    def to(self, to):
        """
        [Edge-only] especifica el destino del lado
        """
        if self._type.lower() != 'edge':
            raise ValueError('Cannot set From/To to non-edge objects')
        self._to = to
        return self

    def set(self, field, value = None):
        """
        [Edge|Vertex] establece datos del recurso
        """
        if value is None and isinstance(field, dict):
            self.content(field)
        if field and value:
            self.data[field] = value
        return self

    def content(self, obj):
        """
        [Edge|Vertex] establece datos del recurso
        """
        self.data.update(obj)
        return self

    def result(self, *args, **kwargs):
        """
        Construye la consulta SQL
        """
        prettify = kwargs.get('pretty', False)

        sql = 'CREATE %s %s' % (self._type, self._class)

        if prettify:
            sql += '\n'
        else:
            sql += ' '

        if self._type.lower() == 'edge':
            sql += " FROM %s TO %s " % (self._from, self._to)

        if self._cluster:
            sql += 'CLUSTER %s' % self._cluster
            if prettify:
                sql += '\n'
            else:
                sql += ' '

        if self.data:
            sql += 'CONTENT ' + json.dumps(self.data)
        print (sql)
        return sql

class Update(object):
    """
    UPDATE Query Builder
    """
    def __init__(self, _class):
        """
        Inicializa la clase, _class = origen donde actualizar
        """
        super(Update, self).__init__()
        self._class = None
        self._cluster = None
        self.data = {}
        self.where_criteria = grouping.BaseGrouper()
        self._class = _class

    def set(self, field, value = None):
        """
        [Edge|Vertex] establece datos del recurso
        """
        if value is None and isinstance(field, dict):
            self.content(field)
        if field and value:
            self.data[field] = value
        return self

    def content(self, obj):
        """
        [Edge|Vertex] establece datos del recurso
        """
        self.data.update(obj)
        return self

    def where(self, field, value = None, operator = None):
        """
        Establece condiciones para la consulta unidas por AND
        """
        if field is None:
            return self
        conjunction = None
        if value is None and isinstance(field, dict):
            for f,v in field.items():
                if self.where_criteria.size() > 0:
                    conjunction = 'AND'
                self.where_criteria.append(expressions.ConditionExpression(f, v, operator=operator, conjunction=conjunction))

        else:
            if self.where_criteria.size() > 0:
                    conjunction = 'AND'
            self.where_criteria.append(expressions.ConditionExpression(field, value, operator=operator, conjunction=conjunction))
        return self

    def where_or(self, field, value = None, operator = None):
        """
        Establece condiciones para la consulta unidas por OR
        """
        if field is None:
            return self
        conjunction = None
        if value is None and isinstance(field, dict):
            for f,v in field.items():
                if self.where_criteria.size() > 0:
                    conjunction = 'OR'
                self.where_criteria.append(expressions.ConditionExpression(f, v, operator=operator, conjunction=conjunction))

        else:
            if self.where_criteria.size() > 0:
                    conjunction = 'OR'
            self.where_criteria.append(expressions.ConditionExpression(field, value, operator=operator, conjunction=conjunction))
        return self

    def result(self, *args, **kwargs):
        """
        Construye la consulta SQL
        """
        prettify = kwargs.get('pretty', False)

        sql = 'UPDATE %s' % self._class

        if prettify:
            sql += '\n'
        else:
            sql += ' '

        if self.data:
            sql += 'MERGE ' + json.dumps(self.data)
            if prettify:
                sql += '\n'
            else:
                sql += ' '

        if self.where_criteria.size() > 0:
            sql +=  'WHERE '
            sql +=  self.where_criteria.result()
            if prettify:
                sql += '\n'
            else:
                sql += ' '

        print (sql)
        return sql
<|endoftext|>