Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 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
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corriger l'erreur plutôt que de l'ignorer
idem sur tous les # 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idem

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Les commentaires devraient être en anglais, comme pour le reste du code

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
Copy link
Contributor

@Nayor Nayor Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Préférer l'utilisation de %s ou .format pour une meilleure homogénéité du code (les f-string ne sont utilisés nulle part ailleurs)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Est-ce vraiment un warning ? C'est plutôt un comportement normal qui ne nécessite pas d'attention particulière ? (dans ce cas, préférer un .info)

delete_waypoint_stopareas(connection, waypoint_id)
return

# --- NOUVEAU : Filtrage par diversité de transport (comme dans bash) ---
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

traduire en anglias

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idem: format + # noqa: E501

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

éviter les f-string (%s ou .format)


log.warning("Deleting previously registered stops")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idem -> log.info

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