Skip to content

Commit

Permalink
Merge branch 'dev' into en_lang_as_default
Browse files Browse the repository at this point in the history
  • Loading branch information
wlorenzetti authored Nov 27, 2024
2 parents c6142a8 + c8bc035 commit 72928a3
Show file tree
Hide file tree
Showing 64 changed files with 6,076 additions and 28,193 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Software releases follow theese main branches as described in the compatibility
| Branch | Python | Django | QGIS | [client] | First release | Status |
|------------|----------------|----------------|---------------|--------------|---------------|----------------|
| [dev] | 3.10 | 3.2 | 3.34 | dev | Unreleased | ⚠️️ Unstable |
| [v.3.8.x] | 3.10 | 3.2 | 3.34 | 3.10.0 | Jun 2024 | 🪲️ Bug fixing |
| [v.3.8.x] | 3.10 | 3.2 | 3.34 | 3.10.3 | Sep 2024 | 🪲️ Bug fixing |
| [v.3.7.x] | 3.10 | 3.2 | 3.34 | 3.9.6 | Dec 2023 | 🪲️ Bug fixing |
| [v.3.6.x] | 3.10 | 3.2 | 3.28 | 3.8.15 | May 2023 | 🚨 End of Life |
| [v.3.5.x] | 3.10 | 2.2 | 3.22 | 3.7 | Nov 2022 | 🚨 End of Life |
Expand Down
71 changes: 71 additions & 0 deletions g3w-admin/base/management/commands/load_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from django.core.management.base import BaseCommand
from core.models import Group, G3WSpatialRefSys
from qdjango.utils.data import QgisProject
from qdjango.models import Project
from django.core.files import File
from django.conf import settings
import os, subprocess, shutil

class Command(BaseCommand):

help = 'Add a project to DB'

def add_arguments(self, parser):
parser.add_argument(
'--file',
# default=os.path.realpath(f"{ settings.BASE_DIR }/../qdjango/tests/data/g3wsuite_project_test_qgis328.qgs"),
default="building-management-demo.qgs",
help='Absolute path to .qgis project file.'
)
parser.add_argument(
'--data',
# default=os.path.realpath(f"{ settings.BASE_DIR }/../qdjango/tests/data/geodata/"),
default="-1",
help='Absolute path to data folder related to .qgis project.'
)

def handle(self, *args, **options):

# Fallback to remote "g3w-suite-demo-projects" repository (eg. --file="public-building-management-demo.qgs")
if (not os.path.exists(options['file'])):
if (not os.path.isdir('/tmp/g3w-suite-demo-projects')):
subprocess.call("git clone https://github.com/g3w-suite/g3w-suite-demo-projects.git --single-branch --depth 1 --branch master /tmp/g3w-suite-demo-projects", shell=True)
else:
subprocess.call("git config --global --add safe.directory /tmp/g3w-suite-demo-projects", shell=True)
subprocess.call("git -C /tmp/g3w-suite-demo-projects pull https://github.com/g3w-suite/g3w-suite-demo-projects.git", shell=True)
shutil.copytree('/tmp/g3w-suite-demo-projects/project_data/', settings.DATASOURCE_PATH, dirs_exist_ok=True)
options['file'] = f"/tmp/g3w-suite-demo-projects/projects/{ options['file'] }"
options['data'] = '-1'

# Ensure "projects_data" is there
if (options['data'] and "-1" != options['data']):
options['data']=options['data'].rstrip('/') + '/'
out_dir=os.path.join(settings.DATASOURCE_PATH + os.path.basename(os.path.dirname(options['data'])))
print(f"Copying { options['data']}")
shutil.copytree(options['data'], out_dir, dirs_exist_ok=True)
print(f" ---> {out_dir}")

# Create or Update project
project = QgisProject(File(open(options['file'], 'r')))

try:
instance = Project.objects.get(title=project.title)
group = instance.group
except Project.DoesNotExist:
instance = None
group,_ = Group.objects.get_or_create(
name=project.srid,
defaults={
'title': project.srid,
'srid': G3WSpatialRefSys.objects.get(auth_srid=project.srid)
}
)

group.is_active = True
group.save()

project.group = group

project.save(instance=instance)

print(f" ---> { (instance or project.instance).qgis_file.path }")
29 changes: 29 additions & 0 deletions g3w-admin/base/management/commands/reload_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from django.core.management.base import BaseCommand
from django.core.management import call_command
from django.conf import settings
import os, glob, subprocess, shutil

class Command(BaseCommand):

help = 'Import all demo projects from: https://github.com/g3w-suite/g3w-suite-demo-projects'

def handle(self, *args, **options):

# Clone qgis project from remote repository into a temporary folder (eg. --file="public-building-management-demo.qgs")
if (not os.path.isdir('/tmp/g3w-suite-demo-projects')):
subprocess.call("git clone https://github.com/g3w-suite/g3w-suite-demo-projects.git --single-branch --depth 1 --branch master /tmp/g3w-suite-demo-projects", shell=True)
else:
subprocess.call("git config --global --add safe.directory /tmp/g3w-suite-demo-projects", shell=True)
subprocess.call("git -C /tmp/g3w-suite-demo-projects pull https://github.com/g3w-suite/g3w-suite-demo-projects.git", shell=True)

shutil.copytree('/tmp/g3w-suite-demo-projects/project_data/', settings.DATASOURCE_PATH, dirs_exist_ok=True)

# Load all qgis projects into DB
for file in glob.glob('/tmp/g3w-suite-demo-projects/projects/*.qgs'):
call_command('load_project', file=file, data="-1")

# shutil.copytree(os.path.realpath(f"{ settings.BASE_DIR }/../qdjango/tests/data/geodata"), f"{settings.DATASOURCE_PATH}/geodata", dirs_exist_ok=True)
# shutil.copytree(os.path.realpath(f"{ settings.BASE_DIR }/../qdjango/tests/data/editing"), f"{settings.DATASOURCE_PATH}/editing", dirs_exist_ok=True)

# for file in glob.glob(os.path.realpath(f"{ settings.BASE_DIR }/../qdjango/tests/data/*.qgs")):
# call_command('load_project', file=file, data="-1")
1 change: 1 addition & 0 deletions g3w-admin/client/static/client/app.min.css

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions g3w-admin/client/static/client/app.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions g3w-admin/client/static/client/app.min.js.map

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 0 additions & 1 deletion g3w-admin/client/static/client/css/app.min.css

This file was deleted.

98 changes: 0 additions & 98 deletions g3w-admin/client/static/client/css/vendor.min.css

This file was deleted.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 0 additions & 2 deletions g3w-admin/client/static/client/js/app.min.js

This file was deleted.

1 change: 0 additions & 1 deletion g3w-admin/client/static/client/js/app.min.js.map

This file was deleted.

27,778 changes: 0 additions & 27,778 deletions g3w-admin/client/static/client/js/vendor.min.js

This file was deleted.

966 changes: 966 additions & 0 deletions g3w-admin/client/static/client/vendor.min.js

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions g3w-admin/client/static/client/vendor.min.js.map

Large diffs are not rendered by default.

19 changes: 5 additions & 14 deletions g3w-admin/client/templates/client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,19 @@
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, shrink-to-fit=no" name="viewport">
<title>{{ page_title }}</title>
<link rel="icon" href="{% if SETTINGS.G3WSUITE_CUSTOM_STATIC_URL and SETTINGS.G3WSUITE_FAVICON %}{{ SETTINGS.G3WSUITE_FAVICON }}{% else %}{% static '/img/favicon.ico' %}{% endif %}">
<link rel="stylesheet" href="{% with client_default|add:"/css/vendor.min.css" as vendor_css %}{% static vendor_css %}{% endwith %}">
<link rel="stylesheet" href="{% with client_default|add:"/css/app.min.css" as app_css %}{% static app_css %}{% endwith %}">
<link rel="stylesheet" href="{% with client_default|add:"/app.min.css" as app_css %}{% static app_css %}{% endwith %}">
{% if SETTINGS.G3WSUITE_CUSTOM_STATIC_URL %}{% for css in SETTINGS.G3WSUITE_CUSTOM_CSS %}
<link rel="stylesheet" type="text/css" href="{{ css }}" />
{% endfor %}{% endif %}
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body class="hold-transition skin-{{ skin_class|default:'blue' }} fixed {% if sidebar_collapse %} sidebar-collapse {% endif %}">
<body class="{% if sidebar_collapse %} sidebar-collapse {% endif %}">
{% if SETTINGS.SENTRY_JS %}
{{ SETTINGS.SENTRY_JS | safe }}
{% endif %}
<div id="app">
<app></app>
</div>
<script src="{% with client_default|add:"/js/vendor.min.js" as vendor_js %}{% static vendor_js %}{% endwith %}"></script>
<app id="app"></app>
<script src="{% with client_default|add:"/vendor.min.js" as vendor_js %}{% static vendor_js %}{% endwith %}"></script>
<script>{{ group_config | safe }}</script>
<script src="{% with client_default|add:"/js/app.min.js" as app_js %}{% static app_js %}{% endwith %}"></script>
<script src="{% with client_default|add:"/app.min.js" as app_js %}{% static app_js %}{% endwith %}"></script>
{% if SETTINGS.G3WSUITE_CUSTOM_STATIC_URL %}{% for js in SETTINGS.G3WSUITE_CUSTOM_JS %}
<script src="{{ js }}"></script>
{% endfor %}{% endif %}
Expand Down
29 changes: 25 additions & 4 deletions g3w-admin/core/api/base/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ def response_data_mode(self, request, export_features=False):
attrs.append(attr_idx)
qgis_feature_request.setSubsetOfAttributes(attrs)

# Get feature: apply pagination if 'page' parameters is set
self.features = get_qgis_features(
self.metadata_layer.qgis_layer, qgis_feature_request, **kwargs)

Expand Down Expand Up @@ -697,11 +698,31 @@ def response_data_mode(self, request, export_features=False):
f = feature_collection['features'][i]
f['id'] = fids_map[f['id']]

self.results.update(APIVectorLayerStructure(**{
api_vector_data = {
'data': feature_collection,
'count': count_qgis_features(self.metadata_layer.qgis_layer, qgis_feature_request, **kwargs),
'geometryType': self.metadata_layer.geometry_type,
}).as_dict())
'geometryType': self.metadata_layer.geometry_type
}

# Cafe with 'autofilter' parameter: get every id from qgis_feature_request
# ------------------------------------------------------------------------
if 'autofilter' in self.request_data and str(self.request_data['autofilter']) == '1':

# Remove pagination
for k in ('page', 'page_size'):
if k in kwargs:
del(kwargs[k])

# Reset limit
if qgis_feature_request.limit() != -1:
qgis_feature_request = QgsFeatureRequest(qgis_feature_request)
qgis_feature_request.setLimit(-1)

self.total_feature_ids = [str(f.id()) for f in get_qgis_features(
self.metadata_layer.qgis_layer, qgis_feature_request, **kwargs)]


self.results.update(APIVectorLayerStructure(**api_vector_data).as_dict())

# FIXME: add extra fields data by signals and receivers
# FIXME: featurecollection = post_serialize_maplayer.send(layer_serializer, layer=self.layer_name)
Expand Down Expand Up @@ -756,7 +777,7 @@ def get_response(self, request, mode_call=None, project_type=None, layer_id=None
# before to send response
extra_data = before_return_vector_data_layer.send(self)
for ed in extra_data:
if ed[1] and ed[0].__name__ in ('add_constraints', 'add_atomic_capabilities'):
if ed[1] and ed[0].__name__ in ('add_constraints', 'add_atomic_capabilities', 'add_filter_token'):
self.results.results.update(ed[1])

# response a APIVectorLayer
Expand Down
4 changes: 3 additions & 1 deletion g3w-admin/core/api/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,10 @@ def apply_filter(self, request, metadata_layer, qgis_feature_request, view):
if not self._is_valid_field(qgis_layer, ordering, view):
continue

# Because the fields inside a QGIS expression must be declared inside
# the expression string with the double brackets ("field_name")
ordering_rules.append(QgsFeatureRequest.OrderByClause(
ordering, ascending))
f'"{ordering}"', ascending))

if ordering_rules:
order_by = QgsFeatureRequest.OrderBy(ordering_rules)
Expand Down
4 changes: 4 additions & 0 deletions g3w-admin/core/utils/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.contrib.auth import REDIRECT_FIELD_NAME
from guardian.exceptions import GuardianError
from guardian.utils import get_40x_or_None, get_anonymous_user
from qdjango.utils.session import reset_filtertoken

import logging

Expand Down Expand Up @@ -169,6 +170,9 @@ def _wrapped_view(request, *args, **kwargs):
else:
user_pk = request.user.pk

# Reset sessiontokenfilter
reset_filtertoken(request)

key = f"{key_prefix}{'_'.join([str(kwargs[k]) for k in key_args] + [str(user_pk)])}"
logger.debug(f"[CACHING /api/config]: Key {key}")
response = cache.get(key)
Expand Down
16 changes: 13 additions & 3 deletions g3w-admin/core/utils/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import copy

from qgis.core import QgsFieldConstraints, Qgis, QgsExpression, QgsExpressionNode
from qgis.PyQt.QtCore import QVariant, QDate, QDateTime
from qgis.PyQt.QtCore import QVariant, QDate, QDateTime, NULL

# relations data type
RELATIONS_ONE_TO_ONE = 'ONE'
Expand Down Expand Up @@ -128,7 +128,7 @@ def editingFormField(fieldName, type=FIELD_TYPE_STRING, editable=True, required=
if required:
ret['validate']['required'] = True

if 'default' in kwargs and kwargs['default']:
if 'default' in kwargs and kwargs['default'] is not None:
ret['input']['options']['default'] = kwargs['default']

if inputType in (FORM_FIELD_TYPE_LAYERPICKER, ) and 'pickerdata' in kwargs:
Expand Down Expand Up @@ -269,7 +269,7 @@ def mapLayerAttributesFromQgisLayer(qgis_layer, **kwargs):

# 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) \
default_value = qgis_layer.defaultValue(field_index) if qgis_layer.defaultValue(field_index) not in (None, NULL) \
else None
else:
default_value = kwargs['default']
Expand Down Expand Up @@ -393,6 +393,7 @@ class APIVectorLayerStructure(object):
_featureLocks = None
_geometryType = None
_fields = None
_editing = None

def __init__(self, **kwargs):

Expand All @@ -402,6 +403,7 @@ def __init__(self, **kwargs):
self.featureLocks = kwargs.get('featureLocks', self._featureLocks)
self.geometryType = kwargs.get('geometryType', self._geometryType)
self.fields = kwargs.get('fields', self._fields)
self.editing = kwargs.get('editing', self._fields)

def setPkField(self, pkField):
self._pkField = pkField
Expand All @@ -415,6 +417,9 @@ def setFeatureLocks(self, featuresLock):
def setFields(self, fields):
self.fields = fields

def setEditing(self, editing):
self.editing = editing

def as_dict(self):

res = {
Expand All @@ -428,6 +433,11 @@ def as_dict(self):
'featurelocks': self.featureLocks,
}

if self.editing:
res['vector'].update({
'editing': self.editing
})

return res


Expand Down
Loading

0 comments on commit 72928a3

Please sign in to comment.