Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
81a3621
Upgrade Elasticsearch from 2.6 to 5.6.
jphilou May 22, 2024
1fbe503
Upgrade Elasticsearch from 2.6 to 5.6
jphilou May 22, 2024
5a77c75
Upgrade Elasticsearch from 2.6 to 5.6
jphilou May 22, 2024
73e2075
Upgrade Elasticsearch from 2.6 to 5.6
jphilou May 22, 2024
600afad
Upgrade Elasticsearch from 2.6 to 5.6
jphilou May 22, 2024
359cb30
Upgrade Elasticsearch from 2.6 to 5.6
jphilou May 22, 2024
d2f90c7
Upgrade Elasticsearch from 2.6 to 5.6
jphilou May 22, 2024
5d7ef8e
Upgrade Elasticsearch from 5.6 to 6.8
jphilou Jun 5, 2024
f4ae03d
Upgrade Elasticsearch from 5.6 to 6.8
jphilou Jun 7, 2024
27109e7
Upgrade Elasticsearch from 5.6 to 6.8
jphilou Jun 7, 2024
7e3428a
Upgrade Elasticsearch from 5.6 to 6.8 :
jphilou Jun 8, 2024
8efbd58
Upgrade Elasticsearch from 5.6 to 6.8 :
jphilou Jun 8, 2024
9ef3c07
Upgrade Elasticsearch from 5.6 to 6.8 :
jphilou Jun 8, 2024
9fe7d75
Merge remote-tracking branch 'origin/elasticsearch-migration-from-5.6…
jphilou Jun 8, 2024
ae35883
Upgrade Elasticsearch from 5.6 to 6.8 :
jphilou Jun 8, 2024
d92072c
Upgrade Elasticsearch from 5.6 to 6.8 :
jphilou Jun 8, 2024
d2e5c62
Upgrade Elasticsearch from 5.6 to 6.8 :
jphilou Jun 8, 2024
8f973a6
Upgrade Elasticsearch from 5.6 to 6.8 :
jphilou Jun 8, 2024
060bcbe
Upgrade Elasticsearch from 5.6 to 6.8 :
jphilou Jun 8, 2024
169283b
Upgrade Elasticsearch from 5.6 to 6.8 :
jphilou Jun 11, 2024
88630bb
Upgrade Elasticsearch from 5.6 to 6.8 :
jphilou Jun 11, 2024
ce8c934
Upgrade Elasticsearch from 5.6 to 6.8 :
jphilou Jun 11, 2024
3d66fcc
Upgrade Elasticsearch from 6.8 to 7.X :
jphilou Jun 13, 2024
c2a8033
Upgrade Elasticsearch from 6.8 to 7.X :
jphilou Jun 13, 2024
7711b0e
Upgrade Elasticsearch from 6.8 to 7.X :
jphilou Jun 14, 2024
dd72c93
Upgrade Elasticsearch from 6.8 to 7.X :
jphilou Jun 14, 2024
dd5d94b
Upgrade Elasticsearch from 6.8 to 7.0 :
jphilou Jun 17, 2024
cc7e96f
Upgrade Elasticsearch from 6.8 to 7.0 :
jphilou Jun 18, 2024
674238c
Upgrade Elasticsearch from 6.8 to 7.0 :
jphilou Jun 18, 2024
72d441e
Upgrade Elasticsearch from 6.8 to 7.0 :
jphilou Jun 18, 2024
bf42a39
Upgrade Elasticsearch from 6.8 to 7.17.22 :
jphilou Jun 25, 2024
2498cd1
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 26, 2024
37055ab
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 26, 2024
0495840
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 26, 2024
1b21516
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 26, 2024
066c555
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 27, 2024
6f86d28
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 27, 2024
f0106ce
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 27, 2024
63eb4ba
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 27, 2024
1168708
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 27, 2024
473a5a5
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 27, 2024
6469818
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 27, 2024
353a6d1
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 27, 2024
18f6482
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 27, 2024
88460d7
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 27, 2024
c2a66e9
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 27, 2024
efb3dda
Upgrade Elasticsearch from 7.17.22 to 8.14 :
jphilou Jun 27, 2024
637717a
Migrate Elasticsearch from 2.4 to 8.19 (merge existing migration branch)
Nayor Jul 19, 2026
9045ce5
Add connect/read timeouts to ES pycurl calls in initializees.py
Nayor Jul 19, 2026
faf4922
Ignore local sandbox env file
Nayor Jul 20, 2026
30c794b
Migrate to Python 3.12 and SQLAlchemy 2.0
Nayor Jul 20, 2026
e2737e8
Fix remaining SQLAlchemy 2.0 runtime incompatibilities in views/scripts
Nayor Jul 20, 2026
aaccdb4
Fix 4D geometry M-coordinate loss and a scalar subquery warning
Nayor Jul 20, 2026
13e685e
fix codacy PR
Nayor Jul 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.9-slim-bookworm
FROM python:3.12-slim-bookworm

# Avoid prompts from apt
ENV DEBIAN_FRONTEND=noninteractive
Expand Down
24 changes: 13 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.9
python-version: "3.12"
- name: Cache pip
uses: actions/cache@v4.3.0
with:
Expand All @@ -70,19 +70,21 @@ jobs:
PGPORT: 5432
- name: Create config from templates
run: make CONFIG=config/env.test load-env
- name: Install java 8 for (old) elasticsearch
uses: actions/setup-java@v5
with:
distribution: "adopt"
java-version: 8
- name: Run (old) elasticsearch
- name: Install elasticsearch & plugin & 1st start
run: |
mkdir /tmp/elasticsearch
wget -O - https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.4.6/elasticsearch-2.4.6.tar.gz | tar xz --directory=/tmp/elasticsearch --strip-components=1
/tmp/elasticsearch/bin/plugin install analysis-icu
/tmp/elasticsearch/bin/elasticsearch --daemonize --path.data /tmp
wget -O - https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.19.18-linux-x86_64.tar.gz | tar xz --directory=/tmp/elasticsearch --strip-components=1
/tmp/elasticsearch/bin/elasticsearch-plugin install analysis-icu
/tmp/elasticsearch/bin/elasticsearch --daemonize
sleep 30 # ElasticSearch takes few seconds to start, make sure it is available when the build script runs
- run: curl -v http://localhost:9200
- name: ElasticSearch settings update after auto configuration at 1st start & restart
run: |
sed -i 's/true/false/g' /tmp/elasticsearch/config/elasticsearch.yml
pgrep -f elasticsearch | xargs kill -9
/tmp/elasticsearch/bin/elasticsearch --daemonize
sleep 30 # Password change requires time
- name: Check elasticsearch
run: curl -v http://localhost:9200
- name: Run tests (with secrets)
# Accept running on pull requests except if from dependabot
if: ${{ github.event_name != 'pull_request' || github.actor != 'dependabot[bot]' }}
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ config/docker
coverage.xml
/config/env.local
project.tar
scripts/anonymize-database/*/
config/env.sandbox
2 changes: 1 addition & 1 deletion alembic_migration/versions/2c90b2e5ca7e_add_chinese.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def upgrade():
# if there is some value in the table. If yes, it's not a test DB and we have to complete them.

conn = op.get_bind()
res = conn.execute("SELECT count(1) FROM guidebook.langs").fetchall()
res = conn.execute(sa.text("SELECT count(1) FROM guidebook.langs")).fetchall()

if res[0][0] != 0:
op.execute("INSERT INTO guidebook.langs VALUES ('zh');")
Expand Down
2 changes: 1 addition & 1 deletion alembic_migration/versions/305b064bdf66_add_slovenian.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

def upgrade():
conn = op.get_bind()
res = conn.execute("SELECT count(1) FROM guidebook.langs").fetchall()
res = conn.execute(sa.text("SELECT count(1) FROM guidebook.langs")).fetchall()

if res[0][0] != 0:
op.execute("INSERT INTO guidebook.langs VALUES ('sl');")
Expand Down
17 changes: 13 additions & 4 deletions alembic_migration/versions/38df9393c9a9_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,18 +702,27 @@ def upgrade():
op.create_table('documents_geometries',
sa.Column('version', sa.Integer(), nullable=False),
sa.Column('document_id', sa.Integer(), nullable=False),
sa.Column('geom', geoalchemy2.types.Geometry(geometry_type='POINT', srid=3857, management=True), nullable=True),
sa.Column('geom_detail', geoalchemy2.types.Geometry(srid=3857, management=True, use_typmod=False), nullable=True),
sa.Column('geom', geoalchemy2.types.Geometry(geometry_type='POINT', srid=3857), nullable=True),
sa.Column('geom_detail', geoalchemy2.types.Geometry(srid=3857, use_typmod=False, spatial_index=False), nullable=True),
sa.ForeignKeyConstraint(['document_id'], ['guidebook.documents.document_id'], ),
sa.PrimaryKeyConstraint('document_id'),
schema='guidebook'
)
# geom_detail uses use_typmod=False (see below, dimension constraint is dropped to allow
# mixed 2D/3D/4D geometries), which routes it through geoalchemy2's legacy "managed"
# AddGeometryColumn() codepath. That codepath's automatic spatial-index creation collides
# with alembic's own index creation (the Index object re-attaches itself to the table),
# raising a DuplicateTable error - so spatial_index is disabled above and the index is
# created explicitly here instead.
op.create_index(
'idx_documents_geometries_geom_detail', 'documents_geometries', ['geom_detail'],
unique=False, schema='guidebook', postgresql_using='gist')
op.create_table('documents_geometries_archives',
sa.Column('version', sa.Integer(), nullable=False),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('document_id', sa.Integer(), nullable=False),
sa.Column('geom', geoalchemy2.types.Geometry(geometry_type='POINT', srid=3857, spatial_index=False, management=True), nullable=True),
sa.Column('geom_detail', geoalchemy2.types.Geometry(srid=3857, spatial_index=False, management=True, use_typmod=False), nullable=True),
sa.Column('geom', geoalchemy2.types.Geometry(geometry_type='POINT', srid=3857, spatial_index=False), nullable=True),
sa.Column('geom_detail', geoalchemy2.types.Geometry(srid=3857, spatial_index=False, use_typmod=False), nullable=True),
sa.ForeignKeyConstraint(['document_id'], ['guidebook.documents.document_id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('version', 'document_id', name='uq_documents_geometries_archives_document_id_version_lang'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def upgrade():
sa.Column('stoparea_name', sa.String(), nullable=False),
sa.Column('line', sa.String(), nullable=False),
sa.Column('operator', sa.String(), nullable=False),
sa.Column('geom', geoalchemy2.types.Geometry(geometry_type='POINT', srid=3857, management=True), nullable=True),
sa.Column('geom', geoalchemy2.types.Geometry(geometry_type='POINT', srid=3857), nullable=True),
schema='guidebook'
)

Expand Down
28 changes: 26 additions & 2 deletions c2corg_api/ext/colander_ext.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
# Inspired from the c2cgeoform colander extension
# https://github.com/camptocamp/c2cgeoform/blob/master/c2cgeoform/ext/

import struct

from colander import (null, Invalid, SchemaType)

from geoalchemy2 import WKBElement
from geomet import wkb
from geoalchemy2.compat import buffer, bytes
import geojson
from numbers import Number

# EWKB SRID flag, see https://git.osgeo.org/gitea/postgis/postgis/raw/
# master/doc/ZMSgeoms.txt
_EWKB_SRID_FLAG = 0x20000000


class Geometry(SchemaType):
""" A Colander type meant to be used with GeoAlchemy 2 geometry columns.
Expand Down Expand Up @@ -78,16 +83,35 @@ def cstruct_children(self, node, cstruct):

def wkbelement_from_geojson(geojson, srid):
geometry = wkb.dumps(geojson, big_endian=False)
if srid and srid > 0:
# Embed the SRID directly in the WKB header, turning it into EWKB.
# geoalchemy2 binds a non-extended WKBElement by converting it
# through Shapely, which does not support M (4th dimension)
# coordinates and silently drops them. A WKBElement built from real
# EWKB is detected as already-extended and bound as a hex string
# directly instead, preserving all dimensions.
geometry = _to_ewkb(geometry, srid)
return from_wkb(geometry, srid)


def _to_ewkb(wkb_bytes, srid):
byte_order = wkb_bytes[0]
fmt = '<I' if byte_order == 1 else '>I'
geom_type = struct.unpack(fmt, wkb_bytes[1:5])[0]
ewkb_type = geom_type | _EWKB_SRID_FLAG
return (
wkb_bytes[0:1] + struct.pack(fmt, ewkb_type) +
struct.pack(fmt, srid) + wkb_bytes[5:]
)


def geojson_from_wkbelement(wkb_element):
geometry = wkb.loads(bytes(wkb_element.data))
return geojson.dumps(geometry)


def from_wkb(wkb, srid=-1):
return WKBElement(buffer(wkb), srid=srid)
return WKBElement(memoryview(wkb), srid=srid)


def is_valid_geometry(obj):
Expand Down
3 changes: 1 addition & 2 deletions c2corg_api/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy.orm import (
declarative_base,
scoped_session,
sessionmaker,
)
Expand Down
6 changes: 3 additions & 3 deletions c2corg_api/models/area_association.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def update_area(area, reset=False):
# ignore forwarded areas
return

area_geom = select([DocumentGeometry.geom_detail]). \
area_geom = select(DocumentGeometry.geom_detail). \
where(DocumentGeometry.document_id == area.document_id)
intersecting_documents = DBSession. \
query(
Expand Down Expand Up @@ -111,9 +111,9 @@ def update_areas_for_document(document, reset=False):
# ignore forwarded areas
return

document_geom = select([DocumentGeometry.geom]). \
document_geom = select(DocumentGeometry.geom). \
where(DocumentGeometry.document_id == document.document_id)
document_geom_detail = select([DocumentGeometry.geom_detail]). \
document_geom_detail = select(DocumentGeometry.geom_detail). \
where(DocumentGeometry.document_id == document.document_id)
intersecting_areas = DBSession. \
query(
Expand Down
36 changes: 18 additions & 18 deletions c2corg_api/models/association_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,21 @@ def _get_select_waypoints_for_routes():
route_type = text('\'' + ROUTE_TYPE + '\'')

select_linked_waypoints = \
select([
select(
Association.child_document_id.label('route_id'),
Association.parent_document_id.label('waypoint_id')
]). \
). \
where(
and_(
Association.parent_document_type == waypoint_type,
Association.child_document_type == route_type)). \
cte('linked_waypoints')

select_waypoint_parents = \
select([
select(
select_linked_waypoints.c.route_id,
Association.parent_document_id.label('waypoint_id')
]). \
). \
select_from(join(
select_linked_waypoints,
Association,
Expand All @@ -56,10 +56,10 @@ def _get_select_waypoints_for_routes():
cte('waypoint_parents')

select_waypoint_grandparents = \
select([
select(
select_waypoint_parents.c.route_id,
Association.parent_document_id.label('waypoint_id')
]). \
). \
select_from(join(
select_waypoint_parents,
Association,
Expand Down Expand Up @@ -89,12 +89,12 @@ def _get_select_waypoints_for_routes_aggregated():
"""
all_waypoints = _get_select_waypoints_for_routes()
return \
select([
select(
all_waypoints.c.route_id.label('route_id'),
func.array_agg(
all_waypoints.c.waypoint_id,
type_=postgresql.ARRAY(Integer)).label('waypoint_ids')
]). \
). \
select_from(all_waypoints). \
group_by(all_waypoints.c.route_id)

Expand Down Expand Up @@ -135,10 +135,10 @@ def _get_select_waypoints_for_outings_aggregated():
route_type = text('\'' + ROUTE_TYPE + '\'')
all_waypoints_for_routes = _get_select_waypoints_for_routes()
waypoints_for_outings = \
select([
select(
Association.child_document_id.label('outing_id'),
all_waypoints_for_routes.c.waypoint_id
]). \
). \
select_from(join(
Association,
all_waypoints_for_routes,
Expand All @@ -150,12 +150,12 @@ def _get_select_waypoints_for_outings_aggregated():
))). \
cte('waypoints_for_outings')
return \
select([
select(
waypoints_for_outings.c.outing_id.label('outing_id'),
func.array_agg(
waypoints_for_outings.c.waypoint_id,
type_=postgresql.ARRAY(Integer)).label('waypoint_ids')
]). \
). \
select_from(waypoints_for_outings). \
group_by(waypoints_for_outings.c.outing_id)

Expand Down Expand Up @@ -189,12 +189,12 @@ def _get_select_users_for_outings_aggregated():
outing_type = text('\'' + OUTING_TYPE + '\'')
user_type = text('\'' + USERPROFILE_TYPE + '\'')
return \
select([
select(
Association.child_document_id.label('outing_id'),
func.array_agg(
Association.parent_document_id,
type_=postgresql.ARRAY(Integer)).label('user_ids')
]). \
). \
select_from(Association). \
where(and_(
Association.parent_document_type == user_type,
Expand Down Expand Up @@ -232,12 +232,12 @@ def _get_select_routes_for_outings_aggregated():
outing_type = text('\'' + OUTING_TYPE + '\'')
route_type = text('\'' + ROUTE_TYPE + '\'')
return \
select([
select(
Association.child_document_id.label('outing_id'),
func.array_agg(
Association.parent_document_id,
type_=postgresql.ARRAY(Integer)).label('route_ids')
]). \
). \
select_from(Association). \
where(and_(
Association.parent_document_type == route_type,
Expand Down Expand Up @@ -273,12 +273,12 @@ def _get_select_users_for_routes_aggregated():
associated users.
"""
return \
select([
select(
DocumentTag.document_id.label('route_id'),
func.array_agg(
DocumentTag.user_id,
type_=postgresql.ARRAY(Integer)).label('user_ids')
]). \
). \
select_from(DocumentTag). \
group_by(DocumentTag.document_id)

Expand Down
17 changes: 8 additions & 9 deletions c2corg_api/models/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,14 @@ def update(self, other):
"""
copy_attributes(other, self, Document._ATTRIBUTES_WHITELISTED)

for locale_in in other.locales:
locale = self.get_locale(locale_in.lang)
if locale:
locale.update(locale_in)
locale.document_id = self.document_id
else:
self.locales.append(locale_in)
with DBSession.no_autoflush:
for locale_in in other.locales:
locale = self.get_locale(locale_in.lang)
if locale:
locale.update(locale_in)
locale.document_id = self.document_id
else:
self.locales.append(locale_in)

if other.geometry:
if self.geometry:
Expand Down Expand Up @@ -295,7 +296,6 @@ def geom(self):
return Column(
Geometry(
geometry_type='POINT', srid=3857, dimension=2,
management=True,
spatial_index=self.__name__ != 'ArchiveDocumentGeometry'),
info={
'colanderalchemy': {
Expand All @@ -309,7 +309,6 @@ def geom_detail(self):
return Column(
Geometry(
geometry_type='GEOMETRY', srid=3857,
management=True,
use_typmod=False,
spatial_index=self.__name__ != 'ArchiveDocumentGeometry'),
info={
Expand Down
Loading
Loading