Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
// The .devcontainer/docker-compose.yml file contains any overrides you need/want to make.
"dockerComposeFile": [
"../.devcontainer/docker-compose.yml",
"../docker-compose-qr-shortener.yml"
"../.devcontainer/docker-compose-qr-shortener.yml"
// "../.devcontainer/docker-compose-superset.yml"
],
// The 'service' property is the name of the service for the container that VS Code should
// use. Update this value and .devcontainer/docker-compose.yml to the real service name.
Expand All @@ -31,7 +32,7 @@
]
}
},
// "initializeCommand": "cp ./.dockerignore-development ./.dockerignore"
"initializeCommand": "bash .devcontainer/setup-network.sh"
// Uncomment the next line if you want start specific services in your Docker Compose config.
// "runServices": [],
// Uncomment the next line if you want to keep your containers running after VS Code shuts down.
Expand Down
19 changes: 19 additions & 0 deletions .devcontainer/docker-compose-qr-shortener.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
services:
url_shortener:
image: ghcr.io/teritorio/qr-shortener:master
volumes:
- ./data:/data
environment:
RACK_ENV: production
URL_BASE: ${URL_SHORTNER_PUBLIC}
STORAGE_PATH: /data
restart: unless-stopped
ports:
- 127.0.0.1:8635:8635
networks:
- public-network

networks:
public-network:
name: public-network
external: true
52 changes: 52 additions & 0 deletions .devcontainer/docker-compose-superset.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
services:
superset:
image: superset:3.1.3
build:
context: ../docker/superset
depends_on:
- db
- superset_redis
entrypoint: ["/app/pythonpath/entrypoint.sh"]
environment:
SUPERSET_SECRET_KEY: lkfzoiezhfoizehfoizhoihfopizajfoizafjpoizaufepoaz
DATABASE_DIALECT: postgresql+psycopg2
POSTGRES_DB: ${POSTGRES_DB:-planner}
POSTGRES_HOST: db
POSTGRES_PORT: 5432
POSTGRES_USER: ${POSTGRES_USER:-planner}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-planner}
REDIS_HOST: superset_redis
SUPERSET_DB: superset
SUPERSET_ADMIN_USERNAME: admin
SUPERSET_ADMIN_EMAIL: admin@superset.com
SUPERSET_ADMIN_PASSWORD: admin
MAPBOX_API_KEY: ${SUPERSET_MAPBOX_API_KEY}
volumes:
- ../docker/superset:/app/pythonpath
ports:
- 127.0.0.1:8088:8088
restart: unless-stopped
networks:
- public-network

superset_redis:
image: redis:${REDIS_VERSION:-7-alpine}
restart: unless-stopped
networks:
- public-network

superset_nginx:
image: superset_nginx
build:
context: ../docker/superset_nginx
depends_on:
- web
- superset
restart: unless-stopped
networks:
- public-network

networks:
public-network:
name: public-network
external: true
41 changes: 30 additions & 11 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ x-web: &web
LOG_FORMAT: ${LOG_FORMAT}
URL_SHORTENER: ${URL_SHORTENER:-http://url_shortener:8635}
AVAILABLE_SOLVERS: ${AVAILABLE_SOLVERS:-}
POSTGRES_USER: ${POSTGRES_USER:-planner}
POSTGRES_DB: ${POSTGRES_DB:-planner}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-planner}
AZURE_CLIENT_ID: ${AZURE_CLIENT_ID}
AZURE_CLIENT_SECRET: ${AZURE_CLIENT_SECRET}
AZURE_TENANT_ID: ${AZURE_TENANT_ID}
Expand All @@ -25,54 +28,70 @@ x-web: &web
- ../.devcontainer/production.rb:/srv/app/config/environments/production.rb
- ../docker/uploads:/srv/app/public/uploads
depends_on:
- db
- redis-cache
db:
condition: service_healthy
redis-cache:
condition: service_started
restart: unless-stopped

services:
db:
# Place a dump file in the docker/dumps directory to load the database from it if no database is found
# Devcontainer might display that the container is not running, but the database might be loading the dump
# Or the web container might be running the migrations. Wait until the database is ready.
image: postgis/postgis:15-3.5

environment:
- POSTGRES_HOST_AUTH_METHOD=trust
- POSTGRES_USER=${POSTGRES_USER:-planner}
- POSTGRES_DB=${POSTGRES_DB:-planner}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-planner}
volumes:
- pg_data:/var/lib/postgresql/data
- ../docker/dumps:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-planner} -d ${POSTGRES_DB:-planner} && psql -U ${POSTGRES_USER:-planner} -d ${POSTGRES_DB:-planner} -c '\\dt' | grep -q public"]
start_interval: 5s
start_period: 60s
interval: 60s
timeout: 10s
retries: 5
restart: unless-stopped
networks:
- planner-network
- public-network

redis-cache:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --save ""
networks:
- planner-network
- public-network

web:
<<: *web
command: bash -c "LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 bundle exec rake db:prepare && bundle exec puma -v -p 8080 --pidfile 'server.pid' -t ${PUMA_WORKERS:-0:1}"
networks:
- planner-network
- public-network

delayed-job:
<<: *web
command: bash -c "bundle exec rails runner 'Delayed::Job.delete_all' && LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 bundle exec './bin/delayed_job run'"
depends_on:
- db
- redis-cache
db:
condition: service_healthy
redis-cache:
condition: service_started
web:
condition: service_started
deploy:
replicas: ${DELAYED_JOB_REPLICAS:-1}
networks:
- planner-network
- public-network

volumes:
pg_data:

networks:
planner-network:
external: false

public-network:
name: public-network
attachable: true
75 changes: 75 additions & 0 deletions .devcontainer/setup-network.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/bin/bash
# Script to create the shared Docker network public-network
# To be executed once before using Docker Compose projects

set -e

# Get the directory where this script is located
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"

# Change to workspace directory to ensure we're in the right context
cd "$WORKSPACE_DIR"

NETWORK_NAME="public-network"

echo "Checking network $NETWORK_NAME..."

# Clean up stopped containers that might reference old networks
echo "Cleaning up stopped containers..."
docker container prune -f >/dev/null 2>&1 || true

# Clean up orphaned networks, but preserve public-network
echo "Cleaning up orphaned networks (preserving $NETWORK_NAME)..."
# Get list of networks to prune (excluding public-network)
NETWORKS_TO_PRUNE=$(docker network ls --filter "type=custom" --format "{{.Name}}" | grep -v "^${NETWORK_NAME}$" | grep -v "^bridge$" | grep -v "^host$" | grep -v "^none$" || true)
if [ -n "$NETWORKS_TO_PRUNE" ]; then
# Prune only networks that are not in use
for net in $NETWORKS_TO_PRUNE; do
if ! docker network inspect "$net" --format '{{range .Containers}}{{.Name}}{{end}}' 2>/dev/null | grep -q .; then
docker network rm "$net" 2>/dev/null || true
fi
done
fi

# Check if network exists, create if not
if docker network inspect "$NETWORK_NAME" >/dev/null 2>&1; then
echo "✓ Network $NETWORK_NAME already exists"
NETWORK_ID=$(docker network inspect "$NETWORK_NAME" --format '{{.Id}}')
echo " ID: $NETWORK_ID"
docker network inspect "$NETWORK_NAME" --format ' Driver: {{.Driver}}'
docker network inspect "$NETWORK_NAME" --format ' Scope: {{.Scope}}'

# Check for containers still using this network (should be none if external)
CONTAINERS=$(docker ps -a --filter "network=$NETWORK_NAME" --format "{{.Names}}" 2>/dev/null || true)
if [ -n "$CONTAINERS" ]; then
echo " Warning: Found containers using this network:"
echo "$CONTAINERS" | sed 's/^/ /'
fi
else
echo "Creating network $NETWORK_NAME..."
docker network create "$NETWORK_NAME"
echo "✓ Network $NETWORK_NAME created successfully"
fi

# Verify network exists
if ! docker network inspect "$NETWORK_NAME" >/dev/null 2>&1; then
echo "ERROR: Failed to create or verify network $NETWORK_NAME" >&2
exit 1
fi

# Verify network is actually usable by running an ephemeral container on it
echo "Verifying network connectivity..."
if docker run --rm --network "$NETWORK_NAME" alpine:3 true 2>/dev/null; then
echo "✓ Network $NETWORK_NAME is accessible"
else
echo "ERROR: Network $NETWORK_NAME exists but cannot be used by containers" >&2
echo " Try removing and recreating it:"
echo " docker network rm $NETWORK_NAME"
echo " docker network create $NETWORK_NAME"
exit 1
fi

echo ""
echo "Network is ready to be used by all Docker Compose projects."

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ config/initializers/dev.rb
docker/uploads/
docker/**/__pycache__/
docker/*.rb
docker/dumps/*.pg_dump.gz
/.devcontainer/data/*

# Ignore application configuration
Expand Down
1 change: 1 addition & 0 deletions app/api/v01/entities/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ def self.entity_name
expose(:prefered_unit, documentation: { type: String })
expose(:locale, documentation: { type: String, desc: 'Currently used in mailing' })
expose(:time_zone, documentation: { type: String })
expose(:default_display_polylines, documentation: { type: 'Boolean' })
end
1 change: 1 addition & 0 deletions app/api/v01/helper/shared_params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ def filter_tag_ids_belong_to_customer(tag_ids, customer)
optional :prefered_unit, type: String
optional :locale, type: String
optional :time_zone, type: String, values: ActiveSupport::TimeZone.all.map(&:name)
optional :default_display_polylines, type: Boolean
end

params :request_vehicle do |options|
Expand Down
4 changes: 2 additions & 2 deletions app/api/v01/users.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ def user_params
p = ActionController::Parameters.new(params)
p = p[:user] if p.key?(:user)
if @current_user.admin?
p.permit(:ref, :email, :password, :customer_id, :layer_id, :url_click2call, :time_zone, :locale, :prefered_unit)
p.permit(:ref, :email, :password, :customer_id, :layer_id, :url_click2call, :time_zone, :locale, :prefered_unit, :default_display_polylines)
else
p.permit(:layer_id, :url_click2call, :time_zone, :locale, :prefered_unit)
p.permit(:layer_id, :url_click2call, :time_zone, :locale, :prefered_unit, :default_display_polylines)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
//= require_tree ../../templates

// Custom components
//= require active_inactive_drag_drop
//= require utils/active_inactive_drag_drop

// jQuery Turbolinks documentation informs to load all scripts before turbolinks
//= require jquery.turbolinks
Expand Down
40 changes: 26 additions & 14 deletions app/assets/javascripts/plannings.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ const iCalendarExport = function(planningId) {
});
};

const spreadsheetModalExport = function(columns, planningId, export_settings) {
const spreadsheetModalExport = function(columns, planningId, export_settings, custom_columns) {
custom_columns = custom_columns || {};
$('#planning-spreadsheet-modal').on('show.bs.modal', function() {
if ($('[name=spreadsheet-route]').val())
$('[name=spreadsheet-out-of-route]').parent().parent().hide();
Expand Down Expand Up @@ -130,24 +131,33 @@ const spreadsheetModalExport = function(columns, planningId, export_settings) {
const $export = $('#columns-export').empty();
const $skip = $('#columns-skip').empty();

// Fonction pour obtenir le nom d'affichage traduit d'une colonne
// Get display name for a column; falls back to destinations.import_file when key is not in plannings.export_file.
// When column has a custom import header, appends it in parentheses.
function getDisplayName(columnKey) {
var displayName;
var match = columnKey.match(new RegExp('^(.+)\\[(.*)\\]$'));
var rematch = columnKey.match(/^([a-z]+(?:_[a-z]+)*)(\d+)$/);
var baseKey, suffix;
if (match) {
var export_translation = 'plannings.export_file.' + match[1];
displayName = I18n.t(export_translation) + '[' + match[2] + ']';
baseKey = match[1];
suffix = '[' + match[2] + ']';
} else if (rematch) {
baseKey = rematch[1];
suffix = rematch[2];
} else {
baseKey = columnKey;
suffix = '';
}
else if (rematch) {
var export_translation = 'plannings.export_file.' + rematch[1];
displayName = I18n.t(export_translation) + rematch[2];
var exportKey = 'plannings.export_file.' + baseKey;
var importKey = 'destinations.import_file.' + baseKey;
var t = I18n.t(exportKey);
if (!t || t === exportKey || /^\[missing "[a-z0-9_.]+" translation\]$/.test(String(t))) {
t = I18n.t(importKey);
}
else {
var export_translation = 'plannings.export_file.' + columnKey;
displayName = I18n.t(export_translation);
var display = t + suffix;
if (custom_columns[columnKey]) {
display += ' (' + custom_columns[columnKey] + ')';
}
return displayName;
return display;
}

columnsExport.forEach(function(col) {
Expand Down Expand Up @@ -699,6 +709,7 @@ export const plannings_edit = function(params) {

var map = mapInitialize(params);
var popupOptions = params.manage_planning;
var withPolylines = params.default_display_polylines !== false;
var routesLayer = new RoutesLayer(planning_id, {
url_click2call: url_click2call,
unit: prefered_unit,
Expand All @@ -708,6 +719,7 @@ export const plannings_edit = function(params) {
appBaseUrl: params.apiWeb ? '/api-web/0.1/' : '/',
popupOptions: popupOptions,
disableClusters: params.disable_clusters,
withPolylines: withPolylines,
planningId: planning_id
}).on('clickStop', function(stop) {
enlightenStop({index: stop.index, routeId: stop.routeId});
Expand Down Expand Up @@ -2841,7 +2853,7 @@ export const plannings_edit = function(params) {
});
});

spreadsheetModalExport(params.spreadsheet_columns, params.planning_id, params.export_settings);
spreadsheetModalExport(params.spreadsheet_columns, params.planning_id, params.export_settings, params.spreadsheet_custom_columns);

var devicesObservePlanning = (function() {

Expand Down Expand Up @@ -3007,7 +3019,7 @@ var plannings_index = function(params) {
var requestPending = false;

iCalendarExport();
spreadsheetModalExport(params.spreadsheet_columns, null, params.export_settings);
spreadsheetModalExport(params.spreadsheet_columns, null, params.export_settings, params.spreadsheet_custom_columns);

var vehicle_id = $('#vehicle_id').val(),
planning_ids;
Expand Down
Loading
Loading