Skip to content
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
b2d3301
[CI] add CI pipeline as in github
lgourdin Jul 23, 2025
24c84bb
[CI] Update CI after reverting deploy method in 752b2ea17e
lgourdin Jul 28, 2025
0dd9ad6
[hotfix] add distincts scripts and change sqlalchemy events
Floriane-jandot Jul 31, 2025
905fa55
[CI] source env for CI
lgourdin Jul 31, 2025
b11143b
[fix] support updating TC when waypoint is modified and optimize navi…
lgourdin Sep 11, 2025
288e183
[scripts] make distinct navitia scripts executable
lgourdin Sep 11, 2025
1d0480f
[GHCI] source env file before launching pytest
lgourdin Sep 16, 2025
fbaf190
[chore] replace deprecated log.warn by log.warning
lgourdin Oct 10, 2025
b6caaa4
[fix] delete existing stopareas for waypoint if moved in an unserved …
lgourdin Oct 10, 2025
6a88e18
[fix] formatting error in log string that was triggering 500 errors w…
lgourdin Oct 10, 2025
3a540a1
[chore] fix linting issues
lgourdin Oct 10, 2025
8233f1d
[CI] run test script with set -e
lgourdin Oct 10, 2025
bb40e9e
[new feature] add reachable routes route
Nov 24, 2025
830581a
[new feature] add reachable waypoints route
Nov 24, 2025
ddb2f03
[new feature] add coverage table
Nov 24, 2025
bcb9a97
[new feature] add script to retrieve and insert coverages
Nov 24, 2025
e28025a
[fix] fixed a bug when trying to insert doc that is not a waypoint
Nov 28, 2025
94e05a3
[new feature] added routes to get journey/isochrone reachable doc
Nov 28, 2025
eb86874
[fix] add q=title filter in reachable doc routes
Nov 28, 2025
cfa6f73
[fix] make sure waypoints are not reachable if journeys departure day…
Nov 28, 2025
5f13610
[refactor] organization + documentation : factorization and helper fu…
Dec 1, 2025
479e003
[fix] fix issue when filtering with several areas
Dec 2, 2025
2a1a847
[new feature] added route to get coverage for area / area in isochrone
Dec 3, 2025
2bd8db5
[fix] fixed a bug where bbox applied to routes was not done on the ac…
Dec 3, 2025
72df94c
Merge branch 'gp/dev'
Dec 3, 2025
4950c16
[lint] flake8 linting
Dec 3, 2025
8a55a1a
[CI] don't create dir if exists
lgourdin Dec 3, 2025
9054d05
[CI] clean after test and debug comment
lgourdin Dec 3, 2025
971fea1
Merge branch 'main' of https://git.smart-origin.com/SmartOrigin/c2c_v…
Dec 3, 2025
c67b0b3
[fix] fix CI
Dec 3, 2025
e2b423e
[fix] Fix CI
Dec 3, 2025
10057e6
[cleanup] remove S/O files before merging with upstream
lgourdin Dec 10, 2025
3bb8456
[fix] fix #1834 : durations filter wasn't working
Dec 4, 2025
3b999d3
[improve] add range filter to reduce time of computation
Dec 4, 2025
0ba34c3
[fix] fix title query for waypoints in Iitnévert
Dec 5, 2025
3d6d24c
[fix] fix langs filter not working
Dec 8, 2025
7dd7b2b
[fix] fixed incorrect join with area association
Dec 8, 2025
90bb53d
[fix] fix title filter when no other filters
Dec 8, 2025
37e245d
[fix] fix lang filter
Dec 9, 2025
7af8240
[clean] remove debug log
Dec 10, 2025
499b96b
[new feature] add job handling for journey queries to monitor their p…
Dec 10, 2025
53ff11f
[lint] fix linter -> func names in lower case
Dec 10, 2025
2f5d6cb
[fix] fix search doc missing for coverage
Dec 10, 2025
c32e727
Merge branch 'master' into smart-origin/revue_parcours_3
lgourdin Dec 10, 2025
ae0b7ab
[refactor] Use of Elastic Search filters in reachable routes instead …
Dec 15, 2025
52e451e
[fix] fix sort + when no results found
Dec 17, 2025
02f362d
[lint] fix flake8 linter
Dec 18, 2025
3893e0f
[fix] fix too restrictive offset and limit for navitia reachable docs
Jan 6, 2026
7b381cc
[improve] return error id when 404 for navitia/journeys route
Jan 9, 2026
7eec110
[clean] fix noqa, translate french comms to english, remove use of f …
Jan 12, 2026
00438cb
[doc] update SO README
lgourdin Jan 13, 2026
123a739
[fix] fix codacy issues
Jan 16, 2026
d06f85d
[clean] remove unused route
Jan 22, 2026
d9bb042
[fix] fix update_navitia_coverage script for prod usage
Jan 22, 2026
7d44a8d
Merge branch 'master' into smart-origin/revue_parcours_3
lgourdin Jan 22, 2026
9fdaba5
[clean] fix unused variable
Jan 22, 2026
1355038
[clean] remove useless parameters
Jan 26, 2026
cf3bad9
[doc] add coverage doc
Jan 26, 2026
414ed36
[clean] remove useless class
Jan 26, 2026
49220df
[refactor] use of BASE_URL constant for navitia api
Jan 26, 2026
b958616
[clean] remove archive coverage class as coverages are not meant to b…
Jan 26, 2026
5e462ec
[doc] update S/O readme
Jan 26, 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
39 changes: 39 additions & 0 deletions alembic_migration/versions/27bf1b7197a6_add_coverages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""Add coverages

Revision ID: 335e0bc4df28
Revises: 6b40cb9c7c3d
Create Date: 2025-11-18 14:15:26.377504

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '335e0bc4df28'
down_revision = '6b40cb9c7c3d'
branch_labels = None
depends_on = None

def upgrade():
coverage_type = sa.Enum('fr-idf', 'fr-ne', 'fr-nw', 'fr-se', 'fr-sw', name='coverage_type', schema='guidebook')
op.create_table('coverages',
sa.Column('coverage_type', coverage_type, nullable=True),
sa.Column('document_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['document_id'], ['guidebook.documents.document_id'], ),
sa.PrimaryKeyConstraint('document_id'),
schema='guidebook'
)
op.create_table('coverages_archives',
sa.Column('coverage_type', coverage_type, nullable=True),
sa.Column('id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['id'], ['guidebook.documents_archives.id'], ),
sa.PrimaryKeyConstraint('id'),
schema='guidebook'
)


def downgrade():
op.drop_table('coverages_archives', schema='guidebook')
op.drop_table('coverages', schema='guidebook')
sa.Enum('fr-idf', 'fr-ne', 'fr-nw', 'fr-se', 'fr-sw', name='coverage_type', schema='guidebook').drop(op.get_bind())
81 changes: 44 additions & 37 deletions c2corg_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,26 +101,31 @@ def configure_anonymous(settings, config):
config.registry.anonymous_user_id = account_id


def delete_waypoint_stopareas(connection, waypoint_id):
# Delete existing stopareas for waypoint
delete_relation_query = text(
"""
DELETE FROM guidebook.waypoints_stopareas
WHERE waypoint_id = :waypoint_id
"""
)

connection.execute(
delete_relation_query,
{
"waypoint_id": waypoint_id,
},
)


@event.listens_for(DocumentGeometry, "after_insert")
@event.listens_for(DocumentGeometry, "after_update")
def process_new_waypoint(mapper, connection, geometry):
"""Processes a new waypoint to find its public transports after
inserting it into documents_geometries."""
log.debug("Entering process_new_waypoint callback")
# Check if document is a waypoint
waypoint_id = geometry.document_id

max_distance_waypoint_to_stoparea = int(
os.getenv("MAX_DISTANCE_WAYPOINT_TO_STOPAREA")
)
walking_speed = float(os.getenv("WALKING_SPEED"))
max_stop_area_for_1_waypoint = int(os.getenv("MAX_STOP_AREA_FOR_1_WAYPOINT")) # noqa: E501
api_key = os.getenv("NAVITIA_API_KEY")
max_duration = int(max_distance_waypoint_to_stoparea / walking_speed)

# Augmenter le nombre d'arrêts récupérés pour avoir plus de choix (comme dans le bash) # noqa: E501
max_stop_area_fetched = max_stop_area_for_1_waypoint * 3

# Check if document is a waypoint
document_type = connection.execute(
text(
"""
Expand All @@ -134,6 +139,18 @@ def process_new_waypoint(mapper, connection, geometry):
if document_type != "w":
return

log.debug("Entering process_new_waypoint callback")
max_distance_waypoint_to_stoparea = int(
os.getenv("MAX_DISTANCE_WAYPOINT_TO_STOPAREA")
)
walking_speed = float(os.getenv("WALKING_SPEED"))
max_stop_area_for_1_waypoint = int(os.getenv("MAX_STOP_AREA_FOR_1_WAYPOINT")) # noqa: E501
api_key = os.getenv("NAVITIA_API_KEY")
max_duration = int(max_distance_waypoint_to_stoparea / walking_speed)

# Augmenter le nombre d'arrêts récupérés pour avoir plus de choix (comme dans le bash) # noqa: E501
max_stop_area_fetched = max_stop_area_for_1_waypoint * 3

waypoint_type = connection.execute(
text(
"""
Expand Down Expand Up @@ -182,7 +199,8 @@ def process_new_waypoint(mapper, connection, geometry):
places_data = places_response.json()

if "places_nearby" not in places_data or not places_data["places_nearby"]:
log.warning(f"No Navitia stops found for the waypoint {waypoint_id}")
log.warning(f"No Navitia stops found for the waypoint {waypoint_id}; deleting previously registered stops") # noqa: E501
delete_waypoint_stopareas(connection, waypoint_id)
return

# --- NOUVEAU : Filtrage par diversité de transport (comme dans bash) ---
Expand Down Expand Up @@ -226,23 +244,11 @@ def process_new_waypoint(mapper, connection, geometry):
known_transports.update(current_stop_transports)
selected_count += 1

# Delete existing stopareas for waypoint
delete_relation_query = text(
"""
DELETE FROM guidebook.waypoints_stopareas
WHERE waypoint_id = :waypoint_id
"""
)

connection.execute(
delete_relation_query,
{
"waypoint_id": waypoint_id,
},
)

log.warning(f"Selected {selected_count} stops out of {len(places_data['places_nearby'])} for waypoint {waypoint_id}") # noqa: E501

log.warning("Deleting previously registered stops")
delete_waypoint_stopareas(connection, waypoint_id)

# Traiter uniquement les arrêts sélectionnés
for place in selected_stops:
stop_id = place["id"]
Expand Down Expand Up @@ -363,7 +369,7 @@ def calculate_route_duration(mapper, connection, route):
jour du script bash.
"""
route_id = route.document_id
log.warn(f"Calculating duration for route ID: {route_id}")
log.warning(f"Calculating duration for route ID: {route_id}")

# Récupération des activités et normalisation des dénivelés
activities = route.activities if route.activities is not None else []
Expand Down Expand Up @@ -440,15 +446,15 @@ def _calculate_climbing_duration(route, height_diff_up, height_diff_down, route_
return None # Pas de données utilisables pour le calcul

dm = dp / v_diff
log.warn(f"Calculated climbing route duration for route {route_id} (activity {activity}, no difficulties_height): {dm:.2f} hours") # noqa: E501
log.warning(f"Calculated climbing route duration for route {route_id} (activity {activity}, no difficulties_height): {dm:.2f} hours") # noqa: E501
return dm

# CAS 2: Le dénivelé des difficultés est renseigné
d_diff = float(difficulties_height)

# Vérification de cohérence
if dp > 0 and d_diff > dp:
log.warn(f"Route {route_id}: Inconsistent difficulties_height ({d_diff}m) > height_diff_up ({dp}m). Returning NULL.") # noqa: E501
log.warning(f"Route {route_id}: Inconsistent difficulties_height ({d_diff}m) > height_diff_up ({dp}m). Returning NULL.") # noqa: E501
return None

# Calcul du temps des difficultés
Expand All @@ -466,7 +472,7 @@ def _calculate_climbing_duration(route, height_diff_up, height_diff_down, route_
# Calcul final selon le cadrage: max(t_diff, t_app) + 0.5 * min(t_diff, t_app) # noqa: E501
dm = max(t_diff, t_app) + 0.5 * min(t_diff, t_app)

log.warn(f"Calculated climbing route duration for route {route_id} (activity {activity}): {dm:.2f} hours (t_diff={t_diff:.2f}, t_app={t_app:.2f})") # noqa: E501
log.warning(f"Calculated climbing route duration for route {route_id} (activity {activity}): {dm:.2f} hours (t_diff={t_diff:.2f}, t_app={t_app:.2f})") # noqa: E501
return dm


Expand Down Expand Up @@ -517,7 +523,7 @@ def _calculate_standard_duration(activity, route, height_diff_up, height_diff_do
else:
dm = (dv / 2) + dh

log.warn(f"Calculated standard route duration for route {route_id} (activity {activity}): {dm:.2f} hours") # noqa: E501
log.warning(f"Calculated standard route duration for route {route_id} (activity {activity}): {dm:.2f} hours") # noqa: E501
return dm


Expand All @@ -531,8 +537,9 @@ def _validate_and_convert_duration(min_duration, route_id):
or min_duration < min_duration_hours
or min_duration > max_duration_hours
):
log.warn(
f"Route {route_id}: Calculated duration ({min_duration:.2f} hours if not None) is out of bounds (min={min_duration_hours}h, max={max_duration_hours}h) or NULL. Setting duration to NULL." # noqa: E501
min_duration_str = "None" if min_duration is None else f"{min_duration:.2f}" # noqa: E501
log.warning(
f"Route {route_id}: Calculated duration (min_duration={min_duration_str}) is out of bounds (min={min_duration_hours}h, max={max_duration_hours}h) or NULL. Setting duration to NULL." # noqa: E501
)
return None

Expand All @@ -551,6 +558,6 @@ def _update_route_duration(connection, route_id, calculated_duration_in_days):
),
{"duration": calculated_duration_in_days, "route_id": route_id},
)
log.warn(
log.warning(
f"Route {route_id}: Database updated with calculated_duration = {calculated_duration_in_days} days." # noqa: E501
)
3 changes: 3 additions & 0 deletions c2corg_api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class BaseMixin(object):

# all models, for which tables should be created, must be listed here:
from c2corg_api.models import document # noqa
from c2corg_api.models import coverage # noqa
from c2corg_api.models import waypoint # noqa
from c2corg_api.models import route # noqa
from c2corg_api.models import document_history # noqa
Expand Down Expand Up @@ -59,6 +60,7 @@ class BaseMixin(object):
topo_map.MAP_TYPE: topo_map.TopoMap,
area.AREA_TYPE: area.Area,
outing.OUTING_TYPE: outing.Outing,
coverage.COVERAGE_TYPE: coverage.Coverage,
}

document_locale_types = {
Expand All @@ -72,4 +74,5 @@ class BaseMixin(object):
topo_map.MAP_TYPE: document.DocumentLocale,
area.AREA_TYPE: document.DocumentLocale,
outing.OUTING_TYPE: outing.OutingLocale,
coverage.COVERAGE_TYPE: document.DocumentLocale,
}
8 changes: 8 additions & 0 deletions c2corg_api/models/common/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -785,3 +785,11 @@
'highline',
'waterline'
]

coverage_types = [
'fr-idf',
'fr-ne',
'fr-nw',
'fr-se',
'fr-sw'
]
1 change: 1 addition & 0 deletions c2corg_api/models/common/document_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
WAYPOINT_TYPE = 'w'
BOOK_TYPE = 'b'
XREPORT_TYPE = 'x'
COVERAGE_TYPE = 'v'

ALL = [
AREA_TYPE, ARTICLE_TYPE, IMAGE_TYPE, MAP_TYPE, OUTING_TYPE, ROUTE_TYPE,
Expand Down
21 changes: 21 additions & 0 deletions c2corg_api/models/common/fields_coverage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
DEFAULT_FIELDS = [
'coverage_type'
'geometry.geom_detail'
]

DEFAULT_REQUIRED = [
'coverage_type',
'geometry',
'geometry.geom_detail'
]

LISTING_FIELDS = [
'coverage_type',
'geometry.geom_detail'
]

fields_coverage = {
'fields': DEFAULT_FIELDS,
'required': DEFAULT_REQUIRED,
'listing': LISTING_FIELDS
}
36 changes: 36 additions & 0 deletions c2corg_api/models/common/sortable_search_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,39 @@
'slope_40_45': 3,
'slope_gt_45': 4
}


search_attr_by_field = {
'quality': sortable_quality_types,
'access_time': sortable_access_times,
'paragliding_rating': sortable_paragliding_ratings,
'durations': sortable_route_duration_types,
'ski_rating': sortable_ski_ratings,
'ski_exposition': sortable_exposition_ratings,
'labande_ski_rating': sortable_labande_ski_ratings,
'labande_global_rating': sortable_global_ratings,
'global_rating': sortable_global_ratings,
'engagement_rating': sortable_engagement_ratings,
'risk_rating': sortable_risk_ratings,
'equipment_rating': sortable_equipment_ratings,
'ice_rating': sortable_ice_ratings,
'mixed_rating': sortable_mixed_ratings,
'exposition_rock_rating': sortable_exposition_rock_ratings,
'rock_free_rating': sortable_climbing_ratings,
'rock_required_rating': sortable_climbing_ratings,
'aid_rating': sortable_aid_ratings,
'via_ferrata_rating': sortable_via_ferrata_ratings,
'hiking_rating': sortable_hiking_ratings,
'hiking_mtb_exposition': sortable_exposition_ratings,
'snowshoe_rating': sortable_snowshoe_ratings,
'mtb_up_rating': sortable_mtb_up_ratings,
'mtb_down_rating': sortable_mtb_down_ratings,
'frequentation': sortable_frequentation_types,
'condition_rating': sortable_condition_ratings,
'snow_quality': sortable_snow_quality_ratings,
'snow_quantity': sortable_snow_quality_ratings,
'glacier_rating': sortable_glacier_ratings,
'severity': sortable_severities,
'avalanche_level': sortable_avalanche_levels,
'avalanche_slope': sortable_avalanche_slopes
}
Loading
Loading