Skip to content

Commit

Permalink
editoast: front: adapt timetable endpoint and delete post endpoint
Browse files Browse the repository at this point in the history
Signed-off-by: Youness CHRIFI ALAOUI <[email protected]>
  • Loading branch information
younesschrifi committed Feb 10, 2025
1 parent 42f1939 commit f6077bf
Show file tree
Hide file tree
Showing 15 changed files with 143 additions and 232 deletions.
81 changes: 31 additions & 50 deletions editoast/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2719,27 +2719,6 @@ paths:
'404':
description: Timetable not found
/timetable/{id}:
get:
tags:
- timetable
summary: Return a specific timetable with its associated schedules
parameters:
- name: id
in: path
description: A timetable ID
required: true
schema:
type: integer
format: int64
responses:
'200':
description: Timetable with train schedules ids
content:
application/json:
schema:
$ref: '#/components/schemas/TimetableDetailedResult'
'404':
description: Timetable not found
delete:
tags:
- timetable
Expand Down Expand Up @@ -2985,7 +2964,37 @@ paths:
type: string
enum:
- preprocessing_simulation_error
/timetable/{id}/train_schedule:
/timetable/{id}/train_schedules:
get:
tags:
- timetable
summary: Return a specific timetable with its associated schedules
parameters:
- name: id
in: path
description: A timetable ID
required: true
schema:
type: integer
format: int64
responses:
'200':
description: Timetable with train schedules ids
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/PaginationStats'
- type: object
required:
- results
properties:
results:
type: array
items:
$ref: '#/components/schemas/TrainScheduleResult'
'404':
description: Timetable not found
post:
tags:
- timetable
Expand Down Expand Up @@ -3133,34 +3142,6 @@ paths:
'204':
description: No content when successful
/train_schedule:
post:
tags:
- train_schedule
summary: Return a specific train schedule
requestBody:
content:
application/json:
schema:
type: object
required:
- ids
properties:
ids:
type: array
items:
type: integer
format: int64
uniqueItems: true
required: true
responses:
'200':
description: Retrieve a list of train schedule
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/TrainScheduleResult'
delete:
tags:
- timetable
Expand Down
62 changes: 41 additions & 21 deletions editoast/src/views/timetable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use axum::Extension;
use derivative::Derivative;
use editoast_authz::BuiltinRole;
use editoast_derive::EditoastError;
use editoast_schemas::train_schedule::TrainScheduleBase;
use itertools::Itertools;
use serde::Deserialize;
use serde::Serialize;
Expand All @@ -30,25 +29,30 @@ use crate::models::prelude::*;
use crate::models::timetable::Timetable;
use crate::models::timetable::TimetableWithTrains;
use crate::models::train_schedule::TrainSchedule;
use crate::models::train_schedule::TrainScheduleChangeset;
use crate::models::Infra;
use crate::views::train_schedule::train_simulation_batch;
use crate::views::train_schedule::TrainScheduleForm;
use crate::views::train_schedule::TrainScheduleResult;
use crate::views::AuthenticationExt;
use crate::views::AuthorizationError;
use crate::AppState;
use crate::RetrieveBatch;
use editoast_models::DbConnectionPoolV2;
use editoast_schemas::train_schedule::TrainScheduleBase;
use crate::models::train_schedule::TrainScheduleChangeset;
use crate::views::train_schedule::TrainScheduleForm;

use super::pagination::PaginatedList as _;
use super::pagination::PaginationQueryParams;
use super::pagination::PaginationStats;

crate::routes! {
"/timetable" => {
post,
"/{id}" => {
delete,
get,
"/train_schedules" => get,
"/train_schedules" => train_schedule,
"/conflicts" => conflicts,
"/train_schedule" => train_schedule,
&stdcm,
},
},
Expand Down Expand Up @@ -112,37 +116,57 @@ struct TimetableIdParam {
id: i64,
}

#[derive(Serialize, ToSchema, Debug)]
#[cfg_attr(test, derive(Deserialize))]
struct ListTrainSchedulesResponse {
#[schema(value_type = Vec<TrainScheduleResult>)]
results: Vec<TrainScheduleResult>,
#[serde(flatten)]
stats: PaginationStats,
}

/// Return a specific timetable with its associated schedules
#[utoipa::path(
get, path = "",
tag = "timetable",
params(TimetableIdParam),
responses(
(status = 200, description = "Timetable with train schedules ids", body = TimetableDetailedResult),
(status = 200, description = "Timetable with train schedules ids", body = inline(ListTrainSchedulesResponse)),
(status = 404, description = "Timetable not found"),
),
)]
async fn get(
State(db_pool): State<DbConnectionPoolV2>,
Extension(auth): AuthenticationExt,
Path(TimetableIdParam { id: timetable_id }): Path<TimetableIdParam>,
) -> Result<Json<TimetableDetailedResult>> {
Query(pagination_params): Query<PaginationQueryParams>,
) -> Result<Json<ListTrainSchedulesResponse>> {
let authorized = auth
.check_roles([BuiltinRole::TimetableRead].into())
.await
.map_err(AuthorizationError::AuthError)?;
.check_roles([BuiltinRole::TimetableRead].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Forbidden.into());
}

let conn = &mut db_pool.get().await?;
let timetable = TimetableWithTrains::retrieve_or_fail(conn, timetable_id, || {
Timetable::retrieve_or_fail(conn, timetable_id, || {
TimetableError::NotFound { timetable_id }
})
.await?;
Ok(Json(timetable.into()))

let settings = pagination_params
.validate(25)?
.into_selection_settings()
.filter(move || TrainSchedule::TIMETABLE_ID.eq(timetable_id));

let (train_schedules, stats) = TrainSchedule::list_paginated(conn, settings).await?;
let results = train_schedules.into_iter().map_into().collect();

Ok(Json(ListTrainSchedulesResponse {stats, results}))
}


/// Create a timetable
#[utoipa::path(
post, path = "",
Expand Down Expand Up @@ -362,24 +386,20 @@ mod tests {

let timetable = create_timetable(&mut pool.get_ok()).await;

let request = app.get(&format!("/timetable/{}", timetable.id));
let request = app.get(&format!("/timetable/{}/train_schedules", timetable.id));

let timetable_from_response: TimetableDetailedResult =
let timetable_from_response: ListTrainSchedulesResponse =
app.fetch(request).assert_status(StatusCode::OK).json_into();

assert_eq!(
timetable_from_response,
TimetableDetailedResult {
timetable_id: timetable.id,
train_ids: vec![],
}
timetable_from_response.results.len(),
0
);
}

#[rstest]
async fn get_unexisting_timetable() {
let app = TestAppBuilder::default_app();
let request = app.get(&format!("/timetable/{}", 0));
let request = app.get(&format!("/timetable/{}/train_schedules", 0));
app.fetch(request).assert_status(StatusCode::NOT_FOUND);
}

Expand Down
53 changes: 0 additions & 53 deletions editoast/src/views/train_schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ crate::routes! {
"/train_schedule" => {
delete,
"/simulation_summary" => simulation_summary,
get_batch,
&projection,
"/{id}" => {
get,
Expand Down Expand Up @@ -202,39 +201,6 @@ struct BatchRequest {
ids: HashSet<i64>,
}

/// Return a specific train schedule
#[utoipa::path(
post, path = "",
tag = "train_schedule",
request_body = inline(BatchRequest),
responses(
(status = 200, description = "Retrieve a list of train schedule", body = Vec<TrainScheduleResult>)
)
)]
async fn get_batch(
State(db_pool): State<DbConnectionPoolV2>,
Extension(auth): AuthenticationExt,
Json(BatchRequest { ids: train_ids }): Json<BatchRequest>,
) -> Result<Json<Vec<TrainScheduleResult>>> {
let authorized = auth
.check_roles([BuiltinRole::InfraRead, BuiltinRole::TimetableRead].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Forbidden.into());
}

let conn = &mut db_pool.get().await?;
let train_schedules: Vec<TrainSchedule> =
TrainSchedule::retrieve_batch_or_fail(conn, train_ids, |missing| {
TrainScheduleError::BatchTrainScheduleNotFound {
number: missing.len(),
}
})
.await?;
Ok(Json(train_schedules.into_iter().map_into().collect()))
}

/// Delete a train schedule and its result
#[utoipa::path(
delete, path = "",
Expand Down Expand Up @@ -899,25 +865,6 @@ mod tests {
);
}

#[rstest]
async fn train_schedule_get_batch() {
let app = TestAppBuilder::default_app();
let pool = app.db_pool();

let timetable = create_timetable(&mut pool.get_ok()).await;
let ts1 = create_simple_train_schedule(&mut pool.get_ok(), timetable.id).await;
let ts2 = create_simple_train_schedule(&mut pool.get_ok(), timetable.id).await;
let ts3 = create_simple_train_schedule(&mut pool.get_ok(), timetable.id).await;

// Should succeed
let request = app.post("/train_schedule").json(&json!({
"ids": vec![ts1.id, ts2.id, ts3.id]
}));
let response: Vec<TrainScheduleResult> =
app.fetch(request).assert_status(StatusCode::OK).json_into();
assert_eq!(response.len(), 3);
}

#[rstest]
async fn train_schedule_post() {
let app = TestAppBuilder::default_app();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ const handleTrainrunOperation = async ({
);
const startDate = new Date();
const newTrainSchedules = await dispatch(
osrdEditoastApi.endpoints.postTimetableByIdTrainSchedule.initiate({
osrdEditoastApi.endpoints.postTimetableByIdTrainSchedules.initiate({
id: timeTableId,
body: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import ManageTrainSchedule from 'applications/operationalStudies/views/ManageTra
import SimulationResults from 'applications/operationalStudies/views/SimulationResults';
import { osrdEditoastApi } from 'common/api/osrdEditoastApi';
import type {
GetTimetableByIdTrainSchedulesApiResponse,
InfraWithState,
ScenarioResponse,
TimetableDetailedResult,
TrainScheduleResult,
} from 'common/api/osrdEditoastApi';
import ScenarioLoaderMessage from 'modules/scenario/components/ScenarioLoaderMessage';
Expand All @@ -35,7 +35,7 @@ import MacroEditorState from '../MacroEditor/MacroEditorState';

type ScenarioDescriptionProps = {
scenario: ScenarioResponse;
timetable: TimetableDetailedResult;
timetable: GetTimetableByIdTrainSchedulesApiResponse;
infra: InfraWithState;
infraMetadata: { isInfraLoaded: boolean; reloadCount: number };
};
Expand Down Expand Up @@ -68,28 +68,22 @@ const ScenarioContent = ({
const [ngeDto, setNgeDto] = useState<NetzgrafikDto>();

const dtoImport = useCallback(async () => {
const timetablePromise = dispatch(
osrdEditoastApi.endpoints.getTimetableById.initiate(
{ id: timetable.timetable_id },
{ forceRefetch: true, subscribe: false }
)
);
const { train_ids } = await timetablePromise.unwrap();
const trainSchedulesPromise = dispatch(
osrdEditoastApi.endpoints.postTrainSchedule.initiate(
{ body: { ids: train_ids } },
osrdEditoastApi.endpoints.getTimetableByIdTrainSchedules.initiate(
{ id: timetable.results[0].timetable_id }, // TODO
{ forceRefetch: true, subscribe: false }
)
);
const schedules = (await trainSchedulesPromise.unwrap()).filter(

const schedules = (await trainSchedulesPromise.unwrap()).results.filter(
(trainSchedule) => trainSchedule.path.length >= 2
);
const state = new MacroEditorState(scenario, schedules || []);
await loadAndIndexNge(state, dispatch);
const dto = getNgeDto(state);
macroEditorState.current = state;
setNgeDto(dto);
}, [dispatch, scenario, timetable.timetable_id]);
}, [dispatch, scenario, timetable.results[0].timetable_id]);

const toggleMicroMacroButton = useCallback(
(isMacroMode: boolean) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const useScenario = () => {
}
);

const { data: timetable } = osrdEditoastApi.endpoints.getTimetableById.useQuery(
const { data: timetable } = osrdEditoastApi.endpoints.getTimetableByIdTrainSchedules.useQuery(
{ id: scenario?.timetable_id! },
{
skip: !scenario,
Expand Down
Loading

0 comments on commit f6077bf

Please sign in to comment.