Skip to content

Commit

Permalink
More documentation improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
vharmain committed Dec 27, 2024
1 parent 763b3df commit 30f7831
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 66 deletions.
17 changes: 9 additions & 8 deletions webapp/src/clj/lipas/backend/api/v2.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[lipas.schema.sports-sites :as sports-sites-schema]
[lipas.schema.sports-sites.activities :as activities-schema]
[lipas.schema.sports-sites.types :as types-schema]
[lipas.schema.sports-sites.location :as location-schema]
[reitit.coercion.malli]
[reitit.openapi :as openapi]
[reitit.swagger-ui :as swagger-ui]
Expand Down Expand Up @@ -107,10 +108,10 @@
:description "The LIPAS system provides comprehensive data about sports and recreational facilities in Finland. The API is organized into three main sections:
**Sports Sites**
The core entities of LIPAS. Each sports facility is classified using a hierarchical type system, where specific facility types belong to subcategories within seven main categories. Each facility type has its own specific set of properties.
The core entities of LIPAS. Each sports facility is classified using a hierarchical type system, where specific facility types belong to subcategories within seven main categories. Each facility type has its own specific set of properties and a defined geometry type (Point, LineString, or Polygon) that describes its spatial representation.
**Sports Site Categories**
Access to the hierarchical type classification system used for categorizing sports facilities. This helps in understanding the structure and relationships between different facility types.
Access to the hierarchical type classification system used for categorizing sports facilities.
**Locations of Interest**
Additional non-facility entities in LIPAS, that complement the sports facility data."
Expand Down Expand Up @@ -154,7 +155,7 @@ Additional non-facility entities in LIPAS, that complement the sports facility d

["/{type-code}"
{:get
{:summary "Get single sports site category by type-code"
{:summary "Get sports site category by type code"
:handler
(fn [req]
(let [type-code (-> req :parameters :path :type-code)]
Expand Down Expand Up @@ -206,10 +207,10 @@ Additional non-facility entities in LIPAS, that complement the sports facility d
[:city-codes
{:optional true
:decode/string decode-heisenparam
:description (-> sports-sites-schema/city-codes
:description (-> location-schema/city-codes
second
:description)}
#'sports-sites-schema/city-codes]
#'location-schema/city-codes]

[:type-codes
{:optional true
Expand Down Expand Up @@ -249,7 +250,7 @@ Additional non-facility entities in LIPAS, that complement the sports facility d

["/{lipas-id}"
{:get
{:summary "Get single sports facility by lipas-id"
{:summary "Get sports facility by lipas-id"

:handler (fn [req]
(tap> (:parameters req))
Expand All @@ -269,7 +270,7 @@ Additional non-facility entities in LIPAS, that complement the sports facility d
}

[""
{:get {:summary "Get a paginated list of locations of interest"
{:get {:summary "Get a paginated list of Locations of Interest"
:handler (fn [req]
(tap> (:parameters req))
(let [params (:query (:parameters req))
Expand Down Expand Up @@ -317,7 +318,7 @@ Additional non-facility entities in LIPAS, that complement the sports facility d

["/{loi-id}"
{:get
{:summary "Get single Location of Interest by id"
{:summary "Get Location of Interest by id"

:handler (fn [req]
(tap> (:parameters req))
Expand Down
3 changes: 2 additions & 1 deletion webapp/src/cljc/lipas/data/activities.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
(:require
#?(:clj [cheshire.core :as json])
#?(:clj [clojure.data.csv :as csv])
#?(:clj [clojure.string :as str])
[clojure.string :as str]
[lipas.data.materials :as materials]
[lipas.data.types :as types]
[lipas.schema.common :as common-schema]
[lipas.utils :as utils]
[malli.core :as m]
Expand Down
5 changes: 5 additions & 0 deletions webapp/src/cljc/lipas/schema/common.cljc
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
(ns lipas.schema.common
(:require [lipas.data.status :as status]))

(def -iso8601-pattern #"^(?:[1-9]\d{3}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1\d|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[1-9]\d(?:0[48]|[2468][048]|[13579][26])|(?:[2468][048]|[13579][26])00)-02-29)T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?Z$")

(def iso8601-timestamp [:re {:description "ISO 8601 timestamp in UTC timezone"}
-iso8601-pattern])

(def status (into [:enum] (keys status/statuses)))
(def statuses [:set status])
;; https://github.com/metosin/malli/issues/670
Expand Down
11 changes: 7 additions & 4 deletions webapp/src/cljc/lipas/schema/lois.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,19 @@
(into
[:map {:description (str cat-k " > " (:value type-v))
:title (-> type-v :label :en)}
[:id [:string ]]
[:event-date [:string]]
[:id loi-id]
[:event-date {:description "Timestamp when this information became valid (ISO 8601, UTC time zone)"}
#'common/iso8601-timestamp]
#_[:created-at [:string]]
[:geometries (case (:geom-type type-v)
("Polygon") #'common/polygon-feature-collection
("LineString") #'common/line-string-feature-collection
#'common/point-feature-collection)]
[:status common/status]
[:loi-category [:enum cat-k]]
[:loi-type [:enum (:value type-v)]]]
[:loi-category {:description "The category of the type of the Location of Interest"}
[:enum cat-k]]
[:loi-type {:description "The type of the Location of Interest"}
[:enum (:value type-v)]]]
(for [[prop-k prop-v] (:props type-v)]
[prop-k {:optional true} (:schema prop-v)]))])))

Expand Down
49 changes: 5 additions & 44 deletions webapp/src/cljc/lipas/schema/sports_sites.cljc
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
(ns lipas.schema.sports-sites
(:require [lipas.data.admins :as admins]
[lipas.data.cities :as cities]
[lipas.data.owners :as owners]
[lipas.data.activities :as activities]
[lipas.data.prop-types :as prop-types]
Expand All @@ -17,46 +16,6 @@
(def lipas-id
[:int {:min 0 :label "Lipas-id" :description "Unique identifier of sports facility in LIPAS system."}])

(def city-code
(into [:enum {:title "CityCode"
:description "Official municipality identifier https://stat.fi/fi/luokitukset/kunta/kunta_1_20240101"}]
(sort (keys cities/by-city-code))))

(def city-codes
[:set {:title "CityCodes"
:description (-> city-code second :description)}
city-code])

(defn make-location-schema [feature-schema]
[:map {:description "Location of the sports facility."}
[:city
[:map
[:city-code #'city-code]
[:neighborhood {:optional true} #'location-schema/neighborhood]]]
[:address #'location-schema/address]
[:postal-code #'location-schema/postal-code]
[:postal-office {:optional true} #'location-schema/postal-code]
[:geometries
[:map
[:type [:enum "FeatureCollection"]]
[:features
[:vector feature-schema]]]]])

(def line-string-feature-props
[:map
[:name {:optional true} :string]
[:lipas-id {:optional true} #'lipas-id]
[:type-code {:optional true} :int]
[:route-part-difficulty {:optional true} :string]
[:travel-direction {:optional true} :string]])

(def line-string-feature
(mu/assoc common/line-string-feature :properties line-string-feature-props))

(def point-location (make-location-schema common/point-feature))
(def line-string-location (make-location-schema line-string-feature))
(def polygon-location (make-location-schema common/polygon-feature))

(def owner
(into [:enum {:title "Onwer"
:description "Owner entity of the sports facility."}]
Expand Down Expand Up @@ -130,6 +89,8 @@
:description description
:closed false}
[:lipas-id #'lipas-id]
[:event-date {:description "Timestamp when this information became valid (ISO 8601, UTC time zone)"}
#'common/iso8601-timestamp]
[:status #'common/status]
[:name #'name]
[:marketing-name {:optional true} #'marketing-name]
Expand Down Expand Up @@ -162,9 +123,9 @@
:description (get-in x [:description :en])
:type-codes #{type-code}
:location-schema (case geometry-type
"Point" #'point-location
"LineString" #'line-string-location
"Polygon" #'polygon-location)
"Point" #'location-schema/point-location
"LineString" #'location-schema/line-string-location
"Polygon" #'location-schema/polygon-location)
:extras-schema (cond-> [:map]
(seq props)
(conj [:properties
Expand Down
48 changes: 40 additions & 8 deletions webapp/src/cljc/lipas/schema/sports_sites/activities.cljc
Original file line number Diff line number Diff line change
@@ -1,16 +1,48 @@
(ns lipas.schema.sports-sites.activities
(:require [lipas.data.activities :as activities-data]))
(:require [clojure.string :as str]
[lipas.data.activities :as activities-data]
[lipas.data.types :as types]
[malli.util :as mu]))

(def activity (into [:enum] (keys activities-data/activities)))
(def activities
[:set {:title "Activities"
:description "Enriched activity related content for Luontoon.fi service. Certain sports facility types may contain data about activities that can be practiced at the facility."}
activity])

(def fishing activities-data/fishing-schema)
(def outdoor-recreation-areas activities-data/outdoor-recreation-areas-schema)
(def outdoor-recreation-routes activities-data/outdoor-recreation-routes-schema)
(def outdoor-recreation-facilities activities-data/outdoor-recreation-facilities-schema)
(def cycling activities-data/cycling-schema)
(def paddling activities-data/paddling-schema)
(def birdwatching activities-data/birdwatching-schema)
(defn -append-description
[schema {:keys [type-codes label]}]
(let [s (str (:en label)
" is an activity associated with facility types "
(str/join ", " (for [[k m] (select-keys types/all type-codes)]
(str k " " (get-in m [:name :en]))))
". Enriched activity information is collected for Luontoon.fi service.")]
(mu/update-properties schema assoc :description s)))

(def fishing (-append-description
activities-data/fishing-schema
activities-data/fishing))

(def outdoor-recreation-areas (-append-description
activities-data/outdoor-recreation-areas-schema
activities-data/outdoor-recreation-areas))

(def outdoor-recreation-routes (-append-description
activities-data/outdoor-recreation-routes-schema
activities-data/outdoor-recreation-routes))

(def outdoor-recreation-facilities (-append-description
activities-data/outdoor-recreation-facilities-schema
activities-data/outdoor-recreation-facilities))

(def cycling (-append-description
activities-data/cycling-schema
activities-data/cycling))

(def paddling (-append-description
activities-data/paddling-schema
activities-data/paddling))

(def birdwatching (-append-description
activities-data/birdwatching-schema
activities-data/birdwatching))
48 changes: 47 additions & 1 deletion webapp/src/cljc/lipas/schema/sports_sites/location.cljc
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
(ns lipas.schema.sports-sites.location
(:require [lipas.schema.core :as specs]))
(:require [lipas.schema.core :as specs]
[lipas.data.cities :as cities]
[lipas.schema.common :as common]
[malli.util :as mu]))

(def address
[:string {:description "Street address of the sports facility."
Expand All @@ -19,3 +22,46 @@
[:string {:description "Neighborhood or common name for the area of the location."
:min 1
:max 100}])


(def city-code
(into [:enum {:title "CityCode"
:description "Official municipality identifier https://stat.fi/fi/luokitukset/kunta/kunta_1_20240101"}]
(sort (keys cities/by-city-code))))

(def city-codes
[:set {:title "CityCodes"
:description (-> city-code second :description)}
city-code])

(defn make-location-schema [feature-schema geom-type]
[:map {:description (str "Location of the sports facility with required "
geom-type
" feature.")}
[:city
[:map
[:city-code #'city-code]
[:neighborhood {:optional true} #'neighborhood]]]
[:address #'address]
[:postal-code #'postal-code]
[:postal-office {:optional true} #'postal-code]
[:geometries
[:map
[:type [:enum "FeatureCollection"]]
[:features
[:vector feature-schema]]]]])

(def line-string-feature-props
[:map
[:name {:optional true} :string]
#_[:lipas-id {:optional true} #'lipas-id]
[:type-code {:optional true} :int]
[:route-part-difficulty {:optional true} :string]
[:travel-direction {:optional true} :string]])

(def line-string-feature
(mu/assoc common/line-string-feature :properties line-string-feature-props))

(def point-location (make-location-schema common/point-feature "Point"))
(def line-string-location (make-location-schema line-string-feature "LineString"))
(def polygon-location (make-location-schema common/polygon-feature "Polygon"))
57 changes: 57 additions & 0 deletions webapp/test/clj/lipas/timestamps_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
(ns lipas.timestamps-test
(:require [clojure.test :refer [deftest testing is]]
[lipas.schema.common :as common-schema]))

(def iso8601-pattern common-schema/-iso8601-pattern)

(deftest iso8601-timestamp-test
(testing "Valid UTC timestamps"
(is (re-matches iso8601-pattern "2024-12-27T13:25:00Z"))
(is (re-matches iso8601-pattern "2024-12-27T13:25:00.123Z"))
(is (re-matches iso8601-pattern "2024-01-01T00:00:00Z"))
(is (re-matches iso8601-pattern "2024-12-31T23:59:59Z"))
(is (re-matches iso8601-pattern "2024-02-29T00:00:00Z")) ; Valid leap year
(is (re-matches iso8601-pattern "2000-02-29T00:00:00Z"))) ; Valid century leap year

(testing "Invalid timestamps"
; Non-UTC timestamps (should all fail)
(is (nil? (re-matches iso8601-pattern "2024-12-27T13:25:00+02:00")))
(is (nil? (re-matches iso8601-pattern "2024-12-27T13:25:00-02:00")))
(is (nil? (re-matches iso8601-pattern "2024-12-27T13:25:00.123+02:00")))

; Invalid dates
(is (nil? (re-matches iso8601-pattern "2024-13-01T00:00:00Z"))) ; Invalid month
(is (nil? (re-matches iso8601-pattern "2024-00-01T00:00:00Z"))) ; Invalid month
(is (nil? (re-matches iso8601-pattern "2024-12-32T00:00:00Z"))) ; Invalid day
(is (nil? (re-matches iso8601-pattern "2024-12-00T00:00:00Z"))) ; Invalid day
(is (nil? (re-matches iso8601-pattern "2023-02-29T00:00:00Z"))) ; Invalid leap year date

; Invalid times
(is (nil? (re-matches iso8601-pattern "2024-12-27T24:00:00Z"))) ; Invalid hour
(is (nil? (re-matches iso8601-pattern "2024-12-27T12:60:00Z"))) ; Invalid minute
(is (nil? (re-matches iso8601-pattern "2024-12-27T12:00:60Z"))) ; Invalid second

; Invalid formats
(is (nil? (re-matches iso8601-pattern "2024-12-27 12:00:00Z"))) ; Space instead of T
(is (nil? (re-matches iso8601-pattern "2024-12-27T12:00:00"))) ; Missing Z
(is (nil? (re-matches iso8601-pattern "24-12-27T12:00:00Z"))) ; Two-digit year
(is (nil? (re-matches iso8601-pattern "2024/12/27T12:00:00Z")))) ; Wrong separators

(testing "Edge cases for months with different lengths"
(is (re-matches iso8601-pattern "2024-01-31T00:00:00Z")) ; January 31 valid
(is (nil? (re-matches iso8601-pattern "2024-04-31T00:00:00Z"))) ; April 31 invalid
(is (re-matches iso8601-pattern "2024-04-30T00:00:00Z")) ; April 30 valid
(is (nil? (re-matches iso8601-pattern "2024-02-30T00:00:00Z"))) ; February 30 invalid
(is (re-matches iso8601-pattern "2024-06-30T00:00:00Z")) ; June 30 valid
(is (nil? (re-matches iso8601-pattern "2024-06-31T00:00:00Z")))) ; June 31 invalid

(testing "Fractional seconds variations"
(is (re-matches iso8601-pattern "2024-12-27T13:25:00.1Z"))
(is (re-matches iso8601-pattern "2024-12-27T13:25:00.12Z"))
(is (re-matches iso8601-pattern "2024-12-27T13:25:00.123Z"))
(is (re-matches iso8601-pattern "2024-12-27T13:25:00.1234567890Z"))))


(comment
(clojure.test/run-tests *ns*)
)

0 comments on commit 30f7831

Please sign in to comment.