import json
from unittest import mock

import pytest
from django.contrib.contenttypes.models import ContentType

import tests.utils
from passerelle.apps.okina.models import Okina
from passerelle.base.models import AccessRight, ApiUser

CITIES = '''[ {
  "city" : 83355,
  "cityObject" : {
    "id" : 83355,
    "insee" : "36005",
    "nameCity" : "ARDENTES",
    "zipCode" : "36120",
    "nameCityRouting" : "ARDENTES",
    "line5" : null
  }
}, {
  "city" : 80237,
  "cityObject" : {
    "id" : 80237,
    "insee" : "36044",
    "nameCity" : "CHATEAUROUX",
    "zipCode" : "36000",
    "nameCityRouting" : "CHATEAUROUX",
    "line5" : null
  }
} ]

'''

# new API (march 2020)
CITIES_NEW = '''[
  {
    "id": 83355,
    "insee": "36005",
    "name": "ARDENTES",
    "zipCode": "36120",
    "nameCityRouting": null,
    "line5": null,
    "latitude": null,
    "longitude": null,
    "geoJson": null
  }
]
'''

CLASSES = '''
[
  {
    "id": 1,
    "label": "École maternelle - Petite section"
  },
  {
    "id": 2,
    "label": "École maternelle - Moyenne section"
  },
  {
    "id": 4,
    "label": "École élémentaire – CP"
  },
  {
    "id": 5,
    "label": "École élémentaire – CE 1"
  }
]
'''

INSTITUTIONS = '''[ {
  "id" : 276,
  "schoolEstablishment" : {
    "uai" : "0360155Y",
    "uaiLabel" : "Ecole élémentaire Saint-Martin",
    "mainTitle" : "ECOLE ELEMENTAIRE PUBLIQUE",
    "type" : "Public",
    "address" : "RUE CALMETTE ET GUERIN",
    "zipCode" : "36120",
    "cityName" : "ARDENTES",
    "locationX" : "1.82965415466176",
    "locationY" : "46.740247926141706",
    "nature" : "151",
    "natureTitle" : "Ecole élémentaire"
  },
  "schoolEstablishmentUai" : "0360155Y",
  "zipAndCity" : "36120 - ARDENTES",
  "institutionContacts" : [ ]
}, {
  "id" : 277,
  "schoolEstablishment" : {
    "uai" : "0360048G",
    "uaiLabel" : "Collège Touvent",
    "mainTitle" : "COLLEGE",
    "type" : "Public",
    "address" : "4 ALLÉE DES LAURIERS",
    "zipCode" : "36007",
    "cityName" : "CHATEAUROUX CEDEX",
    "locationX" : "1.6926355997775921",
    "locationY" : "46.791332586468016",
    "nature" : "340",
    "natureTitle" : "Collège"
  },
  "schoolEstablishmentUai" : "0360048G",
  "zipAndCity" : "36007 - CHATEAUROUX CEDEX",
  "institutionContacts" : [ ]
} ]

'''

# new API (march 2020)
INSTITUTIONS_NEW = '''[ {
  "id" : 276,
  "schoolEstablishment" : {
    "uai" : "0360155Y",
    "uaiLabel" : "Ecole élémentaire Saint-Martin",
    "type" : "Public",
    "address" : "Rue Calmette et Guérin",
    "zipCode" : "36120",
    "codeCity" : "36005",
    "cityName" : "Ardentes",
    "locationX" : "1.8296541547236063",
    "locationY" : "46.74024796567764",
    "nature" : "1000",
    "natureTitle" : "Ecole",
    "restoration" : true,
    "accommodation" : false,
    "ulis" : false,
    "segpa" : false,
    "sirenSiret" : "21360005900033",
    "webSite" : null,
    "phone" : "254362184",
    "fax" : "",
    "email" : "ec-saint-martin-ardentes@ac-orleans-tours.fr",
    "departmentLabel" : "Indre",
    "academyLabel" : "Orléans-Tours",
    "regionLabel" : "Centre-Val de Loire",
    "schoolSectionList" : [ ],
    "schoolEstablishmentTypeInfos" : [ {
      "key" : "ecole_elementaire",
      "value" : "École élémentaire"
    } ]
  },
  "schoolEstablishmentDisplayName" : "Ecole élémentaire Saint-Martin",
  "schoolEstablishmentUai" : "0360155Y",
  "zipAndCity" : "36120 - Ardentes",
  "withContract" : true,
  "institutionContacts" : [ ],
  "institutionTimes" : [ {
    "id" : 27,
    "day" : "MONDAY",
    "way" : "WAY_TO",
    "time" : "08:46:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 28,
    "day" : "MONDAY",
    "way" : "WAY_BACK",
    "time" : "16:30:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 29,
    "day" : "TUESDAY",
    "way" : "WAY_TO",
    "time" : "08:46:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 30,
    "day" : "TUESDAY",
    "way" : "WAY_BACK",
    "time" : "16:30:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 31,
    "day" : "THURSDAY",
    "way" : "WAY_TO",
    "time" : "08:46:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 32,
    "day" : "THURSDAY",
    "way" : "WAY_BACK",
    "time" : "16:30:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 33,
    "day" : "FRIDAY",
    "way" : "WAY_TO",
    "time" : "08:46:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 34,
    "day" : "FRIDAY",
    "way" : "WAY_BACK",
    "time" : "16:30:00",
    "institutionId" : 0,
    "timeString" : null
  } ],
  "cities" : null,
  "optionsDerogatory" : [ ],
  "institutionCenterName" : "ARDENTES",
  "institutionCenterId" : 3,
  "customLocationX" : "1.8296541547236063",
  "customLocationY" : "46.74024796567764"
}, {
  "id" : 277,
  "schoolEstablishment" : {
    "uai" : "0360048G",
    "uaiLabel" : "Collège Touvent",
    "type" : "Public",
    "address" : "4 allée des Lauriers",
    "zipCode" : "36000",
    "codeCity" : "36044",
    "cityName" : "Châteauroux",
    "locationX" : "1.6926355997775921",
    "locationY" : "46.791332586468016",
    "nature" : "340",
    "natureTitle" : "Collège",
    "restoration" : false,
    "accommodation" : false,
    "ulis" : true,
    "segpa" : false,
    "sirenSiret" : "19360048300013",
    "webSite" : null,
    "phone" : "02 54 34 03 30",
    "fax" : "02 54 08 01 09",
    "email" : "ce.0360048g@ac-orleans-tours.fr",
    "departmentLabel" : "Indre",
    "academyLabel" : "Orléans-Tours",
    "regionLabel" : "Centre-Val de Loire",
    "schoolSectionList" : [ {
      "key" : "section_sport",
      "name" : "Section sport"
    } ],
    "schoolEstablishmentTypeInfos" : [ ]
  },
  "schoolEstablishmentDisplayName" : "Collège Touvent",
  "schoolEstablishmentUai" : "0360048G",
  "zipAndCity" : "36000 - Châteauroux",
  "withContract" : true,
  "institutionContacts" : [ ],
  "institutionTimes" : [ {
    "id" : 249,
    "day" : "MONDAY",
    "way" : "WAY_TO",
    "time" : "08:20:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 250,
    "day" : "MONDAY",
    "way" : "WAY_BACK",
    "time" : "17:05:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 251,
    "day" : "TUESDAY",
    "way" : "WAY_TO",
    "time" : "08:20:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 252,
    "day" : "TUESDAY",
    "way" : "WAY_BACK",
    "time" : "17:05:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 253,
    "day" : "WEDNESDAY",
    "way" : "WAY_TO",
    "time" : "07:50:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 254,
    "day" : "WEDNESDAY",
    "way" : "WAY_BACK",
    "time" : "12:05:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 255,
    "day" : "THURSDAY",
    "way" : "WAY_TO",
    "time" : "08:20:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 256,
    "day" : "THURSDAY",
    "way" : "WAY_BACK",
    "time" : "17:05:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 257,
    "day" : "FRIDAY",
    "way" : "WAY_TO",
    "time" : "08:20:00",
    "institutionId" : 0,
    "timeString" : null
  }, {
    "id" : 258,
    "day" : "FRIDAY",
    "way" : "WAY_BACK",
    "time" : "17:05:00",
    "institutionId" : 0,
    "timeString" : null
  } ],
  "cities" : null,
  "optionsDerogatory" : [ ],
  "institutionCenterName" : "CHATEAUROUX",
  "institutionCenterId" : 2,
  "customLocationX" : "1.6926355997775921",
  "customLocationY" : "46.791332586468016"
} ]
'''

SEARCH = '''[ {
  "id" : 3312,
  "stop_point_id" : 1498,
  "name" : "Le Grand Verger",
  "latitude" : 46.8444186,
  "longitude" : 1.708197,
  "bearing" : 0.0,
  "position" : 6,
  "forBoarding" : null,
  "forAlighting" : null,
  "colorUsage" : null,
  "isRecommended" : false,
  "titreRelaisId" : null,
  "networkType" : "SCHOLAR",
  "commercial_stop_area" : 3312,
  "commercial_name" : "Le Grand Verger",
  "liaisons" : [ ],
  "registrationNumber" : "36063_009",
  "cityCode" : "36063",
  "cityName" : "Déols",
  "zipCode" : "36130",
  "isDuplicatedCompute" : 0,
  "nbClosedStopAreas" : null,
  "isValidated" : null,
  "isUnique" : null,
  "isDuplicated" : null,
  "network" : {
    "id" : 2,
    "name" : "réseau scolaire",
    "supprime" : false,
    "networkType" : "SCHOLAR"
  },
  "external_ref" : null,
  "distanceBetweenPreviousStopCalculated" : 0.0,
  "distanceBetweenPreviousStopReal" : null
}, {
  "id" : 3311,
  "stop_point_id" : 1499,
  "name" : "Jean Bizet U",
  "latitude" : 46.8359017,
  "longitude" : 1.7045139,
  "bearing" : 0.0,
  "position" : 7,
  "forBoarding" : null,
  "forAlighting" : null,
  "colorUsage" : null,
  "isRecommended" : true,
  "titreRelaisId" : null,
  "networkType" : "SCHOLAR",
  "commercial_stop_area" : 3311,
  "commercial_name" : "Jean Bizet U",
  "liaisons" : [ ],
  "registrationNumber" : "36063_008",
  "cityCode" : "36063",
  "cityName" : "Déols",
  "zipCode" : "36130",
  "isDuplicatedCompute" : 0,
  "nbClosedStopAreas" : null,
  "isValidated" : null,
  "isUnique" : null,
  "isDuplicated" : null,
  "network" : {
    "id" : 2,
    "name" : "réseau scolaire",
    "supprime" : false,
    "networkType" : "SCHOLAR"
  },
  "external_ref" : null,
  "distanceBetweenPreviousStopCalculated" : 0.0,
  "distanceBetweenPreviousStopReal" : null
} ]
'''

STOPS = '''
[ {
  "id" : 3281,
  "stop_point_id" : 827,
  "name" : "Les Loges de Dressais",
  "latitude" : 46.7317874000000018,
  "longitude" : 1.7887462999999999,
  "bearing" : 0.0,
  "position" : 2,
  "commercial_stop_area" : 3281,
  "commercial_name" : "Les Loges de Dressais",
  "liaisons" : [ ],
  "registrationNumber" : null
}, {
  "id" : 3279,
  "stop_point_id" : 828,
  "name" : "La Cueille",
  "latitude" : 46.7500219000000001,
  "longitude" : 1.7890467000000001,
  "bearing" : 0.0,
  "position" : 3,
  "commercial_stop_area" : 3279,
  "commercial_name" : "La Cueille",
  "liaisons" : [ ],
  "registrationNumber" : null
}, {
  "id" : 3560,
  "stop_point_id" : 869,
  "name" : "Ecole Primaire SAINT-VINCENT",
  "latitude" : 46.7423843999999988,
  "longitude" : 1.83196010000000009,
  "bearing" : 0.0,
  "position" : 3,
  "commercial_stop_area" : 3560,
  "commercial_name" : "Ecole Primaire SAINT-VINCENT",
  "liaisons" : [ ],
  "registrationNumber" : null
} ]

'''

# https://iti.okina.fr/api/Okina/REST/b2b/ods
ODS = '''[ {
  "id" : {
    "institutionId" : 276,
    "sequenceNumber" : 1
  },
  "institution" : {
    "id" : 276,
    "schoolEstablishmentUai" : "0360155Y",
    "okinaStopAreas" : null
  },
  "originDestinationSequenceElements" : [ {
    "id" : 6,
    "okinaVehicleJourney" : {
      "detached" : false,
      "objectId" : "Europe Voyages 23:VehicleJourney:84",
      "objectVersion" : 1,
      "creationTime" : 1491516000000,
      "creatorId" : null,
      "saved" : false,
      "id" : 84,
      "comment" : null,
      "transportMode" : null,
      "publishedJourneyName" : "SYNDICAT ARDENTES 4 (semaine Aller)",
      "publishedJourneyIdentifier" : "SYNDICAT ARDENTES 4 (semaine Aller)",
      "facility" : null,
      "vehicleTypeIdentifier" : null,
      "number" : 84,
      "mobilityRestrictedSuitability" : null,
      "flexibleService" : null,
      "route" : null,
      "journeyPattern" : null,
      "company" : null,
      "footnotes" : null,
      "timetables" : null,
      "vehicleJourneyAtStops" : null,
      "journeyCategory" : "Timesheet",
      "journeyFrequencies" : null,
      "okinaJourneyPattern" : null,
      "okinaRoute" : null,
      "etat" : 0,
      "supprime" : false,
      "okinaVehicleJourneyAtStops" : null,
      "counterGroups" : null,
      "registrationNumber" : null,
      "filled" : false
    },
    "okinaJourneyPattern" : null,
    "position" : 1,
    "originDestination" : {
      "institutionId" : 276,
      "sequenceNumber" : 1
    }
  } ],
  "oppositeOds" : null,
  "way" : "WAY_TO"
}, {
  "id" : {
    "institutionId" : 276,
    "sequenceNumber" : 2
  },
  "institution" : {
    "id" : 276,
    "schoolEstablishmentUai" : "0360155Y",
    "okinaStopAreas" : null
  },
  "originDestinationSequenceElements" : [ {
    "id" : 203,
    "okinaVehicleJourney" : {
      "detached" : false,
      "objectId" : "STI CENTRE:VehicleJourney:90",
      "objectVersion" : 1,
      "creationTime" : 1491516000000,
      "creatorId" : null,
      "saved" : false,
      "id" : 90,
      "comment" : null,
      "transportMode" : null,
      "publishedJourneyName" : "SYNDICAT ARDENTES 6 (semaine Aller)",
      "publishedJourneyIdentifier" : "SYNDICAT ARDENTES 6 (semaine Aller)",
      "facility" : null,
      "vehicleTypeIdentifier" : null,
      "number" : 90,
      "mobilityRestrictedSuitability" : null,
      "flexibleService" : null,
      "route" : null,
      "journeyPattern" : null,
      "company" : null,
      "footnotes" : null,
      "timetables" : null,
      "vehicleJourneyAtStops" : null,
      "journeyCategory" : "Timesheet",
      "journeyFrequencies" : null,
      "okinaJourneyPattern" : null,
      "okinaRoute" : null,
      "etat" : 0,
      "supprime" : false,
      "okinaVehicleJourneyAtStops" : null,
      "counterGroups" : null,
      "registrationNumber" : null,
      "filled" : false
    },
    "okinaJourneyPattern" : null,
    "position" : 1,
    "originDestination" : {
      "institutionId" : 276,
      "sequenceNumber" : 2
    }
  } ],
  "oppositeOds" : null,
  "way" : "WAY_TO"
} ]

'''

# https://iti.okina.fr/api/Okina/REST/b2b/ods/institution/276/stop-area/3282
ODS_LINES = '''[ [ { "id" : 24, "name" : "LIGNE 24" } ],
  [ { "id": 22,"name": "LIGNE 22" } ],
  [ { "id": 24, "name" : "LIGNE 24" }, { "id": 23, "name": "LIGNE 23" } ] ]

'''

# https://iti.okina.fr/api/Okina/REST/b2b/ods/institution/276/subscriberCity/36005
# https://iti.okina.fr/api/Okina/REST/b2b/ods/subscriberCity/36005
ODS_FULL = '''[ {
  "id" : {
    "institutionId" : 276,
    "sequenceNumber" : 1
  },
  "institution" : {
    "id" : 276,
    "schoolEstablishmentUai" : "0360155Y",
    "okinaStopAreas" : null
  },
  "originDestinationSequenceElements" : [ {
    "id" : 6,
    "okinaVehicleJourney" : {
      "detached" : false,
      "objectId" : "Europe Voyages 23:VehicleJourney:84",
      "objectVersion" : 1,
      "creationTime" : 1491516000000,
      "creatorId" : null,
      "saved" : false,
      "id" : 84,
      "comment" : null,
      "transportMode" : null,
      "publishedJourneyName" : "SYNDICAT ARDENTES 4 (semaine Aller)",
      "publishedJourneyIdentifier" : "SYNDICAT ARDENTES 4 (semaine Aller)",
      "facility" : null,
      "vehicleTypeIdentifier" : null,
      "number" : 84,
      "mobilityRestrictedSuitability" : null,
      "flexibleService" : null,
      "route" : null,
      "journeyPattern" : null,
      "company" : null,
      "footnotes" : null,
      "timetables" : null,
      "vehicleJourneyAtStops" : null,
      "journeyCategory" : "Timesheet",
      "journeyFrequencies" : null,
      "okinaJourneyPattern" : {
        "detached" : false,
        "objectId" : "europevoyages:JourneyPattern:86",
        "objectVersion" : 1,
        "creationTime" : 1491516000000,
        "creatorId" : null,
        "saved" : false,
        "id" : 86,
        "name" : "Brenne 1 vers écoles Antoine Fée, Saint Martin et Saint Vincent (semaine Aller)",
        "comment" : null,
        "registrationNumber" : null,
        "publishedName" : "Brenne 1 vers écoles Antoine Fée, Saint Martin et Saint Vincent",
        "sectionStatus" : "Todo",
        "route" : null,
        "departureStopPoint" : null,
        "arrivalStopPoint" : null,
        "stopPoints" : null,
        "vehicleJourneys" : null,
        "routeSections" : null,
        "okinaStopPoints" : [ {
          "id" : 825,
          "detached" : false,
          "objectId" : "chateauroux:StopPoint:825",
          "objectVersion" : 1,
          "creationTime" : 1491551801241,
          "creatorId" : null,
          "saved" : false,
          "position" : 0,
          "forBoarding" : null,
          "forAlighting" : null,
          "containedInStopArea" : null,
          "route" : null,
          "comment" : null,
          "okinaRoute" : null,
          "okinaContainedInStopArea" : {
            "detached" : false,
            "objectId" : "chateauroux:StopArea:Boarding_36005_004",
            "objectVersion" : 1,
            "creationTime" : null,
            "creatorId" : null,
            "saved" : false,
            "longitude" : 1.81673930000000006,
            "latitude" : 46.719458699999997,
            "longLatType" : null,
            "x" : null,
            "y" : null,
            "projectionType" : null,
            "countryCode" : "36005",
            "zipCode" : "36120",
            "cityName" : "Ardentes",
            "streetName" : null,
            "id" : 3273,
            "name" : "Brenne 1",
            "comment" : null,
            "areaType" : "BoardingPosition",
            "registrationNumber" : "36005_004",
            "nearestTopicName" : null,
            "url" : null,
            "timeZone" : null,
            "fareCode" : null,
            "liftAvailable" : null,
            "mobilityRestrictedSuitable" : null,
            "stairsAvailable" : null,
            "intUserNeeds" : null,
            "parent" : null,
            "routingConstraintLines" : null,
            "routingConstraintAreas" : null,
            "containedStopAreas" : null,
            "containedStopPoints" : null,
            "accessLinks" : null,
            "routeSectionDepartures" : null,
            "routeSectionArrivals" : null,
            "connectionStartLinks" : null,
            "connectionEndLinks" : null,
            "accessPoints" : null,
            "okinaContainedStopPoints" : null,
            "okinaParent" : null,
            "bearing" : 0.0,
            "cityCode" : "36005",
            "okinaContainedStopAreas" : null,
            "userNeeds" : [ ],
            "filled" : false
          },
          "journeyPatternsDestination" : null,
          "journeyPatternsOrigin" : null,
          "okinaJourneyPatterns" : null,
          "vehicleJourneyAtStops" : null,
          "registrationNumber" : null,
          "filled" : false
        }, {
          "id" : 826,
          "detached" : false,
          "objectId" : "chateauroux:StopPoint:826",
          "objectVersion" : 1,
          "creationTime" : 1491551801241,
          "creatorId" : null,
          "saved" : false,
          "position" : 1,
          "forBoarding" : null,
          "forAlighting" : null,
          "containedInStopArea" : null,
          "route" : null,
          "comment" : null,
          "okinaRoute" : null,
          "okinaContainedInStopArea" : {
            "detached" : false,
            "objectId" : "chateauroux:StopArea:Boarding_36005_005",
            "objectVersion" : 1,
            "creationTime" : null,
            "creatorId" : null,
            "saved" : false,
            "longitude" : 1.80696429999999997,
            "latitude" : 46.7233643000000001,
            "longLatType" : null,
            "x" : null,
            "y" : null,
            "projectionType" : null,
            "countryCode" : "36005",
            "zipCode" : "36120",
            "cityName" : "Ardentes",
            "streetName" : null,
            "id" : 3274,
            "name" : "Brenne 2",
            "comment" : null,
            "areaType" : "BoardingPosition",
            "registrationNumber" : "36005_005",
            "nearestTopicName" : null,
            "url" : null,
            "timeZone" : null,
            "fareCode" : null,
            "liftAvailable" : null,
            "mobilityRestrictedSuitable" : null,
            "stairsAvailable" : null,
            "intUserNeeds" : null,
            "parent" : null,
            "routingConstraintLines" : null,
            "routingConstraintAreas" : null,
            "containedStopAreas" : null,
            "containedStopPoints" : null,
            "accessLinks" : null,
            "routeSectionDepartures" : null,
            "routeSectionArrivals" : null,
            "connectionStartLinks" : null,
            "connectionEndLinks" : null,
            "accessPoints" : null,
            "okinaContainedStopPoints" : null,
            "okinaParent" : null,
            "bearing" : 0.0,
            "cityCode" : "36005",
            "okinaContainedStopAreas" : null,
            "userNeeds" : [ ],
            "filled" : false
          },
          "journeyPatternsDestination" : null,
          "journeyPatternsOrigin" : null,
          "okinaJourneyPatterns" : null,
          "vehicleJourneyAtStops" : null,
          "registrationNumber" : null,
          "filled" : false
        }, {
          "id" : 827,
          "detached" : false,
          "objectId" : "chateauroux:StopPoint:827",
          "objectVersion" : 1,
          "creationTime" : 1491551801242,
          "creatorId" : null,
          "saved" : false,
          "position" : 2,
          "forBoarding" : null,
          "forAlighting" : null,
          "containedInStopArea" : null,
          "route" : null,
          "comment" : null,
          "okinaRoute" : null,
          "okinaContainedInStopArea" : {
            "detached" : false,
            "objectId" : "chateauroux:StopArea:Boarding_36005_012",
            "objectVersion" : 1,
            "creationTime" : null,
            "creatorId" : null,
            "saved" : false,
            "longitude" : 1.7887462999999999,
            "latitude" : 46.7317874000000018,
            "longLatType" : null,
            "x" : null,
            "y" : null,
            "projectionType" : null,
            "countryCode" : "36005",
            "zipCode" : "36120",
            "cityName" : "Ardentes",
            "streetName" : null,
            "id" : 3281,
            "name" : "Les Loges de Dressais",
            "comment" : null,
            "areaType" : "BoardingPosition",
            "registrationNumber" : "36005_012",
            "nearestTopicName" : null,
            "url" : null,
            "timeZone" : null,
            "fareCode" : null,
            "liftAvailable" : null,
            "mobilityRestrictedSuitable" : null,
            "stairsAvailable" : null,
            "intUserNeeds" : null,
            "parent" : null,
            "routingConstraintLines" : null,
            "routingConstraintAreas" : null,
            "containedStopAreas" : null,
            "containedStopPoints" : null,
            "accessLinks" : null,
            "routeSectionDepartures" : null,
            "routeSectionArrivals" : null,
            "connectionStartLinks" : null,
            "connectionEndLinks" : null,
            "accessPoints" : null,
            "okinaContainedStopPoints" : null,
            "okinaParent" : null,
            "bearing" : 0.0,
            "cityCode" : "36005",
            "okinaContainedStopAreas" : null,
            "userNeeds" : [ ],
            "filled" : false
          },
          "journeyPatternsDestination" : null,
          "journeyPatternsOrigin" : null,
          "okinaJourneyPatterns" : null,
          "vehicleJourneyAtStops" : null,
          "registrationNumber" : null,
          "filled" : false
        }, {
          "id" : 828,
          "detached" : false,
          "objectId" : "chateauroux:StopPoint:828",
          "objectVersion" : 1,
          "creationTime" : 1491551801242,
          "creatorId" : null,
          "saved" : false,
          "position" : 3,
          "forBoarding" : null,
          "forAlighting" : null,
          "containedInStopArea" : null,
          "route" : null,
          "comment" : null,
          "okinaRoute" : null,
          "okinaContainedInStopArea" : {
            "detached" : false,
            "objectId" : "chateauroux:StopArea:Boarding_36005_010",
            "objectVersion" : 1,
            "creationTime" : null,
            "creatorId" : null,
            "saved" : false,
            "longitude" : 1.7890467000000001,
            "latitude" : 46.7500219000000001,
            "longLatType" : null,
            "x" : null,
            "y" : null,
            "projectionType" : null,
            "countryCode" : "36005",
            "zipCode" : "36120",
            "cityName" : "Ardentes",
            "streetName" : null,
            "id" : 3279,
            "name" : "La Cueille",
            "comment" : null,
            "areaType" : "BoardingPosition",
            "registrationNumber" : "36005_010",
            "nearestTopicName" : null,
            "url" : null,
            "timeZone" : null,
            "fareCode" : null,
            "liftAvailable" : null,
            "mobilityRestrictedSuitable" : null,
            "stairsAvailable" : null,
            "intUserNeeds" : null,
            "parent" : null,
            "routingConstraintLines" : null,
            "routingConstraintAreas" : null,
            "containedStopAreas" : null,
            "containedStopPoints" : null,
            "accessLinks" : null,
            "routeSectionDepartures" : null,
            "routeSectionArrivals" : null,
            "connectionStartLinks" : null,
            "connectionEndLinks" : null,
            "accessPoints" : null,
            "okinaContainedStopPoints" : null,
            "okinaParent" : null,
            "bearing" : 0.0,
            "cityCode" : "36005",
            "okinaContainedStopAreas" : null,
            "userNeeds" : [ ],
            "filled" : false
          },
          "journeyPatternsDestination" : null,
          "journeyPatternsOrigin" : null,
          "okinaJourneyPatterns" : null,
          "vehicleJourneyAtStops" : null,
          "registrationNumber" : null,
          "filled" : false
        }, {
          "id" : 829,
          "detached" : false,
          "objectId" : "chateauroux:StopPoint:829",
          "objectVersion" : 1,
          "creationTime" : 1491551801242,
          "creatorId" : null,
          "saved" : false,
          "position" : 4,
          "forBoarding" : null,
          "forAlighting" : null,
          "containedInStopArea" : null,
          "route" : null,
          "comment" : null,
          "okinaRoute" : null,
          "okinaContainedInStopArea" : {
            "detached" : false,
            "objectId" : "chateauroux:StopArea:Boarding_36005_013",
            "objectVersion" : 1,
            "creationTime" : null,
            "creatorId" : null,
            "saved" : false,
            "longitude" : 1.79444730000000008,
            "latitude" : 46.7477992999999969,
            "longLatType" : null,
            "x" : null,
            "y" : null,
            "projectionType" : null,
            "countryCode" : "36005",
            "zipCode" : "36120",
            "cityName" : "Ardentes",
            "streetName" : null,
            "id" : 3282,
            "name" : "Les Loges du Plessis",
            "comment" : null,
            "areaType" : "BoardingPosition",
            "registrationNumber" : "36005_013",
            "nearestTopicName" : null,
            "url" : null,
            "timeZone" : null,
            "fareCode" : null,
            "liftAvailable" : null,
            "mobilityRestrictedSuitable" : null,
            "stairsAvailable" : null,
            "intUserNeeds" : null,
            "parent" : null,
            "routingConstraintLines" : null,
            "routingConstraintAreas" : null,
            "containedStopAreas" : null,
            "containedStopPoints" : null,
            "accessLinks" : null,
            "routeSectionDepartures" : null,
            "routeSectionArrivals" : null,
            "connectionStartLinks" : null,
            "connectionEndLinks" : null,
            "accessPoints" : null,
            "okinaContainedStopPoints" : null,
            "okinaParent" : null,
            "bearing" : 0.0,
            "cityCode" : "36005",
            "okinaContainedStopAreas" : null,
            "userNeeds" : [ ],
            "filled" : false
          },
          "journeyPatternsDestination" : null,
          "journeyPatternsOrigin" : null,
          "okinaJourneyPatterns" : null,
          "vehicleJourneyAtStops" : null,
          "registrationNumber" : null,
          "filled" : false
        }, {
          "id" : 830,
          "detached" : false,
          "objectId" : "chateauroux:StopPoint:830",
          "objectVersion" : 1,
          "creationTime" : 1491551801243,
          "creatorId" : null,
          "saved" : false,
          "position" : 5,
          "forBoarding" : null,
          "forAlighting" : null,
          "containedInStopArea" : null,
          "route" : null,
          "comment" : null,
          "okinaRoute" : null,
          "okinaContainedInStopArea" : null,
          "journeyPatternsDestination" : null,
          "journeyPatternsOrigin" : null,
          "okinaJourneyPatterns" : null,
          "vehicleJourneyAtStops" : null,
          "registrationNumber" : null,
          "filled" : false
        }, {
          "id" : 831,
          "detached" : false,
          "objectId" : "chateauroux:StopPoint:831",
          "objectVersion" : 1,
          "creationTime" : 1491551801243,
          "creatorId" : null,
          "saved" : false,
          "position" : 6,
          "forBoarding" : null,
          "forAlighting" : null,
          "containedInStopArea" : null,
          "route" : null,
          "comment" : null,
          "okinaRoute" : null,
          "okinaContainedInStopArea" : null,
          "journeyPatternsDestination" : null,
          "journeyPatternsOrigin" : null,
          "okinaJourneyPatterns" : null,
          "vehicleJourneyAtStops" : null,
          "registrationNumber" : null,
          "filled" : false
        }, {
          "id" : 832,
          "detached" : false,
          "objectId" : "chateauroux:StopPoint:832",
          "objectVersion" : 1,
          "creationTime" : 1491551801244,
          "creatorId" : null,
          "saved" : false,
          "position" : 7,
          "forBoarding" : null,
          "forAlighting" : null,
          "containedInStopArea" : null,
          "route" : null,
          "comment" : null,
          "okinaRoute" : null,
          "okinaContainedInStopArea" : null,
          "journeyPatternsDestination" : null,
          "journeyPatternsOrigin" : null,
          "okinaJourneyPatterns" : null,
          "vehicleJourneyAtStops" : null,
          "registrationNumber" : null,
          "filled" : false
        } ],
        "okinaVehicleJourneys" : null,
        "etat" : 0,
        "supprime" : false,
        "okinaRoute" : null,
        "okinaDepartureStopPoint" : null,
        "okinaArrivalStopPoint" : null,
        "filled" : false
      },
      "okinaRoute" : null,
      "etat" : 0,
      "supprime" : false,
      "okinaVehicleJourneyAtStops" : null,
      "counterGroups" : null,
      "registrationNumber" : null,
      "filled" : false
    },
    "okinaJourneyPattern" : null,
    "position" : 1,
    "originDestination" : {
      "institutionId" : 276,
      "sequenceNumber" : 1
    }
  } ],
  "oppositeOds" : [ {
    "id" : {
      "institutionId" : 276,
      "sequenceNumber" : 3
    },
    "institution" : {
      "id" : 276,
      "schoolEstablishmentUai" : "0360155Y",
      "okinaStopAreas" : null
    },
    "originDestinationSequenceElements" : [ {
      "id" : 3,
      "okinaVehicleJourney" : {
        "detached" : false,
        "objectId" : "Europe Voyages 23:VehicleJourney:86",
        "objectVersion" : 1,
        "creationTime" : 1491516000000,
        "creatorId" : null,
        "saved" : false,
        "id" : 86,
        "comment" : null,
        "transportMode" : null,
        "publishedJourneyName" : "SYNDICAT ARDENTES 4 (mercredi Retour)",
        "publishedJourneyIdentifier" : "SYNDICAT ARDENTES 4 (mercredi Retour)",
        "facility" : null,
        "vehicleTypeIdentifier" : null,
        "number" : 86,
        "mobilityRestrictedSuitability" : null,
        "flexibleService" : null,
        "route" : null,
        "journeyPattern" : null,
        "company" : null,
        "footnotes" : null,
        "timetables" : null,
        "vehicleJourneyAtStops" : null,
        "journeyCategory" : "Timesheet",
        "journeyFrequencies" : null,
        "okinaJourneyPattern" : {
          "detached" : false,
          "objectId" : "europevoyages:JourneyPattern:89",
          "objectVersion" : 1,
          "creationTime" : 1491516000000,
          "creatorId" : null,
          "saved" : false,
          "id" : 89,
          "name" : "écoles St Vincent, Antoine Fée et St Martin vers Brenne 1 (mercredi Retour) 2017-04-07 10:10:43.949",
          "comment" : null,
          "registrationNumber" : null,
          "publishedName" : "écoles St Vincent, Antoine Fée et St Martin vers Brenne 1 2017-04-07 10:10:43.949",
          "sectionStatus" : "Todo",
          "route" : null,
          "departureStopPoint" : null,
          "arrivalStopPoint" : null,
          "stopPoints" : null,
          "vehicleJourneys" : null,
          "routeSections" : null,
          "okinaStopPoints" : [ {
            "id" : 849,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:849",
            "objectVersion" : 1,
            "creationTime" : 1491552643953,
            "creatorId" : null,
            "saved" : false,
            "position" : 0,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 850,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:850",
            "objectVersion" : 1,
            "creationTime" : 1491552643953,
            "creatorId" : null,
            "saved" : false,
            "position" : 1,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 851,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:851",
            "objectVersion" : 1,
            "creationTime" : 1491552643954,
            "creatorId" : null,
            "saved" : false,
            "position" : 2,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 852,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:852",
            "objectVersion" : 1,
            "creationTime" : 1491552643954,
            "creatorId" : null,
            "saved" : false,
            "position" : 3,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 853,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:853",
            "objectVersion" : 1,
            "creationTime" : 1491552643954,
            "creatorId" : null,
            "saved" : false,
            "position" : 4,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 854,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:854",
            "objectVersion" : 1,
            "creationTime" : 1491552643954,
            "creatorId" : null,
            "saved" : false,
            "position" : 5,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 855,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:855",
            "objectVersion" : 1,
            "creationTime" : 1491552643954,
            "creatorId" : null,
            "saved" : false,
            "position" : 6,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 856,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:856",
            "objectVersion" : 1,
            "creationTime" : 1491552643954,
            "creatorId" : null,
            "saved" : false,
            "position" : 7,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          } ],
          "okinaVehicleJourneys" : null,
          "etat" : 1,
          "supprime" : true,
          "okinaRoute" : null,
          "okinaDepartureStopPoint" : null,
          "okinaArrivalStopPoint" : null,
          "filled" : false
        },
        "okinaRoute" : null,
        "etat" : 1,
        "supprime" : false,
        "okinaVehicleJourneyAtStops" : null,
        "counterGroups" : null,
        "registrationNumber" : null,
        "filled" : false
      },
      "okinaJourneyPattern" : null,
      "position" : 1,
      "originDestination" : {
        "institutionId" : 276,
        "sequenceNumber" : 3
      }
    } ],
    "oppositeOds" : [ {
      "institutionId" : 276,
      "sequenceNumber" : 1
    } ],
    "way" : "WAY_BACK"
  }, {
    "id" : {
      "institutionId" : 276,
      "sequenceNumber" : 4
    },
    "institution" : {
      "id" : 276,
      "schoolEstablishmentUai" : "0360155Y",
      "okinaStopAreas" : null
    },
    "originDestinationSequenceElements" : [ {
      "id" : 4,
      "okinaVehicleJourney" : {
        "detached" : false,
        "objectId" : "Europe Voyages 23:VehicleJourney:85",
        "objectVersion" : 1,
        "creationTime" : 1491516000000,
        "creatorId" : null,
        "saved" : false,
        "id" : 85,
        "comment" : null,
        "transportMode" : null,
        "publishedJourneyName" : "SYNDICAT ARDENTES 4 (semaine Retour)",
        "publishedJourneyIdentifier" : "SYNDICAT ARDENTES 4 (semaine Retour)",
        "facility" : null,
        "vehicleTypeIdentifier" : null,
        "number" : 85,
        "mobilityRestrictedSuitability" : null,
        "flexibleService" : null,
        "route" : null,
        "journeyPattern" : null,
        "company" : null,
        "footnotes" : null,
        "timetables" : null,
        "vehicleJourneyAtStops" : null,
        "journeyCategory" : "Timesheet",
        "journeyFrequencies" : null,
        "okinaJourneyPattern" : {
          "detached" : false,
          "objectId" : "europevoyages:JourneyPattern:87",
          "objectVersion" : 1,
          "creationTime" : 1491516000000,
          "creatorId" : null,
          "saved" : false,
          "id" : 87,
          "name" : "écoles St Vincent, Antoine Fée et St Martin vers Brenne 1 (semaine Retour)",
          "comment" : null,
          "registrationNumber" : null,
          "publishedName" : "écoles St Vincent, Antoine Fée et St Martin vers Brenne 1",
          "sectionStatus" : "Todo",
          "route" : null,
          "departureStopPoint" : null,
          "arrivalStopPoint" : null,
          "stopPoints" : null,
          "vehicleJourneys" : null,
          "routeSections" : null,
          "okinaStopPoints" : [ {
            "id" : 833,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:833",
            "objectVersion" : 1,
            "creationTime" : 1491552070527,
            "creatorId" : null,
            "saved" : false,
            "position" : 0,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 834,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:834",
            "objectVersion" : 1,
            "creationTime" : 1491552070528,
            "creatorId" : null,
            "saved" : false,
            "position" : 1,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 835,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:835",
            "objectVersion" : 1,
            "creationTime" : 1491552070528,
            "creatorId" : null,
            "saved" : false,
            "position" : 2,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 836,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:836",
            "objectVersion" : 1,
            "creationTime" : 1491552070529,
            "creatorId" : null,
            "saved" : false,
            "position" : 3,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 837,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:837",
            "objectVersion" : 1,
            "creationTime" : 1491552070529,
            "creatorId" : null,
            "saved" : false,
            "position" : 4,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 838,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:838",
            "objectVersion" : 1,
            "creationTime" : 1491552070530,
            "creatorId" : null,
            "saved" : false,
            "position" : 5,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 839,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:839",
            "objectVersion" : 1,
            "creationTime" : 1491552070530,
            "creatorId" : null,
            "saved" : false,
            "position" : 6,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          }, {
            "id" : 840,
            "detached" : false,
            "objectId" : "chateauroux:StopPoint:840",
            "objectVersion" : 1,
            "creationTime" : 1491552070530,
            "creatorId" : null,
            "saved" : false,
            "position" : 7,
            "forBoarding" : null,
            "forAlighting" : null,
            "containedInStopArea" : null,
            "route" : null,
            "comment" : null,
            "okinaRoute" : null,
            "okinaContainedInStopArea" : null,
            "journeyPatternsDestination" : null,
            "journeyPatternsOrigin" : null,
            "okinaJourneyPatterns" : null,
            "vehicleJourneyAtStops" : null,
            "registrationNumber" : null,
            "filled" : false
          } ],
          "okinaVehicleJourneys" : null,
          "etat" : 0,
          "supprime" : true,
          "okinaRoute" : null,
          "okinaDepartureStopPoint" : null,
          "okinaArrivalStopPoint" : null,
          "filled" : false
        },
        "okinaRoute" : null,
        "etat" : 0,
        "supprime" : false,
        "okinaVehicleJourneyAtStops" : null,
        "counterGroups" : null,
        "registrationNumber" : null,
        "filled" : false
      },
      "okinaJourneyPattern" : null,
      "position" : 1,
      "originDestination" : {
        "institutionId" : 276,
        "sequenceNumber" : 4
      }
    } ],
    "oppositeOds" : [ {
      "institutionId" : 276,
      "sequenceNumber" : 1
    } ],
    "way" : "WAY_BACK"
  } ],
  "way" : "WAY_TO"
} ]

'''

# https://iti.okina.fr/api/Okina/REST/b2b/topology/lines
LINES = '''[ {
  "id" : 45,
  "name" : "019 - 02 - VELLES LYCEES DE CHATEAUROUX"
}, {
  "id" : 3,
  "name" : "ARTHON COLLEGE 1"
}, {
  "id" : 4,
  "name" : "ARTHON COLLEGE 2"
} ]

'''

# https://iti.okina.fr/api/Okina/REST/b2b/topology/networks
NETWORKS = '''[ {
  "id" : 2,
  "name" : "réseau scolaire"
} ]

'''

# https://iti.okina.fr/api/Okina/REST/b2b/topology/vehicle-journeys
VEHICLE_JOURNEYS = '''[ {
  "id" : 306,
  "name" : "019 02 - VELLES LYCEES DE CHATEAUROUX"
}, {
  "id" : 307,
  "name" : "019-02 - VELLES LYCEES DE CHATEAUROUX"
}, {
  "id" : 305,
  "name" : "019-02 - VELLES LYCEES DE CHATEAUROUX"
} ]

'''

QRCODE_400 = '''{
  "code" : 3,
  "status" : 400,
  "message" : "Subscriber with ID 123 has no passcard number to generate qr code from."
}'''

QRCODE_404 = '''{
  "code" : 1,
  "status" : 404,
  "message" : "Subscriber with ID 123 not found."
}'''


@pytest.fixture
def okina(db):
    return Okina.objects.create(
        slug='test', service_url='https://okina.example.net/b2b/', username='usertest', password='userpass'
    )


def test_okina_cities(app, okina):
    endpoint = tests.utils.generic_endpoint_url('okina', 'cities', slug=okina.slug)
    assert endpoint == '/okina/test/cities'
    with mock.patch('passerelle.utils.Request.get') as requests_get:
        requests_get.return_value = tests.utils.FakedResponse(content=CITIES, status_code=200)
        resp = app.get(endpoint, status=200)
        assert requests_get.call_count == 1
        assert requests_get.call_args[0][0] == 'https://okina.example.net/b2b/cities'
        assert requests_get.call_args[1]['auth'] == ('usertest', 'userpass')
        assert 'data' in resp.json
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 2
        assert resp.json['data'][0]['id'] == '83355'
        assert resp.json['data'][0]['insee'] == '36005'
        assert resp.json['data'][0]['text'] == 'ARDENTES (36120)'
        assert resp.json['data'][0]['lat'] is None

        requests_get.return_value = tests.utils.FakedResponse(content=CITIES_NEW, status_code=200)
        resp = app.get(endpoint, status=200)
        assert requests_get.call_count == 2
        assert 'data' in resp.json
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 1
        assert resp.json['data'][0]['id'] == '83355'
        assert resp.json['data'][0]['insee'] == '36005'
        assert resp.json['data'][0]['text'] == 'ARDENTES (36120)'
        assert resp.json['data'][0]['lat'] is None


def test_okina_classes(app, okina):
    endpoint = tests.utils.generic_endpoint_url('okina', 'classes', slug=okina.slug)
    assert endpoint == '/okina/test/classes'
    with mock.patch('passerelle.utils.Request.get') as requests_get:
        requests_get.return_value = tests.utils.FakedResponse(content=CLASSES, status_code=200)
        resp = app.get(endpoint, status=200)
        assert requests_get.call_count == 1
        assert requests_get.call_args[0][0] == 'https://okina.example.net/b2b/classes'
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 4
        assert resp.json['data'][0]['id'] == '1'
        assert resp.json['data'][0]['text'] == 'École maternelle - Petite section'


def test_okina_institutions(app, okina):
    endpoint = tests.utils.generic_endpoint_url('okina', 'institutions', slug=okina.slug)
    assert endpoint == '/okina/test/institutions'
    with mock.patch('passerelle.utils.Request.get') as requests_get:
        for content in (INSTITUTIONS, INSTITUTIONS_NEW):
            requests_get.return_value = tests.utils.FakedResponse(content=content, status_code=200)
            resp = app.get(endpoint, status=200)
            assert requests_get.call_args[0][0] == 'https://okina.example.net/b2b/institutions'
            assert resp.json['err'] == 0
            assert len(resp.json['data']) == 2
            assert resp.json['data'][0]['id'] == '277'
            assert resp.json['data'][0]['text'] == 'Collège Touvent'
            assert resp.json['data'][0]['lat'] == '46.791332586468016'
            assert resp.json['data'][0]['lon'] == '1.6926355997775921'

            resp = app.get(endpoint, params={'insee': '36005'}, status=200)
            assert (
                requests_get.call_args[0][0] == 'https://okina.example.net/b2b/institutions?inseeCode=36005'
            )
            assert resp.json['err'] == 0
            assert len(resp.json['data']) == 2
            assert resp.json['data'][0]['id'] == '277'
            assert resp.json['data'][0]['text'] == 'Collège Touvent'

            resp = app.get(endpoint + '/from-city/36005/', status=200)
            assert (
                requests_get.call_args[0][0]
                == 'https://okina.example.net/b2b/institutions/subscriberCity/36005'
            )
            assert resp.json['err'] == 0
            assert len(resp.json['data']) == 2
            assert resp.json['data'][0]['id'] == '277'
            assert resp.json['data'][0]['text'] == 'Collège Touvent'


def test_okina_search(app, okina):
    endpoint = tests.utils.generic_endpoint_url('okina', 'search', slug=okina.slug)
    assert endpoint == '/okina/test/search'
    with mock.patch('passerelle.utils.Request.post') as requests_post:
        requests_post.return_value = tests.utils.FakedResponse(content=SEARCH, status_code=200)

        # refuse anonymous access
        resp = app.get(endpoint + '?lat=46.828652&lon=1.701463&institution=277', status=403)

        apiuser = ApiUser.objects.create(username='all', keytype='', key='')
        obj_type = ContentType.objects.get_for_model(okina)
        AccessRight.objects.create(
            codename='can_access', apiuser=apiuser, resource_type=obj_type, resource_pk=okina.pk
        )

        app.get(endpoint + '?lat=46.828652', status=400)  # missing argument
        resp = app.get(endpoint + '?lat=46.828652&lon=1.701463&institution=277', status=200)
        assert requests_post.call_args[0][0] == 'https://okina.example.net/b2b/wishes/search'
        assert json.loads(requests_post.call_args[1]['data']) == {
            "type": "CLOSE_SCHOLAR",
            "from-address": "",
            "from-lat": "46.828652",
            "from-long": "1.701463",
            "institution-id": "277",
        }
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 2
        assert resp.json['data'][0]['id'] == '3312'
        assert resp.json['data'][0]['text'] == 'Le Grand Verger'
        assert resp.json['data'][0]['lat'] == 46.8444186
        assert resp.json['data'][0]['lon'] == 1.708197
        # french decimals
        resp = app.get(
            endpoint + '?lat=46,8&lon=1,71&address=nowhere&mode=FAR_ALL&institution=280', status=200
        )
        assert requests_post.call_args[0][0] == 'https://okina.example.net/b2b/wishes/search'
        assert json.loads(requests_post.call_args[1]['data']) == {
            "type": "FAR_ALL",
            "from-address": "nowhere",
            "from-lat": "46.8",
            "from-long": "1.71",
            "institution-id": "280",
        }
        assert resp.json['err'] == 0


def test_okina_stops_area(app, okina):
    endpoint = tests.utils.generic_endpoint_url('okina', 'stop-areas', slug=okina.slug)
    assert endpoint == '/okina/test/stop-areas'
    with mock.patch('passerelle.utils.Request.get') as requests_get:
        requests_get.return_value = tests.utils.FakedResponse(content=STOPS, status_code=200)
        resp = app.get(endpoint + '/from-city/36005/to-institution/276/', status=200)
        assert (
            requests_get.call_args[0][0]
            == 'https://okina.example.net/b2b/stop-areas/subscriberCity/36005/institution/276'
        )
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 3
        assert resp.json['data'][0]['id'] == '3281'
        assert resp.json['data'][0]['text'] == 'Les Loges de Dressais'


def test_okina_ods(app, okina):
    endpoint = tests.utils.generic_endpoint_url('okina', 'origin-destinations', slug=okina.slug)
    assert endpoint == '/okina/test/origin-destinations'
    with mock.patch('passerelle.utils.Request.get') as requests_get:
        requests_get.return_value = tests.utils.FakedResponse(content=ODS, status_code=200)
        resp = app.get(endpoint, status=200)
        assert requests_get.call_count == 1
        assert requests_get.call_args[0][0] == 'https://okina.example.net/b2b/ods'
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 2
        assert resp.json['data'][0]['id'] == 'inst:276-seq:1-6-84'
        assert resp.json['data'][0]['text'] == 'SYNDICAT ARDENTES 4 (semaine Aller)'
        assert resp.json['data'][0]['identifier'] == 'SYNDICAT ARDENTES 4 (semaine Aller)'
        assert resp.json['data'][0]['vehicle_journey_id'] == '84'

        requests_get.return_value = tests.utils.FakedResponse(content=ODS, status_code=200)
        resp = app.get(endpoint + '/to-institution/276/', status=200)
        assert requests_get.call_args[0][0] == 'https://okina.example.net/b2b/ods/institution/276'
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 2
        assert resp.json['data'][0]['id'] == 'inst:276-seq:1-6-84'
        assert resp.json['data'][0]['text'] == 'SYNDICAT ARDENTES 4 (semaine Aller)'
        assert resp.json['data'][0]['identifier'] == 'SYNDICAT ARDENTES 4 (semaine Aller)'
        assert resp.json['data'][0]['vehicle_journey_id'] == '84'

        requests_get.return_value = tests.utils.FakedResponse(content=ODS_LINES, status_code=200)
        resp = app.get(endpoint + '/from-stop-area/3282/to-institution/276/', status=200)
        assert (
            requests_get.call_args[0][0] == 'https://okina.example.net/b2b/ods/institution/276/stop-area/3282'
        )
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 3
        assert resp.json['data'][0]['id'] == '1'
        assert resp.json['data'][0]['text'] == 'LIGNE 24'
        assert len(resp.json['data'][0]['lines']) == 1
        assert resp.json['data'][0]['lines'][0]['id'] == '24'
        assert resp.json['data'][0]['lines'][0]['text'] == 'LIGNE 24'
        assert resp.json['data'][1]['id'] == '2'
        assert resp.json['data'][1]['text'] == 'LIGNE 22'
        assert len(resp.json['data'][1]['lines']) == 1
        assert resp.json['data'][1]['lines'][0]['id'] == '22'
        assert resp.json['data'][1]['lines'][0]['text'] == 'LIGNE 22'
        assert resp.json['data'][2]['id'] == '3'
        assert resp.json['data'][2]['text'] == 'LIGNE 24 + LIGNE 23'
        assert len(resp.json['data'][2]['lines']) == 2
        assert resp.json['data'][2]['lines'][0]['id'] == '24'
        assert resp.json['data'][2]['lines'][0]['text'] == 'LIGNE 24'
        assert resp.json['data'][2]['lines'][1]['id'] == '23'
        assert resp.json['data'][2]['lines'][1]['text'] == 'LIGNE 23'

        requests_get.return_value = tests.utils.FakedResponse(content=ODS_FULL, status_code=200)
        resp = app.get(endpoint + '/from-city/36005/to-institution/276/', status=200)
        assert (
            requests_get.call_args[0][0]
            == 'https://okina.example.net/b2b/ods/institution/276/subscriberCity/36005'
        )
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 1
        assert resp.json['data'][0]['id'] == 'inst:276-seq:1-6-84'
        assert (
            resp.json['data'][0]['text'] == 'Brenne 1 vers écoles Antoine Fée, Saint Martin et Saint Vincent'
        )
        assert resp.json['data'][0]['identifier'] == 'SYNDICAT ARDENTES 4 (semaine Aller)'
        assert resp.json['data'][0]['vehicle_journey_id'] == '84'

        requests_get.return_value = tests.utils.FakedResponse(content=ODS_FULL, status_code=200)
        resp = app.get(endpoint + '/from-city/36005/', status=200)
        assert requests_get.call_args[0][0] == 'https://okina.example.net/b2b/ods/subscriberCity/36005'
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 1
        assert resp.json['data'][0]['id'] == 'inst:276-seq:1-6-84'
        assert (
            resp.json['data'][0]['text'] == 'Brenne 1 vers écoles Antoine Fée, Saint Martin et Saint Vincent'
        )
        assert resp.json['data'][0]['identifier'] == 'SYNDICAT ARDENTES 4 (semaine Aller)'
        assert resp.json['data'][0]['vehicle_journey_id'] == '84'


def test_okina_topology(app, okina):
    endpoint = tests.utils.generic_endpoint_url('okina', 'topology', slug=okina.slug)
    assert endpoint == '/okina/test/topology'
    with mock.patch('passerelle.utils.Request.get') as requests_get:
        requests_get.return_value = tests.utils.FakedResponse(content=LINES, status_code=200)
        resp = app.get(endpoint + '/lines/', status=200)
        assert requests_get.call_args[0][0] == 'https://okina.example.net/b2b/topology/lines'
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 3
        assert resp.json['data'][0]['id'] == '45'
        assert resp.json['data'][0]['text'] == '019 - 02 - VELLES LYCEES DE CHATEAUROUX'

        requests_get.return_value = tests.utils.FakedResponse(content=NETWORKS, status_code=200)
        resp = app.get(endpoint + '/networks/', status=200)
        assert requests_get.call_args[0][0] == 'https://okina.example.net/b2b/topology/networks'
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 1
        assert resp.json['data'][0]['id'] == '2'
        assert resp.json['data'][0]['text'] == 'réseau scolaire'

        requests_get.return_value = tests.utils.FakedResponse(content=VEHICLE_JOURNEYS, status_code=200)
        resp = app.get(endpoint + '/vehicle-journeys/', status=200)
        assert requests_get.call_args[0][0] == 'https://okina.example.net/b2b/topology/vehicle-journeys'
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 3
        assert resp.json['data'][0]['id'] == '306'
        assert resp.json['data'][0]['text'] == '019 02 - VELLES LYCEES DE CHATEAUROUX'


def test_okina_errors(app, okina):
    with mock.patch('passerelle.utils.Request.get') as requests_get:
        for response in (
            (200, '{"foo": "bar"}'),  # not a list
            (200, '{["foo": "bar"]}'),  # list of malformatted dict
            (200, '<h1>Welcome</h1>'),  # bad json
            (403, '<h1>Bad creds</h1>'),
            (404, '<h1>Not Found</h1>'),
            (400, '{"message": "bad request"}'),
            (400, '[{"message": "bad request"}]'),
            (500, '<h1>Crash</h1>'),
        ):
            requests_get.return_value = tests.utils.FakedResponse(
                content=response[1], status_code=response[0]
            )
            resp = app.get('/okina/test/cities', status=200)
            assert resp.json['err'] == 1
            if resp.json['data'] and 'status_code' in resp.json['data']:
                assert resp.json['data']['status_code'] == response[0]

        # "normal" 401/403 response, ie problem with login/password
        for status_code in (401, 403):
            requests_get.return_value = tests.utils.FakedResponse(
                content='''{"message": "Invalid credentials",
                    "code": 4, "status" : %d}'''
                % status_code,
                status_code=status_code,
            )
            resp = app.get('/okina/test/cities', status=200)
            assert resp.json['err'] == 1
            assert resp.json['err_desc'].startswith("Invalid credentials")


def test_okina_suscribe(app, okina):

    for service in ('subscriber', 'subscription'):
        endpoint = tests.utils.generic_endpoint_url('okina', service, slug=okina.slug)
        assert endpoint == '/okina/test/%s' % service
        with mock.patch('passerelle.utils.Request.get') as requests_get:
            resp = app.post(endpoint, status=403)
            assert requests_get.call_count == 0
            assert resp.json['err'] == 1
            assert resp.json['err_class'] == 'django.core.exceptions.PermissionDenied'
    # open access
    api = ApiUser.objects.create(username='all', keytype='', key='')
    obj_type = ContentType.objects.get_for_model(okina)
    AccessRight.objects.create(
        codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=okina.pk
    )
    for service in ('subscriber', 'subscription'):
        endpoint = tests.utils.generic_endpoint_url('okina', service, slug=okina.slug)
        assert endpoint == '/okina/test/%s' % service
        with mock.patch('passerelle.utils.Request.post') as requests_get:
            resp = app.post(endpoint, status=400)  # no payload
            assert requests_get.call_count == 0
            assert resp.json['err'] == 1
            assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError'
            assert resp.json['err_desc'] == 'payload must be a JSON object'

            resp = app.post_json(endpoint, params=[], status=400)  # bad payload
            assert requests_get.call_count == 0
            assert resp.json['err'] == 1
            assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError'
            assert resp.json['err_desc'] == 'payload must be a dict'

            requests_get.return_value = tests.utils.FakedResponse(content='{"foo":"bar"}', status_code=200)
            resp = app.post_json(endpoint, params={'x': 'y'})
            assert requests_get.call_count == 1
            assert resp.json['err'] == 0
            assert resp.json['data'] == {'foo': 'bar'}

    # multiple subscriptions
    endpoint = tests.utils.generic_endpoint_url('okina', 'subscription', slug=okina.slug)
    with mock.patch('passerelle.utils.Request.post') as requests_post:
        requests_post.return_value = tests.utils.FakedResponse(content='{"sub":"ok"}', status_code=200)
        resp = app.post_json(endpoint, params={'lineIds': '1 ,2, 3', 'foo': 'bar'})
        assert requests_post.call_count == 3
        assert json.loads(requests_post.call_args_list[0][1]['data']) == {'foo': 'bar', 'lineId': '1'}
        assert json.loads(requests_post.call_args_list[1][1]['data']) == {'foo': 'bar', 'lineId': '2'}
        assert json.loads(requests_post.call_args_list[2][1]['data']) == {'foo': 'bar', 'lineId': '3'}
        assert resp.json['err'] == 0
        assert resp.json['data'] == [{'sub': 'ok'}, {'sub': 'ok'}, {'sub': 'ok'}]

    # qrcode
    endpoint = '/okina/test/subscriber/123/qrcode'
    with mock.patch('passerelle.utils.Request.get') as requests_get:
        requests_get.return_value = tests.utils.FakedResponse(
            content='PNGfoo', headers={'Content-Type': 'image/png'}, status_code=200
        )
        resp = app.get(endpoint)
        assert requests_get.call_count == 1
        assert resp.headers['content-type'] == 'image/png'
        assert resp.content == b'PNGfoo'

        requests_get.return_value = tests.utils.FakedResponse(
            content=QRCODE_400, headers={'Content-Type': 'application/json'}, status_code=400
        )
        resp = app.get(endpoint, status=200)
        assert requests_get.call_count == 2
        assert resp.json['err'] == 1
        assert resp.json['err_desc'].startswith(
            'Subscriber with ID 123 has no passcard number to generate qr code from.'
        )

        requests_get.return_value = tests.utils.FakedResponse(
            content=QRCODE_404, headers={'Content-Type': 'application/json'}, status_code=404
        )
        resp = app.get(endpoint, status=200)
        assert requests_get.call_count == 3
        assert resp.json['err'] == 1
        assert resp.json['err_desc'].startswith('Subscriber with ID 123 not found.')
