Initial commit
This commit is contained in:
commit
d81a830e3f
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
target/
|
||||
.idea/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
24
Dockerfile
Normal file
24
Dockerfile
Normal file
@ -0,0 +1,24 @@
|
||||
FROM openjdk:14-alpine as jdkbuilder
|
||||
RUN apk add --no-cache binutils
|
||||
ENV MODULES java.base,java.compiler,java.desktop,java.logging,java.management,java.naming,java.security.jgss,java.xml,jdk.crypto.ec,jdk.unsupported
|
||||
RUN jlink --output /myjdk --module-path $JAVA_HOME/jmods --add-modules $MODULES --no-header-files --no-man-pages --strip-debug --compress=2
|
||||
RUN strip -p --strip-unneeded /myjdk/lib/server/libjvm.so
|
||||
|
||||
FROM maven:3.6.3-jdk-14 as builder
|
||||
WORKDIR /app
|
||||
COPY pom.xml .
|
||||
RUN mvn verify clean --fail-never
|
||||
COPY src/main src/main
|
||||
RUN mvn package
|
||||
|
||||
FROM alpine
|
||||
ENV APPLICATION_USER app
|
||||
RUN adduser -D -g '' $APPLICATION_USER
|
||||
RUN mkdir /app
|
||||
RUN chown -R $APPLICATION_USER /app
|
||||
USER $APPLICATION_USER
|
||||
COPY --from=builder /app/target/c2c*.jar /app/app.jar
|
||||
COPY --from=jdkbuilder /myjdk /myjdk
|
||||
WORKDIR /app
|
||||
EXPOSE 4000
|
||||
CMD ["/myjdk/bin/java", "-server", "-XX:+UseG1GC", "-XX:+UseStringDeduplication", "-jar", "app.jar"]
|
||||
227
pom.xml
Normal file
227
pom.xml
Normal file
@ -0,0 +1,227 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>be.simplenotes.c2c</groupId>
|
||||
<artifactId>c2c</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<properties>
|
||||
<java.version>14</java.version>
|
||||
<kotlin.version>1.4.10</kotlin.version>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<main.class>be.simplenotes.c2c/C2cKt</main.class>
|
||||
<openhtml.version>1.0.4</openhtml.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.koin</groupId>
|
||||
<artifactId>koin-core</artifactId>
|
||||
<version>2.1.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.http4k</groupId>
|
||||
<artifactId>http4k-core</artifactId>
|
||||
<version>3.261.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.http4k</groupId>
|
||||
<artifactId>http4k-contract</artifactId>
|
||||
<version>3.261.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.http4k</groupId>
|
||||
<artifactId>http4k-format-jackson</artifactId>
|
||||
<version>3.261.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.http4k</groupId>
|
||||
<artifactId>http4k-server-apache</artifactId>
|
||||
<version>3.261.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.http4k</groupId>
|
||||
<artifactId>http4k-client-apache</artifactId>
|
||||
<version>3.261.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.2.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>2.8.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.13.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.openhtmltopdf</groupId>
|
||||
<artifactId>openhtmltopdf-core</artifactId>
|
||||
<version>${openhtml.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.openhtmltopdf</groupId>
|
||||
<artifactId>openhtmltopdf-pdfbox</artifactId>
|
||||
<version>${openhtml.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mitchellbosecke</groupId>
|
||||
<artifactId>pebble</artifactId>
|
||||
<version>2.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.7.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>5.7.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>3.17.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.mockk</groupId>
|
||||
<artifactId>mockk</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jcenter</id>
|
||||
<url>https://jcenter.bintray.com</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-jdk7</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib-common</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<build>
|
||||
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
|
||||
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.1.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M4</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.surefire</groupId>
|
||||
<artifactId>surefire-junit-platform</artifactId>
|
||||
<version>3.0.0-M4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>${main.class}</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>com.github.ben-manes.caffeine:caffeine</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>org.jetbrains.kotlin:kotlin-reflect</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
102
src/main/kotlin/C2c.kt
Normal file
102
src/main/kotlin/C2c.kt
Normal file
@ -0,0 +1,102 @@
|
||||
package be.simplenotes.c2c
|
||||
|
||||
import be.simplenotes.c2c.api.Api
|
||||
import be.simplenotes.c2c.api.ApiUrls
|
||||
import be.simplenotes.c2c.pdf.PdfCreator
|
||||
import be.simplenotes.c2c.pdf.StreamFactory
|
||||
import be.simplenotes.c2c.routes.IndexRoute
|
||||
import be.simplenotes.c2c.routes.PdfRoute
|
||||
import be.simplenotes.c2c.routes.RouteRoute
|
||||
import be.simplenotes.c2c.routes.SearchRoute
|
||||
import com.mitchellbosecke.pebble.PebbleEngine
|
||||
import com.mitchellbosecke.pebble.loader.ClasspathLoader
|
||||
import org.apache.hc.client5.http.impl.DefaultRedirectStrategy
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder
|
||||
import org.http4k.client.ApacheClient
|
||||
import org.http4k.contract.ContractRoute
|
||||
import org.http4k.contract.contract
|
||||
import org.http4k.contract.openapi.ApiInfo
|
||||
import org.http4k.contract.openapi.v3.OpenApi3
|
||||
import org.http4k.core.Filter
|
||||
import org.http4k.core.Response
|
||||
import org.http4k.core.Status
|
||||
import org.http4k.core.then
|
||||
import org.http4k.filter.RequestFilters
|
||||
import org.http4k.filter.ServerFilters
|
||||
import org.http4k.format.Jackson
|
||||
import org.http4k.server.asServer
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
|
||||
val module = module {
|
||||
single {
|
||||
ApacheClient(
|
||||
HttpClientBuilder
|
||||
.create()
|
||||
.setRedirectStrategy(DefaultRedirectStrategy())
|
||||
.build()
|
||||
)
|
||||
}
|
||||
single { ApiUrls(get()) }
|
||||
single { Jackson.mapper }
|
||||
single { Api(get(), get()) }
|
||||
single { PdfCreator(get()) }
|
||||
single { StreamFactory(get()) }
|
||||
single {
|
||||
PebbleEngine
|
||||
.Builder()
|
||||
.loader(ClasspathLoader().apply {
|
||||
prefix = "views/"
|
||||
suffix = ".twig"
|
||||
})
|
||||
.cacheActive(false)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
val routes = module {
|
||||
single(named<SearchRoute>()) { SearchRoute(get()).searchRoute() } bind ContractRoute::class
|
||||
single(named<RouteRoute>()) { RouteRoute(get()).routeRoute() } bind ContractRoute::class
|
||||
single(named<PdfRoute>()) { PdfRoute(get(), get()).route() } bind ContractRoute::class
|
||||
single(named<IndexRoute>()) { IndexRoute(get()).route() } bind ContractRoute::class
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val koin = startKoin {
|
||||
modules(module, routes)
|
||||
}.koin
|
||||
|
||||
val appRoutes = koin.getAll<ContractRoute>()
|
||||
val app = contract {
|
||||
renderer = OpenApi3(ApiInfo("Camp2Camp", "1.0-SNAPSHOT"), Jackson)
|
||||
descriptionPath = "/api/swagger.json"
|
||||
routes.all.addAll(appRoutes)
|
||||
}
|
||||
|
||||
val logger = LoggerFactory.getLogger("Camp2Camp")
|
||||
|
||||
val loggingFilter = RequestFilters.Tap {
|
||||
logger.info("${it.method} ${it.uri}")
|
||||
}
|
||||
|
||||
val catchAll = Filter { next ->
|
||||
{
|
||||
try {
|
||||
next(it)
|
||||
} catch (e: Exception) {
|
||||
val sw = StringWriter()
|
||||
e.printStackTrace(PrintWriter(sw))
|
||||
logger.error(sw.toString())
|
||||
Response(Status.INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catchAll.then(ServerFilters.GZip()).then(loggingFilter).then(app).asServer(CustomApacheServer(4000)).start()
|
||||
logger.info("Listening on http://localhost:4000")
|
||||
}
|
||||
33
src/main/kotlin/CustomApacheServer.kt
Normal file
33
src/main/kotlin/CustomApacheServer.kt
Normal file
@ -0,0 +1,33 @@
|
||||
package be.simplenotes.c2c
|
||||
|
||||
import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap
|
||||
import org.apache.hc.core5.http.io.SocketConfig
|
||||
import org.http4k.core.HttpHandler
|
||||
import org.http4k.server.Http4kRequestHandler
|
||||
import org.http4k.server.Http4kServer
|
||||
import org.http4k.server.ServerConfig
|
||||
|
||||
class CustomApacheServer(val port: Int) : ServerConfig {
|
||||
override fun toServer(httpHandler: HttpHandler): Http4kServer = object : Http4kServer {
|
||||
val handler = Http4kRequestHandler(httpHandler)
|
||||
|
||||
val server = ServerBootstrap.bootstrap()
|
||||
.setListenerPort(port)
|
||||
.setSocketConfig(SocketConfig.custom()
|
||||
.setTcpNoDelay(true)
|
||||
.setSoKeepAlive(true)
|
||||
.setSoReuseAddress(true)
|
||||
.setBacklogSize(1000)
|
||||
.build())
|
||||
.apply {
|
||||
register("*", handler)
|
||||
registerVirtual("dev.simplenotes.be", "*", handler) // TODO: find a way to put a wildcard
|
||||
}.create()
|
||||
|
||||
override fun start() = apply { server.start() }
|
||||
|
||||
override fun stop() = apply { server.stop() }
|
||||
|
||||
override fun port(): Int = port
|
||||
}
|
||||
}
|
||||
44
src/main/kotlin/api/Api.kt
Normal file
44
src/main/kotlin/api/Api.kt
Normal file
@ -0,0 +1,44 @@
|
||||
package be.simplenotes.c2c.api
|
||||
|
||||
import be.simplenotes.c2c.extractors.extractRoute
|
||||
import be.simplenotes.c2c.extractors.extractRoutes
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import org.checkerframework.checker.nullness.qual.Nullable
|
||||
import org.http4k.format.Jackson
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class Api(private val apiUrls: ApiUrls, private val mapper: ObjectMapper = Jackson.mapper) {
|
||||
private val searchCache = Caffeine.newBuilder()
|
||||
.maximumSize(100)
|
||||
.expireAfterWrite(15, TimeUnit.MINUTES)
|
||||
.build<String, SearchResults>()
|
||||
|
||||
fun cachedSearch(terms: String) = searchCache[terms, ::search]!!
|
||||
|
||||
private fun search(terms: String): SearchResults {
|
||||
val root = mapper.readTree(apiUrls.search(terms))
|
||||
val routes = root["routes"]["documents"].map(::extractRoutes)
|
||||
return SearchResults(routes)
|
||||
}
|
||||
|
||||
private val routeCache = Caffeine.newBuilder()
|
||||
.maximumSize(100)
|
||||
.recordStats()
|
||||
.expireAfterWrite(15, TimeUnit.MINUTES)
|
||||
.build<String, Route>()
|
||||
|
||||
private val logger = LoggerFactory.getLogger("API")
|
||||
|
||||
fun getRoute(id: String): Route? {
|
||||
val res = routeCache.get(id) {
|
||||
apiUrls.getRoute(it)?.let { json ->
|
||||
extractRoute(mapper.readTree(json))
|
||||
}
|
||||
}
|
||||
logger.info("Cache: ${routeCache.stats()}")
|
||||
return res
|
||||
}
|
||||
|
||||
}
|
||||
19
src/main/kotlin/api/ApiUrls.kt
Normal file
19
src/main/kotlin/api/ApiUrls.kt
Normal file
@ -0,0 +1,19 @@
|
||||
package be.simplenotes.c2c.api
|
||||
|
||||
import org.http4k.core.HttpHandler
|
||||
import org.http4k.core.Method
|
||||
import org.http4k.core.Request
|
||||
import org.http4k.urlEncoded
|
||||
|
||||
class ApiUrls(private val client: HttpHandler) {
|
||||
private val baseUrl = "https://api.camptocamp.org"
|
||||
|
||||
private fun get(url: String): String? {
|
||||
val res = client(Request(Method.GET, "$baseUrl$url"))
|
||||
return if(res.status.successful) res.bodyString()
|
||||
else null
|
||||
}
|
||||
fun search(terms: String) = get("/search?q=${terms.urlEncoded()}&t=w,r&limit=7")
|
||||
fun getRoute(id: String): String? = get("/routes/${id}?cook=fr")
|
||||
fun getOuting(id: String) = get("/outings/${id}?cook=fr")
|
||||
}
|
||||
85
src/main/kotlin/api/Models.kt
Normal file
85
src/main/kotlin/api/Models.kt
Normal file
@ -0,0 +1,85 @@
|
||||
package be.simplenotes.c2c.api
|
||||
|
||||
data class SearchResults(val routes: List<RouteResult>)
|
||||
|
||||
data class RouteResult(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val summary: String?,
|
||||
val activities: List<Activity>,
|
||||
)
|
||||
|
||||
data class Route(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val summary: String?,
|
||||
val routeHistory: String,
|
||||
val description: String,
|
||||
val remarks: String,
|
||||
val gear: String,
|
||||
val activities: List<Activity>,
|
||||
val height: Height,
|
||||
val rating: Rating,
|
||||
val associations: Associations,
|
||||
)
|
||||
|
||||
data class Associations(
|
||||
val outings: List<Outing>,
|
||||
val images: List<Image>,
|
||||
val waypoints: List<Waypoint>,
|
||||
)
|
||||
|
||||
data class Image(
|
||||
val id: String,
|
||||
val square: String,
|
||||
val medium: String,
|
||||
val big: String,
|
||||
val full: String,
|
||||
val title: String,
|
||||
)
|
||||
|
||||
data class Outing(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val dateStart: String,
|
||||
val dateEnd: String,
|
||||
val condition: String?,
|
||||
val quality: String,
|
||||
val activities: List<Activity>,
|
||||
)
|
||||
|
||||
data class Waypoint(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val elevation: String,
|
||||
)
|
||||
|
||||
data class Height(
|
||||
val heightDiffDifficulties: String?,
|
||||
val up: String?,
|
||||
val down: String?,
|
||||
val min: String?,
|
||||
val max: String?,
|
||||
)
|
||||
|
||||
data class Rating(
|
||||
val global: String?,
|
||||
val free: String?,
|
||||
val required: String?,
|
||||
val engagement: String?,
|
||||
val equipmentQuality: String?,
|
||||
)
|
||||
|
||||
enum class Activity {
|
||||
SKITOURING,
|
||||
SNOW_ICE_MIXED,
|
||||
MOUNTAIN_CLIMBING,
|
||||
ROCK_CLIMBING,
|
||||
ICE_CLIMBING,
|
||||
HIKING,
|
||||
SNOWSHOEING,
|
||||
PARAGLIDING,
|
||||
MOUNTAIN_BIKING,
|
||||
VIA_FERRATA,
|
||||
SLACKLINING,
|
||||
}
|
||||
8
src/main/kotlin/extractors/Extensions.kt
Normal file
8
src/main/kotlin/extractors/Extensions.kt
Normal file
@ -0,0 +1,8 @@
|
||||
package be.simplenotes.c2c.extractors
|
||||
|
||||
import be.simplenotes.c2c.api.Activity
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
|
||||
fun JsonNode.activities(): List<Activity> = map {
|
||||
Activity.valueOf(it.asText().toUpperCase())
|
||||
}
|
||||
7
src/main/kotlin/extractors/LocaleExtractor.kt
Normal file
7
src/main/kotlin/extractors/LocaleExtractor.kt
Normal file
@ -0,0 +1,7 @@
|
||||
package be.simplenotes.c2c.extractors
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
|
||||
fun extractLocale(locales: JsonNode): JsonNode = locales.find { locale ->
|
||||
locale["lang"].asText() == "fr"
|
||||
} ?: locales[0]
|
||||
85
src/main/kotlin/extractors/RouteExtractor.kt
Normal file
85
src/main/kotlin/extractors/RouteExtractor.kt
Normal file
@ -0,0 +1,85 @@
|
||||
package be.simplenotes.c2c.extractors
|
||||
|
||||
import be.simplenotes.c2c.api.*
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
|
||||
fun extractRoutes(node: JsonNode): RouteResult {
|
||||
val locale = extractLocale(node["locales"])
|
||||
|
||||
return RouteResult(
|
||||
id = node["document_id"].asText(),
|
||||
title = locale["title_prefix"].asText() + " : " + locale["title"].asText(),
|
||||
summary = locale["summary"].asText(null),
|
||||
activities = node["activities"].activities(),
|
||||
)
|
||||
}
|
||||
|
||||
fun extractRoute(root: JsonNode): Route {
|
||||
val locale = extractLocale(root["locales"])
|
||||
|
||||
return Route(
|
||||
root["document_id"].asText(),
|
||||
locale["title_prefix"].asText() + " : " + locale["title"].asText(),
|
||||
locale["summary"].asText(null),
|
||||
locale["route_history"].asText(),
|
||||
locale["description"].asText(),
|
||||
locale["remarks"].asText(),
|
||||
locale["gear"].asText(),
|
||||
root["activities"].activities(),
|
||||
Height(
|
||||
root["height_diff_difficulties"].asText(null),
|
||||
root["height_diff_up"].asText(null),
|
||||
root["height_diff_down"].asText(null),
|
||||
root["elevation_min"].asText(null),
|
||||
root["elevation_max"].asText(null),
|
||||
),
|
||||
Rating(
|
||||
root["global_rating"].asText(null),
|
||||
root["rock_free_rating"].asText(null),
|
||||
root["rock_required_rating"].asText(null),
|
||||
root["engagement_rating"].asText(null),
|
||||
root["equipment_rating"].asText(null),
|
||||
),
|
||||
Associations(
|
||||
extractOutings(root["associations"]["recent_outings"]["documents"]),
|
||||
extractImages(root["associations"]["images"]),
|
||||
extractWaypoints(root["associations"]["waypoints"]),
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
fun extractOutings(jsonNode: JsonNode) = jsonNode.map { node ->
|
||||
val locale = extractLocale(node["locales"])
|
||||
Outing(
|
||||
id = node["document_id"].asText(),
|
||||
title = locale["title"].asText(),
|
||||
dateStart = node["date_start"].asText(),
|
||||
dateEnd = node["date_end"].asText(),
|
||||
condition = node["condition_rating"].asText(null),
|
||||
quality = node["quality"].asText(),
|
||||
activities = node["activities"].activities()
|
||||
)
|
||||
}
|
||||
|
||||
fun extractImages(jsonNode: JsonNode) = jsonNode.map { node ->
|
||||
val locale = extractLocale(node["locales"])
|
||||
val filename = node["filename"].asText()
|
||||
Image(
|
||||
id = node["document_id"].asText(),
|
||||
title = locale["title"].asText(),
|
||||
square = "https://media.camptocamp.org/c2corg-active/${filename.replace(".", "SI.").replace(".svg", ".jpg")}",
|
||||
medium = "https://media.camptocamp.org/c2corg-active/${filename.replace(".", "MI.").replace(".svg", ".jpg")}",
|
||||
big = "https://media.camptocamp.org/c2corg-active/${filename.replace(".", "BI.").replace(".svg", ".jpg")}",
|
||||
full = "https://media.camptocamp.org/c2corg-active/$filename",
|
||||
)
|
||||
}
|
||||
|
||||
fun extractWaypoints(jsonNode: JsonNode) = jsonNode.map { node ->
|
||||
val locale = extractLocale(node["locales"])
|
||||
Waypoint(
|
||||
id = node["document_id"].asText(),
|
||||
title = locale["title"].asText(),
|
||||
elevation = node["elevation"].asText(),
|
||||
)
|
||||
}
|
||||
80
src/main/kotlin/pdf/PdfCreator.kt
Normal file
80
src/main/kotlin/pdf/PdfCreator.kt
Normal file
@ -0,0 +1,80 @@
|
||||
package be.simplenotes.c2c.pdf
|
||||
|
||||
import be.simplenotes.c2c.api.Route
|
||||
import be.simplenotes.c2c.templates.render
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.mitchellbosecke.pebble.PebbleEngine
|
||||
import com.mitchellbosecke.pebble.loader.ClasspathLoader
|
||||
import com.openhtmltopdf.extend.FSStream
|
||||
import com.openhtmltopdf.extend.FSStreamFactory
|
||||
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder
|
||||
import com.openhtmltopdf.util.XRLog
|
||||
import org.http4k.core.HttpHandler
|
||||
import org.http4k.core.Method
|
||||
import org.http4k.core.Request
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.helper.W3CDom
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class StreamFactory(private val client: HttpHandler) : FSStreamFactory {
|
||||
private val logger = LoggerFactory.getLogger(javaClass)
|
||||
|
||||
private val imgCache = Caffeine.newBuilder()
|
||||
.maximumSize(15)
|
||||
.expireAfterWrite(15, TimeUnit.MINUTES)
|
||||
.build<String, ByteArray>()
|
||||
|
||||
override fun getUrl(url: String) = object : FSStream {
|
||||
override fun getStream(): InputStream? {
|
||||
val bytes = imgCache.get(url) {
|
||||
logger.info("Downloading $url")
|
||||
val res = client(Request(Method.GET, url))
|
||||
if (res.status.successful) res.body.stream.readAllBytes()
|
||||
else null
|
||||
}
|
||||
return bytes?.inputStream()
|
||||
}
|
||||
|
||||
override fun getReader() = null
|
||||
}
|
||||
}
|
||||
|
||||
class PdfCreator(private val streamFactory: StreamFactory) {
|
||||
private val engine = PebbleEngine
|
||||
.Builder()
|
||||
.loader(ClasspathLoader().apply {
|
||||
prefix = "templates/"
|
||||
suffix = ".twig"
|
||||
})
|
||||
.cacheActive(true)
|
||||
.build()
|
||||
|
||||
fun create(route: Route): ByteArrayInputStream {
|
||||
val html = engine.render("index", mapOf("route" to route))
|
||||
|
||||
XRLog.setLoggingEnabled(false)
|
||||
|
||||
val document = W3CDom().fromJsoup(Jsoup.parse(html))
|
||||
val out = ByteArrayOutputStream()
|
||||
|
||||
PdfRendererBuilder()
|
||||
.withW3cDocument(document, "")
|
||||
.useFastMode()
|
||||
.toStream(out)
|
||||
.useProtocolsStreamImplementation(streamFactory, "http", "https")
|
||||
|
||||
.buildPdfRenderer().apply {
|
||||
layout()
|
||||
createPDF()
|
||||
close()
|
||||
}
|
||||
|
||||
out.close()
|
||||
return ByteArrayInputStream(out.toByteArray())
|
||||
}
|
||||
|
||||
}
|
||||
24
src/main/kotlin/routes/IndexRoute.kt
Normal file
24
src/main/kotlin/routes/IndexRoute.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package be.simplenotes.c2c.routes
|
||||
|
||||
import be.simplenotes.c2c.templates.render
|
||||
import com.mitchellbosecke.pebble.PebbleEngine
|
||||
import org.http4k.contract.ContractRoute
|
||||
import org.http4k.contract.bindContract
|
||||
import org.http4k.core.HttpHandler
|
||||
import org.http4k.core.Method
|
||||
import org.http4k.core.Response
|
||||
import org.http4k.core.Status
|
||||
|
||||
class IndexRoute(private val engine: PebbleEngine) {
|
||||
|
||||
fun route(): ContractRoute {
|
||||
val spec = "/" bindContract Method.GET
|
||||
|
||||
val route: HttpHandler = {
|
||||
Response(Status.OK).body(engine.render("index"))
|
||||
}
|
||||
|
||||
return spec to route
|
||||
}
|
||||
|
||||
}
|
||||
38
src/main/kotlin/routes/PdfRoute.kt
Normal file
38
src/main/kotlin/routes/PdfRoute.kt
Normal file
@ -0,0 +1,38 @@
|
||||
package be.simplenotes.c2c.routes
|
||||
|
||||
import be.simplenotes.c2c.api.Api
|
||||
import be.simplenotes.c2c.pdf.PdfCreator
|
||||
import org.http4k.contract.ContractRoute
|
||||
import org.http4k.contract.div
|
||||
import org.http4k.contract.meta
|
||||
import org.http4k.core.HttpHandler
|
||||
import org.http4k.core.Method
|
||||
import org.http4k.core.Response
|
||||
import org.http4k.core.Status
|
||||
import org.http4k.lens.Path
|
||||
|
||||
// TODO: rename..
|
||||
class PdfRoute(private val api: Api, private val pdfCreator: PdfCreator) {
|
||||
|
||||
fun route(): ContractRoute {
|
||||
val spec = "/route" / Path.of("id", "Route's ID") / "pdf" meta {
|
||||
summary = "Route details"
|
||||
} bindContract Method.GET
|
||||
|
||||
fun route(id: String, pdf: String): HttpHandler = {
|
||||
when (val route = api.getRoute(id)) {
|
||||
null -> Response(Status.NOT_FOUND)
|
||||
else -> {
|
||||
val bytes = pdfCreator.create(route)
|
||||
Response(Status.OK)
|
||||
.body(bytes)
|
||||
.header("Content-Type", "application/pdf")
|
||||
.header("Content-Disposition", "attachment; filename=\"out.pdf\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spec to ::route
|
||||
}
|
||||
|
||||
}
|
||||
31
src/main/kotlin/routes/RouteRoute.kt
Normal file
31
src/main/kotlin/routes/RouteRoute.kt
Normal file
@ -0,0 +1,31 @@
|
||||
package be.simplenotes.c2c.routes
|
||||
|
||||
import be.simplenotes.c2c.api.Api
|
||||
import be.simplenotes.c2c.api.Route
|
||||
import org.http4k.contract.ContractRoute
|
||||
import org.http4k.contract.div
|
||||
import org.http4k.contract.meta
|
||||
import org.http4k.core.*
|
||||
import org.http4k.format.Jackson.auto
|
||||
import org.http4k.lens.Path
|
||||
|
||||
// TODO: rename..
|
||||
class RouteRoute(private val api: Api) {
|
||||
|
||||
fun routeRoute(): ContractRoute {
|
||||
val responseLens = Body.auto<Route>("Route").toLens()
|
||||
|
||||
val spec = "/route" / Path.of("id", "Route's ID") meta {
|
||||
summary = "Route details"
|
||||
} bindContract Method.GET
|
||||
|
||||
fun route(id: String): HttpHandler = {
|
||||
api.getRoute(id)?.let {
|
||||
Response(Status.OK).with(responseLens of it)
|
||||
} ?: Response(Status.NOT_FOUND)
|
||||
}
|
||||
|
||||
return spec to ::route
|
||||
}
|
||||
|
||||
}
|
||||
38
src/main/kotlin/routes/SearchRoute.kt
Normal file
38
src/main/kotlin/routes/SearchRoute.kt
Normal file
@ -0,0 +1,38 @@
|
||||
package be.simplenotes.c2c.routes
|
||||
|
||||
import be.simplenotes.c2c.api.Activity
|
||||
import be.simplenotes.c2c.api.Api
|
||||
import be.simplenotes.c2c.api.RouteResult
|
||||
import be.simplenotes.c2c.api.SearchResults
|
||||
import org.http4k.contract.ContractRoute
|
||||
import org.http4k.contract.Tag
|
||||
import org.http4k.contract.meta
|
||||
import org.http4k.core.*
|
||||
import org.http4k.format.Jackson.auto
|
||||
import org.http4k.lens.Query
|
||||
|
||||
class SearchRoute(private val api: Api) {
|
||||
|
||||
fun searchRoute(): ContractRoute {
|
||||
val query = Query.required("q", "Search terms")
|
||||
val responseLens = Body.auto<SearchResults>("Search results").toLens()
|
||||
|
||||
val spec = "/search" meta {
|
||||
summary = "Search routes and waypoints"
|
||||
tags += Tag("Query")
|
||||
returning(Status.OK, responseLens to SearchResults(listOf(RouteResult(
|
||||
id = "57103",
|
||||
title = "Tour de Bavon : Thor",
|
||||
summary = "Belle et longue voie équipée sur des dalles compactes qui sort sous le sommet. ",
|
||||
activities = listOf(Activity.ROCK_CLIMBING),
|
||||
))))
|
||||
} bindContract Method.GET
|
||||
|
||||
val search: HttpHandler = { req: Request ->
|
||||
Response(Status.OK).with(responseLens of api.cachedSearch(query(req)))
|
||||
}
|
||||
|
||||
return spec to search
|
||||
}
|
||||
|
||||
}
|
||||
11
src/main/kotlin/templates/Templates.kt
Normal file
11
src/main/kotlin/templates/Templates.kt
Normal file
@ -0,0 +1,11 @@
|
||||
package be.simplenotes.c2c.templates
|
||||
|
||||
import com.mitchellbosecke.pebble.PebbleEngine
|
||||
import java.io.StringWriter
|
||||
|
||||
fun PebbleEngine.render(name: String, args: Map<String, Any?> = mapOf()): String {
|
||||
val template = getTemplate(name)
|
||||
val writer = StringWriter()
|
||||
template.evaluate(writer, args)
|
||||
return writer.toString()
|
||||
}
|
||||
15
src/main/resources/logback.xml
Normal file
15
src/main/resources/logback.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<configuration>
|
||||
<appender class="ch.qos.logback.core.ConsoleAppender" name="STDOUT">
|
||||
<withJansi>true</withJansi>
|
||||
<encoder>
|
||||
<pattern>%cyan(%d{YYYY-MM-dd HH:mm:ss.SSS}) [%thread] %highlight(%-5level) %green(%logger{36}) - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
<logger level="INFO" name="com.mitchellbosecke.pebble"/>
|
||||
<logger level="INFO" name="org.apache.hc.client5"/>
|
||||
<logger level="INFO" name="io.mockk"/>
|
||||
</configuration>
|
||||
10
src/main/resources/templates/index.twig
Normal file
10
src/main/resources/templates/index.twig
Normal file
@ -0,0 +1,10 @@
|
||||
<h1>{{ route.title }}</h1>
|
||||
<p>{{ route.summary }}</p>
|
||||
<p>{{ route.description }}</p>
|
||||
{% for img in route.associations.images %}
|
||||
<figure>
|
||||
<img src="{{ img.medium }}"
|
||||
alt="{{ img.title }}">
|
||||
<figcaption>{{ img.title }}</figcaption>
|
||||
</figure>
|
||||
{% endfor %}
|
||||
11
src/main/resources/views/index.twig
Normal file
11
src/main/resources/views/index.twig
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
Hello
|
||||
</body>
|
||||
</html>
|
||||
1
src/test/kotlin/Empty.kt
Normal file
1
src/test/kotlin/Empty.kt
Normal file
@ -0,0 +1 @@
|
||||
package be.simplenotes.c2c
|
||||
85
src/test/kotlin/api/ApiTest.kt
Normal file
85
src/test/kotlin/api/ApiTest.kt
Normal file
@ -0,0 +1,85 @@
|
||||
package be.simplenotes.c2c.api
|
||||
|
||||
import be.simplenotes.c2c.extractors.extractLocale
|
||||
import be.simplenotes.c2c.extractors.extractRoute
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import io.mockk.clearMocks
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.http4k.format.Jackson
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.CsvSource
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
internal class ApiTest {
|
||||
private val urls = mockk<ApiUrls>()
|
||||
private val api = Api(urls)
|
||||
private val mapper = Jackson.mapper
|
||||
|
||||
@BeforeEach
|
||||
fun beforeEach() {
|
||||
clearMocks(urls)
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource("bavon", "portalet", "test")
|
||||
fun parseRoutes(fileName: String) {
|
||||
val file = javaClass.getResource("/search/$fileName").readText()
|
||||
val (terms, input, output) = file.split("---", limit = 3).map { it.trim() }
|
||||
every { urls.search(terms) } returns input
|
||||
val results = api.cachedSearch(terms)
|
||||
val expectedRoutes = mapper.readValue<List<RouteResult>>(output)
|
||||
assertThat(results.routes).containsExactlyInAnyOrderElementsOf(expectedRoutes)
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource("57103")
|
||||
fun route(id: String) {
|
||||
val input = javaClass.getResource("/routes/$id-in.json").readText()
|
||||
every { urls.getRoute(id) } returns input
|
||||
// test
|
||||
val root = mapper.readTree(input)
|
||||
val route = extractRoute(root)
|
||||
|
||||
root["cooked"].forEach {
|
||||
println(it.asText())
|
||||
}
|
||||
//
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource("bavon")
|
||||
fun outing(id: String) {
|
||||
val input = javaClass.getResource("/outings/$id-in.json").readText()
|
||||
every { urls.getOuting(id) } returns input
|
||||
//
|
||||
val json = urls.getOuting(id)
|
||||
val root = mapper.readTree(json)
|
||||
|
||||
//
|
||||
|
||||
val locale = extractLocale(root["locales"])
|
||||
val doc = mapOf(
|
||||
"id" to root["document_id"].asText(null),
|
||||
"title" to locale["title"].asText(null),
|
||||
"weather" to locale["weather"].asText(null),
|
||||
"conditions_levels" to locale["conditions_levels"].asText(null),
|
||||
"route_description" to locale["route_description"].asText(null),
|
||||
"summary" to locale["summary"].asText(null),
|
||||
"version" to locale["version"].asText(null),
|
||||
"access_comment" to locale["access_comment"].asText(null),
|
||||
"timing" to locale["timing"].asText(null),
|
||||
"participants" to locale["participants"].asText(null),
|
||||
"hut_comment" to locale["hut_comment"].asText(null),
|
||||
"topic_id" to locale["topic_id"].asText(null),
|
||||
"description" to locale["description"].asText(null),
|
||||
"conditions" to locale["conditions"].asText(null),
|
||||
)
|
||||
|
||||
|
||||
println(doc.entries.joinToString("\n"))
|
||||
}
|
||||
}
|
||||
387
src/test/resources/document.json
Normal file
387
src/test/resources/document.json
Normal file
@ -0,0 +1,387 @@
|
||||
{
|
||||
"area":{
|
||||
"letter":"a",
|
||||
"fields":[
|
||||
{"id": "title"},
|
||||
{"id": "summary"},
|
||||
{"id": "description"},
|
||||
{"id": "lang", "properties": {"url":"l"}},
|
||||
|
||||
{"id": "area_type", "properties": {"url":"atyp"}},
|
||||
{"id": "quality", "properties": {"url":"qa"}}
|
||||
]
|
||||
},
|
||||
|
||||
"article":{
|
||||
"letter":"c",
|
||||
"fields":[
|
||||
{"id": "title"},
|
||||
{"id": "summary"},
|
||||
{"id": "description"},
|
||||
{"id": "lang", "properties": {"url":"l"}},
|
||||
|
||||
{"id": "activities", "properties": {"url":"act"}},
|
||||
{"id": "article_categories", "properties": {"url":"acat"}},
|
||||
{"id": "quality", "properties": {"url":"qa"}},
|
||||
{"id": "article_type", "properties": {"url":"atyp"}},
|
||||
|
||||
{"id": "articles", "properties": {"url":"c", "associationEditorOrder": 1}},
|
||||
{"id": "books", "properties": {"url":"b", "associationEditorOrder": 4}},
|
||||
{"id": "outings", "properties": {"url":"o", "associationEditorOrder": 5}},
|
||||
{"id": "routes", "properties": {"url":"r", "associationEditorOrder": 3}},
|
||||
{"id": "waypoints", "properties": {"url":"w", "associationEditorOrder": 2}},
|
||||
{"id": "images", "properties":{"associationEditorOrder": 8}},
|
||||
{"id": "users", "properties":{"associationEditorOrder": 7}},
|
||||
{"id": "xreports", "properties":{"associationEditorOrder": 6}}
|
||||
]
|
||||
},
|
||||
|
||||
"book":{
|
||||
"letter":"b",
|
||||
"fields":[
|
||||
{"id": "title"},
|
||||
{"id": "summary"},
|
||||
{"id": "description"},
|
||||
{"id": "lang", "properties": {"url":"l"}},
|
||||
|
||||
{"id": "author"},
|
||||
{"id": "editor"},
|
||||
{"id": "activities", "properties": {"url":"act"}},
|
||||
{"id": "url"},
|
||||
{"id": "isbn"},
|
||||
{"id": "book_types", "properties": {"url":"btyp"}},
|
||||
{"id": "publication_date"},
|
||||
{"id": "quality", "properties": {"url":"qa"}},
|
||||
{"id": "langs"},
|
||||
{"id": "nb_pages"},
|
||||
|
||||
{"id": "articles", "properties": {"url":"c"}},
|
||||
{"id": "routes", "properties": {"url":"r"}},
|
||||
{"id": "waypoints", "properties": {"url":"w"}},
|
||||
{"id": "images"}
|
||||
]
|
||||
},
|
||||
|
||||
"image":{
|
||||
"letter":"i",
|
||||
"geoLocalized":true,
|
||||
"fields":[
|
||||
{"id": "title", "properties": {"required":false}},
|
||||
{"id": "summary"},
|
||||
{"id": "description"},
|
||||
{"id": "lang", "properties": {"url":"l"}},
|
||||
|
||||
{"id": "activities", "properties": {"url":"act"}},
|
||||
{"id": "author"},
|
||||
{"id": "camera_name"},
|
||||
{"id": "date_time"},
|
||||
{"id": "elevation", "properties": {"url":"ialt"}},
|
||||
{"id": "height"},
|
||||
{"id": "image_categories", "properties": {"url":"cat"}},
|
||||
{"id": "image_type", "properties": {"url":"ityp"}},
|
||||
{"id": "iso_speed"},
|
||||
{"id": "file_size"},
|
||||
{"id": "filename"},
|
||||
{"id": "exposure_time"},
|
||||
{"id": "focal_length"},
|
||||
{"id": "fnumber"},
|
||||
{"id": "quality", "properties": {"url":"qa"}},
|
||||
{"id": "width"},
|
||||
|
||||
{"id": "areas", "properties":{"associationEditorOrder": 6}},
|
||||
{"id": "articles", "properties": {"url":"c", "associationEditorOrder": 5}},
|
||||
{"id": "books", "properties": {"url":"b", "associationEditorOrder": 7}},
|
||||
{"id": "outings", "properties":{"associationEditorOrder": 3}},
|
||||
{"id": "routes", "properties": {"url":"r", "associationEditorOrder": 1}},
|
||||
{"id": "users", "properties":{"associationEditorOrder": 8}},
|
||||
{"id": "waypoints", "properties": {"url":"w", "associationEditorOrder": 2}},
|
||||
{"id": "xreports", "properties":{"associationEditorOrder": 4}},
|
||||
{"id": "images", "properties":{"associationEditorOrder": 9}}
|
||||
]
|
||||
},
|
||||
|
||||
"map":{
|
||||
"letter":"m",
|
||||
"fields":[
|
||||
{"id": "title"},
|
||||
{"id": "summary"},
|
||||
{"id": "description"},
|
||||
{"id": "lang", "properties": {"url":"l"}},
|
||||
|
||||
{"id": "code"},
|
||||
{"id": "scale"},
|
||||
{"id": "editor"}
|
||||
]
|
||||
},
|
||||
|
||||
"outing":{
|
||||
"letter":"o",
|
||||
"geoLocalized":true,
|
||||
"fields":[
|
||||
{"id": "title"},
|
||||
{"id": "summary"},
|
||||
{"id": "description"},
|
||||
{"id": "conditions_levels", "properties": {"activities": ["skitouring", "snowshoeing", "ice_climbing", "snow_ice_mixed"]}},
|
||||
{"id": "participants"},
|
||||
{"id": "access_comment"},
|
||||
{"id": "weather"},
|
||||
{"id": "timing"},
|
||||
{"id": "conditions"},
|
||||
{"id": "hut_comment"},
|
||||
{"id": "route_description"},
|
||||
{"id": "avalanches", "properties": {"activities":["ice_climbing", "skitouring", "snowshoeing", "snow_ice_mixed"]}},
|
||||
{"id": "lang", "properties": {"url":"l"}},
|
||||
|
||||
{"id": "access_condition"},
|
||||
{"id": "activities", "properties": {"url":"act", "required":true}},
|
||||
{"id": "avalanche_signs", "properties": {"url":"avdate", "activities":["ice_climbing", "skitouring", "snowshoeing", "snow_ice_mixed"]}},
|
||||
{"id": "condition_rating", "properties": {"url":"ocond"}},
|
||||
{"id": "date_end"},
|
||||
{"id": "date_start"},
|
||||
{"id": "disable_comments"},
|
||||
{"id": "elevation_access", "properties": {"url":"oparka"}},
|
||||
{"id": "elevation_down_snow", "properties": {"url":"swld", "activities":["ice_climbing", "mountain_climbing", "skitouring", "snow_ice_mixed", "snowshoeing"]}},
|
||||
{"id": "elevation_max", "properties": {"url":"oalt"}},
|
||||
{"id": "elevation_min"},
|
||||
{"id": "elevation_up_snow", "properties": {"url":"swlu", "activities":["ice_climbing", "mountain_climbing", "skitouring", "snow_ice_mixed", "snowshoeing"]}},
|
||||
{"id": "engagement_rating", "properties": {"url":"erat", "activities":["mountain_climbing", "snow_ice_mixed"]}},
|
||||
{"id": "equipment_rating", "properties": {"url":"prat", "activities":["rock_climbing"]}},
|
||||
{"id": "frequentation", "properties": {"url":"ofreq"}},
|
||||
{"id": "glacier_rating", "properties": {"url":"oglac", "activities":["mountain_climbing", "skitouring", "snow_ice_mixed", "snowshoeing"]}},
|
||||
{"id": "global_rating", "properties": {"url":"grat", "activities":["mountain_climbing", "rock_climbing", "snow_ice_mixed"]}},
|
||||
{"id": "height_diff_difficulties", "properties": {"url":"dhei", "activities":["mountain_climbing", "snow_ice_mixed"]}},
|
||||
{"id": "height_diff_down"},
|
||||
{"id": "height_diff_up", "properties": {"url":"odif"}},
|
||||
{"id": "hiking_rating", "properties": {"url":"hrat", "activities":["hiking"]}},
|
||||
{"id": "hut_status"},
|
||||
{"id": "ice_rating", "properties": {"url":"irat", "activities":["ice_climbing"]}},
|
||||
{"id": "labande_global_rating", "properties": {"url":"lrat", "activities":["skitouring"]}},
|
||||
{"id": "length_total", "properties": {"url":"olen"}},
|
||||
{"id": "lift_status"},
|
||||
{"id": "mtb_down_rating", "properties": {"url":"mbdr", "activities":["mountain_biking"]}},
|
||||
{"id": "mtb_up_rating", "properties": {"url":"mbur", "activities":["mountain_biking"]}},
|
||||
{"id": "partial_trip"},
|
||||
{"id": "participant_count"},
|
||||
{"id": "public_transport", "properties": {"url":"owpt"}},
|
||||
{"id": "quality", "properties": {"url":"qa"}},
|
||||
{"id": "rock_free_rating", "properties": {"url":"frat", "activities":["rock_climbing"]}},
|
||||
{"id": "ski_rating", "properties": {"url":"trat", "activities":["skitouring"]}},
|
||||
{"id": "snow_quality", "properties": {"url":"swqual", "activities":["ice_climbing", "mountain_climbing", "skitouring", "snow_ice_mixed", "snowshoeing"]}},
|
||||
{"id": "snow_quantity", "properties": {"url":"swquan", "activities":["ice_climbing", "mountain_climbing", "skitouring", "snow_ice_mixed", "snowshoeing"]}},
|
||||
{"id": "snowshoe_rating", "properties": {"url":"wrat", "activities":["snowshoeing"]}},
|
||||
{"id": "via_ferrata_rating", "properties": {"url":"krat", "activities":["via_ferrata"]}},
|
||||
|
||||
{"id": "articles", "properties": {"url":"c", "associationEditorOrder": 5}},
|
||||
{"id": "images", "properties": {"associationEditorOrder": 3}},
|
||||
{"id": "routes", "properties": {"url":"r", "required":true, "associationEditorOrder": 1}},
|
||||
{"id": "users", "properties": {"url":"u", "required":true, "associationEditorOrder": 2}},
|
||||
{"id": "xreports", "properties": {"url":"x", "associationEditorOrder": 4}}
|
||||
]
|
||||
},
|
||||
|
||||
"profile":{
|
||||
"letter":"u",
|
||||
"geoLocalized":true,
|
||||
"fields":[
|
||||
{"id": "summary"},
|
||||
{"id": "description"},
|
||||
{"id": "lang"},
|
||||
|
||||
{"id": "activities", "properties": {"url":null, "required":false}},
|
||||
{"id": "categories", "properties": {"url":null}},
|
||||
{"id": "name"},
|
||||
|
||||
{"id": "images"}
|
||||
]
|
||||
},
|
||||
|
||||
"route":{
|
||||
"letter":"r",
|
||||
"geoLocalized":true,
|
||||
"fields":[
|
||||
{"id": "title"},
|
||||
{"id": "summary"},
|
||||
{"id": "route_history"},
|
||||
{"id": "description"},
|
||||
{"id": "slackline_anchor1", "properties": {"activities":["slacklining"]}},
|
||||
{"id": "slackline_anchor2", "properties": {"activities":["slacklining"]}},
|
||||
{"id": "slope", "properties": {"activities":["snow_ice_mixed", "ice_climbing", "snowshoeing", "skitouring"]}},
|
||||
{"id": "remarks"},
|
||||
{"id": "gear"},
|
||||
{"id": "external_resources"},
|
||||
{"id": "lang", "properties": {"url":"l"}},
|
||||
|
||||
{"id": "activities", "properties": {"url":"act"}},
|
||||
{"id": "aid_rating", "properties": {"url":"arat", "activities":["rock_climbing", "mountain_climbing"]}},
|
||||
{"id": "climbing_outdoor_type", "properties": {"url":"crtyp", "activities":["rock_climbing"]}},
|
||||
{"id": "configuration", "properties": {"url":"conf", "activities":["rock_climbing", "via_ferrata", "snow_ice_mixed", "mountain_climbing", "snowshoeing", "skitouring"]}},
|
||||
{"id": "difficulties_height", "properties": {"url":"ralt", "activities":["rock_climbing", "via_ferrata", "snow_ice_mixed", "mountain_climbing", "ice_climbing"]}},
|
||||
{"id": "durations", "properties": {"url":"time", "activities":["hiking", "rock_climbing", "via_ferrata", "snow_ice_mixed", "mountain_biking", "mountain_climbing", "ice_climbing", "snowshoeing", "skitouring"]}},
|
||||
{"id": "elevation_max", "properties": {"url":"rmaxa", "activities":["hiking", "rock_climbing", "via_ferrata", "snow_ice_mixed", "mountain_biking", "mountain_climbing", "ice_climbing", "snowshoeing", "skitouring"]}},
|
||||
{"id": "elevation_min", "properties": {"url":"rmina", "activities":["hiking", "rock_climbing", "via_ferrata", "snow_ice_mixed", "mountain_biking", "mountain_climbing", "ice_climbing", "snowshoeing", "skitouring"]}},
|
||||
{"id": "engagement_rating", "properties": {"url":"erat", "activities":["rock_climbing", "via_ferrata", "snow_ice_mixed", "mountain_climbing", "ice_climbing"]}},
|
||||
{"id": "equipment_rating", "properties": {"url":"prat", "activities":["rock_climbing", "via_ferrata", "snow_ice_mixed", "mountain_climbing", "ice_climbing"]}},
|
||||
{"id": "exposition_rock_rating", "properties": {"url":"rexpo", "activities":["rock_climbing", "mountain_climbing"]}},
|
||||
{"id": "glacier_gear", "properties": {"url":"glac", "activities":["hiking", "rock_climbing", "snow_ice_mixed", "mountain_climbing", "ice_climbing", "snowshoeing", "skitouring"]}},
|
||||
{"id": "global_rating", "properties": {"url":"grat", "activities":["rock_climbing", "snow_ice_mixed", "mountain_climbing", "ice_climbing"]}},
|
||||
{"id": "height_diff_access", "properties": {"url":"rappr", "activities":["hiking", "rock_climbing", "via_ferrata", "snow_ice_mixed", "mountain_biking", "mountain_climbing", "ice_climbing", "snowshoeing", "skitouring"]}},
|
||||
{"id": "height_diff_difficulties", "properties": {"url":"dhei", "activities":["hiking", "rock_climbing", "via_ferrata", "snow_ice_mixed", "mountain_biking", "mountain_climbing", "ice_climbing", "snowshoeing", "skitouring"]}},
|
||||
{"id": "height_diff_down", "properties": {"url":"ddif", "activities":["hiking", "rock_climbing", "via_ferrata", "snow_ice_mixed", "mountain_biking", "mountain_climbing", "ice_climbing", "snowshoeing", "skitouring"]}},
|
||||
{"id": "height_diff_up", "properties": {"url":"hdif", "activities":["hiking", "rock_climbing", "via_ferrata", "snow_ice_mixed", "mountain_biking", "mountain_climbing", "ice_climbing", "snowshoeing", "skitouring"]}},
|
||||
{"id": "hiking_mtb_exposition", "properties": {"url":"hexpo", "activities":["hiking", "mountain_biking"]}},
|
||||
{"id": "hiking_rating", "properties": {"url":"hrat", "activities":["hiking"]}},
|
||||
{"id": "ice_rating", "properties": {"url":"irat", "activities":["snow_ice_mixed", "ice_climbing"]}},
|
||||
{"id": "labande_global_rating", "properties": {"url":"lrat", "activities":["skitouring"]}},
|
||||
{"id": "labande_ski_rating", "properties": {"url":"srat", "activities":["skitouring"]}},
|
||||
{"id": "lift_access"},
|
||||
{"id": "main_waypoint_id"},
|
||||
{"id": "mixed_rating", "properties": {"url":"mrat", "activities":["snow_ice_mixed", "ice_climbing"]}},
|
||||
{"id": "mtb_down_rating", "properties": {"url":"mbdr", "activities":["mountain_biking"]}},
|
||||
{"id": "mtb_height_diff_portages", "properties": {"url":"mbpush", "activities":["mountain_biking"]}},
|
||||
{"id": "mtb_length_asphalt", "properties": {"url":"mbroad", "activities":["mountain_biking"]}},
|
||||
{"id": "mtb_length_trail", "properties": {"url":"mbtrack", "activities":["mountain_biking"]}},
|
||||
{"id": "mtb_up_rating", "properties": {"url":"mbur", "activities":["mountain_biking"]}},
|
||||
{"id": "orientations", "properties": {"url":"fac"}},
|
||||
{"id": "quality", "properties": {"url":"qa"}},
|
||||
{"id": "risk_rating", "properties": {"url":"orrat", "activities":["rock_climbing", "snow_ice_mixed", "mountain_climbing", "ice_climbing"]}},
|
||||
{"id": "rock_free_rating", "properties": {"url":"frat", "activities":["rock_climbing", "mountain_climbing"]}},
|
||||
{"id": "rock_required_rating", "properties": {"url":"rrat", "activities":["rock_climbing", "mountain_climbing"]}},
|
||||
{"id": "rock_types", "properties": {"url":"rock", "activities":["rock_climbing", "via_ferrata", "snow_ice_mixed", "mountain_climbing"]}},
|
||||
{"id": "route_length", "properties": {"url":"rlen", "activities":["hiking", "slacklining", "via_ferrata", "snow_ice_mixed", "mountain_biking", "snowshoeing", "skitouring"]}},
|
||||
{"id": "route_types", "properties": {"url":"rtyp"}},
|
||||
{"id": "ski_exposition", "properties": {"url":"sexpo", "activities":["skitouring"]}},
|
||||
{"id": "ski_rating", "properties": {"url":"trat", "activities":["skitouring"]}},
|
||||
{"id": "slackline_height", "properties": {"activities":["slacklining"]}},
|
||||
{"id": "slackline_type", "properties": {"url":"sltyp", "activities":["slacklining"]}},
|
||||
{"id": "snowshoe_rating", "properties": {"url":"wrat", "activities":["snowshoeing"]}},
|
||||
{"id": "via_ferrata_rating", "properties": {"url":"krat", "activities":["via_ferrata"]}},
|
||||
|
||||
{"id": "articles", "properties": {"url":"c", "associationEditorOrder": 4}},
|
||||
{"id": "books", "properties": {"url":"b", "associationEditorOrder": 2}},
|
||||
{"id": "images", "properties": {"associationEditorOrder": 5}},
|
||||
{"id": "routes", "properties": {"url":"r", "associationEditorOrder": 3}},
|
||||
{"id": "waypoints", "properties": {"url":"w", "associationEditorOrder": 1}},
|
||||
{"id": "xreports", "properties": {"url":"x", "associationEditorOrder": 6}}
|
||||
]
|
||||
},
|
||||
|
||||
"waypoint":{
|
||||
"letter":"w",
|
||||
"geoLocalized":true,
|
||||
"fields":[
|
||||
{"id": "title"},
|
||||
{"id": "summary"},
|
||||
{"id": "description"},
|
||||
{"id": "access", "properties": {"waypoint_types":["access", "climbing_indoor", "climbing_outdoor", "hut", "local_product", "slackline_spot"]}},
|
||||
{"id": "access_period", "properties": {"waypoint_types":["access", "camp_site", "climbing_outdoor", "gite", "hut", "local_product"]}},
|
||||
{"id": "lang", "properties": {"url":"l"}},
|
||||
|
||||
{"id": "access_time", "properties": {"url":"tappt", "waypoint_types":["climbing_outdoor", "slackline_spot"]}},
|
||||
{"id": "best_periods", "properties": {"url":"period", "waypoint_types":["climbing_outdoor", "slackline_spot"]}},
|
||||
{"id": "blanket_unstaffed", "properties": {"waypoint_types":["hut", "shelter"]}},
|
||||
{"id": "capacity", "properties": {"url":"hucap", "waypoint_types":["bivouac", "camp_site", "gite", "hut", "shelter"]}},
|
||||
{"id": "capacity_staffed", "properties": {"url":"hscap", "waypoint_types":["camp_site", "gite", "hut"]}},
|
||||
{"id": "children_proof", "properties": {"url":"chil", "waypoint_types":["climbing_outdoor"]}},
|
||||
{"id": "climbing_indoor_types", "properties": {"url":"ctin", "waypoint_types":["climbing_indoor"]}},
|
||||
{"id": "climbing_outdoor_types", "properties": {"url":"ctout", "waypoint_types":["climbing_outdoor"]}},
|
||||
{"id": "climbing_rating_max", "properties": {"url":"tmaxr", "waypoint_types":["climbing_indoor", "climbing_outdoor"]}},
|
||||
{"id": "climbing_rating_median", "properties": {"url":"tmedr", "waypoint_types":["climbing_indoor", "climbing_outdoor"]}},
|
||||
{"id": "climbing_rating_min", "properties": {"url":"tminr", "waypoint_types":["climbing_indoor", "climbing_outdoor"]}},
|
||||
{"id": "climbing_styles", "properties": {"url":"tcsty", "waypoint_types":["climbing_indoor", "climbing_outdoor"]}},
|
||||
{"id": "custodianship", "properties": {"url":"hsta", "waypoint_types":["camp_site", "gite", "hut"]}},
|
||||
{"id": "elevation", "properties": {"url":"walt"}},
|
||||
{"id": "elevation_min", "properties": {"waypoint_types":["access"]}},
|
||||
{"id": "equipment_ratings", "properties": {"url":"anchq", "waypoint_types":["climbing_outdoor"]}},
|
||||
{"id": "gas_unstaffed", "properties": {"waypoint_types":["hut", "shelter"]}},
|
||||
{"id": "ground_types", "properties": {"waypoint_types":["paragliding_landing", "paragliding_takeoff"]}},
|
||||
{"id": "heating_unstaffed", "properties": {"waypoint_types":["hut", "shelter"]}},
|
||||
{"id": "height_max", "properties": {"url":"tmaxh", "waypoint_types":["climbing_indoor", "climbing_outdoor"]}},
|
||||
{"id": "height_median", "properties": {"url":"tmedh", "waypoint_types":["climbing_indoor", "climbing_outdoor"]}},
|
||||
{"id": "height_min", "properties": {"url":"tminh", "waypoint_types":["climbing_indoor", "climbing_outdoor"]}},
|
||||
{"id": "length", "properties": {"url":"len", "waypoint_types":["paragliding_landing", "paragliding_takeoff"]}},
|
||||
{"id": "lift_access", "properties": {"url":"plift", "waypoint_types":["access"]}},
|
||||
{"id": "maps_info"},
|
||||
{"id": "matress_unstaffed", "properties": {"waypoint_types":["hut", "shelter"]}},
|
||||
{"id": "orientations", "properties": {"url":"wfac", "waypoint_types":["climbing_outdoor", "paragliding_landing", "paragliding_takeoff", "slackline_spot"]}},
|
||||
{"id": "paragliding_rating", "properties": {"url":"pgrat", "waypoint_types":["paragliding_landing", "paragliding_takeoff"]}},
|
||||
{"id": "parking_fee", "properties": {"waypoint_types":["access"]}},
|
||||
{"id": "phone", "properties": {"waypoint_types":["camp_site", "climbing_indoor", "gite", "hut", "local_product"]}},
|
||||
{"id": "phone_custodian", "properties": {"waypoint_types":["camp_site", "gite", "hut"]}},
|
||||
{"id": "product_types", "properties": {"url":"ftyp", "waypoint_types":["local_product"]}},
|
||||
{"id": "prominence", "properties": {"url":"prom", "waypoint_types":["summit"]}},
|
||||
{"id": "public_transportation_rating", "properties": {"url":"tp", "waypoint_types":["access"]}},
|
||||
{"id": "public_transportation_types", "properties": {"url":"tpty", "waypoint_types":["access"]}},
|
||||
{"id": "quality", "properties": {"url":"qa"}},
|
||||
{"id": "rain_proof", "properties": {"url":"rain", "waypoint_types":["climbing_outdoor"]}},
|
||||
{"id": "rock_types", "properties": {"url":"wrock", "waypoint_types":["climbing_outdoor"]}},
|
||||
{"id": "routes_quantity", "properties": {"url":"rqua", "waypoint_types":["climbing_indoor", "climbing_outdoor"]}},
|
||||
{"id": "slackline_length_max", "properties": {"waypoint_types":["slackline_spot"]}},
|
||||
{"id": "slackline_length_min", "properties": {"waypoint_types":["slackline_spot"]}},
|
||||
{"id": "slackline_types", "properties": {"waypoint_types":["slackline_spot"]}},
|
||||
{"id": "waypoint_slope", "properties": {"waypoint_types":["paragliding_landing", "paragliding_takeoff"]}},
|
||||
{"id": "snow_clearance_rating", "properties": {"url":"psnow", "waypoint_types":["access"]}},
|
||||
{"id": "url", "properties": {"waypoint_types":["camp_site", "climbing_indoor", "climbing_outdoor", "gite", "hut", "local_product", "weather_station", "webcam"]}},
|
||||
{"id": "waypoint_type", "properties": {"url":"wtyp"}},
|
||||
{"id": "weather_station_types", "properties": {"waypoint_types": ["weather_station"], "url":"whtyp"}},
|
||||
|
||||
{"id": "articles", "properties": {"url":"c", "associationEditorOrder": 3}},
|
||||
{"id": "books", "properties": {"url":"b", "associationEditorOrder": 2}},
|
||||
{"id": "images", "properties": {"associationEditorOrder": 4}},
|
||||
{"id": "waypoints", "properties": {"url":"w", "associationEditorOrder": 1}},
|
||||
{"id": "waypoints", "properties": {"name": "waypoint_children", "url":"w", "associationEditorOrder": 1}}
|
||||
]
|
||||
},
|
||||
|
||||
"xreport":{
|
||||
"letter":"x",
|
||||
"geoLocalized":true,
|
||||
"fields":[
|
||||
{"id": "title"},
|
||||
{"id": "summary"},
|
||||
{"id": "description"},
|
||||
{"id": "place"},
|
||||
{"id": "route_study"},
|
||||
{"id": "conditions"},
|
||||
{"id": "training"},
|
||||
{"id": "motivations"},
|
||||
{"id": "group_management"},
|
||||
{"id": "risk"},
|
||||
{"id": "time_management"},
|
||||
{"id": "safety"},
|
||||
{"id": "reduce_impact"},
|
||||
{"id": "increase_impact"},
|
||||
{"id": "modifications"},
|
||||
{"id": "other_comments"},
|
||||
{"id": "lang", "properties": {"url":"l"}},
|
||||
|
||||
{"id": "elevation", "properties": {"url":"xalt"}},
|
||||
{"id": "date"},
|
||||
{"id": "event_type", "properties": {"url":"xtyp"}},
|
||||
{"id": "event_activity", "properties": {"url":"act"}},
|
||||
{"id": "nb_participants", "properties": {"url":"xpar"}},
|
||||
{"id": "nb_impacted", "properties": {"url":"ximp"}},
|
||||
{"id": "rescue"},
|
||||
{"id": "avalanche_level"},
|
||||
{"id": "avalanche_slope"},
|
||||
{"id": "severity", "properties": {"url":"xsev"}},
|
||||
{"id": "author_status"},
|
||||
{"id": "activity_rate"},
|
||||
{"id": "age"},
|
||||
{"id": "gender"},
|
||||
{"id": "previous_injuries"},
|
||||
{"id": "autonomy"},
|
||||
{"id": "qualification"},
|
||||
{"id": "supervision"},
|
||||
{"id": "disable_comments"},
|
||||
{"id": "anonymous"},
|
||||
{"id": "quality", "properties": {"url":"qa"}},
|
||||
|
||||
{"id": "articles", "properties": {"url":"c", "helper": null, "associationEditorOrder": 6}},
|
||||
{"id": "images", "properties": {"associationEditorOrder":5}},
|
||||
{"id": "outings", "properties": {"url":"o", "helper": null, "associationEditorOrder": 1}},
|
||||
{"id": "routes", "properties": {"url":"r", "helper": null, "associationEditorOrder": 3}},
|
||||
{"id": "users", "properties": {"associationEditorOrder":2}},
|
||||
{"id": "waypoints", "properties": {"associationEditorOrder":4}}
|
||||
]
|
||||
}
|
||||
}
|
||||
727
src/test/resources/outings/bavon-in.json
Normal file
727
src/test/resources/outings/bavon-in.json
Normal file
@ -0,0 +1,727 @@
|
||||
{
|
||||
"disable_comments": null,
|
||||
"document_id": 547170,
|
||||
"areas": [
|
||||
{
|
||||
"type": "a",
|
||||
"available_langs": null,
|
||||
"document_id": 14067,
|
||||
"version": 10,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "Suisse",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"area_type": "country",
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"type": "a",
|
||||
"available_langs": null,
|
||||
"document_id": 14384,
|
||||
"version": 9,
|
||||
"locales": [
|
||||
{
|
||||
"version": 12,
|
||||
"title": "Valais",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"area_type": "admin_limits",
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"type": "a",
|
||||
"available_langs": null,
|
||||
"document_id": 14437,
|
||||
"version": 6,
|
||||
"locales": [
|
||||
{
|
||||
"version": 7,
|
||||
"title": "Valais W - Alpes Pennines W",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"area_type": "range",
|
||||
"protected": false
|
||||
}
|
||||
],
|
||||
"version": 2,
|
||||
"frequentation": "quiet",
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": "{\"coordinates\": [796963.8418227625, 5777640.64308189], \"type\": \"Point\"}",
|
||||
"geom_detail": null
|
||||
},
|
||||
"associations": {
|
||||
"articles": [],
|
||||
"xreports": [],
|
||||
"users": [
|
||||
{
|
||||
"forum_username": "Yayo",
|
||||
"document_id": 737,
|
||||
"name": "Yayo",
|
||||
"areas": [
|
||||
{
|
||||
"document_id": 14274,
|
||||
"available_langs": null,
|
||||
"type": "a",
|
||||
"version": 11,
|
||||
"locales": [
|
||||
{
|
||||
"version": 5,
|
||||
"title": "France",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"area_type": "country",
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 14335,
|
||||
"available_langs": null,
|
||||
"type": "a",
|
||||
"version": 3,
|
||||
"locales": [
|
||||
{
|
||||
"version": 3,
|
||||
"title": "Haute-Garonne",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"area_type": "admin_limits",
|
||||
"protected": false
|
||||
}
|
||||
],
|
||||
"activities": [
|
||||
"mountain_climbing",
|
||||
"snow_ice_mixed",
|
||||
"hiking",
|
||||
"paragliding",
|
||||
"snowshoeing",
|
||||
"skitouring",
|
||||
"rock_climbing",
|
||||
"ice_climbing"
|
||||
],
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"type": "u",
|
||||
"version": 3,
|
||||
"locales": [
|
||||
{
|
||||
"version": 77,
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"protected": false,
|
||||
"categories": [
|
||||
"amateur"
|
||||
]
|
||||
},
|
||||
{
|
||||
"forum_username": "etoile",
|
||||
"document_id": 106794,
|
||||
"name": "etoile",
|
||||
"areas": [],
|
||||
"activities": [
|
||||
"skitouring",
|
||||
"snow_ice_mixed",
|
||||
"mountain_climbing",
|
||||
"hiking",
|
||||
"snowshoeing",
|
||||
"rock_climbing",
|
||||
"ice_climbing"
|
||||
],
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"type": "u",
|
||||
"version": 2,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"protected": false,
|
||||
"categories": [
|
||||
"amateur"
|
||||
]
|
||||
}
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"orientations": [
|
||||
"SE"
|
||||
],
|
||||
"document_id": 57103,
|
||||
"equipment_rating": "P1",
|
||||
"version": 4,
|
||||
"activities": [
|
||||
"rock_climbing"
|
||||
],
|
||||
"geometry": {
|
||||
"has_geom_detail": false,
|
||||
"version": 11,
|
||||
"geom": "{\"coordinates\": [796963.8418227625, 5777640.64308189], \"type\": \"Point\"}"
|
||||
},
|
||||
"available_langs": [
|
||||
"es",
|
||||
"en",
|
||||
"fr"
|
||||
],
|
||||
"aid_rating": null,
|
||||
"risk_rating": null,
|
||||
"areas": [
|
||||
{
|
||||
"document_id": 14067,
|
||||
"available_langs": null,
|
||||
"type": "a",
|
||||
"version": 10,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "Suisse",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"area_type": "country",
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 14384,
|
||||
"available_langs": null,
|
||||
"type": "a",
|
||||
"version": 9,
|
||||
"locales": [
|
||||
{
|
||||
"version": 12,
|
||||
"title": "Valais",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"area_type": "admin_limits",
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 14437,
|
||||
"available_langs": null,
|
||||
"type": "a",
|
||||
"version": 6,
|
||||
"locales": [
|
||||
{
|
||||
"version": 7,
|
||||
"title": "Valais W - Alpes Pennines W",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"area_type": "range",
|
||||
"protected": false
|
||||
}
|
||||
],
|
||||
"exposition_rock_rating": null,
|
||||
"height_diff_down": null,
|
||||
"elevation_min": null,
|
||||
"height_diff_difficulties": 400,
|
||||
"engagement_rating": null,
|
||||
"locales": [
|
||||
{
|
||||
"lang": "fr",
|
||||
"version": 22,
|
||||
"title_prefix": "Tour de Bavon",
|
||||
"title": "Thor",
|
||||
"summary": "Belle et longue voie \u00e9quip\u00e9e sur des dalles compactes qui sort sous le sommet. "
|
||||
}
|
||||
],
|
||||
"protected": false,
|
||||
"rock_required_rating": "5c",
|
||||
"rock_free_rating": "6a",
|
||||
"type": "r",
|
||||
"elevation_max": 2476,
|
||||
"height_diff_up": null,
|
||||
"global_rating": "TD-",
|
||||
"quality": "medium"
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"document_id": 547972,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407166923_293646121.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "Le pont sur l'A (1750m) \u00e0 partir duquel il faut rester en rive gauche.",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"author": null,
|
||||
"filename": "1407166942_1005392824.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"document_id": 547973,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "Qd vs passez au pied de cette dalle attirante, ne cherchez pas, THOR n'est PAS L\u00e0...",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 547974,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407166959_1186093463.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR : d\u00e9part de la voie dans la dalle blanche, imm\u00e9diatement \u00e0 droite de la rampe d'herbe oblique",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"author": null,
|
||||
"filename": "1407526184_1731628825.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"document_id": 548914,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "La dalle d'attaque, blanche, tout \u00e2 gauche",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 548915,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407526245_461968093.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR: L1, grande dalle, des \u00e9cailles pos\u00e9es sur le r\u00e9glettes, R1 \u00e0 D",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 548916,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407526253_1591812939.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR: L4 sur le vague \u00e9peron au centre, \u00e0 droite de la cavit\u00e9",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 548937,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407526536_793924939.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR : L5",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"author": null,
|
||||
"filename": "1407526546_1198758627.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"document_id": 548938,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR: vue plongeante depuis R4",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 548939,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407526557_1468063167.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR : L6",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 548940,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407526566_1671042872.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR : L7",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 548941,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407526578_453362453.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR: L8, sur de grandes dalles, vers la grande fissure",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 548942,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407526591_223027668.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR : Tr\u00e8s belle L9, sur un caillou correct!",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 548943,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407526601_981616281.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR: fin de L9 avec un petit di\u00e8dre raide",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"author": null,
|
||||
"filename": "1407526615_1928563952.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"document_id": 548944,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR : L10",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 548945,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407526622_1455324122.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR: \u00c9toile sortie ! (R10)",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"author": null,
|
||||
"filename": "1407526644_995429802.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"document_id": 548946,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "Edelweiss",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 548947,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407526652_629783242.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "Duo d'Edelweiss",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 548948,
|
||||
"type": "i",
|
||||
"areas": [],
|
||||
"filename": "1407526661_962365898.jpg",
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"author": null,
|
||||
"version": 1,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "THOR: pentes de descente, tirer vers les arbres",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"geometry": {
|
||||
"version": 1,
|
||||
"geom": null
|
||||
},
|
||||
"protected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"access_condition": null,
|
||||
"partial_trip": null,
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"height_diff_down": null,
|
||||
"equipment_rating": "P1",
|
||||
"elevation_min": null,
|
||||
"public_transport": null,
|
||||
"participant_count": null,
|
||||
"elevation_access": null,
|
||||
"elevation_max": 2476,
|
||||
"date_start": "2014-07-31",
|
||||
"locales": [
|
||||
{
|
||||
"weather": "Bouch\u00e9 le matin, nuages 50m au dessus de la base des dalles puis \u00e9claircies. ",
|
||||
"conditions_levels": null,
|
||||
"route_description": null,
|
||||
"summary": null,
|
||||
"version": 4,
|
||||
"access_comment": null,
|
||||
"timing": "Approche : 2h30 avec la recherche au pied de la mauvaise dalle. \r\nVoie : 6h",
|
||||
"participants": null,
|
||||
"title": "Tour de Bavon\u00a0: Thor",
|
||||
"lang": "fr",
|
||||
"hut_comment": null,
|
||||
"topic_id": null,
|
||||
"description": "Jolie voie mais le caillou demande de l'attention. Selon \u00c9toile : \"\u00e7a manque de colle entre les dalles de schistes!!\"\r\n\r\nPour l'approche, on est mont\u00e9 trop t\u00f4t \u00e0 droite (300m trop au N), vers les premi\u00e8res grandes dalles au-dessus du chemin, faute de visibilit\u00e9. On a cherch\u00e9 les spits une bonne heure en longeant la paroi. On est redescendu puis on a poursuivi le long du ruisseau jusqu'au pied de la bonne rampe. Indication : poursuivre dans le fond du vallon jusqu'\u00e0 passer un replat avec 3 \u00e9normes blocs. Poursuivre 100m puis obliquer a droite vers une dalle blanche surmont\u00e9e d'un grand toit sombre. (Mise a jour topo a venir)\r\n\r\n[TROUV\u00c9](http://m.camptocamp.org/forums/viewforum.php?id=78) : un brin de rappel au pied de la paroi 300m au N de la voie. ",
|
||||
"conditions": "## Rocher\r\nSec dans l'ensemble sauf L2, tremp\u00e9e sous le 2\u00e8me point. \r\nPas mal de sections au rocher moyen, dont le ressaut final de L2 et du d\u00e9but de L5, bien p\u00e9teux. Bcp de vigilance n\u00e9cessaire tout le long pour ne pas partir avec une \u00e9caille. \r\nBcp de cailloux pos\u00e9s \u00e9galement et qui n'attendent que le passage de la corde pour se d\u00e9loger. \r\n\r\n## Equipement\r\n\u00c9quip\u00e9 a\u00e9r\u00e9 mais bien \u00e9quip\u00e9.\r\n\r\n\r\n"
|
||||
}
|
||||
],
|
||||
"protected": false,
|
||||
"rock_free_rating": "6a",
|
||||
"date_end": "2014-07-31",
|
||||
"type": "o",
|
||||
"condition_rating": "good",
|
||||
"activities": [
|
||||
"rock_climbing"
|
||||
],
|
||||
"hut_status": null,
|
||||
"height_diff_up": null,
|
||||
"global_rating": "TD-",
|
||||
"length_total": null,
|
||||
"quality": "medium",
|
||||
"cooked": {
|
||||
"weather": "<p>Bouch\u00e9 le matin, nuages 50m au dessus de la base des dalles puis \u00e9claircies. </p>",
|
||||
"conditions_levels": null,
|
||||
"route_description": null,
|
||||
"summary": null,
|
||||
"version": 4,
|
||||
"access_comment": null,
|
||||
"timing": "<p>Approche : 2h30 avec la recherche au pied de la mauvaise dalle. <br>\nVoie : 6h</p>",
|
||||
"participants": null,
|
||||
"title": "Tour de Bavon\u00a0: Thor",
|
||||
"lang": "fr",
|
||||
"hut_comment": null,
|
||||
"topic_id": null,
|
||||
"description": "<p>Jolie voie mais le caillou demande de l'attention. Selon \u00c9toile : \"\u00e7a manque de colle entre les dalles de schistes!!\"</p>\n<p>Pour l'approche, on est mont\u00e9 trop t\u00f4t \u00e0 droite (300m trop au N), vers les premi\u00e8res grandes dalles au-dessus du chemin, faute de visibilit\u00e9. On a cherch\u00e9 les spits une bonne heure en longeant la paroi. On est redescendu puis on a poursuivi le long du ruisseau jusqu'au pied de la bonne rampe. Indication : poursuivre dans le fond du vallon jusqu'\u00e0 passer un replat avec 3 \u00e9normes blocs. Poursuivre 100m puis obliquer a droite vers une dalle blanche surmont\u00e9e d'un grand toit sombre. (Mise a jour topo a venir)</p>\n<p><a href=\"http://m.camptocamp.org/forums/viewforum.php?id=78\">TROUV\u00c9</a> : un brin de rappel au pied de la paroi 300m au N de la voie. </p>",
|
||||
"conditions": "<h3 id=\"rocher\">Rocher</h3>\n<p>Sec dans l'ensemble sauf L2, tremp\u00e9e sous le 2\u00e8me point. <br>\nPas mal de sections au rocher moyen, dont le ressaut final de L2 et du d\u00e9but de L5, bien p\u00e9teux. Bcp de vigilance n\u00e9cessaire tout le long pour ne pas partir avec une \u00e9caille. <br>\nBcp de cailloux pos\u00e9s \u00e9galement et qui n'attendent que le passage de la corde pour se d\u00e9loger. </p>\n<h3 id=\"equipement\">Equipement</h3>\n<p>\u00c9quip\u00e9 a\u00e9r\u00e9 mais bien \u00e9quip\u00e9.</p>"
|
||||
},
|
||||
"lift_status": null
|
||||
}
|
||||
1206
src/test/resources/routes/57103-in.json
Normal file
1206
src/test/resources/routes/57103-in.json
Normal file
File diff suppressed because one or more lines are too long
86
src/test/resources/routes/outing.json
Normal file
86
src/test/resources/routes/outing.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"img_count": 0,
|
||||
"geometry": {
|
||||
"has_geom_detail": false,
|
||||
"version": 1,
|
||||
"geom": "{\"coordinates\": [796963.8418227625, 5777640.64308189], \"type\": \"Point\"}"
|
||||
},
|
||||
"equipment_rating": "P1",
|
||||
"elevation_max": 2476,
|
||||
"date_start": "2015-09-06",
|
||||
"locales": [
|
||||
{
|
||||
"lang": "fr",
|
||||
"title": "Tour de Bavon : Thor",
|
||||
"version": 1,
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"author": {
|
||||
"user_id": 253878,
|
||||
"name": "Mireille Vuadens"
|
||||
},
|
||||
"protected": false,
|
||||
"rock_free_rating": "6a",
|
||||
"date_end": "2015-09-06",
|
||||
"height_diff_up": null,
|
||||
"condition_rating": "excellent",
|
||||
"areas": [
|
||||
{
|
||||
"document_id": 14437,
|
||||
"available_langs": null,
|
||||
"type": "a",
|
||||
"version": 6,
|
||||
"locales": [
|
||||
{
|
||||
"version": 7,
|
||||
"title": "Valais W - Alpes Pennines W",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"area_type": "range",
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 14384,
|
||||
"available_langs": null,
|
||||
"type": "a",
|
||||
"version": 9,
|
||||
"locales": [
|
||||
{
|
||||
"version": 12,
|
||||
"title": "Valais",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"area_type": "admin_limits",
|
||||
"protected": false
|
||||
},
|
||||
{
|
||||
"document_id": 14067,
|
||||
"available_langs": null,
|
||||
"type": "a",
|
||||
"version": 10,
|
||||
"locales": [
|
||||
{
|
||||
"version": 1,
|
||||
"title": "Suisse",
|
||||
"lang": "fr"
|
||||
}
|
||||
],
|
||||
"area_type": "country",
|
||||
"protected": false
|
||||
}
|
||||
],
|
||||
"activities": [
|
||||
"rock_climbing"
|
||||
],
|
||||
"available_langs": [
|
||||
"fr"
|
||||
],
|
||||
"document_id": 673121,
|
||||
"global_rating": "TD-",
|
||||
"quality": "medium",
|
||||
"version": 2,
|
||||
"type": "o"
|
||||
}
|
||||
2246
src/test/resources/search/bavon
Normal file
2246
src/test/resources/search/bavon
Normal file
File diff suppressed because it is too large
Load Diff
62
src/test/resources/search/portalet
Normal file
62
src/test/resources/search/portalet
Normal file
File diff suppressed because one or more lines are too long
1386
src/test/resources/search/test
Normal file
1386
src/test/resources/search/test
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user