Initial commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
target/
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
+24
@@ -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"]
|
||||||
@@ -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>
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
@@ -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())
|
||||||
|
}
|
||||||
@@ -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]
|
||||||
@@ -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(),
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
@@ -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 %}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="">
|
||||||
|
<title>Title</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Hello
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
package be.simplenotes.c2c
|
||||||
@@ -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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -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"
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user