Fixed typos.

Fixed typos.

jsoniq version "1.0";

(:
 : Copyright 2006-2013 The FLWOR Foundation.
 :
 : Licensed under the Apache License, Version 2.0 (the "License");
 : you may not use this file except in compliance with the License.
 : You may obtain a copy of the License at
 :
 : http://www.apache.org/licenses/LICENSE-2.0
 :
 : Unless required by applicable law or agreed to in writing, software
 : distributed under the License is distributed on an "AS IS" BASIS,
 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 : See the License for the specific language governing permissions and
 : limitations under the License.
 :)

(:~
 : JSound (JSON schema) validator.
 :
 : @author Paul J. Lucas
 :)

module namespace jsd = "http://jsound.io/modules/jsound";

import module namespace fetch = "http://zorba.io/modules/fetch";

declare namespace jse = "http://jsound.io/errors";

declare namespace ver = "http://zorba.io/options/versioning";
declare option ver:module-version "1.0";

(:===========================================================================:)

(:~
 : Annotates the given JSON object against the given JSound schema type.
 :
 : @param $ns The namespace URI of the JSound schema to annotate against.
 : @param $type-name The name of the type in the schema to annotate against.
 : @param $item The JSON item to annotate.
 : @return Returns a (possibly annotated) version of $item.
 : If $item is valid against the schema,
 : the result will be a copy of $item with default values, if any,
 : filled in.
 : If $item is not valid,
 : the result will be a copy of $item except each invalid item
 : will be replaced by an object of the form:
 :

 :    {
 :      "$invalid" : "true",
 :      "$expected" : type-name,
 :      "$value" : invalid-value,
 :      "$reason" : "reason the item is invalid"
 :    }
 :

 : @error jse:DUPLICATE_PREFIX The prefix specified in the value of a
 : $prefix key of an $imports object has already been
 : used.
 : @error jse:DUPLICATE_TYPE The type name specified in the value of a
 : $name key of a type object has already been used.
 : @error jse:ILLEGAL_ARRAY_SIZE The value of a $content facet of
 : an array must be an array with exactly one element.
 : @error jse:ILLEGAL_BASE_TYPE
 : @error jse:ILLEGAL_FACET The given facet is illegal for the current kind of
 : object.
 : @error jse:ILLEGAL_FACET_VALUE The value for a given facet is illegal.
 : @error jse:ILLEGAL_KEY The given key is illegal for the current type or
 : field descriptor.
 : @error jse:ILLEGAL_NAMESPACE An explicit namespace given for the value of a
 : $name key must match the schema namespace.
 : @error jse:ILLEGAL_PREFIX The value given for the $prefix key
 : is illegal (must not contain a ':').
 : @error jse:ILLEGAL_SCHEMA The schema at the given $location
 : (if any) is illegal.
 : @error jse:ILLEGAL_TYPE The type of the value given for the
 : $type key is illegal.
 : @error jse:MISSING_KEY A required key is missing.
 : @error jse:NEW_KEY_NOT_ALLOWED A derived object type contains a key not
 : present in one of its base types and the value of the $open
 : facet is false.
 : @error jse:SCHEMA_NOT_FOUND The schema specified by the given
 : $namespace (and possible $location) is not found.
 : @error jse:TYPE_MISMATCH The type of a value for a given key is not the
 : expected type.
 : @error jse:UNKNOWN_PREFIX The prefix given as part of a type name is
 : unknown.
 : @error jse:UNKNOWN_TYPE The type specified for the value of the
 : $type key is unknown.
 :)
declare function jsd:ns-annotate( $ns as string, $type-name as string,
                                  $item as item )
  as boolean
{
  let $schema_doc := fetch:content( $ns, "SCHEMA" )
  let $jsd := jn:parse-json( $schema_doc )
  return jsd:jsd-annotate( $jsd, $type-name, $item )
};

(:~
 : Annotates the given JSON object against the given JSound schema type.
 :
 : @param $jsd The JSound schema to use.
 : @param $type-name The name of the type in the schema to annotate against.
 : @param $item The JSON item to annotate.
 : @return Returns a (possibly annotated) version of $item.
 : If $item is valid against the schema,
 : the result will be a copy of $item with default values, if any,
 : filled in.
 : If $item is not valid,
 : the result will be a copy of $item except each invalid item
 : will be replaced by an object of the form:
 :

 :    {
 :      "$invalid" : "true",
 :      "$expected" : type-name,
 :      "$value" : invalid-value,
 :      "$reason" : "reason the item is invalid"
 :    }
 :

 : @error jse:DUPLICATE_PREFIX The prefix specified in the value of a
 : $prefix key of an $imports object has already been
 : used.
 : @error jse:DUPLICATE_TYPE The type name specified in the value of a
 : $name key of a type object has already been used.
 : @error jse:ILLEGAL_ARRAY_SIZE The value of a $content facet of
 : an array must be an array with exactly one element.
 : @error jse:ILLEGAL_BASE_TYPE
 : @error jse:ILLEGAL_FACET The given facet is illegal for the current kind of
 : object.
 : @error jse:ILLEGAL_FACET_VALUE The value for a given facet is illegal.
 : @error jse:ILLEGAL_KEY The given key is illegal for the current type or
 : field descriptor.
 : @error jse:ILLEGAL_NAMESPACE An explicit namespace given for the value of a
 : $name key must match the schema namespace.
 : @error jse:ILLEGAL_PREFIX The value given for the $prefix key
 : is illegal (must not contain a ':').
 : @error jse:ILLEGAL_SCHEMA The schema at the given $location
 : (if any) is illegal.
 : @error jse:ILLEGAL_TYPE The type of the value given for the
 : $type key is illegal.
 : @error jse:MISSING_KEY A required key is missing.
 : @error jse:NEW_KEY_NOT_ALLOWED A derived object type contains a key not
 : present in one of its base types and the value of the $open
 : facet is false.
 : @error jse:SCHEMA_NOT_FOUND The schema specified by the given
 : $namespace (and possible $location) is not found.
 : @error jse:TYPE_MISMATCH The type of a value for a given key is not the
 : expected type.
 : @error jse:UNKNOWN_PREFIX The prefix given as part of a type name is
 : unknown.
 : @error jse:UNKNOWN_TYPE The type specified for the value of the
 : $type key is unknown.
 :)
declare function jsd:jsd-annotate( $jsd as object, $type-name as string,
                                   $item as item )
 as item external;

(:~
 : Validates the given JSON object against the given JSound schema type.
 :
 : @param $ns The namespace URI of the JSound schema to validate against.
 : @param $type-name The name of the type in the schema to validate against.
 : @param $item The JSON item to validate.
 : @return true only if $item is valid.
 : @error jse:DUPLICATE_PREFIX The prefix specified in the value of a
 : $prefix key of an $imports object has already been
 : used.
 : @error jse:DUPLICATE_TYPE The type name specified in the value of a
 : $name key of a type object has already been used.
 : @error jse:ILLEGAL_ARRAY_SIZE The value of a $content facet of
 : an array must be an array with exactly one element.
 : @error jse:ILLEGAL_BASE_TYPE
 : @error jse:ILLEGAL_FACET The given facet is illegal for the current kind of
 : object.
 : @error jse:ILLEGAL_FACET_VALUE The value for a given facet is illegal.
 : @error jse:ILLEGAL_KEY The given key is illegal for the current type or
 : field descriptor.
 : @error jse:ILLEGAL_NAMESPACE An explicit namespace given for the value of a
 : $name key must match the schema namespace.
 : @error jse:ILLEGAL_PREFIX The value given for the $prefix key
 : is illegal (must not contain a ':').
 : @error jse:ILLEGAL_SCHEMA The schema at the given $location
 : (if any) is illegal.
 : @error jse:ILLEGAL_TYPE The type of the value given for the
 : $type key is illegal.
 : @error jse:MISSING_KEY A required key is missing.
 : @error jse:NEW_KEY_NOT_ALLOWED A derived object type contains a key not
 : present in one of its base types and the value of the $open
 : facet is false.
 : @error jse:SCHEMA_NOT_FOUND The schema specified by the given
 : $namespace (and possible $location) is not found.
 : @error jse:TYPE_MISMATCH The type of a value for a given key is not the
 : expected type.
 : @error jse:UNKNOWN_PREFIX The prefix given as part of a type name is
 : unknown.
 : @error jse:UNKNOWN_TYPE The type specified for the value of the
 : $type key is unknown.
 :)
declare function jsd:ns-validate( $ns as string, $type-name as string,
                                  $item as item )
  as boolean
{
  let $schema_doc := fetch:content( $ns, "SCHEMA" )
  let $jsd := jn:parse-json( $schema_doc )
  return jsd:jsd-validate( $jsd, $type-name, $item )
};

(:~
 : Validates the given JSON object against the given JSound schema type.
 :
 : @param $jsd The JSound schema to validate against.
 : @param $type-name The name of the type in the schema to validate against.
 : @param $item The JSON item to validate.
 : @return true only if $item is valid.
 : @error jse:DUPLICATE_PREFIX The prefix specified in the value of a
 : $prefix key of an $imports object has already been
 : used.
 : @error jse:DUPLICATE_TYPE The type name specified in the value of a
 : $name key of a type object has already been used.
 : @error jse:ILLEGAL_ARRAY_SIZE The value of a $content facet of
 : an array must be an array with exactly one element.
 : @error jse:ILLEGAL_BASE_TYPE
 : @error jse:ILLEGAL_FACET The given facet is illegal for the current kind of
 : object.
 : @error jse:ILLEGAL_FACET_VALUE The value for a given facet is illegal.
 : @error jse:ILLEGAL_KEY The given key is illegal for the current type or
 : field descriptor.
 : @error jse:ILLEGAL_NAMESPACE An explicit namespace given for the value of a
 : $name key must match the schema namespace.
 : @error jse:ILLEGAL_PREFIX The value given for the $prefix key
 : is illegal (must not contain a ':').
 : @error jse:ILLEGAL_SCHEMA The schema at the given $location
 : (if any) is illegal.
 : @error jse:ILLEGAL_TYPE The type of the value given for the
 : $type key is illegal.
 : @error jse:MISSING_KEY A required key is missing.
 : @error jse:NEW_KEY_NOT_ALLOWED A derived object type contains a key not
 : present in one of its base types and the value of the $open
 : facet is false.
 : @error jse:SCHEMA_NOT_FOUND The schema specified by the given
 : $namespace (and possible $location) is not found.
 : @error jse:TYPE_MISMATCH The type of a value for a given key is not the
 : expected type.
 : @error jse:UNKNOWN_PREFIX The prefix given as part of a type name is
 : unknown.
 : @error jse:UNKNOWN_TYPE The type specified for the value of the
 : $type key is unknown.
 :)
declare function jsd:jsd-validate( $jsd as object, $type-name as string,
                                   $item as item )
 as boolean external;

(:===========================================================================:)

(: vim:set syntax=xquery et sw=2 ts=2: :)

jsoniq version "1.0";

(:
 : Copyright 2006-2013 The FLWOR Foundation.
 :
 : Licensed under the Apache License, Version 2.0 (the "License");
 : you may not use this file except in compliance with the License.
 : You may obtain a copy of the License at
 :
 : http://www.apache.org/licenses/LICENSE-2.0
 :
 : Unless required by applicable law or agreed to in writing, software
 : distributed under the License is distributed on an "AS IS" BASIS,
 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 : See the License for the specific language governing permissions and
 : limitations under the License.
 :)

(:~
 : JSound (JSON schema) validator.
 :
 : @author Paul J. Lucas
 :)

module namespace jsd = "http://jsound.io/modules/jsound";

import module namespace fetch = "http://zorba.io/modules/fetch";

declare namespace jse = "http://jsound.io/errors";

declare namespace ver = "http://zorba.io/options/versioning";
declare option ver:module-version "1.0";

(:===========================================================================:)

(:~
 : Annotates the given JSON object against the given JSound schema type.
 :
 : @param $ns The namespace URI of the JSound schema to annotate against.
 : @param $type-name The name of the type in the schema to annotate against.
 : @param $item The JSON item to annotate.
 : @return Returns a (possibly annotated) version of $item.
 : If $item is valid against the schema,
 : the result will be a copy of $item with default values, if any,
 : filled in.
 : If $item is not valid,
 : the result will be a copy of $item except each invalid item
 : will be replaced by an object of the form:
 :

 :    {
 :      "$invalid" : "true",
 :      "$expected" : type-name,
 :      "$value" : invalid-value,
 :      "$reason" : "reason the item is invalid"
 :    }
 :

 : @error jse:DUPLICATE_PREFIX The prefix specified in the value of a
 : $prefix key of an $imports object has already been
 : used.
 : @error jse:DUPLICATE_TYPE The type name specified in the value of a
 : $name key of a type object has already been used.
 : @error jse:ILLEGAL_ARRAY_SIZE The value of a $content facet of
 : an array must be an array with exactly one element.
 : @error jse:ILLEGAL_BASE_TYPE
 : @error jse:ILLEGAL_FACET The given facet is illegal for the current kind of
 : object.
 : @error jse:ILLEGAL_FACET_VALUE The value for a given facet in illegal.
 : @error jse:ILLEGAL_KEY The given key is illegal for the current type or
 : field descriptor.
 : @error jse:ILLEGAL_NAMESPACE An explicit namespace given for the value of a
 : $name key must match the schema namespace.
 : @error jse:ILLEGAL_PREFIX The value given for the $prefix key
 : is illegal (must not contain a ':').
 : @error jse:ILLEGAL_SCHEMA The schema at the given $location
 : (if any) is illegal.
 : @error jse:ILLEGAL_TYPE The type of the value given for the
 : $type key is illegal.
 : @error jse:MISSING_KEY A required key is missing.
 : @error jse:NEW_KEY_NOT_ALLOWED A derived object type contains a key not
 : present in one of its base types and the value of the $open
 : facet is false.
 : @error jse:SCHEMA_NOT_FOUND The schema specified by the given
 : $namespace (and possible $location) is not found.
 : @error jse:TYPE_MISMATCH The type of a value for a given key is not the
 : expected type.
 : @error jse:UNKNOWN_PREFIX The prefix given as part of a type name is
 : unknown.
 : @error jse:UNKNOWN_TYPE The type specified for the value of the
 : $type key is unknown.
 :)
declare function jsd:ns-annotate( $ns as string, $type-name as string,
                                  $item as item )
  as boolean
{
  let $schema_doc := fetch:content( $ns, "SCHEMA" )
  let $jsd := jn:parse-json( $schema_doc )
  return jsd:jsd-annotate( $jsd, $type-name, $item )
};

(:~
 : Annotates the given JSON object against the given JSound schema type.
 :
 : @param $jsd The JSound schema to use.
 : @param $type-name The name of the type in the schema to annotate against.
 : @param $item The JSON item to annotate.
 : @return Returns a (possibly annotated) version of $item.
 : If $item is valid against the schema,
 : the result will be a copy of $item with default values, if any,
 : filled in.
 : If $item is not valid,
 : the result will be a copy of $item except each invalid item
 : will be replaced by an object of the form:
 :

 :    {
 :      "$invalid" : "true",
 :      "$expected" : type-name,
 :      "$value" : invalid-value,
 :      "$reason" : "reason the item is invalid"
 :    }
 :

 : @error jse:DUPLICATE_PREFIX The prefix specified in the value of a
 : $prefix key of an $imports object has already been
 : used.
 : @error jse:DUPLICATE_TYPE The type name specified in the value of a
 : $name key of a type object has already been used.
 : @error jse:ILLEGAL_ARRAY_SIZE The value of a $content facet of
 : an array must be an array with exactly one element.
 : @error jse:ILLEGAL_BASE_TYPE
 : @error jse:ILLEGAL_FACET The given facet is illegal for the current kind of
 : object.
 : @error jse:ILLEGAL_FACET_VALUE The value for a given facet in illegal.
 : @error jse:ILLEGAL_KEY The given key is illegal for the current type or
 : field descriptor.
 : @error jse:ILLEGAL_NAMESPACE An explicit namespace given for the value of a
 : $name key must match the schema namespace.
 : @error jse:ILLEGAL_PREFIX The value given for the $prefix key
 : is illegal (must not contain a ':').
 : @error jse:ILLEGAL_SCHEMA The schema at the given $location
 : (if any) is illegal.
 : @error jse:ILLEGAL_TYPE The type of the value given for the
 : $type key is illegal.
 : @error jse:MISSING_KEY A required key is missing.
 : @error jse:NEW_KEY_NOT_ALLOWED A derived object type contains a key not
 : present in one of its base types and the value of the $open
 : facet is false.
 : @error jse:SCHEMA_NOT_FOUND The schema specified by the given
 : $namespace (and possible $location) is not found.
 : @error jse:TYPE_MISMATCH The type of a value for a given key is not the
 : expected type.
 : @error jse:UNKNOWN_PREFIX The prefix given as part of a type name is
 : unknown.
 : @error jse:UNKNOWN_TYPE The type specified for the value of the
 : $type key is unknown.
 :)
declare function jsd:jsd-annotate( $jsd as object, $type-name as string,
                                   $item as item )
 as item external;

(:~
 : Validates the given JSON object against the given JSound schema type.
 :
 : @param $ns The namespace URI of the JSound schema to validate against.
 : @param $type-name The name of the type in the schema to validate against.
 : @param $item The JSON item to validate.
 : @return true only if $item is valid.
 : @error jse:DUPLICATE_PREFIX The prefix specified in the value of a
 : $prefix key of an $imports object has already been
 : used.
 : @error jse:DUPLICATE_TYPE The type name specified in the value of a
 : $name key of a type object has already been used.
 : @error jse:ILLEGAL_ARRAY_SIZE The value of a $content facet of
 : an array must be an array with exactly one element.
 : @error jse:ILLEGAL_BASE_TYPE
 : @error jse:ILLEGAL_FACET The given facet is illegal for the current kind of
 : object.
 : @error jse:ILLEGAL_FACET_VALUE The value for a given facet in illegal.
 : @error jse:ILLEGAL_KEY The given key is illegal for the current type or
 : field descriptor.
 : @error jse:ILLEGAL_NAMESPACE An explicit namespace given for the value of a
 : $name key must match the schema namespace.
 : @error jse:ILLEGAL_PREFIX The value given for the $prefix key
 : is illegal (must not contain a ':').
 : @error jse:ILLEGAL_SCHEMA The schema at the given $location
 : (if any) is illegal.
 : @error jse:ILLEGAL_TYPE The type of the value given for the
 : $type key is illegal.
 : @error jse:MISSING_KEY A required key is missing.
 : @error jse:NEW_KEY_NOT_ALLOWED A derived object type contains a key not
 : present in one of its base types and the value of the $open
 : facet is false.
 : @error jse:SCHEMA_NOT_FOUND The schema specified by the given
 : $namespace (and possible $location) is not found.
 : @error jse:TYPE_MISMATCH The type of a value for a given key is not the
 : expected type.
 : @error jse:UNKNOWN_PREFIX The prefix given as part of a type name is
 : unknown.
 : @error jse:UNKNOWN_TYPE The type specified for the value of the
 : $type key is unknown.
 :)
declare function jsd:ns-validate( $ns as string, $type-name as string,
                                  $item as item )
  as boolean
{
  let $schema_doc := fetch:content( $ns, "SCHEMA" )
  let $jsd := jn:parse-json( $schema_doc )
  return jsd:jsd-validate( $jsd, $type-name, $item )
};

(:~
 : Validates the given JSON object against the given JSound schema type.
 :
 : @param $jsd The JSound schema to validate against.
 : @param $type-name The name of the type in the schema to validate against.
 : @param $item The JSON item to validate.
 : @return true only if $item is valid.
 : @error jse:DUPLICATE_PREFIX The prefix specified in the value of a
 : $prefix key of an $imports object has already been
 : used.
 : @error jse:DUPLICATE_TYPE The type name specified in the value of a
 : $name key of a type object has already been used.
 : @error jse:ILLEGAL_ARRAY_SIZE The value of a $content facet of
 : an array must be an array with exactly one element.
 : @error jse:ILLEGAL_BASE_TYPE
 : @error jse:ILLEGAL_FACET The given facet is illegal for the current kind of
 : object.
 : @error jse:ILLEGAL_FACET_VALUE The value for a given facet in illegal.
 : @error jse:ILLEGAL_KEY The given key is illegal for the current type or
 : field descriptor.
 : @error jse:ILLEGAL_NAMESPACE An explicit namespace given for the value of a
 : $name key must match the schema namespace.
 : @error jse:ILLEGAL_PREFIX The value given for the $prefix key
 : is illegal (must not contain a ':').
 : @error jse:ILLEGAL_SCHEMA The schema at the given $location
 : (if any) is illegal.
 : @error jse:ILLEGAL_TYPE The type of the value given for the
 : $type key is illegal.
 : @error jse:MISSING_KEY A required key is missing.
 : @error jse:NEW_KEY_NOT_ALLOWED A derived object type contains a key not
 : present in one of its base types and the value of the $open
 : facet is false.
 : @error jse:SCHEMA_NOT_FOUND The schema specified by the given
 : $namespace (and possible $location) is not found.
 : @error jse:TYPE_MISMATCH The type of a value for a given key is not the
 : expected type.
 : @error jse:UNKNOWN_PREFIX The prefix given as part of a type name is
 : unknown.
 : @error jse:UNKNOWN_TYPE The type specified for the value of the
 : $type key is unknown.
 :)
declare function jsd:jsd-validate( $jsd as object, $type-name as string,
                                   $item as item )
 as boolean external;

(:===========================================================================:)

(: vim:set syntax=xquery et sw=2 ts=2: :)


Revert "Default to timestamp and type fields when converting to openaps format"

This reverts commit d58d87a20ca73f4bf4eab1da92f16e71fb01f93a.

# This is a jq filter that converts the output of
# https://github.com/ecc1/medtronic/cmd/pumphistory
# to the schema that openaps expects.
# Usage:
#   pumphistory | jq -f openaps.jq > history.json

# Convert a Go duration to minutes.
def duration_to_minutes:
  if test("^(\\d+h)?(\\d+m)?0s$") then
    capture("^((?<h>\\d+)h)?((?<m>\\d+)m)?0s$") |
    60*(.h // 0 | tonumber) + (.m // 0 | tonumber)
  else
    ("unexpected duration: " + .) | error
  end;

# Output a single JSON array.
[
  # Treat null as an empty array.
  (. // []) |
  # Perform the following on each element of the input array.
  .[] |
  # Filter out fake meter entries.
  select((.Type == "BGReceived" and .Info.MeterID == "000000") | not) |
  # Start with the timestamp field, common to all record types,
  # and the type, which is the same as the decocare type in many cases.
  {
    timestamp: .Time,
    _type: .Type
  } +
  # Add type-specific fields.
  if .Type == "TempBasalDuration" then
    {
      "duration (min)": .Info | duration_to_minutes
    }
  elif .Type == "TempBasalRate" then
    {
      _type: "TempBasal",
      temp: .Info.Type | ascii_downcase,
      rate: .Info.Value
    }
  elif .Type == "Bolus" then
    {
      amount: .Info.Amount,
      programmed: .Info.Programmed,
      unabsorbed: .Info.Unabsorbed,
      duration: .Info.Duration | duration_to_minutes
    }
  elif .Type == "BolusWizard" then
    {
      bg: (.Info.Glucose // 0),
      carb_input: (.Info.CarbInput // 0),
      bg_target_low: .Info.TargetLow,
      bg_target_high: .Info.TargetHigh,
      sensitivity: .Info.Sensitivity,
      carb_ratio: .Info.CarbRatio,
      correction_estimate: .Info.Correction,
      food_estimate: .Info.Food,
      unabsorbed_insulin_total: .Info.Unabsorbed,
      bolus_estimate: .Info.Bolus
    }
  elif .Type == "Prime" then
    {
      amount: .Info.Manual,
      fixed: .Info.Fixed,
      type: (if .Info.Fixed == 0 then "manual" else "fixed" end),
    }
  elif .Type == "BGReceived" then
    {
      glucose: .Info.Glucose,
      meterID: .Info.MeterID
    }
  elif .Type == "SuspendPump" then
    {
      _type: "PumpSuspend"
    }
  elif .Type == "ResumePump" then
    {
      _type: "PumpResume"
    }
  elif .Type == "BatteryChange" then
    {
      _type: "Battery"
    }
  elif .Type == "Rewind" then
    {
    }
  # Add additional cases here as needed.
  else
    # Warn about record types being skipped.
    ("skipping " + .Type + " record") | debug | empty
  end
]

# This is a jq filter that converts the output of
# https://github.com/ecc1/medtronic/cmd/pumphistory
# to the schema that openaps expects.
# Usage:
#   pumphistory | jq -f openaps.jq > history.json

# Convert a Go duration to minutes.
def duration_to_minutes:
  if test("^(\\d+h)?(\\d+m)?0s$") then
    capture("^((?<h>\\d+)h)?((?<m>\\d+)m)?0s$") |
    60*(.h // 0 | tonumber) + (.m // 0 | tonumber)
  else
    ("unexpected duration: " + .) | error
  end;

# Output a single JSON array.
[
  # Treat null as an empty array.
  (. // []) |
  # Perform the following on each element of the input array.
  .[] |
  # Filter out fake meter entries.
  select((.Type == "BGReceived" and .Info.MeterID == "000000") | not) |
  # Start with the timestamp field, common to all record types,
  # and the type, which is the same as the decocare type in many cases.
  {
    timestamp: .Time,
    _type: .Type
  } +
  # Add type-specific fields.
  if .Type == "TempBasalDuration" then
    {
      "duration (min)": .Info | duration_to_minutes
    }
  elif .Type == "TempBasalRate" then
    {
      _type: "TempBasal",
      temp: .Info.Type | ascii_downcase,
      rate: .Info.Value
    }
  elif .Type == "Bolus" then
    {
      amount: .Info.Amount,
      programmed: .Info.Programmed,
      unabsorbed: .Info.Unabsorbed,
      duration: .Info.Duration | duration_to_minutes
    }
  elif .Type == "BolusWizard" then
    {
      bg: (.Info.Glucose // 0),
      carb_input: (.Info.CarbInput // 0),
      bg_target_low: .Info.TargetLow,
      bg_target_high: .Info.TargetHigh,
      sensitivity: .Info.Sensitivity,
      carb_ratio: .Info.CarbRatio,
      correction_estimate: .Info.Correction,
      food_estimate: .Info.Food,
      unabsorbed_insulin_total: .Info.Unabsorbed,
      bolus_estimate: .Info.Bolus
    }
  elif .Type == "Prime" then
    {
      amount: .Info.Manual,
      fixed: .Info.Fixed,
      type: (if .Info.Fixed == 0 then "manual" else "fixed" end),
    }
  elif .Type == "BGReceived" then
    {
      glucose: .Info.Glucose,
      meterID: .Info.MeterID
    }
  elif .Type == "SuspendPump" then
    {
      _type: "PumpSuspend"
    }
  elif .Type == "ResumePump" then
    {
      _type: "PumpResume"
    }
  elif .Type == "BatteryChange" then
    {
      _type: "Battery"
    }
  else
    # Default: just print timestamp and type.
    {
    }
  end
]