Beispiel #1
0
def exp2func(expstr, name=None, mapLib=None):
    """
    Convert a QgsExpression into a JS function.
    """
    global whenfunctions
    whenfunctions = []
    exp = QgsExpression(expstr)
    js = walkExpression(exp.rootNode(), mapLib=mapLib)
    if name is None:
        import random
        import string
        name = ''.join(random.choice(string.ascii_lowercase) for _ in range(4))
    name += "_eval_expression"
    temp = """
function %s(context) {
    // %s

    var feature = context.feature;
    %s
    if (feature.properties) {
        return %s;
    } else {
        return %s;
    }
}""" % (name, exp.dump(), "\n".join(whenfunctions), js,
        js.replace("feature.properties['", "feature['"))
    return temp, name, exp.dump()
Beispiel #2
0
def exp2func(expstr, name=None, mapLib=None):
    """
    Convert a QgsExpression into a JS function.
    """
    global whenfunctions
    whenfunctions = []
    exp = QgsExpression(expstr)
    js = walkExpression(exp.rootNode(), mapLib=mapLib)
    if name is None:
        import random
        import string
        name = ''.join(random.choice(string.ascii_lowercase) for _ in range(4))
    name += "_eval_expression"
    temp = """
function %s(context) {
    // %s

    var feature = context.feature;
    %s
    if (feature.properties) {
        return %s;
    } else {
        return %s;
    }
}""" % (name,
        exp.dump(),
        "\n".join(whenfunctions),
        js,
        js.replace("feature.properties['", "feature['"))
    return temp, name, exp.dump()
Beispiel #3
0
def handle_condition(node, mapLib):
    global condtioncounts
    subexps = re.findall("WHEN(\s+.*?\s+)THEN(\s+.*?\s+)", node.dump())
    QgsMessageLog.logMessage(subexps, "qgis2web", level=QgsMessageLog.INFO)
    count = 1
    js = ""
    for sub in subexps:
        when = sub[0].strip()
        then = sub[1].strip()
        QgsMessageLog.logMessage(then, "qgis2web", level=QgsMessageLog.INFO)
        whenpart = QgsExpression(when)
        thenpart = QgsExpression(then)
        whenjs = walkExpression(whenpart.rootNode(), mapLib)
        thenjs = walkExpression(thenpart.rootNode(), mapLib)
        style = "if" if count == 1 else "else if"
        js += """
        %s %s {
          return %s;
        }
        """ % (style, whenjs, thenjs)
        js = js.strip()
        count += 1

    elsejs = "null"
    if "ELSE" in node.dump():
        elseexps = re.findall("ELSE(\s+.*?\s+)END", node.dump())
        elsestr = elseexps[0].strip()
        exp = QgsExpression(elsestr)
        elsejs = walkExpression(exp.rootNode(), mapLib)
    funcname = "_CASE()"
    temp = """function %s {
    %s
    else {
     return %s;
    }
    };""" % (funcname, js, elsejs)
    whenfunctions.append(temp)
    return funcname
Beispiel #4
0
def handle_condition(node, mapLib):
    global condtioncounts
    subexps = re.findall(r"WHEN(\s+.*?\s+)THEN(\s+.*?\s+)", node.dump())
    QgsMessageLog.logMessage(subexps, "qgis2web", level=QgsMessageLog.INFO)
    count = 1;
    js = ""
    for sub in subexps:
        when = sub[0].strip()
        then = sub[1].strip()
        QgsMessageLog.logMessage(then, "qgis2web", level=QgsMessageLog.INFO)
        whenpart =  QgsExpression(when)
        thenpart = QgsExpression(then)
        whenjs = walkExpression(whenpart.rootNode(), mapLib)
        thenjs = walkExpression(thenpart.rootNode(), mapLib)
        style = "if" if count == 1 else "else if"
        js += """
        %s %s {
          return %s;
        }
        """ % (style, whenjs, thenjs)
        js = js.strip()
        count += 1

    elsejs = "null"
    if "ELSE" in node.dump():
        elseexps = re.findall(r"ELSE(\s+.*?\s+)END", node.dump())
        elsestr = elseexps[0].strip()
        exp =  QgsExpression(elsestr)
        elsejs = walkExpression(exp.rootNode(), mapLib)
    funcname = "_CASE()"
    temp = """function %s {
    %s
    else {
     return %s;
    }
    };""" % (funcname, js, elsejs)
    whenfunctions.append(temp)
    return funcname
Beispiel #5
0
    def save_vector_data(self,
                         metadata_layer,
                         post_layer_data,
                         has_transactions,
                         post_save_signal=True,
                         **kwargs):
        """Save vector editing data

        :param metadata_layer: metadata of the layer being edited
        :type metadata_layer: MetadataVectorLayer
        :param post_layer_data: post data with 'add', 'delete' etc.
        :type post_layer_data: dict
        :param has_transactions: true if the layer support transactions
        :type has_transactions: bool
        :param post_save_signal: if this is a post_save_signal call, defaults to True
        :type post_save_signal: bool, optional
        """

        # Check atomic capabilities for validation
        # -----------------------------------------------
        #for mode_editing in (EDITING_POST_DATA_ADDED, EDITING_POST_DATA_UPDATED, EDITING_POST_DATA_DELETED):

        # try to get layer model object from metatada_layer
        layer = getattr(metadata_layer, 'layer', self.layer)

        if EDITING_POST_DATA_ADDED in post_layer_data and len(
                post_layer_data[EDITING_POST_DATA_ADDED]) > 0:
            if not self.request.user.has_perm('qdjango.add_feature', layer):
                raise ValidationError(
                    _('Sorry but your user doesn\'t has \'Add Feature\' capability'
                      ))

        if EDITING_POST_DATA_DELETED in post_layer_data and len(
                post_layer_data[EDITING_POST_DATA_DELETED]) > 0:
            if not self.request.user.has_perm('qdjango.delete_feature', layer):
                raise ValidationError(
                    _('Sorry but your user doesn\'t has \'Delete Feature\' capability'
                      ))

        if EDITING_POST_DATA_UPDATED in post_layer_data and len(
                post_layer_data[EDITING_POST_DATA_UPDATED]) > 0:
            if not self.request.user.has_perm('qdjango.change_feature', layer) and \
                    not self.request.user.has_perm('qdjango.change_attr_feature', layer):
                raise ValidationError(
                    _('Sorry but your user doesn\'t has \'Change or Change Attributes Features\' capability'
                      ))

        # get initial featurelocked
        metadata_layer.lock.getInitialFeatureLockedIds()

        # get lockids from client
        metadata_layer.lock.setLockeFeaturesFromClient(
            post_layer_data['lockids'])

        # data for response
        insert_ids = list()
        lock_ids = list()

        # FIXME: check this out
        # for add check if is a metadata_layer and referenced field is a pk
        is_referenced_field_is_pk = 'referenced_layer_insert_ids' in kwargs and kwargs['referenced_layer_insert_ids'] \
            and hasattr(metadata_layer, 'referenced_field_is_pk') \
            and metadata_layer.referenced_field_is_pk

        # Get the layer
        qgis_layer = metadata_layer.qgis_layer

        for mode_editing in (EDITING_POST_DATA_ADDED,
                             EDITING_POST_DATA_UPDATED):

            if mode_editing in post_layer_data:

                for geojson_feature in post_layer_data[mode_editing]:
                    data_extra_fields = {'feature': geojson_feature}

                    # Clear any old error
                    qgis_layer.dataProvider().clearErrors()

                    # add media data
                    self.add_media_property(geojson_feature, metadata_layer)

                    # for GEOSGeometry of Django 2.2 it must add crs to feature if is not set if a geo feature
                    if metadata_layer.geometry_type != QGIS_LAYER_TYPE_NO_GEOM:
                        if geojson_feature[
                                'geometry'] and 'crs' not in geojson_feature[
                                    'geometry']:
                            geojson_feature['geometry'][
                                'crs'] = "{}:{}".format(
                                    self.layer.project.group.srid.auth_name,
                                    self.layer.project.group.srid.auth_srid)

                    # reproject data if necessary
                    if kwargs[
                            'reproject'] and metadata_layer.geometry_type != QGIS_LAYER_TYPE_NO_GEOM:
                        self.reproject_feature(geojson_feature, to_layer=True)

                    # case relation data ADD, if father referenced field is pk
                    if is_referenced_field_is_pk:
                        for newid in kwargs['referenced_layer_insert_ids']:
                            if geojson_feature['properties'][
                                    metadata_layer.
                                    referencing_field] == newid['clientid']:
                                geojson_feature['properties'][
                                    metadata_layer.
                                    referencing_field] = newid['id']

                    if mode_editing == EDITING_POST_DATA_UPDATED:
                        # control feature locked
                        if not metadata_layer.lock.checkFeatureLocked(
                                geojson_feature['id']):
                            raise Exception(
                                self.no_more_lock_feature_msg.format(
                                    geojson_feature['id'],
                                    metadata_layer.client_var))

                    # Send for validation
                    # Note that this may raise a validation error
                    pre_save_maplayer.send(self,
                                           layer_metadata=metadata_layer,
                                           mode=mode_editing,
                                           data=data_extra_fields,
                                           user=self.request.user)

                    # Validate and save
                    try:

                        original_feature = None
                        feature = QgsFeature(qgis_layer.fields())
                        if mode_editing == EDITING_POST_DATA_UPDATED:

                            # add patch for shapefile type, geojson_feature['id'] id int() instead of str()
                            # path to fix into QGIS api
                            geojson_feature[
                                'id'] = get_layer_fids_from_server_fids(
                                    [str(geojson_feature['id'])],
                                    qgis_layer)[0]
                            feature.setId(geojson_feature['id'])

                            # Get feature from data provider before update
                            original_feature = qgis_layer.getFeature(
                                geojson_feature['id'])

                        # We use this feature for geometry parsing only:
                        imported_feature = QgsJsonUtils.stringToFeatureList(
                            json.dumps(geojson_feature),
                            qgis_layer.fields(),
                            None  # UTF8 codec
                        )[0]

                        feature.setGeometry(imported_feature.geometry())

                        # There is something wrong in QGIS 3.10 (fixed in later versions)
                        # so, better loop through the fields and set attributes individually
                        for name, value in geojson_feature['properties'].items(
                        ):
                            feature.setAttribute(name, value)

                        # Loop again for set expressions value:
                        # For update store expression result to use later into update condition
                        field_expresion_values = {}
                        for qgis_field in qgis_layer.fields():
                            if qgis_field.defaultValueDefinition().expression(
                            ):
                                exp = QgsExpression(
                                    qgis_field.defaultValueDefinition(
                                    ).expression())
                                if exp.rootNode().nodeType(
                                ) != QgsExpressionNode.ntLiteral and not exp.hasParserError(
                                ):
                                    context = QgsExpressionContextUtils.createFeatureBasedContext(
                                        feature, qgis_layer.fields())
                                    context.appendScopes(
                                        QgsExpressionContextUtils.
                                        globalProjectLayerScopes(qgis_layer))
                                    result = exp.evaluate(context)
                                    if not exp.hasEvalError():
                                        feature.setAttribute(
                                            qgis_field.name(), result)

                                        # Check update if expression default value has to run also on update e not
                                        # only on insert newone
                                        if qgis_field.defaultValueDefinition(
                                        ).applyOnUpdate():
                                            field_expresion_values[
                                                qgis_field.name()] = result

                            elif qgis_field.typeName() in ('date', 'datetime',
                                                           'time'):

                                if qgis_field.typeName() == 'date':
                                    qtype = QDate
                                elif qgis_field.typeName() == 'datetime':
                                    qtype = QDateTime
                                else:
                                    qtype = QTime

                                field_idx = qgis_layer.fields().indexFromName(
                                    qgis_field.name())
                                options = qgis_layer.editorWidgetSetup(
                                    field_idx).config()

                                if 'field_iso_format' in options and not options[
                                        'field_iso_format']:
                                    if geojson_feature['properties'][
                                            qgis_field.name()]:
                                        value = qtype.fromString(
                                            geojson_feature['properties'][
                                                qgis_field.name()],
                                            options['field_format'])
                                        feature.setAttribute(
                                            qgis_field.name(), value)

                        # Call validator!
                        errors = feature_validator(feature,
                                                   metadata_layer.qgis_layer)
                        if errors:
                            raise ValidationError(errors)

                        # Save the feature
                        if mode_editing == EDITING_POST_DATA_ADDED:
                            if has_transactions:
                                if not qgis_layer.addFeature(feature):
                                    raise Exception(
                                        _('Error adding feature: %s') %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))
                            else:
                                if not qgis_layer.dataProvider().addFeature(
                                        feature):
                                    raise Exception(
                                        _('Error adding feature: %s') %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))

                                # Patch for Spatialite provider on pk
                                if qgis_layer.dataProvider().name(
                                ) == 'spatialite':
                                    pks = qgis_layer.primaryKeyAttributes()
                                    if len(pks) > 1:
                                        raise Exception(
                                            _(f'Error adding feature on Spatialite provider: '
                                              f'layer {qgis_layer.id()} has more than one pk column'
                                              ))

                                    # update pk attribute:
                                    feature.setAttribute(
                                        pks[0],
                                        server_fid(feature,
                                                   qgis_layer.dataProvider()))

                        elif mode_editing == EDITING_POST_DATA_UPDATED:
                            attr_map = {}
                            for name, value in geojson_feature[
                                    'properties'].items():
                                if name in qgis_layer.dataProvider(
                                ).fieldNameMap():
                                    if name in field_expresion_values:
                                        value = field_expresion_values[name]
                                    attr_map[qgis_layer.dataProvider().
                                             fieldNameMap()[name]] = value

                            if has_transactions:
                                if not qgis_layer.changeAttributeValues(
                                        geojson_feature['id'], attr_map):
                                    raise Exception(
                                        _('Error changing attribute values: %s'
                                          ) %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))
                                # Check for errors because of https://github.com/qgis/QGIS/issues/36583
                                if qgis_layer.dataProvider().errors():
                                    raise Exception(', '.join(
                                        qgis_layer.dataProvider().errors()))
                                if not feature.geometry().isNull(
                                ) and not qgis_layer.changeGeometry(
                                        geojson_feature['id'],
                                        feature.geometry()):
                                    raise Exception(
                                        _('Error changing geometry: %s') %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))
                            else:
                                if not qgis_layer.dataProvider(
                                ).changeAttributeValues(
                                    {geojson_feature['id']: attr_map}):
                                    raise Exception(
                                        _('Error changing attribute values: %s'
                                          ) %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))
                                if not feature.geometry().isNull(
                                ) and not qgis_layer.dataProvider(
                                ).changeGeometryValues({
                                        geojson_feature['id']:
                                        feature.geometry()
                                }):
                                    raise Exception(
                                        _('Error changing geometry: %s') %
                                        ', '.join(qgis_layer.dataProvider().
                                                  errors()))

                        to_res = {}
                        to_res_lock = {}

                        if mode_editing == EDITING_POST_DATA_ADDED:

                            # to exclude QgsFormater used into QgsJsonjExporter is necessary build by hand single json feature
                            ex = QgsJsonExporter(qgis_layer)
                            ex.setIncludeAttributes(False)

                            fnames = [f.name() for f in feature.fields()]
                            jfeature = json.loads(
                                ex.exportFeature(
                                    feature,
                                    dict(zip(fnames, feature.attributes()))))

                            to_res.update({
                                'clientid':
                                geojson_feature['id'],
                                # This might be the internal QGIS feature id (< 0)
                                'id':
                                server_fid(
                                    feature,
                                    metadata_layer.qgis_layer.dataProvider()),
                                'properties':
                                jfeature['properties']
                            })

                            # lock news:
                            to_res_lock = metadata_layer.lock.modelLock2dict(
                                metadata_layer.lock.lockFeature(server_fid(
                                    feature,
                                    metadata_layer.qgis_layer.dataProvider()),
                                                                save=True))

                        if bool(to_res):
                            insert_ids.append(to_res)
                        if bool(to_res_lock):
                            lock_ids.append(to_res_lock)

                        # Send post vase signal
                        post_save_maplayer.send(
                            self,
                            layer_metadata=metadata_layer,
                            mode=mode_editing,
                            data=data_extra_fields,
                            user=self.request.user,
                            original_feature=original_feature,
                            to_res=to_res)

                    except ValidationError as ex:
                        raise ValidationError({
                            metadata_layer.client_var: {
                                mode_editing: {
                                    'id': geojson_feature['id'],
                                    'fields': ex.detail,
                                }
                            }
                        })

                    except Exception as ex:
                        raise ValidationError({
                            metadata_layer.client_var: {
                                mode_editing: {
                                    'id': geojson_feature['id'],
                                    'fields': str(ex),
                                }
                            }
                        })

        # erasing feature if to do
        if EDITING_POST_DATA_DELETED in post_layer_data:

            fids = post_layer_data[EDITING_POST_DATA_DELETED]

            # get feature fids from server fids from client.
            fids = get_layer_fids_from_server_fids([str(id) for id in fids],
                                                   qgis_layer)

            for feature_id in fids:

                # control feature locked
                if not metadata_layer.lock.checkFeatureLocked(str(feature_id)):
                    raise Exception(
                        self.no_more_lock_feature_msg.format(
                            feature_id, metadata_layer.client_var))

                # Get feature to delete
                ex = QgsJsonExporter(qgis_layer)
                deleted_feature = ex.exportFeature(
                    qgis_layer.getFeature(feature_id))

                pre_delete_maplayer.send(self,
                                         layer_metatada=metadata_layer,
                                         data=deleted_feature,
                                         user=self.request.user)

                qgis_layer.dataProvider().clearErrors()

                if has_transactions:
                    if not qgis_layer.deleteFeatures(
                        [feature_id]) or qgis_layer.dataProvider().errors():
                        raise Exception(
                            _('Cannot delete feature: %s') %
                            ', '.join(qgis_layer.dataProvider().errors()))
                else:
                    if not qgis_layer.dataProvider().deleteFeatures(
                        [feature_id]) or qgis_layer.dataProvider().errors():
                        raise Exception(
                            _('Cannot delete feature: %s') %
                            ', '.join(qgis_layer.dataProvider().errors()))

        return insert_ids, lock_ids
Beispiel #6
0
def mapLayerAttributesFromQgisLayer(qgis_layer, **kwargs):
    """
    map QGIS layer's simple and direct field to Attributes for client editing system
    only concrete field not virtual field and many2many
    """

    # Set fields to exclude
    fieldsToExclude = kwargs['exclude'] if 'exclude' in kwargs else []

    toRes = OrderedDict()
    fields = qgis_layer.fields()

    data_provider = qgis_layer.dataProvider()

    field_index = 0

    pk_attributes = qgis_layer.primaryKeyAttributes()

    # Determine if we are using an old and bugged version of QGIS
    IS_QGIS_3_10 = Qgis.QGIS_VERSION.startswith('3.10')

    # FIXME: find better way for layer join 1:1 managment
    for field in fields:
        if field.name() not in fieldsToExclude and field.name(
        ) in kwargs['fields']:
            #internal_typename = field.typeName().split('(')[0]
            internal_typename = QVariant.typeToName(field.type()).upper()
            if internal_typename in FIELD_TYPES_MAPPING:

                # Get constraints and default clause to define if the field is editable
                # or set editable property by kwargs.
                # Only consider "strong" constraints
                constraints = qgis_layer.fieldConstraints(field_index)
                not_null = bool(constraints & QgsFieldConstraints.ConstraintNotNull) and \
                    field.constraints().constraintStrength(
                        QgsFieldConstraints.ConstraintNotNull) == QgsFieldConstraints.ConstraintStrengthHard
                unique = bool(constraints & QgsFieldConstraints.ConstraintUnique) and \
                    field.constraints().constraintStrength(
                        QgsFieldConstraints.ConstraintUnique) == QgsFieldConstraints.ConstraintStrengthHard
                has_expression = bool(constraints & QgsFieldConstraints.ConstraintExpression) and \
                    field.constraints().constraintStrength(
                        QgsFieldConstraints.ConstraintExpression) == QgsFieldConstraints.ConstraintStrengthHard
                default_clause = data_provider.defaultValueClause(field_index)

                # default value for editing from qgis_layer
                if 'default' not in kwargs:
                    default_value = qgis_layer.defaultValue(field_index) if qgis_layer.defaultValue(field_index) \
                        else None
                else:
                    default_value = kwargs['default']

                if isinstance(default_value, QDate) or isinstance(
                        default_value, QDateTime):
                    try:
                        default_value = default_value.toString(
                            kwargs['fields'][field.name()]['input']['options']
                            ['formats'][0]['displayformat'])
                    except Exception as e:
                        default_value = ''

                expression = ''
                if has_expression:
                    expression = field.constraints().constraintExpression()

                # Check for defaultValueDefinition with expression
                # 2021/10/04 snippet by Alessandro Pasotti (elpaso)
                has_default_value_expression = False
                if field.defaultValueDefinition().expression() != '':
                    exp = QgsExpression(
                        field.defaultValueDefinition().expression())
                    has_default_value_expression = exp.rootNode().nodeType(
                    ) != QgsExpressionNode.ntLiteral

                if not_null and unique and default_clause or has_default_value_expression:
                    editable = False
                else:
                    editable = kwargs['fields'][field.name()]['editable']

                # remove editable from kwargs:
                del (kwargs['fields'][field.name()]['editable'])

                comment = field.comment() if field.comment() else field.name()
                fieldType = FIELD_TYPES_MAPPING[internal_typename]

                if IS_QGIS_3_10:
                    is_pk = unique and default_clause and not_null
                else:
                    is_pk = (field_index in pk_attributes)

                toRes[field.name()] = editingFormField(
                    field.name(),
                    required=not_null,
                    fieldLabel=comment,
                    type=fieldType,
                    inputType=FORM_FIELDS_MAPPING[fieldType],
                    editable=editable,
                    default_clause=default_clause,
                    unique=unique,
                    expression=expression,
                    pk=is_pk,
                    default=default_value)

                # add upload url to image type if module is set
                if 'editing' in settings.G3WADMIN_LOCAL_MORE_APPS:
                    if fieldType == FIELD_TYPE_IMAGE:
                        toRes[field.name()].update(
                            {'uploadurl': reverse('editing-upload')})

                # update with fields configs data
                if 'fields' in kwargs and field.name() in kwargs['fields']:
                    deepupdate(toRes[field.name()],
                               kwargs['fields'][field.name()])
                    if fieldType == FIELD_TYPE_BOOLEAN:
                        toRes[field.name()]['input']['options']['values'] = [{
                            'checked':
                            True,
                            'value':
                            True
                        }, {
                            'checked':
                            False,
                            'value':
                            False
                        }]

        field_index += 1

    return toRes