source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/packages/dal/pydal/helpers/methods.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 14.5 KB
Line 
1# -*- coding: utf-8 -*-
2
3import os
4import re
5import uuid
6from .._compat import (
7    PY2,
8    BytesIO,
9    iteritems,
10    integer_types,
11    string_types,
12    to_bytes,
13    pjoin,
14    exists,
15    text_type,
16)
17from .regex import REGEX_CREDENTIALS, REGEX_UNPACK, REGEX_CONST_STRING, REGEX_W
18from .classes import SQLCustomType
19
20UNIT_SEPARATOR = "\x1f"  # ASCII unit separater for delimiting data
21
22
23def hide_password(uri):
24    if isinstance(uri, (list, tuple)):
25        return [hide_password(item) for item in uri]
26    return re.sub(REGEX_CREDENTIALS, "******", uri)
27
28
29def cleanup(text):
30    """
31    Validates that the given text is clean: only contains [0-9a-zA-Z_]
32    """
33    # if not REGEX_ALPHANUMERIC.match(text):
34    #     raise SyntaxError('invalid table or field name: %s' % text)
35    return text
36
37
38def list_represent(values, row=None):
39    return ", ".join(str(v) for v in (values or []))
40
41
42def xorify(orderby):
43    if not orderby:
44        return None
45    orderby2 = orderby[0]
46    for item in orderby[1:]:
47        orderby2 = orderby2 | item
48    return orderby2
49
50
51def use_common_filters(query):
52    return (
53        query
54        and hasattr(query, "ignore_common_filters")
55        and not query.ignore_common_filters
56    )
57
58
59def merge_tablemaps(*maplist):
60    """
61    Merge arguments into a single dict, check for name collisions.
62    """
63    maplist = list(maplist)
64    for i, item in enumerate(maplist):
65        if isinstance(item, dict):
66            maplist[i] = dict(**item)
67    ret = maplist[0]
68    for item in maplist[1:]:
69        if len(ret) > len(item):
70            big, small = ret, item
71        else:
72            big, small = item, ret
73        # Check for name collisions
74        for key, val in small.items():
75            if big.get(key, val) is not val:
76                raise ValueError("Name conflict in table list: %s" % key)
77        # Merge
78        big.update(small)
79        ret = big
80    return ret
81
82
83def bar_escape(item):
84    item = str(item).replace("|", "||")
85    if item.startswith("||"):
86        item = "%s%s" % (UNIT_SEPARATOR, item)
87    if item.endswith("||"):
88        item = "%s%s" % (item, UNIT_SEPARATOR)
89    return item
90
91
92def bar_unescape(item):
93    item = item.replace("||", "|")
94    if item.startswith(UNIT_SEPARATOR):
95        item = item[1:]
96    if item.endswith(UNIT_SEPARATOR):
97        item = item[:-1]
98    return item
99
100
101def bar_encode(items):
102    return "|%s|" % "|".join(bar_escape(item) for item in items if str(item).strip())
103
104
105def bar_decode_integer(value):
106    long = integer_types[-1]
107    if not hasattr(value, "split") and hasattr(value, "read"):
108        value = value.read()
109    return [long(x) for x in value.split("|") if x.strip()]
110
111
112def bar_decode_string(value):
113    return [bar_unescape(x) for x in re.split(REGEX_UNPACK, value[1:-1]) if x.strip()]
114
115
116def archive_record(qset, fs, archive_table, current_record):
117    tablenames = qset.db._adapter.tables(qset.query)
118    if len(tablenames) != 1:
119        raise RuntimeError("cannot update join")
120    for row in qset.select():
121        fields = archive_table._filter_fields(row)
122        for k, v in iteritems(fs):
123            if fields[k] != v:
124                fields[current_record] = row.id
125                archive_table.insert(**fields)
126                break
127    return False
128
129
130def smart_query(fields, text):
131    from ..objects import Field, Table
132
133    if not isinstance(fields, (list, tuple)):
134        fields = [fields]
135    new_fields = []
136    for field in fields:
137        if isinstance(field, Field):
138            new_fields.append(field)
139        elif isinstance(field, Table):
140            for ofield in field:
141                new_fields.append(ofield)
142        else:
143            raise RuntimeError("fields must be a list of fields")
144    fields = new_fields
145    field_map = {}
146    for field in fields:
147        n = field.name.lower()
148        if not n in field_map:
149            field_map[n] = field
150        n = str(field).lower()
151        if not n in field_map:
152            field_map[n] = field
153    constants = {}
154    i = 0
155    while True:
156        m = re.search(REGEX_CONST_STRING, text)
157        if not m:
158            break
159        text = "%s#%i%s" % (text[: m.start()], i, text[m.end() :])
160        constants[str(i)] = m.group()[1:-1]
161        i += 1
162    text = re.sub("\s+", " ", text).lower()
163    for a, b in [
164        ("&", "and"),
165        ("|", "or"),
166        ("~", "not"),
167        ("==", "="),
168        ("<", "<"),
169        (">", ">"),
170        ("<=", "<="),
171        (">=", ">="),
172        ("<>", "!="),
173        ("=<", "<="),
174        ("=>", ">="),
175        ("=", "="),
176        (" less or equal than ", "<="),
177        (" greater or equal than ", ">="),
178        (" equal or less than ", "<="),
179        (" equal or greater than ", ">="),
180        (" less or equal ", "<="),
181        (" greater or equal ", ">="),
182        (" equal or less ", "<="),
183        (" equal or greater ", ">="),
184        (" not equal to ", "!="),
185        (" not equal ", "!="),
186        (" equal to ", "="),
187        (" equal ", "="),
188        (" equals ", "="),
189        (" less than ", "<"),
190        (" greater than ", ">"),
191        (" starts with ", "startswith"),
192        (" ends with ", "endswith"),
193        (" not in ", "notbelongs"),
194        (" in ", "belongs"),
195        (" is ", "="),
196    ]:
197        if a[0] == " ":
198            text = text.replace(" is" + a, " %s " % b)
199        text = text.replace(a, " %s " % b)
200    text = re.sub("\s+", " ", text).lower()
201    text = re.sub("(?P<a>[\<\>\!\=])\s+(?P<b>[\<\>\!\=])", "\g<a>\g<b>", text)
202    query = field = neg = op = logic = None
203    for item in text.split():
204        if field is None:
205            if item == "not":
206                neg = True
207            elif not neg and not logic and item in ("and", "or"):
208                logic = item
209            elif item in field_map:
210                field = field_map[item]
211            else:
212                raise RuntimeError("Invalid syntax")
213        elif not field is None and op is None:
214            op = item
215        elif not op is None:
216            if item.startswith("#"):
217                if not item[1:] in constants:
218                    raise RuntimeError("Invalid syntax")
219                value = constants[item[1:]]
220            else:
221                value = item
222                if field.type in ("text", "string", "json"):
223                    if op == "=":
224                        op = "like"
225            if op == "=":
226                new_query = field == value
227            elif op == "<":
228                new_query = field < value
229            elif op == ">":
230                new_query = field > value
231            elif op == "<=":
232                new_query = field <= value
233            elif op == ">=":
234                new_query = field >= value
235            elif op == "!=":
236                new_query = field != value
237            elif op == "belongs":
238                new_query = field.belongs(value.split(","))
239            elif op == "notbelongs":
240                new_query = ~field.belongs(value.split(","))
241            elif field.type == "list:string":
242                if op == "contains":
243                    new_query = field.contains(value)
244                else:
245                    raise RuntimeError("Invalid operation")
246            elif field.type in ("text", "string", "json", "upload"):
247                if op == "contains":
248                    new_query = field.contains(value)
249                elif op == "like":
250                    new_query = field.ilike(value)
251                elif op == "startswith":
252                    new_query = field.startswith(value)
253                elif op == "endswith":
254                    new_query = field.endswith(value)
255                else:
256                    raise RuntimeError("Invalid operation")
257            elif field._db._adapter.dbengine == "google:datastore" and field.type in (
258                "list:integer",
259                "list:string",
260                "list:reference",
261            ):
262                if op == "contains":
263                    new_query = field.contains(value)
264                else:
265                    raise RuntimeError("Invalid operation")
266            else:
267                raise RuntimeError("Invalid operation")
268            if neg:
269                new_query = ~new_query
270            if query is None:
271                query = new_query
272            elif logic == "and":
273                query &= new_query
274            elif logic == "or":
275                query |= new_query
276            field = op = neg = logic = None
277    return query
278
279
280def auto_validators(field):
281    db = field.db
282    field_type = field.type
283    #: don't apply default validation on custom types
284    if isinstance(field_type, SQLCustomType):
285        if hasattr(field_type, "validator"):
286            return field_type.validator
287        else:
288            field_type = field_type.type
289    elif not isinstance(field_type, str):
290        return []
291    #: if a custom method is provided, call it
292    if callable(db.validators_method):
293        return db.validators_method(field)
294    #: apply validators from validators dict if present
295    if not db.validators or not isinstance(db.validators, dict):
296        return []
297    field_validators = db.validators.get(field_type, [])
298    if not isinstance(field_validators, (list, tuple)):
299        field_validators = [field_validators]
300    return field_validators
301
302
303def _fieldformat(r, id):
304    row = r(id)
305    if not row:
306        return str(id)
307    elif hasattr(r, "_format") and isinstance(r._format, str):
308        return r._format % row
309    elif hasattr(r, "_format") and callable(r._format):
310        return r._format(row)
311    else:
312        return str(id)
313
314
315class _repr_ref(object):
316    def __init__(self, ref=None):
317        self.ref = ref
318
319    def __call__(self, value, row=None):
320        return value if value is None else _fieldformat(self.ref, value)
321
322
323class _repr_ref_list(_repr_ref):
324    def __call__(self, value, row=None):
325        if not value:
326            return None
327        refs = None
328        db, id = self.ref._db, self.ref._id
329        if db._adapter.dbengine == "google:datastore":
330
331            def count(values):
332                return db(id.belongs(values)).select(id)
333
334            rx = range(0, len(value), 30)
335            refs = reduce(lambda a, b: a & b, [count(value[i : i + 30]) for i in rx])
336        else:
337            refs = db(id.belongs(value)).select(id)
338        return refs and ", ".join(_fieldformat(self.ref, x) for x in value) or ""
339
340
341def auto_represent(field):
342    if field.represent:
343        return field.represent
344    if (
345        field.db
346        and field.type.startswith("reference")
347        and field.type.find(".") < 0
348        and field.type[10:] in field.db.tables
349    ):
350        referenced = field.db[field.type[10:]]
351        return _repr_ref(referenced)
352    elif (
353        field.db
354        and field.type.startswith("list:reference")
355        and field.type.find(".") < 0
356        and field.type[15:] in field.db.tables
357    ):
358        referenced = field.db[field.type[15:]]
359        return _repr_ref_list(referenced)
360    return field.represent
361
362
363def varquote_aux(name, quotestr="%s"):
364    return name if REGEX_W.match(name) else quotestr % name
365
366def uuidstr():
367    return str(uuid.uuid4())
368
369def uuid2int(uuidv):
370    return uuid.UUID(uuidv).int
371
372
373def int2uuid(n):
374    return str(uuid.UUID(int=n))
375
376
377# Geodal utils
378def geoPoint(x, y):
379    return "POINT (%f %f)" % (x, y)
380
381
382def geoLine(*line):
383    return "LINESTRING (%s)" % ",".join("%f %f" % item for item in line)
384
385
386def geoPolygon(*line):
387    return "POLYGON ((%s))" % ",".join("%f %f" % item for item in line)
388
389
390# upload utils
391def attempt_upload(table, fields):
392    for fieldname in table._upload_fieldnames & set(fields):
393        value = fields[fieldname]
394        if not (value is None or isinstance(value, string_types)):
395            if not PY2 and isinstance(value, bytes):
396                continue
397            if hasattr(value, "file") and hasattr(value, "filename"):
398                new_name = table[fieldname].store(value.file, filename=value.filename)
399            elif isinstance(value, dict):
400                if "data" in value and "filename" in value:
401                    stream = BytesIO(to_bytes(value["data"]))
402                    new_name = table[fieldname].store(
403                        stream, filename=value["filename"]
404                    )
405                else:
406                    new_name = None
407            elif hasattr(value, "read") and hasattr(value, "name"):
408                new_name = table[fieldname].store(value, filename=value.name)
409            else:
410                raise RuntimeError("Unable to handle upload")
411            fields[fieldname] = new_name
412
413
414def attempt_upload_on_insert(table):
415    def wrapped(fields):
416        return attempt_upload(table, fields)
417
418    return wrapped
419
420
421def attempt_upload_on_update(table):
422    def wrapped(dbset, fields):
423        return attempt_upload(table, fields)
424
425    return wrapped
426
427
428def delete_uploaded_files(dbset, upload_fields=None):
429    table = dbset.db._adapter.tables(dbset.query).popitem()[1]
430    # ## mind uploadfield==True means file is not in DB
431    if upload_fields:
432        fields = list(upload_fields)
433        # Explicitly add compute upload fields (ex: thumbnail)
434        fields += [f for f in table.fields if table[f].compute is not None]
435    else:
436        fields = table.fields
437    fields = [
438        f
439        for f in fields
440        if table[f].type == "upload"
441        and table[f].uploadfield == True
442        and table[f].autodelete
443    ]
444    if not fields:
445        return False
446    for record in dbset.select(*[table[f] for f in fields]):
447        for fieldname in fields:
448            field = table[fieldname]
449            oldname = record.get(fieldname, None)
450            if not oldname:
451                continue
452            if (
453                upload_fields
454                and fieldname in upload_fields
455                and oldname == upload_fields[fieldname]
456            ):
457                continue
458            if field.custom_delete:
459                field.custom_delete(oldname)
460            else:
461                uploadfolder = field.uploadfolder
462                if not uploadfolder:
463                    uploadfolder = pjoin(dbset.db._adapter.folder, "..", "uploads")
464                if field.uploadseparate:
465                    items = oldname.split(".")
466                    uploadfolder = pjoin(
467                        uploadfolder, "%s.%s" % (items[0], items[1]), items[2][:2]
468                    )
469                oldpath = pjoin(uploadfolder, oldname)
470                if field.uploadfs:
471                    oldname = text_type(oldname)
472                    if field.uploadfs.exists(oldname):
473                        field.uploadfs.remove(oldname)
474                else:
475                    if exists(oldpath):
476                        os.unlink(oldpath)
477    return False
Note: See TracBrowser for help on using the repository browser.