orangeqs.juice.identifiers#

Identifiers for tagging data reported by Juice services.

This module provides functions to retrieve and generate identifiers used to tag data (quantities, reports, graphs, etc.) in OrangeQS Juice. Identifiers are structured strings composed of three parts: a prefix, a service name, and a suffix. An example identifier is "run_device_20251104105800". The prefix indicates the type of identifier (e.g., “run”, “cycle”), the service name specifies which Juice service generated the identifier, and the suffix encodes a timestamp.

To set this up, one must configure identifiers.toml to define which identifiers are associated with each service.

  • Identifiers are always generated by a service.

  • A service can only generate identifiers with prefixes it is configured to manage.

  • A service can manage multiple identifiers, each with a different prefix.

  • A service can use identifiers generated by other services.

  • Multiple services can generate identifiers with the same prefix.

Extensions that provide wrappers for specific identifier prefixes should follow the naming conventions for functions that provide access to identifiers.

  • Functions with a current_ prefix return the current identifiers of the current context. These functions should be context-aware, meaning they return identifiers based on the service context they are called from.

  • Functions with a latest_ prefix return the latest identifier of a given type. These functions should be context-agnostic, meaning they return the latest identifier regardless of the context they are called from.

  • Functions with a new_ prefix generate a new identifier of a given type.

Examples#

We have a setup with 2 fridges and 3 devices, where each device is assigned to a fridge. The fridges are managed by the fridge-a and fridge-b services, while the devices are managed by the device-1, device-2, and device-3 services. Device 1 is assigned to fridge A, device 2 and 3 to fridge B. We want the fridges to generate cycle identifiers, and the devices to generate run identifiers. Additionally, we want the devices to tag their measurements with the cycle identifiers generated by their respective fridges. The configuration would look like this:

# Identifiers for the fridge-a service.
[services.fridge-a]
# The `cycle` identifier is managed by this service itself.
cycle = "fridge-a"

[services.fridge-b]
cycle = "fridge-b"

# Identifiers for the device services.
[services.device-1]
# The `run` identifier is managed by this service itself.
run = "device-1"
# The `cycle` identifier is managed by the fridge-a service.
cycle = "fridge-a"

[services.device-2]
run = "device-2"
# The `cycle` identifier is managed by the fridge-b service.
cycle = "fridge-b"

[services.device-3]
run = "device-3"
cycle = "fridge-b"

This configuration allows each service to generate and use identifiers appropriately.

# Executed on the fridge-a service
from orangeqs.juice import identifiers

new_cycle_id = identifiers.new_id("cycle")
# >>> "cycle_fridge-a_20251104120000"
# Executed on the device-1 service
from orangeqs.juice import identifiers

new_run_id = identifiers.new_id("run")
# >>> "run_device-1_20251104120100"

# Retrieve the current identifiers, should be called each time before attaching them
# to measurements or events to ensure they are up-to-date.
current_identifiers = identifiers.current_ids()
# >>> {
#     "run": "run_device-1_20251104120100",
#     "cycle": "cycle_fridge-a_20251104120000",
# }

Package Contents#

Functions#

latest_id

Retrieve the latest identifier with the given prefix.

current_ids

Retrieve the current identifiers for all prefixes used by the current service.

new_id

Generate a new identifier with the given prefix.

parse_id

Parse an identifier into its components: prefix, service, and timestamp.

API#

orangeqs.juice.identifiers.latest_id(prefix: str, service: str | None = None, start: str = '-1w', stop: str = 'now()') str | None#

Retrieve the latest identifier with the given prefix.

The main use case of this function is determining the identifier before querying data from the database or loading files from disk. This function is context-agnostic, meaning it it will produce identical results regardless of where it was called from. It can be called from both services and user containers.

Note that multiple services can generate identifiers with the same prefix. If service is None, the most recent identifier with the given prefix across all services will be returned.

Fetches the most recent identifier through pubsub or from the local cache. If not found, queries InfluxDB to update the cache and tries again.

Parameters#

  • prefix (str): The prefix for the identifier.

  • service (str | None): The service name to filter for. If None, do not filter by service.

  • start (str): The start time for the query, in InfluxDB time format. Defaults to -1w.

  • stop (str): The stop time for the query, in InfluxDB time format. Defaults to now().

Returns#

  • (str | None): The most recent identifier with the given prefix, or None if not found.

orangeqs.juice.identifiers.current_ids(context: str | None = None, start: str = '-1w', stop: str = 'now()') dict[str, str]#

Retrieve the current identifiers for all prefixes used by the current service.

The main use case of this function is to get the identifiers for tagging data before writing to disk or the database. Should be called each time before writing data, to ensure the identifiers are up-to-date.

Determines the identifiers in the context of the current service by default. It is also possible to get the identifiers in the context of a different service by specifying the context parameter.

See the identifiers.toml configuration for more details.

Fetches the most recent identifier through pubsub or from the local cache. If not found, queries InfluxDB to update the cache and tries again.

Parameters#

  • context (str, optional): The service to use as context for retrieving identifiers. Must be the name of a service configured for identifiers. Defaults to None, which means the current service.

  • start (str): The start time for the query, in InfluxDB time format. Defaults to -1w.

  • stop (str): The stop time for the query, in InfluxDB time format. Defaults to now().

Returns#

  • (dict[str, str]): A dictionary mapping prefixes to their most recent identifiers. Contains an entry for each prefix configured for the service.

Raises#

  • (ValueError): If not able to determine the current service context, or if the service is not configured for identifiers, or if no identifier could be found for a configured prefix.

orangeqs.juice.identifiers.new_id(prefix: str) str#

Generate a new identifier with the given prefix.

This function can only be called from a service, and only if the service is configured to manage identifiers with the given prefix. See the identifiers.toml configuration for more details.

Stores the identifier in the database and publish an Identifier event. This synchronizes the identifier across all services that call latest_id() or current_ids().

The identifier will be in the format {prefix}_{service}_{timestamp}, where service is the name of the current service and timestamp is the current UTC time in YYYYMMDDHHMMSS format.

Parameters#

  • prefix (str): The prefix for the identifier.

Returns#

  • (str): The generated identifier.

orangeqs.juice.identifiers.parse_id(identifier: str) tuple[str, str, datetime.datetime]#

Parse an identifier into its components: prefix, service, and timestamp.

The identifier is expected to be in the format {prefix}_{service}_{timestamp}, where timestamp is in YYYYMMDDHHMMSS format.

Parameters#

  • identifier (str): The identifier to decode.

Returns#

  • (tuple[str, str, datetime.datetime]): A tuple containing the prefix, service, and timestamp.

Raises#

  • (ValueError): If the identifier format is invalid.