diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml
new file mode 100644
index 0000000000..8e711ca6aa
--- /dev/null
+++ b/.github/workflows/format.yml
@@ -0,0 +1,85 @@
+name: Format code and generate schemas
+permissions:
+ contents: write
+on:
+ workflow_call:
+jobs:
+ format:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ name: Checkout
+ with:
+ ref: ${{ github.head_ref }}
+ - uses: ./.github/actions/setup_rust
+ name: Setup Rust
+ - name: Setup nightly rust
+ run: |
+ rustup toolchain install nightly --allow-downgrade -c rustfmt
+ - name: Install Protoc
+ uses: arduino/setup-protoc@v3
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ - uses: Swatinem/rust-cache@v2
+ name: Cargo cache
+ with:
+ cache-all-crates: true
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "3.14.0"
+ cache: 'pip'
+ - name: Install Python dependencies
+ run: |
+ python -m pip install black mypy pandas-stubs
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ - name: Install raphtory
+ run: |
+ MATURIN_PEP517_ARGS="--profile=build-fast" pip install -e "./python[all]"
+
+ - name: Run rust format
+ run: |
+ cargo +nightly fmt --all
+
+ - name: Run stubsgen
+ run: |
+ python -m pip install -e stub_gen
+ cd python/scripts && python gen-stubs.py
+
+ - name: Update graphQL schema
+ run: |
+ raphtory schema > raphtory-graphql/schema.graphql
+ - name: Validate graphQL schema
+ run: |
+ npx graphql-schema-linter --rules fields-have-descriptions,types-have-descriptions raphtory-graphql/schema.graphql || true
+ - name: Update docs from graphQL schema
+ run: |
+ python docs/scripts/gen_docs_graphql_pages.py
+
+ - name: Run python linter
+ run: |
+ cd python && black .
+
+ - name: Run mypy
+ run: |
+ mypy -m raphtory
+
+ - name: Check for uncommitted changes
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ if [ -n "$(git status --porcelain)" ]; then
+ echo "Changes detected. Committing and pushing..."
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+ git checkout ${{ github.head_ref }}
+ git add .
+ git commit -m "chore: apply tidy-public auto-fixes"
+ git push --force-with-lease origin HEAD:${{ github.head_ref }}
+ else
+ echo "No changes to commit."
+ fi
\ No newline at end of file
diff --git a/.github/workflows/test_during_pr.yml b/.github/workflows/test_during_pr.yml
index f206450206..a882e409f1 100644
--- a/.github/workflows/test_during_pr.yml
+++ b/.github/workflows/test_during_pr.yml
@@ -11,55 +11,58 @@ concurrency:
cancel-in-progress: true
jobs:
- rust-format-check:
- name: Rust format check
- uses: ./.github/workflows/rust_format_check.yml
call-test-rust-workflow-in-local-repo:
name: Run Rust tests
uses: ./.github/workflows/test_rust_workflow.yml
secrets: inherit
- needs: rust-format-check
call-test-rust-storage-workflow-in-local-repo:
name: Run Rust storage tests
uses: ./.github/workflows/test_rust_disk_storage_workflow.yml
secrets: inherit
- needs: rust-format-check
call-test-python-workflow-in-local-repo:
name: Run Python tests
uses: ./.github/workflows/test_python_workflow.yml
with:
test_python_lower: false
secrets: inherit
- needs: rust-format-check
call-test-python-disk-storage-workflow-in-local-repo:
name: Run Python storage tests
uses: ./.github/workflows/test_python_disk_storage_workflow.yml
with:
test_python_lower: false
secrets: inherit
- needs: rust-format-check
call-test-ui-in-local-repo:
name: Run UI Tests
uses: ./.github/workflows/test_ui.yml
secrets: inherit
- needs: rust-format-check
permissions:
contents: read
call-benchmark-workflow-in-local-repo:
name: Run benchmarks
uses: ./.github/workflows/benchmark.yml
secrets: inherit
- needs: rust-format-check
call-graphql-bench-workflow-in-local-repo:
name: Run benchmarks
uses: ./.github/workflows/bench-graphql.yml
secrets: inherit
- needs: rust-format-check
call-stress-test-workflow-in-local-repo:
name: Run benchmarks
uses: ./.github/workflows/stress-test.yml
secrets: inherit
- needs: rust-format-check
+ call-format:
+ name: Formatting autofixes
+ uses: ./.github/workflows/format.yml
+ secrets: inherit
+ needs: [
+ call-test-rust-workflow-in-local-repo,
+ call-test-rust-storage-workflow-in-local-repo,
+ call-test-python-workflow-in-local-repo,
+ call-test-python-disk-storage-workflow-in-local-repo,
+ # call-test-ui-in-local-repo, # TODO: currently broken, re-enable when fixed
+ call-benchmark-workflow-in-local-repo,
+ call-graphql-bench-workflow-in-local-repo,
+ call-stress-test-workflow-in-local-repo
+ ]
# call-code-coverage:
# name: Code Coverage
# uses: ./.github/workflows/code_coverage.yml
diff --git a/.github/workflows/test_python_disk_storage_workflow.yml b/.github/workflows/test_python_disk_storage_workflow.yml
index 8a938feb8d..8983052f86 100644
--- a/.github/workflows/test_python_disk_storage_workflow.yml
+++ b/.github/workflows/test_python_disk_storage_workflow.yml
@@ -20,7 +20,7 @@ jobs:
steps:
- id: set-matrix
run: |
- echo "python-versions=[\"3.11\",\"3.14\"]" >> $GITHUB_OUTPUT
+ echo "python-versions=[\"3.11\",\"3.14.0\"]" >> $GITHUB_OUTPUT
python-test:
if: ${{ !inputs.skip_tests }}
name: Python Tests
diff --git a/.github/workflows/test_python_workflow.yml b/.github/workflows/test_python_workflow.yml
index 8be35c686a..102547627d 100644
--- a/.github/workflows/test_python_workflow.yml
+++ b/.github/workflows/test_python_workflow.yml
@@ -21,7 +21,7 @@ jobs:
steps:
- id: set-matrix
run: |
- echo "python-versions=[\"3.11\",\"3.14\"]" >> $GITHUB_OUTPUT
+ echo "python-versions=[\"3.11\",\"3.14.0\"]" >> $GITHUB_OUTPUT
python-test:
if: ${{ !inputs.skip_tests }}
name: Python Tests
@@ -64,53 +64,9 @@ jobs:
python -m pip install black
echo "Installing linting dependencies from cache..."
python -m pip install maturin mypy networkx pyvis pandas-stubs
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version: '20'
- name: Run Python tests
run: |
cd python && tox run
- name: Run Python extension tests
run: |
cd examples/netflow/test && pytest .
- - name: Validate graphQL schema
- if: matrix.os == 'ubuntu-latest' && matrix.python == '3.13'
- run: |
- npx graphql-schema-linter --rules fields-have-descriptions,types-have-descriptions raphtory-graphql/schema.graphql || true
- - name: Update docs from graphQL schema
- if: matrix.os == 'ubuntu-latest' && matrix.python == '3.13'
- run: |
- python docs/scripts/gen_docs_graphql_pages.py
- - name: Run python linter
- if: matrix.os == 'ubuntu-latest' && matrix.python == '3.13'
- run: |
- cd python && black .
- - name: Run stubsgen
- if: matrix.os == 'ubuntu-latest' && matrix.python == '3.13'
- run: |
- echo "Installing Raphtory from ./python"
- pip install -e ./python
- echo "Installing stubsgen"
- python -m pip install -e stub_gen
- cd python/scripts && python gen-stubs.py
- - name: Run mypy
- run: |
- mypy -m raphtory
-
- - name: Check for uncommitted changes
- if: matrix.os == 'ubuntu-latest' && matrix.python == '3.13'
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- if [ -n "$(git status --porcelain)" ]; then
- echo "Changes detected. Committing and pushing..."
- git config user.name "github-actions[bot]"
- git config user.email "github-actions[bot]@users.noreply.github.com"
- git checkout ${{ github.head_ref }}
- git add .
- git commit -m "chore: apply tidy-public auto-fixes"
- git push --force-with-lease origin HEAD:${{ github.head_ref }}
- else
- echo "No changes to commit."
- fi
diff --git a/.github/workflows/test_ui.yml b/.github/workflows/test_ui.yml
index f390927938..ebe27b7e0b 100644
--- a/.github/workflows/test_ui.yml
+++ b/.github/workflows/test_ui.yml
@@ -11,7 +11,7 @@ jobs:
steps:
- id: set-matrix
run: |
- echo "python-versions=[\"3.11\",\"3.14\"]" >> $GITHUB_OUTPUT
+ echo "python-versions=[\"3.11\",\"3.14.0\"]" >> $GITHUB_OUTPUT
ui-test:
name: UI Tests
needs: select-strategy
diff --git a/Cargo.lock b/Cargo.lock
index 7851797e36..938b93ae2c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4467,7 +4467,7 @@ dependencies = [
[[package]]
name = "pometry-storage"
-version = "0.16.3"
+version = "0.17.0"
[[package]]
name = "portable-atomic"
@@ -4920,7 +4920,7 @@ dependencies = [
[[package]]
name = "raphtory"
-version = "0.16.3"
+version = "0.17.0"
dependencies = [
"ahash",
"arrow",
@@ -5001,7 +5001,7 @@ dependencies = [
[[package]]
name = "raphtory-api"
-version = "0.16.3"
+version = "0.17.0"
dependencies = [
"arrow-array",
"arrow-ipc",
@@ -5034,7 +5034,7 @@ dependencies = [
[[package]]
name = "raphtory-benchmark"
-version = "0.16.3"
+version = "0.17.0"
dependencies = [
"chrono",
"criterion",
@@ -5055,7 +5055,7 @@ dependencies = [
[[package]]
name = "raphtory-core"
-version = "0.16.3"
+version = "0.17.0"
dependencies = [
"bigdecimal",
"chrono",
@@ -5079,7 +5079,7 @@ dependencies = [
[[package]]
name = "raphtory-cypher"
-version = "0.16.3"
+version = "0.17.0"
dependencies = [
"arrow",
"arrow-array",
@@ -5109,7 +5109,7 @@ dependencies = [
[[package]]
name = "raphtory-graphql"
-version = "0.16.3"
+version = "0.17.0"
dependencies = [
"ahash",
"arrow-array",
@@ -5160,7 +5160,7 @@ dependencies = [
[[package]]
name = "raphtory-pymodule"
-version = "0.16.3"
+version = "0.17.0"
dependencies = [
"pyo3",
"pyo3-build-config",
@@ -5170,7 +5170,7 @@ dependencies = [
[[package]]
name = "raphtory-storage"
-version = "0.16.3"
+version = "0.17.0"
dependencies = [
"arrow-array",
"arrow-schema",
@@ -5203,7 +5203,7 @@ dependencies = [
[[package]]
name = "raphtory_netflow"
-version = "0.16.3"
+version = "0.17.0"
dependencies = [
"pyo3",
"pyo3-build-config",
@@ -5454,7 +5454,7 @@ dependencies = [
[[package]]
name = "rust-examples"
-version = "0.16.3"
+version = "0.17.0"
dependencies = [
"chrono",
"itertools 0.13.0",
diff --git a/Cargo.toml b/Cargo.toml
index 5cdfc201d6..8aa14c2c53 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,7 +16,7 @@ default-members = ["raphtory"]
resolver = "2"
[workspace.package]
-version = "0.16.3"
+version = "0.17.0"
documentation = "https://raphtory.readthedocs.io/en/latest/"
repository = "https://github.com/Raphtory/raphtory/"
license = "GPL-3.0"
@@ -53,11 +53,11 @@ incremental = false
pometry-storage = { version = ">=0.8.1", path = "pometry-storage" }
#[private-storage]
# pometry-storage = { path = "pometry-storage-private", package = "pometry-storage-private" }
-raphtory = { path = "raphtory", version = "0.16.3" }
-raphtory-api = { path = "raphtory-api", version = "0.16.3" }
-raphtory-core = { path = "raphtory-core", version = "0.16.3" }
-raphtory-storage = { path = "raphtory-storage", version = "0.16.3" }
-raphtory-graphql = { path = "raphtory-graphql", version = "0.16.3" }
+raphtory = { path = "raphtory", version = "0.17.0" }
+raphtory-api = { path = "raphtory-api", version = "0.17.0" }
+raphtory-core = { path = "raphtory-core", version = "0.17.0" }
+raphtory-storage = { path = "raphtory-storage", version = "0.17.0" }
+raphtory-graphql = { path = "raphtory-graphql", version = "0.17.0" }
async-graphql = { version = "7.0.16", features = ["dynamic-schema"] }
bincode = "1.3.3"
async-graphql-poem = "7.0.16"
diff --git a/docs/assets/images/gandalf-importance.png b/docs/assets/images/gandalf-importance.png
new file mode 100644
index 0000000000..60c85100dc
Binary files /dev/null and b/docs/assets/images/gandalf-importance.png differ
diff --git a/docs/reference/graphql/graphql_API.md b/docs/reference/graphql/graphql_API.md
deleted file mode 100644
index 37a11850c6..0000000000
--- a/docs/reference/graphql/graphql_API.md
+++ /dev/null
@@ -1,7878 +0,0 @@
----
-hide:
- - navigation
----
-
-
-
-# Schema Types
-
-
-## Query (QueryRoot)
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-hello
-String !
-
-
-Hello world demo
-
-
-
-
-graph
-Graph !
-
-
-Returns a graph
-
-
-
-
-path
-String !
-
-
-
-updateGraph
-MutableGraph !
-
-
-Update graph query, has side effects to update graph state
-
-Returns:: GqlMutableGraph
-
-
-
-
-path
-String !
-
-
-
-vectorisedGraph
-VectorisedGraph
-
-
-Create vectorised graph in the format used for queries
-
-Returns:: GqlVectorisedGraph
-
-
-
-
-path
-String !
-
-
-
-namespaces
-CollectionOfNamespace !
-
-
-Returns all namespaces using recursive search
-
-Returns:: List of namespaces on root
-
-
-
-
-namespace
-Namespace !
-
-
-Returns a specific namespace at a given path
-
-Returns:: Namespace or error if no namespace found
-
-
-
-
-path
-String !
-
-
-
-root
-Namespace !
-
-
-Returns root namespace
-
-Returns:: Root namespace
-
-
-
-
-plugins
-QueryPlugin !
-
-
-Returns a plugin.
-
-
-
-
-receiveGraph
-String !
-
-
-Encodes graph and returns as string
-
-Returns:: Base64 url safe encoded string
-
-
-
-
-path
-String !
-
-
-
-version
-String !
-
-
-
-
-
-## Mutation (MutRoot)
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-plugins
-MutationPlugin !
-
-
-Returns a collection of mutation plugins.
-
-
-
-
-deleteGraph
-Boolean !
-
-
-Delete graph from a path on the server.
-
-
-
-
-path
-String !
-
-
-
-newGraph
-Boolean !
-
-
-Creates a new graph.
-
-
-
-
-path
-String !
-
-
-
-graphType
-GraphType !
-
-
-
-moveGraph
-Boolean !
-
-
-Move graph from a path path on the server to a new_path on the server.
-
-If namespace is not provided, it will be set to the current working directory.
-This applies to both the graph namespace and new graph namespace.
-
-
-
-
-path
-String !
-
-
-
-newPath
-String !
-
-
-
-copyGraph
-Boolean !
-
-
-Copy graph from a path path on the server to a new_path on the server.
-
-If namespace is not provided, it will be set to the current working directory.
-This applies to both the graph namespace and new graph namespace.
-
-
-
-
-path
-String !
-
-
-
-newPath
-String !
-
-
-
-uploadGraph
-String !
-
-
-Upload a graph file from a path on the client using GQL multipart uploading.
-
-Returns::
-name of the new graph
-
-
-
-
-path
-String !
-
-
-
-graph
-Upload !
-
-
-
-overwrite
-Boolean !
-
-
-
-sendGraph
-String !
-
-
-Send graph bincode as base64 encoded string.
-
-Returns::
-path of the new graph
-
-
-
-
-path
-String !
-
-
-
-graph
-String !
-
-
-
-overwrite
-Boolean !
-
-
-
-createSubgraph
-String !
-
-
-Returns a subgraph given a set of nodes from an existing graph in the server.
-
-Returns::
-name of the new graph
-
-
-
-
-parentPath
-String !
-
-
-
-nodes
-[String !]!
-
-
-
-newPath
-String !
-
-
-
-overwrite
-Boolean !
-
-
-
-createIndex
-Boolean !
-
-
-(Experimental) Creates search index.
-
-
-
-
-path
-String !
-
-
-
-indexSpec
-IndexSpecInput
-
-
-
-inRam
-Boolean !
-
-
-
-
-
-## Objects
-
-### CollectionOfMetaGraph
-
-Collection of items
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-list
-[MetaGraph !]!
-
-
-Returns a list of collection objects.
-
-
-
-
-page
-[MetaGraph !]!
-
-
-Fetch one page with a number of items up to a specified limit, optionally offset by a specified amount. The page_index sets the number of pages to skip (defaults to 0).
-
-For example, if page(5, 2, 1) is called, a page with 5 items, offset by 11 items (2 pages of 5 + 1),
-will be returned.
-
-
-
-
-limit
-Int !
-
-
-
-offset
-Int
-
-
-
-pageIndex
-Int
-
-
-
-count
-Int !
-
-
-Returns a count of collection objects.
-
-
-
-
-
-
-### CollectionOfNamespace
-
-Collection of items
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-list
-[Namespace !]!
-
-
-Returns a list of collection objects.
-
-
-
-
-page
-[Namespace !]!
-
-
-Fetch one page with a number of items up to a specified limit, optionally offset by a specified amount. The page_index sets the number of pages to skip (defaults to 0).
-
-For example, if page(5, 2, 1) is called, a page with 5 items, offset by 11 items (2 pages of 5 + 1),
-will be returned.
-
-
-
-
-limit
-Int !
-
-
-
-offset
-Int
-
-
-
-pageIndex
-Int
-
-
-
-count
-Int !
-
-
-Returns a count of collection objects.
-
-
-
-
-
-
-### CollectionOfNamespacedItem
-
-Collection of items
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-list
-[NamespacedItem !]!
-
-
-Returns a list of collection objects.
-
-
-
-
-page
-[NamespacedItem !]!
-
-
-Fetch one page with a number of items up to a specified limit, optionally offset by a specified amount. The page_index sets the number of pages to skip (defaults to 0).
-
-For example, if page(5, 2, 1) is called, a page with 5 items, offset by 11 items (2 pages of 5 + 1),
-will be returned.
-
-
-
-
-limit
-Int !
-
-
-
-offset
-Int
-
-
-
-pageIndex
-Int
-
-
-
-count
-Int !
-
-
-Returns a count of collection objects.
-
-
-
-
-
-
-### Document
-
-Document in a vector graph
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-entity
-DocumentEntity !
-
-
-Entity associated with document.
-
-
-
-
-content
-String !
-
-
-Content of the document.
-
-
-
-
-embedding
-[Float !]!
-
-
-Similarity score with a specified query
-
-
-
-
-score
-Float !
-
-
-
-
-
-### Edge
-
-Raphtory graph edge.
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-defaultLayer
-Edge !
-
-
-Return a view of Edge containing only the default edge layer.
-
-
-
-
-layers
-Edge !
-
-
-Returns a view of Edge containing all layers in the list of names.
-
-Errors if any of the layers do not exist.
-
-
-
-
-names
-[String !]!
-
-
-
-excludeLayers
-Edge !
-
-
-Returns a view of Edge containing all layers except the excluded list of names.
-
-Errors if any of the layers do not exist.
-
-
-
-
-names
-[String !]!
-
-
-
-layer
-Edge !
-
-
-Returns a view of Edge containing the specified layer.
-
-Errors if any of the layers do not exist.
-
-
-
-
-name
-String !
-
-
-
-excludeLayer
-Edge !
-
-
-Returns a view of Edge containing all layers except the excluded layer specified.
-
-Errors if any of the layers do not exist.
-
-
-
-
-name
-String !
-
-
-
-rolling
-EdgeWindowSet !
-
-
-Creates a WindowSet with the given window duration and optional step using a rolling window.
-
-A rolling window is a window that moves forward by step size at each iteration.
-
-alignment_unit optionally aligns the windows to the specified unit. "Unaligned" can be passed for no alignment.
-If unspecified (i.e. by default), alignment is done on the smallest unit of time in the step (or window if no step is passed).
-e.g. "1 month and 1 day" will align at the start of the day.
-Note that passing a step larger than window while alignment_unit is not "Unaligned" may lead to some entries appearing before
-the start of the first window and/or after the end of the last window (i.e. not included in any window).
-
-
-
-
-window
-WindowDuration !
-
-
-
-step
-WindowDuration
-
-
-
-alignmentUnit
-AlignmentUnit
-
-
-
-expanding
-EdgeWindowSet !
-
-
-Creates a WindowSet with the given step size using an expanding window.
-
-An expanding window is a window that grows by step size at each iteration.
-
-alignment_unit optionally aligns the windows to the specified unit. "Unaligned" can be passed for no alignment.
-If unspecified (i.e. by default), alignment is done on the smallest unit of time in the step.
-e.g. "1 month and 1 day" will align at the start of the day.
-
-
-
-
-step
-WindowDuration !
-
-
-
-alignmentUnit
-AlignmentUnit
-
-
-
-window
-Edge !
-
-
-Creates a view of the Edge including all events between the specified start (inclusive) and end (exclusive).
-
-For persistent graphs, any edge which exists at any point during the window will be included. You may want to restrict this to only edges that are present at the end of the window using the is_valid function.
-
-
-
-
-start
-Int !
-
-
-
-end
-Int !
-
-
-
-at
-Edge !
-
-
-Creates a view of the Edge including all events at a specified time.
-
-
-
-
-time
-Int !
-
-
-
-latest
-Edge !
-
-
-Returns a view of the edge at the latest time of the graph.
-
-
-
-
-snapshotAt
-Edge !
-
-
-Creates a view of the Edge including all events that are valid at time.
-
-This is equivalent to before(time + 1) for Graph and at(time) for PersistentGraph.
-
-
-
-
-time
-Int !
-
-
-
-snapshotLatest
-Edge !
-
-
-Creates a view of the Edge including all events that are valid at the latest time.
-
-This is equivalent to a no-op for Graph and latest() for PersistentGraph.
-
-
-
-
-before
-Edge !
-
-
-Creates a view of the Edge including all events before a specified end (exclusive).
-
-
-
-
-time
-Int !
-
-
-
-after
-Edge !
-
-
-Creates a view of the Edge including all events after a specified start (exclusive).
-
-
-
-
-time
-Int !
-
-
-
-shrinkWindow
-Edge !
-
-
-Shrinks both the start and end of the window.
-
-
-
-
-start
-Int !
-
-
-
-end
-Int !
-
-
-
-shrinkStart
-Edge !
-
-
-Set the start of the window.
-
-
-
-
-start
-Int !
-
-
-
-shrinkEnd
-Edge !
-
-
-Set the end of the window.
-
-
-
-
-end
-Int !
-
-
-
-applyViews
-Edge !
-
-
-Takes a specified selection of views and applies them in given order.
-
-
-
-
-views
-[EdgeViewCollection !]!
-
-
-
-earliestTime
-Int
-
-
-Returns the earliest time of an edge.
-
-
-
-
-firstUpdate
-Int
-
-
-
-latestTime
-Int
-
-
-Returns the latest time of an edge.
-
-
-
-
-lastUpdate
-Int
-
-
-
-time
-Int !
-
-
-Returns the time of an exploded edge. Errors on an unexploded edge.
-
-
-
-
-start
-Int
-
-
-Returns the start time for rolling and expanding windows for this edge. Returns none if no window is applied.
-
-
-
-
-end
-Int
-
-
-Returns the end time of the window. Returns none if no window is applied.
-
-
-
-
-src
-Node !
-
-
-Returns the source node of the edge.
-
-Returns:
-Node:
-
-
-
-
-dst
-Node !
-
-
-Returns the destination node of the edge.
-
-Returns:
-Node:
-
-
-
-
-nbr
-Node !
-
-
-Returns the node at the other end of the edge (same as dst() for out-edges and src() for in-edges).
-
-Returns:
-Node:
-
-
-
-
-id
-[String !]!
-
-
-Returns the id of the edge.
-
-Returns:
-list[str]:
-
-
-
-
-properties
-Properties !
-
-
-Returns a view of the properties of the edge.
-
-
-
-
-metadata
-Metadata !
-
-
-Returns the metadata of an edge.
-
-
-
-
-layerNames
-[String !]!
-
-
-Returns the names of the layers that have this edge as a member.
-
-
-
-
-layerName
-String !
-
-
-Returns the layer name of an exploded edge, errors on an edge.
-
-
-
-
-explode
-Edges !
-
-
-Returns an edge object for each update within the original edge.
-
-
-
-
-explodeLayers
-Edges !
-
-
-Returns an edge object for each layer within the original edge.
-
-Each new edge object contains only updates from the respective layers.
-
-
-
-
-history
-[Int !]!
-
-
-Returns a list of timestamps of when an edge is added or change to an edge is made.
-
-Returns:
-List[int]:
-
-
-
-
-deletions
-[Int !]!
-
-
-Returns a list of timestamps of when an edge is deleted.
-
-Returns:
-List[int]:
-
-
-
-
-isValid
-Boolean !
-
-
-Checks if the edge is currently valid and exists at the current time.
-
-Returns: boolean
-
-
-
-
-isActive
-Boolean !
-
-
-Checks if the edge is currently active and has at least one update within the current period.
-
-Returns: boolean
-
-
-
-
-isDeleted
-Boolean !
-
-
-Checks if the edge is deleted at the current time.
-
-Returns: boolean
-
-
-
-
-isSelfLoop
-Boolean !
-
-
-Returns true if the edge source and destination nodes are the same.
-
-Returns: boolean
-
-
-
-
-
-
-### EdgeSchema
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-srcType
-String !
-
-
-Returns the type of source for these edges
-
-
-
-
-dstType
-String !
-
-
-Returns the type of destination for these edges
-
-
-
-
-properties
-[PropertySchema !]!
-
-
-Returns the list of property schemas for edges connecting these types of nodes
-
-
-
-
-metadata
-[PropertySchema !]!
-
-
-Returns the list of metadata schemas for edges connecting these types of nodes
-
-
-
-
-
-
-### EdgeWindowSet
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-count
-Int !
-
-
-
-page
-[Edge !]!
-
-
-Fetch one page with a number of items up to a specified limit, optionally offset by a specified amount.
-The page_index sets the number of pages to skip (defaults to 0).
-
-For example, if page(5, 2, 1) is called, a page with 5 items, offset by 11 items (2 pages of 5 + 1),
-will be returned.
-
-
-
-
-limit
-Int !
-
-
-
-offset
-Int
-
-
-
-pageIndex
-Int
-
-
-
-list
-[Edge !]!
-
-
-
-
-
-### Edges
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-defaultLayer
-Edges !
-
-
-Returns a collection containing only edges in the default edge layer.
-
-
-
-
-layers
-Edges !
-
-
-Returns a collection containing only edges belonging to the listed layers.
-
-
-
-
-names
-[String !]!
-
-
-
-excludeLayers
-Edges !
-
-
-Returns a collection containing edges belonging to all layers except the excluded list of layers.
-
-
-
-
-names
-[String !]!
-
-
-
-layer
-Edges !
-
-
-Returns a collection containing edges belonging to the specified layer.
-
-
-
-
-name
-String !
-
-
-
-excludeLayer
-Edges !
-
-
-Returns a collection containing edges belonging to all layers except the excluded layer specified.
-
-
-
-
-name
-String !
-
-
-
-rolling
-EdgesWindowSet !
-
-
-Creates a WindowSet with the given window duration and optional step using a rolling window. A rolling window is a window that moves forward by step size at each iteration.
-
-Returns a collection of collections. This means that item in the window set is a collection of edges.
-
-alignment_unit optionally aligns the windows to the specified unit. "Unaligned" can be passed for no alignment.
-If unspecified (i.e. by default), alignment is done on the smallest unit of time in the step (or window if no step is passed).
-e.g. "1 month and 1 day" will align at the start of the day.
-Note that passing a step larger than window while alignment_unit is not "Unaligned" may lead to some entries appearing before
-the start of the first window and/or after the end of the last window (i.e. not included in any window).
-
-
-
-
-window
-WindowDuration !
-
-
-
-step
-WindowDuration
-
-
-
-alignmentUnit
-AlignmentUnit
-
-
-
-expanding
-EdgesWindowSet !
-
-
-Creates a WindowSet with the given step size using an expanding window. An expanding window is a window that grows by step size at each iteration.
-
-Returns a collection of collections. This means that item in the window set is a collection of edges.
-
-alignment_unit optionally aligns the windows to the specified unit. "Unaligned" can be passed for no alignment.
-If unspecified (i.e. by default), alignment is done on the smallest unit of time in the step.
-e.g. "1 month and 1 day" will align at the start of the day.
-
-
-
-
-step
-WindowDuration !
-
-
-
-alignmentUnit
-AlignmentUnit
-
-
-
-window
-Edges !
-
-
-Creates a view of the Edge including all events between the specified start (inclusive) and end (exclusive).
-
-
-
-
-start
-Int !
-
-
-
-end
-Int !
-
-
-
-at
-Edges !
-
-
-Creates a view of the Edge including all events at a specified time.
-
-
-
-
-time
-Int !
-
-
-
-latest
-Edges !
-
-
-
-snapshotAt
-Edges !
-
-
-Creates a view of the Edge including all events that are valid at time. This is equivalent to before(time + 1) for Graph and at(time) for PersistentGraph.
-
-
-
-
-time
-Int !
-
-
-
-snapshotLatest
-Edges !
-
-
-Creates a view of the Edge including all events that are valid at the latest time. This is equivalent to a no-op for Graph and latest() for PersistentGraph.
-
-
-
-
-before
-Edges !
-
-
-Creates a view of the Edge including all events before a specified end (exclusive).
-
-
-
-
-time
-Int !
-
-
-
-after
-Edges !
-
-
-Creates a view of the Edge including all events after a specified start (exclusive).
-
-
-
-
-time
-Int !
-
-
-
-shrinkWindow
-Edges !
-
-
-Shrinks both the start and end of the window.
-
-
-
-
-start
-Int !
-
-
-
-end
-Int !
-
-
-
-shrinkStart
-Edges !
-
-
-Set the start of the window.
-
-
-
-
-start
-Int !
-
-
-
-shrinkEnd
-Edges !
-
-
-Set the end of the window.
-
-
-
-
-end
-Int !
-
-
-
-applyViews
-Edges !
-
-
-Takes a specified selection of views and applies them in order given.
-
-
-
-
-views
-[EdgesViewCollection !]!
-
-
-
-explode
-Edges !
-
-
-Returns an edge object for each update within the original edge.
-
-
-
-
-explodeLayers
-Edges !
-
-
-Returns an edge object for each layer within the original edge.
-
-Each new edge object contains only updates from the respective layers.
-
-
-
-
-sorted
-Edges !
-
-
-Specify a sort order from: source, destination, property, time. You can also reverse the ordering.
-
-
-
-
-sortBys
-[EdgeSortBy !]!
-
-
-
-start
-Int
-
-
-Returns the start time of the window or none if there is no window.
-
-
-
-
-end
-Int
-
-
-Returns the end time of the window or none if there is no window.
-
-
-
-
-count
-Int !
-
-
-Returns the number of edges.
-
-Returns:
-int:
-
-
-
-
-page
-[Edge !]!
-
-
-Fetch one page with a number of items up to a specified limit, optionally offset by a specified amount.
-The page_index sets the number of pages to skip (defaults to 0).
-
-For example, if page(5, 2, 1) is called, a page with 5 items, offset by 11 items (2 pages of 5 + 1),
-will be returned.
-
-
-
-
-limit
-Int !
-
-
-
-offset
-Int
-
-
-
-pageIndex
-Int
-
-
-
-list
-[Edge !]!
-
-
-Returns a list of all objects in the current selection of the collection. You should filter filter the collection first then call list.
-
-
-
-
-
-
-### EdgesWindowSet
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-count
-Int !
-
-
-
-page
-[Edges !]!
-
-
-Fetch one page with a number of items up to a specified limit, optionally offset by a specified amount.
-The page_index sets the number of pages to skip (defaults to 0).
-
-For example, if page(5, 2, 1) is called, a page with 5 items, offset by 11 items (2 pages of 5 + 1),
-will be returned.
-
-
-
-
-limit
-Int !
-
-
-
-offset
-Int
-
-
-
-pageIndex
-Int
-
-
-
-list
-[Edges !]!
-
-
-
-
-
-### Graph
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-uniqueLayers
-[String !]!
-
-
-Returns the names of all layers in the graphview.
-
-
-
-
-defaultLayer
-Graph !
-
-
-Returns a view containing only the default layer.
-
-
-
-
-layers
-Graph !
-
-
-Returns a view containing all the specified layers.
-
-
-
-
-names
-[String !]!
-
-
-
-excludeLayers
-Graph !
-
-
-Returns a view containing all layers except the specified excluded layers.
-
-
-
-
-names
-[String !]!
-
-
-
-layer
-Graph !
-
-
-Returns a view containing the layer specified.
-
-
-
-
-name
-String !
-
-
-
-excludeLayer
-Graph !
-
-
-Returns a view containing all layers except the specified excluded layer.
-
-
-
-
-name
-String !
-
-
-
-subgraph
-Graph !
-
-
-Returns a subgraph of a specified set of nodes which contains only the edges that connect nodes of the subgraph to each other.
-
-
-
-
-nodes
-[String !]!
-
-
-
-valid
-Graph !
-
-
-Returns a view of the graph that only includes valid edges.
-
-
-
-
-subgraphNodeTypes
-Graph !
-
-
-Returns a subgraph filtered by the specified node types.
-
-
-
-
-nodeTypes
-[String !]!
-
-
-
-excludeNodes
-Graph !
-
-
-Returns a subgraph containing all nodes except the specified excluded nodes.
-
-
-
-
-nodes
-[String !]!
-
-
-
-rolling
-GraphWindowSet !
-
-
-Creates a rolling window with the specified window size and an optional step.
-
-A rolling window is a window that moves forward by step size at each iteration.
-
-alignment_unit optionally aligns the windows to the specified unit. "Unaligned" can be passed for no alignment.
-If unspecified (i.e. by default), alignment is done on the smallest unit of time in the step (or window if no step is passed).
-e.g. "1 month and 1 day" will align at the start of the day.
-Note that passing a step larger than window while alignment_unit is not "Unaligned" may lead to some entries appearing before
-the start of the first window and/or after the end of the last window (i.e. not included in any window).
-
-
-
-
-window
-WindowDuration !
-
-
-
-step
-WindowDuration
-
-
-
-alignmentUnit
-AlignmentUnit
-
-
-
-expanding
-GraphWindowSet !
-
-
-Creates an expanding window with the specified step size.
-
-An expanding window is a window that grows by step size at each iteration.
-
-alignment_unit optionally aligns the windows to the specified unit. "Unaligned" can be passed for no alignment.
-If unspecified (i.e. by default), alignment is done on the smallest unit of time in the step.
-e.g. "1 month and 1 day" will align at the start of the day.
-
-
-
-
-step
-WindowDuration !
-
-
-
-alignmentUnit
-AlignmentUnit
-
-
-
-window
-Graph !
-
-
-Return a graph containing only the activity between start and end, by default raphtory stores times in milliseconds from the unix epoch.
-
-
-
-
-start
-Int !
-
-
-
-end
-Int !
-
-
-
-at
-Graph !
-
-
-Creates a view including all events at a specified time.
-
-
-
-
-time
-Int !
-
-
-
-latest
-Graph !
-
-
-Creates a view including all events at the latest time.
-
-
-
-
-snapshotAt
-Graph !
-
-
-Create a view including all events that are valid at the specified time.
-
-
-
-
-time
-Int !
-
-
-
-snapshotLatest
-Graph !
-
-
-Create a view including all events that are valid at the latest time.
-
-
-
-
-before
-Graph !
-
-
-Create a view including all events before a specified end (exclusive).
-
-
-
-
-time
-Int !
-
-
-
-after
-Graph !
-
-
-Create a view including all events after a specified start (exclusive).
-
-
-
-
-time
-Int !
-
-
-
-shrinkWindow
-Graph !
-
-
-Shrink both the start and end of the window.
-
-
-
-
-start
-Int !
-
-
-
-end
-Int !
-
-
-
-shrinkStart
-Graph !
-
-
-Set the start of the window to the larger of the specified value or current start.
-
-
-
-
-start
-Int !
-
-
-
-shrinkEnd
-Graph !
-
-
-Set the end of the window to the smaller of the specified value or current end.
-
-
-
-
-end
-Int !
-
-
-
-created
-Int !
-
-
-Returns the timestamp for the creation of the graph.
-
-
-
-
-lastOpened
-Int !
-
-
-Returns the graph's last opened timestamp according to system time.
-
-
-
-
-lastUpdated
-Int !
-
-
-Returns the graph's last updated timestamp.
-
-
-
-
-earliestTime
-Int
-
-
-Returns the timestamp of the earliest activity in the graph.
-
-
-
-
-latestTime
-Int
-
-
-Returns the timestamp of the latest activity in the graph.
-
-
-
-
-start
-Int
-
-
-Returns the start time of the window. Errors if there is no window.
-
-
-
-
-end
-Int
-
-
-Returns the end time of the window. Errors if there is no window.
-
-
-
-
-earliestEdgeTime
-Int
-
-
-Returns the earliest time that any edge in this graph is valid.
-
-
-
-
-includeNegative
-Boolean
-
-
-
-latestEdgeTime
-Int
-
-
-/// Returns the latest time that any edge in this graph is valid.
-
-
-
-
-includeNegative
-Boolean
-
-
-
-countEdges
-Int !
-
-
-Returns the number of edges in the graph.
-
-Returns:
-int:
-
-
-
-
-countTemporalEdges
-Int !
-
-
-Returns the number of temporal edges in the graph.
-
-
-
-
-countNodes
-Int !
-
-
-Returns the number of nodes in the graph.
-
-Optionally takes a list of node ids to return a subset.
-
-
-
-
-hasNode
-Boolean !
-
-
-Returns true if the graph contains the specified node.
-
-
-
-
-name
-String !
-
-
-
-hasEdge
-Boolean !
-
-
-Returns true if the graph contains the specified edge. Edges are specified by providing a source and destination node id. You can restrict the search to a specified layer.
-
-
-
-
-src
-String !
-
-
-
-dst
-String !
-
-
-
-layer
-String
-
-
-
-node
-Node
-
-
-Gets the node with the specified id.
-
-
-
-
-name
-String !
-
-
-
-nodes
-Nodes !
-
-
-Gets (optionally a subset of) the nodes in the graph.
-
-
-
-
-ids
-[String !]
-
-
-
-edge
-Edge
-
-
-Gets the edge with the specified source and destination nodes.
-
-
-
-
-src
-String !
-
-
-
-dst
-String !
-
-
-
-edges
-Edges !
-
-
-Gets the edges in the graph.
-
-
-
-
-properties
-Properties !
-
-
-Returns the properties of the graph.
-
-
-
-
-metadata
-Metadata !
-
-
-Returns the metadata of the graph.
-
-
-
-
-name
-String !
-
-
-Returns the graph name.
-
-
-
-
-path
-String !
-
-
-Returns path of graph.
-
-
-
-
-namespace
-String !
-
-
-Returns namespace of graph.
-
-
-
-
-schema
-GraphSchema !
-
-
-Returns the graph schema.
-
-
-
-
-algorithms
-GraphAlgorithmPlugin !
-
-
-
-sharedNeighbours
-[Node !]!
-
-
-
-selectedNodes
-[String !]!
-
-
-
-exportTo
-Boolean !
-
-
-Export all nodes and edges from this graph view to another existing graph
-
-
-
-
-path
-String !
-
-
-
-nodeFilter
-Graph !
-
-
-
-filter
-NodeFilter !
-
-
-
-edgeFilter
-Graph !
-
-
-
-filter
-EdgeFilter !
-
-
-
-getIndexSpec
-IndexSpec !
-
-
-(Experimental) Get index specification.
-
-
-
-
-searchNodes
-[Node !]!
-
-
-(Experimental) Searches for nodes which match the given filter expression.
-
-Uses Tantivy's exact search.
-
-
-
-
-filter
-NodeFilter !
-
-
-
-limit
-Int !
-
-
-
-offset
-Int !
-
-
-
-searchEdges
-[Edge !]!
-
-
-(Experimental) Searches the index for edges which match the given filter expression.
-
-Uses Tantivy's exact search.
-
-
-
-
-filter
-EdgeFilter !
-
-
-
-limit
-Int !
-
-
-
-offset
-Int !
-
-
-
-applyViews
-Graph !
-
-
-Returns the specified graph view or if none is specified returns the default view.
-This allows you to specify multiple operations together.
-
-
-
-
-views
-[GraphViewCollection !]!
-
-
-
-
-
-### GraphAlgorithmPlugin
-
-
-
-### GraphSchema
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-nodes
-[NodeSchema !]!
-
-
-
-layers
-[LayerSchema !]!
-
-
-
-
-
-### GraphWindowSet
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-count
-Int !
-
-
-Returns the number of items.
-
-
-
-
-page
-[Graph !]!
-
-
-Fetch one page with a number of items up to a specified limit, optionally offset by a specified amount.
-The page_index sets the number of pages to skip (defaults to 0).
-
-For example, if page(5, 2, 1) is called, a page with 5 items, offset by 11 items (2 pages of 5 + 1),
-will be returned.
-
-
-
-
-limit
-Int !
-
-
-
-offset
-Int
-
-
-
-pageIndex
-Int
-
-
-
-list
-[Graph !]!
-
-
-
-
-
-### IndexSpec
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-nodeMetadata
-[String !]!
-
-
-Returns node metadata.
-
-
-
-
-nodeProperties
-[String !]!
-
-
-Returns node properties.
-
-
-
-
-edgeMetadata
-[String !]!
-
-
-Returns edge metadata.
-
-
-
-
-edgeProperties
-[String !]!
-
-
-Returns edge properties.
-
-
-
-
-
-
-### LayerSchema
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-name
-String !
-
-
-Returns the name of the layer with this schema
-
-
-
-
-edges
-[EdgeSchema !]!
-
-
-Returns the list of edge schemas for this edge layer
-
-
-
-
-
-
-### MetaGraph
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-name
-String
-
-
-Returns the graph name.
-
-
-
-
-path
-String !
-
-
-Returns path of graph.
-
-
-
-
-created
-Int !
-
-
-Returns the timestamp for the creation of the graph.
-
-
-
-
-lastOpened
-Int !
-
-
-Returns the graph's last opened timestamp according to system time.
-
-
-
-
-lastUpdated
-Int !
-
-
-Returns the graph's last updated timestamp.
-
-
-
-
-nodeCount
-Int !
-
-
-Returns the number of nodes in the graph.
-
-
-
-
-edgeCount
-Int !
-
-
-Returns the number of edges in the graph.
-
-Returns:
-int:
-
-
-
-
-metadata
-[Property !]!
-
-
-Returns the metadata of the graph.
-
-
-
-
-
-
-### Metadata
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-get
-Property
-
-
-Get metadata value matching the specified key.
-
-
-
-
-key
-String !
-
-
-
-contains
-Boolean !
-
-
-/// Check if the key is in the metadata.
-
-
-
-
-key
-String !
-
-
-
-keys
-[String !]!
-
-
-Return all metadata keys.
-
-
-
-
-values
-[Property !]!
-
-
-/// Return all metadata values.
-
-
-
-
-keys
-[String !]
-
-
-
-
-
-### MutableEdge
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-success
-Boolean !
-
-
-Use to check if adding the edge was successful.
-
-
-
-
-edge
-Edge !
-
-
-Get the non-mutable edge for querying.
-
-
-
-
-src
-MutableNode !
-
-
-Get the mutable source node of the edge.
-
-
-
-
-dst
-MutableNode !
-
-
-Get the mutable destination node of the edge.
-
-
-
-
-delete
-Boolean !
-
-
-Mark the edge as deleted at time time.
-
-
-
-
-time
-Int !
-
-
-
-layer
-String
-
-
-
-addMetadata
-Boolean !
-
-
-Add metadata to the edge (errors if the value already exists).
-
-If this is called after add_edge, the layer is inherited from the add_edge and does not
-need to be specified again.
-
-
-
-
-properties
-[PropertyInput !]!
-
-
-
-layer
-String
-
-
-
-updateMetadata
-Boolean !
-
-
-Update metadata of the edge (existing values are overwritten).
-
-If this is called after add_edge, the layer is inherited from the add_edge and does not
-need to be specified again.
-
-
-
-
-properties
-[PropertyInput !]!
-
-
-
-layer
-String
-
-
-
-addUpdates
-Boolean !
-
-
-Add temporal property updates to the edge.
-
-If this is called after add_edge, the layer is inherited from the add_edge and does not
-need to be specified again.
-
-
-
-
-time
-Int !
-
-
-
-properties
-[PropertyInput !]
-
-
-
-layer
-String
-
-
-
-
-
-### MutableGraph
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-graph
-Graph !
-
-
-Get the non-mutable graph.
-
-
-
-
-node
-MutableNode
-
-
-Get mutable existing node.
-
-
-
-
-name
-String !
-
-
-
-addNode
-MutableNode !
-
-
-Add a new node or add updates to an existing node.
-
-
-
-
-time
-Int !
-
-
-
-name
-String !
-
-
-
-properties
-[PropertyInput !]
-
-
-
-nodeType
-String
-
-
-
-createNode
-MutableNode !
-
-
-Create a new node or fail if it already exists.
-
-
-
-
-time
-Int !
-
-
-
-name
-String !
-
-
-
-properties
-[PropertyInput !]
-
-
-
-nodeType
-String
-
-
-
-addNodes
-Boolean !
-
-
-Add a batch of nodes
-
-
-
-
-nodes
-[NodeAddition !]!
-
-
-
-edge
-MutableEdge
-
-
-Get a mutable existing edge.
-
-
-
-
-src
-String !
-
-
-
-dst
-String !
-
-
-
-addEdge
-MutableEdge !
-
-
-Add a new edge or add updates to an existing edge.
-
-
-
-
-time
-Int !
-
-
-
-src
-String !
-
-
-
-dst
-String !
-
-
-
-properties
-[PropertyInput !]
-
-
-
-layer
-String
-
-
-
-addEdges
-Boolean !
-
-
-Add a batch of edges
-
-
-
-
-edges
-[EdgeAddition !]!
-
-
-
-deleteEdge
-MutableEdge !
-
-
-Mark an edge as deleted (creates the edge if it did not exist).
-
-
-
-
-time
-Int !
-
-
-
-src
-String !
-
-
-
-dst
-String !
-
-
-
-layer
-String
-
-
-
-addProperties
-Boolean !
-
-
-Add temporal properties to graph.
-
-
-
-
-t
-Int !
-
-
-
-properties
-[PropertyInput !]!
-
-
-
-addMetadata
-Boolean !
-
-
-Add metadata to graph (errors if the property already exists).
-
-
-
-
-properties
-[PropertyInput !]!
-
-
-
-updateMetadata
-Boolean !
-
-
-Update metadata of the graph (overwrites existing values).
-
-
-
-
-properties
-[PropertyInput !]!
-
-
-
-
-
-### MutableNode
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-success
-Boolean !
-
-
-Use to check if adding the node was successful.
-
-
-
-
-node
-Node !
-
-
-Get the non-mutable Node.
-
-
-
-
-addMetadata
-Boolean !
-
-
-Add metadata to the node (errors if the property already exists).
-
-
-
-
-properties
-[PropertyInput !]!
-
-
-
-setNodeType
-Boolean !
-
-
-Set the node type (errors if the node already has a non-default type).
-
-
-
-
-newType
-String !
-
-
-
-updateMetadata
-Boolean !
-
-
-Update metadata of the node (overwrites existing property values).
-
-
-
-
-properties
-[PropertyInput !]!
-
-
-
-addUpdates
-Boolean !
-
-
-Add temporal property updates to the node.
-
-
-
-
-time
-Int !
-
-
-
-properties
-[PropertyInput !]
-
-
-
-
-
-### MutationPlugin
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-NoOps
-String !
-
-
-
-
-
-### Namespace
-
-
-
-### Node
-
-Raphtory graph node.
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-id
-String !
-
-
-Returns the unique id of the node.
-
-
-
-
-name
-String !
-
-
-Returns the name of the node.
-
-
-
-
-defaultLayer
-Node !
-
-
-Return a view of the node containing only the default layer.
-
-
-
-
-layers
-Node !
-
-
-Return a view of node containing all layers specified.
-
-
-
-
-names
-[String !]!
-
-
-
-excludeLayers
-Node !
-
-
-Returns a collection containing nodes belonging to all layers except the excluded list of layers.
-
-
-
-
-names
-[String !]!
-
-
-
-layer
-Node !
-
-
-Returns a collection containing nodes belonging to the specified layer.
-
-
-
-
-name
-String !
-
-
-
-excludeLayer
-Node !
-
-
-Returns a collection containing nodes belonging to all layers except the excluded layer.
-
-
-
-
-name
-String !
-
-
-
-rolling
-NodeWindowSet !
-
-
-Creates a WindowSet with the specified window size and optional step using a rolling window.
-
-A rolling window is a window that moves forward by step size at each iteration.
-
-alignment_unit optionally aligns the windows to the specified unit. "Unaligned" can be passed for no alignment.
-If unspecified (i.e. by default), alignment is done on the smallest unit of time in the step (or window if no step is passed).
-e.g. "1 month and 1 day" will align at the start of the day.
-Note that passing a step larger than window while alignment_unit is not "Unaligned" may lead to some entries appearing before
-the start of the first window and/or after the end of the last window (i.e. not included in any window).
-
-
-
-
-window
-WindowDuration !
-
-
-
-step
-WindowDuration
-
-
-
-alignmentUnit
-AlignmentUnit
-
-
-
-expanding
-NodeWindowSet !
-
-
-Creates a WindowSet with the specified step size using an expanding window.
-
-An expanding window is a window that grows by step size at each iteration.
-
-alignment_unit optionally aligns the windows to the specified unit. "Unaligned" can be passed for no alignment.
-If unspecified (i.e. by default), alignment is done on the smallest unit of time in the step.
-e.g. "1 month and 1 day" will align at the start of the day.
-
-
-
-
-step
-WindowDuration !
-
-
-
-alignmentUnit
-AlignmentUnit
-
-
-
-window
-Node !
-
-
-Create a view of the node including all events between the specified start (inclusive) and end (exclusive).
-
-
-
-
-start
-Int !
-
-
-
-end
-Int !
-
-
-
-at
-Node !
-
-
-Create a view of the node including all events at a specified time.
-
-
-
-
-time
-Int !
-
-
-
-latest
-Node !
-
-
-Create a view of the node including all events at the latest time.
-
-
-
-
-snapshotAt
-Node !
-
-
-Create a view of the node including all events that are valid at the specified time.
-
-
-
-
-time
-Int !
-
-
-
-snapshotLatest
-Node !
-
-
-Create a view of the node including all events that are valid at the latest time.
-
-
-
-
-before
-Node !
-
-
-Create a view of the node including all events before specified end time (exclusive).
-
-
-
-
-time
-Int !
-
-
-
-after
-Node !
-
-
-Create a view of the node including all events after the specified start time (exclusive).
-
-
-
-
-time
-Int !
-
-
-
-shrinkWindow
-Node !
-
-
-Shrink a Window to a specified start and end time, if these are earlier and later than the current start and end respectively.
-
-
-
-
-start
-Int !
-
-
-
-end
-Int !
-
-
-
-shrinkStart
-Node !
-
-
-Set the start of the window to the larger of a specified start time and self.start().
-
-
-
-
-start
-Int !
-
-
-
-shrinkEnd
-Node !
-
-
-Set the end of the window to the smaller of a specified end and self.end().
-
-
-
-
-end
-Int !
-
-
-
-applyViews
-Node !
-
-
-
-views
-[NodeViewCollection !]!
-
-
-
-earliestTime
-Int
-
-
-Returns the earliest time that the node exists.
-
-
-
-
-firstUpdate
-Int
-
-
-Returns the time of the first update made to the node.
-
-
-
-
-latestTime
-Int
-
-
-Returns the latest time that the node exists.
-
-
-
-
-lastUpdate
-Int
-
-
-Returns the time of the last update made to the node.
-
-
-
-
-start
-Int
-
-
-Gets the start time for the window. Errors if there is no window.
-
-
-
-
-end
-Int
-
-
-Gets the end time for the window. Errors if there is no window.
-
-
-
-
-history
-[Int !]!
-
-
-Returns the history of a node, including node additions and changes made to node.
-
-
-
-
-edgeHistoryCount
-Int !
-
-
-Get the number of edge events for this node.
-
-
-
-
-isActive
-Boolean !
-
-
-Check if the node is active and it's history is not empty.
-
-
-
-
-nodeType
-String
-
-
-Returns the type of node.
-
-
-
-
-properties
-Properties !
-
-
-Returns the properties of the node.
-
-
-
-
-metadata
-Metadata !
-
-
-Returns the metadata of the node.
-
-
-
-
-degree
-Int !
-
-
-Returns the number of unique counter parties for this node.
-
-
-
-
-outDegree
-Int !
-
-
-Returns the number edges with this node as the source.
-
-
-
-
-inDegree
-Int !
-
-
-Returns the number edges with this node as the destination.
-
-
-
-
-inComponent
-Nodes !
-
-
-
-outComponent
-Nodes !
-
-
-
-edges
-Edges !
-
-
-Returns all connected edges.
-
-
-
-
-outEdges
-Edges !
-
-
-Returns outgoing edges.
-
-
-
-
-inEdges
-Edges !
-
-
-Returns incoming edges.
-
-
-
-
-neighbours
-PathFromNode !
-
-
-Returns neighbouring nodes.
-
-
-
-
-inNeighbours
-PathFromNode !
-
-
-Returns the number of neighbours that have at least one in-going edge to this node.
-
-
-
-
-outNeighbours
-PathFromNode !
-
-
-Returns the number of neighbours that have at least one out-going edge from this node.
-
-
-
-
-nodeFilter
-Node !
-
-
-
-filter
-NodeFilter !
-
-
-
-
-
-### NodeSchema
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-typeName
-String !
-
-
-
-properties
-[PropertySchema !]!
-
-
-Returns the list of property schemas for this node
-
-
-
-
-metadata
-[PropertySchema !]!
-
-
-
-
-
-### NodeWindowSet
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-count
-Int !
-
-
-
-page
-[Node !]!
-
-
-Fetch one page with a number of items up to a specified limit, optionally offset by a specified amount.
-The page_index sets the number of pages to skip (defaults to 0).
-
-For example, if page(5, 2, 1) is called, a page with 5 items, offset by 11 items (2 pages of 5 + 1),
-will be returned.
-
-
-
-
-limit
-Int !
-
-
-
-offset
-Int
-
-
-
-pageIndex
-Int
-
-
-
-list
-[Node !]!
-
-
-
-
-
-### Nodes
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-defaultLayer
-Nodes !
-
-
-Return a view of the nodes containing only the default edge layer.
-
-
-
-
-layers
-Nodes !
-
-
-Return a view of the nodes containing all layers specified.
-
-
-
-
-names
-[String !]!
-
-
-
-excludeLayers
-Nodes !
-
-
-Return a view of the nodes containing all layers except those specified.
-
-
-
-
-names
-[String !]!
-
-
-
-layer
-Nodes !
-
-
-Return a view of the nodes containing the specified layer.
-
-
-
-
-name
-String !
-
-
-
-excludeLayer
-Nodes !
-
-
-Return a view of the nodes containing all layers except those specified.
-
-
-
-
-name
-String !
-
-
-
-rolling
-NodesWindowSet !
-
-
-Creates a WindowSet with the specified window size and optional step using a rolling window.
-
-A rolling window is a window that moves forward by step size at each iteration.
-
-alignment_unit optionally aligns the windows to the specified unit. "Unaligned" can be passed for no alignment.
-If unspecified (i.e. by default), alignment is done on the smallest unit of time in the step (or window if no step is passed).
-e.g. "1 month and 1 day" will align at the start of the day.
-Note that passing a step larger than window while alignment_unit is not "Unaligned" may lead to some entries appearing before
-the start of the first window and/or after the end of the last window (i.e. not included in any window).
-
-
-
-
-window
-WindowDuration !
-
-
-
-step
-WindowDuration
-
-
-
-alignmentUnit
-AlignmentUnit
-
-
-
-expanding
-NodesWindowSet !
-
-
-Creates a WindowSet with the specified step size using an expanding window.
-
-An expanding window is a window that grows by step size at each iteration.
-
-alignment_unit optionally aligns the windows to the specified unit. "Unaligned" can be passed for no alignment.
-If unspecified (i.e. by default), alignment is done on the smallest unit of time in the step.
-e.g. "1 month and 1 day" will align at the start of the day.
-
-
-
-
-step
-WindowDuration !
-
-
-
-alignmentUnit
-AlignmentUnit
-
-
-
-window
-Nodes !
-
-
-Create a view of the node including all events between the specified start (inclusive) and end (exclusive).
-
-
-
-
-start
-Int !
-
-
-
-end
-Int !
-
-
-
-at
-Nodes !
-
-
-Create a view of the nodes including all events at a specified time.
-
-
-
-
-time
-Int !
-
-
-
-latest
-Nodes !
-
-
-Create a view of the nodes including all events at the latest time.
-
-
-
-
-snapshotAt
-Nodes !
-
-
-Create a view of the nodes including all events that are valid at the specified time.
-
-
-
-
-time
-Int !
-
-
-
-snapshotLatest
-Nodes !
-
-
-Create a view of the nodes including all events that are valid at the latest time.
-
-
-
-
-before
-Nodes !
-
-
-Create a view of the nodes including all events before specified end time (exclusive).
-
-
-
-
-time
-Int !
-
-
-
-after
-Nodes !
-
-
-Create a view of the nodes including all events after the specified start time (exclusive).
-
-
-
-
-time
-Int !
-
-
-
-shrinkWindow
-Nodes !
-
-
-Shrink both the start and end of the window.
-
-
-
-
-start
-Int !
-
-
-
-end
-Int !
-
-
-
-shrinkStart
-Nodes !
-
-
-Set the start of the window to the larger of a specified start time and self.start().
-
-
-
-
-start
-Int !
-
-
-
-shrinkEnd
-Nodes !
-
-
-Set the end of the window to the smaller of a specified end and self.end().
-
-
-
-
-end
-Int !
-
-
-
-typeFilter
-Nodes !
-
-
-Filter nodes by node type.
-
-
-
-
-nodeTypes
-[String !]!
-
-
-
-nodeFilter
-Nodes !
-
-
-Returns a view of the node types.
-
-
-
-
-filter
-NodeFilter !
-
-
-
-applyViews
-Nodes !
-
-
-
-views
-[NodesViewCollection !]!
-
-
-
-sorted
-Nodes !
-
-
-
-sortBys
-[NodeSortBy !]!
-
-
-
-start
-Int
-
-
-Returns the start time of the window. Errors if there is no window.
-
-
-
-
-end
-Int
-
-
-Returns the end time of the window. Errors if there is no window.
-
-
-
-
-count
-Int !
-
-
-
-page
-[Node !]!
-
-
-Fetch one page with a number of items up to a specified limit, optionally offset by a specified amount.
-The page_index sets the number of pages to skip (defaults to 0).
-
-For example, if page(5, 2, 1) is called, a page with 5 items, offset by 11 items (2 pages of 5 + 1),
-will be returned.
-
-
-
-
-limit
-Int !
-
-
-
-offset
-Int
-
-
-
-pageIndex
-Int
-
-
-
-list
-[Node !]!
-
-
-
-ids
-[String !]!
-
-
-Returns a view of the node ids.
-
-
-
-
-
-
-### NodesWindowSet
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-count
-Int !
-
-
-
-page
-[Nodes !]!
-
-
-Fetch one page with a number of items up to a specified limit, optionally offset by a specified amount.
-The page_index sets the number of pages to skip (defaults to 0).
-
-For example, if page(5, 2, 1) is called, a page with 5 items, offset by 11 items (2 pages of 5 + 1),
-will be returned.
-
-
-
-
-limit
-Int !
-
-
-
-offset
-Int
-
-
-
-pageIndex
-Int
-
-
-
-list
-[Nodes !]!
-
-
-
-
-
-### PagerankOutput
-
-PageRank score.
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-
-String !
-
-
-
-
-Float !
-
-
-
-
-
-### PathFromNode
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-layers
-PathFromNode !
-
-
-Returns a view of PathFromNode containing the specified layer, errors if the layer does not exist.
-
-
-
-
-names
-[String !]!
-
-
-
-excludeLayers
-PathFromNode !
-
-
-Return a view of PathFromNode containing all layers except the specified excluded layers, errors if any of the layers do not exist.
-
-
-
-
-names
-[String !]!
-
-
-
-layer
-PathFromNode !
-
-
-Return a view of PathFromNode containing the layer specified layer, errors if the layer does not exist.
-
-
-
-
-name
-String !
-
-
-
-excludeLayer
-PathFromNode !
-
-
-Return a view of PathFromNode containing all layers except the specified excluded layers, errors if any of the layers do not exist.
-
-
-
-
-name
-String !
-
-
-
-rolling
-PathFromNodeWindowSet !
-
-
-Creates a WindowSet with the given window size and optional step using a rolling window.
-
-A rolling window is a window that moves forward by step size at each iteration.
-
-alignment_unit optionally aligns the windows to the specified unit. "Unaligned" can be passed for no alignment.
-If unspecified (i.e. by default), alignment is done on the smallest unit of time in the step (or window if no step is passed).
-e.g. "1 month and 1 day" will align at the start of the day.
-Note that passing a step larger than window while alignment_unit is not "Unaligned" may lead to some entries appearing before
-the start of the first window and/or after the end of the last window (i.e. not included in any window).
-
-
-
-
-window
-WindowDuration !
-
-
-
-step
-WindowDuration
-
-
-
-alignmentUnit
-AlignmentUnit
-
-
-
-expanding
-PathFromNodeWindowSet !
-
-
-Creates a WindowSet with the given step size using an expanding window.
-
-An expanding window is a window that grows by step size at each iteration.
-
-alignment_unit optionally aligns the windows to the specified unit. "Unaligned" can be passed for no alignment.
-If unspecified (i.e. by default), alignment is done on the smallest unit of time in the step.
-e.g. "1 month and 1 day" will align at the start of the day.
-
-
-
-
-step
-WindowDuration !
-
-
-
-alignmentUnit
-AlignmentUnit
-
-
-
-window
-PathFromNode !
-
-
-Create a view of the PathFromNode including all events between a specified start (inclusive) and end (exclusive).
-
-
-
-
-start
-Int !
-
-
-
-end
-Int !
-
-
-
-at
-PathFromNode !
-
-
-Create a view of the PathFromNode including all events at time.
-
-
-
-
-time
-Int !
-
-
-
-snapshotLatest
-PathFromNode !
-
-
-Create a view of the PathFromNode including all events that are valid at the latest time.
-
-
-
-
-snapshotAt
-PathFromNode !
-
-
-Create a view of the PathFromNode including all events that are valid at the specified time.
-
-
-
-
-time
-Int !
-
-
-
-latest
-PathFromNode !
-
-
-Create a view of the PathFromNode including all events at the latest time.
-
-
-
-
-before
-PathFromNode !
-
-
-Create a view of the PathFromNode including all events before the specified end (exclusive).
-
-
-
-
-time
-Int !
-
-
-
-after
-PathFromNode !
-
-
-Create a view of the PathFromNode including all events after the specified start (exclusive).
-
-
-
-
-time
-Int !
-
-
-
-shrinkWindow
-PathFromNode !
-
-
-Shrink both the start and end of the window.
-
-
-
-
-start
-Int !
-
-
-
-end
-Int !
-
-
-
-shrinkStart
-PathFromNode !
-
-
-Set the start of the window to the larger of the specified start and self.start().
-
-
-
-
-start
-Int !
-
-
-
-shrinkEnd
-PathFromNode !
-
-
-Set the end of the window to the smaller of the specified end and self.end().
-
-
-
-
-end
-Int !
-
-
-
-typeFilter
-PathFromNode !
-
-
-Filter nodes by type.
-
-
-
-
-nodeTypes
-[String !]!
-
-
-
-start
-Int
-
-
-Returns the earliest time that this PathFromNode is valid or None if the PathFromNode is valid for all times.
-
-
-
-
-end
-Int
-
-
-Returns the latest time that this PathFromNode is valid or None if the PathFromNode is valid for all times.
-
-
-
-
-count
-Int !
-
-
-
-page
-[Node !]!
-
-
-Fetch one page with a number of items up to a specified limit, optionally offset by a specified amount.
-The page_index sets the number of pages to skip (defaults to 0).
-
-For example, if page(5, 2, 1) is called, a page with 5 items, offset by 11 items (2 pages of 5 + 1),
-will be returned.
-
-
-
-
-limit
-Int !
-
-
-
-offset
-Int
-
-
-
-pageIndex
-Int
-
-
-
-list
-[Node !]!
-
-
-
-ids
-[String !]!
-
-
-Returns the node ids.
-
-
-
-
-applyViews
-PathFromNode !
-
-
-Takes a specified selection of views and applies them in given order.
-
-
-
-
-views
-[PathFromNodeViewCollection !]!
-
-
-
-
-
-### PathFromNodeWindowSet
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-count
-Int !
-
-
-
-page
-[PathFromNode !]!
-
-
-Fetch one page with a number of items up to a specified limit, optionally offset by a specified amount.
-The page_index sets the number of pages to skip (defaults to 0).
-
-For example, if page(5, 2, 1) is called, a page with 5 items, offset by 11 items (2 pages of 5 + 1),
-will be returned.
-
-
-
-
-limit
-Int !
-
-
-
-offset
-Int
-
-
-
-pageIndex
-Int
-
-
-
-list
-[PathFromNode !]!
-
-
-
-
-
-### Properties
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-get
-Property
-
-
-Get property value matching the specified key.
-
-
-
-
-key
-String !
-
-
-
-contains
-Boolean !
-
-
-Check if the key is in the properties.
-
-
-
-
-key
-String !
-
-
-
-keys
-[String !]!
-
-
-Return all property keys.
-
-
-
-
-values
-[Property !]!
-
-
-Return all property values.
-
-
-
-
-keys
-[String !]
-
-
-
-temporal
-TemporalProperties !
-
-
-
-
-
-### Property
-
-
-
-### PropertySchema
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-key
-String !
-
-
-
-propertyType
-String !
-
-
-
-variants
-[String !]!
-
-
-
-
-
-### PropertyTuple
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-time
-Int !
-
-
-
-asString
-String !
-
-
-
-value
-PropertyOutput !
-
-
-
-
-
-### QueryPlugin
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-NoOps
-String !
-
-
-
-
-
-### ShortestPathOutput
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-target
-String !
-
-
-
-nodes
-[String !]!
-
-
-
-
-
-### TemporalProperties
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-get
-TemporalProperty
-
-
-Get property value matching the specified key.
-
-
-
-
-key
-String !
-
-
-
-contains
-Boolean !
-
-
-Check if the key is in the properties.
-
-
-
-
-key
-String !
-
-
-
-keys
-[String !]!
-
-
-Return all property keys.
-
-
-
-
-values
-[TemporalProperty !]!
-
-
-Return all property values.
-
-
-
-
-keys
-[String !]
-
-
-
-
-
-### TemporalProperty
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-key
-String !
-
-
-Key of a property.
-
-
-
-
-history
-[Int !]!
-
-
-
-values
-[String !]!
-
-
-Return the values of the properties.
-
-
-
-
-at
-String
-
-
-
-t
-Int !
-
-
-
-latest
-String
-
-
-
-unique
-[String !]!
-
-
-
-orderedDedupe
-[PropertyTuple !]!
-
-
-
-latestTime
-Boolean !
-
-
-
-
-
-### VectorSelection
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-nodes
-[Node !]!
-
-
-Returns a list of nodes in the current selection.
-
-
-
-
-edges
-[Edge !]!
-
-
-Returns a list of edges in the current selection.
-
-
-
-
-getDocuments
-[Document !]!
-
-
-Returns a list of documents in the current selection.
-
-
-
-
-addNodes
-VectorSelection !
-
-
-Adds all the documents associated with the specified nodes to the current selection.
-
-Documents added by this call are assumed to have a score of 0.
-
-
-
-
-nodes
-[String !]!
-
-
-
-addEdges
-VectorSelection !
-
-
-Adds all the documents associated with the specified edges to the current selection.
-
-Documents added by this call are assumed to have a score of 0.
-
-
-
-
-edges
-[InputEdge !]!
-
-
-
-expand
-VectorSelection !
-
-
-Add all the documents a specified number of hops away to the selection.
-
-Two documents A and B are considered to be 1 hop away of each other if they are on the same entity or if they are on the same node and edge pair.
-
-
-
-
-hops
-Int !
-
-
-
-window
-Window
-
-
-
-expandEntitiesBySimilarity
-VectorSelection !
-
-
-Adds documents, from the set of one hop neighbours to the current selection, to the selection based on their similarity score with the specified query. This function loops so that the set of one hop neighbours expands on each loop and number of documents added is determined by the specified limit.
-
-
-
-
-query
-String !
-
-
-
-limit
-Int !
-
-
-
-window
-Window
-
-
-
-expandNodesBySimilarity
-VectorSelection !
-
-
-Add the adjacent nodes with higher score for query to the selection up to a specified limit. This function loops like expand_entities_by_similarity but is restricted to nodes.
-
-
-
-
-query
-String !
-
-
-
-limit
-Int !
-
-
-
-window
-Window
-
-
-
-expandEdgesBySimilarity
-VectorSelection !
-
-
-Add the adjacent edges with higher score for query to the selection up to a specified limit. This function loops like expand_entities_by_similarity but is restricted to edges.
-
-
-
-
-query
-String !
-
-
-
-limit
-Int !
-
-
-
-window
-Window
-
-
-
-
-
-### VectorisedGraph
-
-
-
-
-Field
-Argument
-Type
-Description
-
-
-
-
-emptySelection
-VectorSelection !
-
-
-Returns an empty selection of documents.
-
-
-
-
-entitiesBySimilarity
-VectorSelection !
-
-
-Search the top scoring entities according to a specified query returning no more than a specified limit of entities.
-
-
-
-
-query
-String !
-
-
-
-limit
-Int !
-
-
-
-window
-Window
-
-
-
-nodesBySimilarity
-VectorSelection !
-
-
-Search the top scoring nodes according to a specified query returning no more than a specified limit of nodes.
-
-
-
-
-query
-String !
-
-
-
-limit
-Int !
-
-
-
-window
-Window
-
-
-
-edgesBySimilarity
-VectorSelection !
-
-
-Search the top scoring edges according to a specified query returning no more than a specified limit of edges.
-
-
-
-
-query
-String !
-
-
-
-limit
-Int !
-
-
-
-window
-Window
-
-
-
-
-
-## Inputs
-
-### EdgeAddition
-
-
-
-### EdgeFilter
-
-
-
-### EdgeSortBy
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-reverse
-Boolean
-
-
-Reverse order
-
-
-
-
-src
-Boolean
-
-
-Source node
-
-
-
-
-dst
-Boolean
-
-
-Destination
-
-
-
-
-time
-SortByTime
-
-
-Time
-
-
-
-
-property
-String
-
-
-Property
-
-
-
-
-
-
-### EdgeViewCollection
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-defaultLayer
-Boolean
-
-
-Contains only the default layer.
-
-
-
-
-latest
-Boolean
-
-
-Latest time.
-
-
-
-
-snapshotLatest
-Boolean
-
-
-Snapshot at latest time.
-
-
-
-
-snapshotAt
-Int
-
-
-Snapshot at specified time.
-
-
-
-
-layers
-[String !]
-
-
-List of included layers.
-
-
-
-
-excludeLayers
-[String !]
-
-
-List of excluded layers.
-
-
-
-
-layer
-String
-
-
-Single included layer.
-
-
-
-
-excludeLayer
-String
-
-
-Single excluded layer.
-
-
-
-
-window
-Window
-
-
-Window between a start and end time.
-
-
-
-
-at
-Int
-
-
-View at a specified time.
-
-
-
-
-before
-Int
-
-
-View before a specified time (end exclusive).
-
-
-
-
-after
-Int
-
-
-View after a specified time (start exclusive).
-
-
-
-
-shrinkWindow
-Window
-
-
-Shrink a Window to a specified start and end time.
-
-
-
-
-shrinkStart
-Int
-
-
-Set the window start to a specified time.
-
-
-
-
-shrinkEnd
-Int
-
-
-Set the window end to a specified time.
-
-
-
-
-
-
-### EdgesViewCollection
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-defaultLayer
-Boolean
-
-
-Contains only the default layer.
-
-
-
-
-latest
-Boolean
-
-
-Latest time.
-
-
-
-
-snapshotLatest
-Boolean
-
-
-Snapshot at latest time.
-
-
-
-
-snapshotAt
-Int
-
-
-Snapshot at specified time.
-
-
-
-
-layers
-[String !]
-
-
-List of included layers.
-
-
-
-
-excludeLayers
-[String !]
-
-
-List of excluded layers.
-
-
-
-
-layer
-String
-
-
-Single included layer.
-
-
-
-
-excludeLayer
-String
-
-
-Single excluded layer.
-
-
-
-
-window
-Window
-
-
-Window between a start and end time.
-
-
-
-
-at
-Int
-
-
-View at a specified time.
-
-
-
-
-before
-Int
-
-
-View before a specified time (end exclusive).
-
-
-
-
-after
-Int
-
-
-View after a specified time (start exclusive).
-
-
-
-
-shrinkWindow
-Window
-
-
-Shrink a Window to a specified start and end time.
-
-
-
-
-shrinkStart
-Int
-
-
-Set the window start to a specified time.
-
-
-
-
-shrinkEnd
-Int
-
-
-Set the window end to a specified time.
-
-
-
-
-
-
-### GraphViewCollection
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-defaultLayer
-Boolean
-
-
-Contains only the default layer.
-
-
-
-
-layers
-[String !]
-
-
-List of included layers.
-
-
-
-
-excludeLayers
-[String !]
-
-
-List of excluded layers.
-
-
-
-
-layer
-String
-
-
-Single included layer.
-
-
-
-
-excludeLayer
-String
-
-
-Single excluded layer.
-
-
-
-
-subgraph
-[String !]
-
-
-Subgraph nodes.
-
-
-
-
-subgraphNodeTypes
-[String !]
-
-
-Subgraph node types.
-
-
-
-
-excludeNodes
-[String !]
-
-
-List of excluded nodes.
-
-
-
-
-valid
-Boolean
-
-
-Valid state.
-
-
-
-
-window
-Window
-
-
-Window between a start and end time.
-
-
-
-
-at
-Int
-
-
-View at a specified time.
-
-
-
-
-latest
-Boolean
-
-
-View at the latest time.
-
-
-
-
-snapshotAt
-Int
-
-
-Snapshot at specified time.
-
-
-
-
-snapshotLatest
-Boolean
-
-
-Snapshot at latest time.
-
-
-
-
-before
-Int
-
-
-View before a specified time (end exclusive).
-
-
-
-
-after
-Int
-
-
-View after a specified time (start exclusive).
-
-
-
-
-shrinkWindow
-Window
-
-
-Shrink a Window to a specified start and end time.
-
-
-
-
-shrinkStart
-Int
-
-
-Set the window start to a specified time.
-
-
-
-
-shrinkEnd
-Int
-
-
-Set the window end to a specified time.
-
-
-
-
-nodeFilter
-NodeFilter
-
-
-Node filter.
-
-
-
-
-edgeFilter
-EdgeFilter
-
-
-Edge filter.
-
-
-
-
-
-
-### IndexSpecInput
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-nodeProps
-PropsInput !
-
-
-Node properties.
-
-
-
-
-edgeProps
-PropsInput !
-
-
-Edge properties.
-
-
-
-
-
-
-### InputEdge
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-src
-String !
-
-
-Source node.
-
-
-
-
-dst
-String !
-
-
-Destination node.
-
-
-
-
-
-
-### MetadataFilterExpr
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-name
-String !
-
-
-Node metadata to compare against.
-
-
-
-
-operator
-Operator !
-
-
-Operator.
-
-
-
-
-value
-Value
-
-
-Value.
-
-
-
-
-listAgg
-ListAgg
-
-
-List aggregate
-
-
-
-
-elemQualifier
-ListElemQualifier
-
-
-List qualifier
-
-
-
-
-
-
-### NodeAddition
-
-
-
-### NodeFieldFilter
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-field
-NodeField !
-
-
-Node component to compare against.
-
-
-
-
-operator
-Operator !
-
-
-Operator filter.
-
-
-
-
-value
-Value !
-
-
-Value filter.
-
-
-
-
-
-
-### NodeFilter
-
-
-
-### NodeSortBy
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-reverse
-Boolean
-
-
-Reverse order
-
-
-
-
-id
-Boolean
-
-
-Unique Id
-
-
-
-
-time
-SortByTime
-
-
-Time
-
-
-
-
-property
-String
-
-
-Property
-
-
-
-
-
-
-### NodeViewCollection
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-defaultLayer
-Boolean
-
-
-Contains only the default layer.
-
-
-
-
-latest
-Boolean
-
-
-View at the latest time.
-
-
-
-
-snapshotLatest
-Boolean
-
-
-Snapshot at latest time.
-
-
-
-
-snapshotAt
-Int
-
-
-Snapshot at specified time.
-
-
-
-
-layers
-[String !]
-
-
-List of included layers.
-
-
-
-
-excludeLayers
-[String !]
-
-
-List of excluded layers.
-
-
-
-
-layer
-String
-
-
-Single included layer.
-
-
-
-
-excludeLayer
-String
-
-
-Single excluded layer.
-
-
-
-
-window
-Window
-
-
-Window between a start and end time.
-
-
-
-
-at
-Int
-
-
-View at a specified time.
-
-
-
-
-before
-Int
-
-
-View before a specified time (end exclusive).
-
-
-
-
-after
-Int
-
-
-View after a specified time (start exclusive).
-
-
-
-
-shrinkWindow
-Window
-
-
-Shrink a Window to a specified start and end time.
-
-
-
-
-shrinkStart
-Int
-
-
-Set the window start to a specified time.
-
-
-
-
-shrinkEnd
-Int
-
-
-Set the window end to a specified time.
-
-
-
-
-nodeFilter
-NodeFilter
-
-
-Node filter.
-
-
-
-
-
-
-### NodesViewCollection
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-defaultLayer
-Boolean
-
-
-Contains only the default layer.
-
-
-
-
-latest
-Boolean
-
-
-View at the latest time.
-
-
-
-
-snapshotLatest
-Boolean
-
-
-Snapshot at latest time.
-
-
-
-
-layers
-[String !]
-
-
-List of included layers.
-
-
-
-
-excludeLayers
-[String !]
-
-
-List of excluded layers.
-
-
-
-
-layer
-String
-
-
-Single included layer.
-
-
-
-
-excludeLayer
-String
-
-
-Single excluded layer.
-
-
-
-
-window
-Window
-
-
-Window between a start and end time.
-
-
-
-
-at
-Int
-
-
-View at a specified time.
-
-
-
-
-snapshotAt
-Int
-
-
-Snapshot at specified time.
-
-
-
-
-before
-Int
-
-
-View before a specified time (end exclusive).
-
-
-
-
-after
-Int
-
-
-View after a specified time (start exclusive).
-
-
-
-
-shrinkWindow
-Window
-
-
-Shrink a Window to a specified start and end time.
-
-
-
-
-shrinkStart
-Int
-
-
-Set the window start to a specified time.
-
-
-
-
-shrinkEnd
-Int
-
-
-Set the window end to a specified time.
-
-
-
-
-nodeFilter
-NodeFilter
-
-
-Node filter.
-
-
-
-
-typeFilter
-[String !]
-
-
-List of types.
-
-
-
-
-
-
-### ObjectEntry
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-key
-String !
-
-
-Key.
-
-
-
-
-value
-Value !
-
-
-Value.
-
-
-
-
-
-
-### PathFromNodeViewCollection
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-latest
-Boolean
-
-
-Latest time.
-
-
-
-
-snapshotLatest
-Boolean
-
-
-Latest snapshot.
-
-
-
-
-snapshotAt
-Int
-
-
-Time.
-
-
-
-
-layers
-[String !]
-
-
-List of layers.
-
-
-
-
-excludeLayers
-[String !]
-
-
-List of excluded layers.
-
-
-
-
-layer
-String
-
-
-Single layer.
-
-
-
-
-excludeLayer
-String
-
-
-Single layer to exclude.
-
-
-
-
-window
-Window
-
-
-Window between a start and end time.
-
-
-
-
-at
-Int
-
-
-View at a specified time.
-
-
-
-
-before
-Int
-
-
-View before a specified time (end exclusive).
-
-
-
-
-after
-Int
-
-
-View after a specified time (start exclusive).
-
-
-
-
-shrinkWindow
-Window
-
-
-Shrink a Window to a specified start and end time.
-
-
-
-
-shrinkStart
-Int
-
-
-Set the window start to a specified time.
-
-
-
-
-shrinkEnd
-Int
-
-
-Set the window end to a specified time.
-
-
-
-
-
-
-### PropertyFilterExpr
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-name
-String !
-
-
-Node property to compare against.
-
-
-
-
-operator
-Operator !
-
-
-Operator.
-
-
-
-
-value
-Value
-
-
-Value.
-
-
-
-
-listAgg
-ListAgg
-
-
-List aggregate
-
-
-
-
-elemQualifier
-ListElemQualifier
-
-
-List qualifier
-
-
-
-
-
-
-### PropertyInput
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-key
-String !
-
-
-Key.
-
-
-
-
-value
-Value !
-
-
-Value.
-
-
-
-
-
-
-### PropsInput
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-all
-AllPropertySpec
-
-
-All properties and metadata.
-
-
-
-
-some
-SomePropertySpec
-
-
-Some properties and metadata.
-
-
-
-
-
-
-### SomePropertySpec
-
-SomePropertySpec object containing lists of metadata and property names.
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-metadata
-[String !]!
-
-
-List of metadata.
-
-
-
-
-properties
-[String !]!
-
-
-List of properties.
-
-
-
-
-
-
-### TemporalPropertyFilterExpr
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-name
-String !
-
-
-Name.
-
-
-
-
-temporal
-TemporalType !
-
-
-Type of temporal property. Choose from: any, latest.
-
-
-
-
-operator
-Operator !
-
-
-Operator.
-
-
-
-
-value
-Value
-
-
-Value.
-
-
-
-
-listAgg
-ListAgg
-
-
-List aggregate
-
-
-
-
-elemQualifier
-ListElemQualifier
-
-
-List qualifier
-
-
-
-
-
-
-### TemporalPropertyInput
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-time
-Int !
-
-
-Time.
-
-
-
-
-properties
-[PropertyInput !]
-
-
-Properties.
-
-
-
-
-
-
-### Value
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-u8
-Int
-
-
-8 bit unsigned integer.
-
-
-
-
-u16
-Int
-
-
-16 bit unsigned integer.
-
-
-
-
-u32
-Int
-
-
-32 bit unsigned integer.
-
-
-
-
-u64
-Int
-
-
-64 bit unsigned integer.
-
-
-
-
-i32
-Int
-
-
-32 bit signed integer.
-
-
-
-
-i64
-Int
-
-
-64 bit signed integer.
-
-
-
-
-f32
-Float
-
-
-32 bit float.
-
-
-
-
-f64
-Float
-
-
-64 bit float.
-
-
-
-
-str
-String
-
-
-String.
-
-
-
-
-bool
-Boolean
-
-
-Boolean.
-
-
-
-
-list
-[Value !]
-
-
-List.
-
-
-
-
-object
-[ObjectEntry !]
-
-
-Object.
-
-
-
-
-
-
-### Window
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-start
-Int !
-
-
-Start time.
-
-
-
-
-end
-Int !
-
-
-End time.
-
-
-
-
-
-
-### WindowDuration
-
-
-
-
-Field
-Type
-Description
-
-
-
-
-duration
-String
-
-
-Duration of window period.
-
-Choose from:
-
-
-
-
-epoch
-Int
-
-
-Time.
-
-
-
-
-
-
-## Enums
-
-### AlignmentUnit
-
-Alignment unit used to align window boundaries.
-
-
-
-
-Value
-Description
-
-
-
-
-UNALIGNED
-
-
-
-MILLISECOND
-
-
-
-SECOND
-
-
-
-MINUTE
-
-
-
-HOUR
-
-
-
-DAY
-
-
-
-WEEK
-
-
-
-MONTH
-
-
-
-YEAR
-
-
-
-
-
-### AllPropertySpec
-
-
-
-
-Value
-Description
-
-
-
-
-ALL
-
-
-All properties and metadata.
-
-
-
-
-ALL_METADATA
-
-
-All metadata.
-
-
-
-
-ALL_PROPERTIES
-
-
-All properties.
-
-
-
-
-
-
-### GraphType
-
-
-
-
-Value
-Description
-
-
-
-
-PERSISTENT
-
-
-Persistent.
-
-
-
-
-EVENT
-
-
-Event.
-
-
-
-
-
-
-### ListAgg
-
-
-
-
-Value
-Description
-
-
-
-
-LEN
-
-
-
-SUM
-
-
-
-AVG
-
-
-
-MIN
-
-
-
-MAX
-
-
-
-
-
-### ListElemQualifier
-
-
-
-
-Value
-Description
-
-
-
-
-ANY
-
-
-
-ALL
-
-
-
-
-
-### NodeField
-
-
-
-
-Value
-Description
-
-
-
-
-NODE_ID
-
-
-Node id.
-
-
-
-
-NODE_NAME
-
-
-Node name.
-
-
-
-
-NODE_TYPE
-
-
-Node type.
-
-
-
-
-
-
-### Operator
-
-
-
-
-Value
-Description
-
-
-
-
-EQUAL
-
-
-Equality operator.
-
-
-
-
-NOT_EQUAL
-
-
-Inequality operator.
-
-
-
-
-GREATER_THAN_OR_EQUAL
-
-
-Greater Than Or Equal operator.
-
-
-
-
-LESS_THAN_OR_EQUAL
-
-
-Less Than Or Equal operator.
-
-
-
-
-GREATER_THAN
-
-
-Greater Than operator.
-
-
-
-
-LESS_THAN
-
-
-Less Than operator.
-
-
-
-
-IS_NONE
-
-
-Is None operator.
-
-
-
-
-IS_SOME
-
-
-Is Some operator.
-
-
-
-
-IS_IN
-
-
-Is In operator.
-
-
-
-
-IS_NOT_IN
-
-
-Is Not In operator.
-
-
-
-
-STARTS_WITH
-
-
-
-ENDS_WITH
-
-
-
-CONTAINS
-
-
-Contains operator.
-
-
-
-
-NOT_CONTAINS
-
-
-Not Contains operator.
-
-
-
-
-
-
-### SortByTime
-
-
-
-
-Value
-Description
-
-
-
-
-LATEST
-
-
-Latest time
-
-
-
-
-EARLIEST
-
-
-Earliest time
-
-
-
-
-
-
-### TemporalType
-
-
-
-
-Value
-Description
-
-
-
-
-ANY
-
-
-Any.
-
-
-
-
-LATEST
-
-
-Latest.
-
-
-
-
-FIRST
-
-
-
-ALL
-
-
-
-
-
-## Scalars
-
-### Boolean
-
-The `Boolean` scalar type represents `true` or `false`.
-
-### Float
-
-The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_floating_point).
-
-### Int
-
-The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
-
-### PropertyOutput
-
-### String
-
-The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
-
-### Upload
-
-
-## Unions
-
-### DocumentEntity
-
-Entity associated with document.
-
-
-
-
-Type
-Description
-
-
-
-
-Node
-
-
-Raphtory graph node.
-
-
-
-
-Edge
-
-
-Raphtory graph edge.
-
-
-
-
-
-
-### NamespacedItem
-
-
-
-
diff --git a/docs/user-guide/algorithms/4_view-algorithms.md b/docs/user-guide/algorithms/4_view-algorithms.md
index cffa97ed88..5c681bd80b 100644
--- a/docs/user-guide/algorithms/4_view-algorithms.md
+++ b/docs/user-guide/algorithms/4_view-algorithms.md
@@ -25,7 +25,7 @@ time = []
for windowed_graph in lotr_graph.rolling(window=2000):
result = rp.algorithms.pagerank(windowed_graph)
importance.append(result.get("Gandalf"))
- time.append(windowed_graph.earliest_time)
+ time.append(windowed_graph.earliest_time.t)
plt.plot(time, importance, marker="o")
plt.xlabel("Sentence (Time)")
diff --git a/docs/user-guide/export/2_dataframes.md b/docs/user-guide/export/2_dataframes.md
index fa70d78379..330d7c0da8 100644
--- a/docs/user-guide/export/2_dataframes.md
+++ b/docs/user-guide/export/2_dataframes.md
@@ -133,7 +133,6 @@ Finally, we call `to_df()` again, turning off the property history and exploding
each interaction that occurred between `ANGELE` and `FELIPE`.
!!! info
-
We have further reduced the graph to only one layer (`Grunting-Lipsmacking`) to reduce the output size.
/// tab | :fontawesome-brands-python: Python
@@ -177,8 +176,8 @@ print(df)
///
```{.python continuation hide}
-assert str(grunting_graph) == "Graph(number_of_nodes=2, number_of_edges=2, number_of_temporal_edges=6, earliest_time=1560526320000, latest_time=1562253540000)"
-assert str(grunting_graph.edges) == "Edges(Edge(source=ANGELE, target=FELIPE, earliest_time=1560526320000, latest_time=1561042620000, properties={Weight: 1}, layer(s)=[Grunting-Lipsmacking]), Edge(source=FELIPE, target=ANGELE, earliest_time=1560526320000, latest_time=1562253540000, properties={Weight: 1}, layer(s)=[Grunting-Lipsmacking]))"
+assert str(grunting_graph) == "Graph(number_of_nodes=2, number_of_edges=2, number_of_temporal_edges=6, earliest_time=EventTime(timestamp=1560526320000, event_id=365), latest_time=EventTime(timestamp=1562253540000, event_id=2531))"
+assert str(grunting_graph.edges) == "Edges(Edge(source=ANGELE, target=FELIPE, earliest_time=EventTime(timestamp=1560526320000, event_id=365), latest_time=EventTime(timestamp=1561042620000, event_id=871), properties={Weight: 1}, layer(s)=[Grunting-Lipsmacking]), Edge(source=FELIPE, target=ANGELE, earliest_time=EventTime(timestamp=1560526320000, event_id=366), latest_time=EventTime(timestamp=1562253540000, event_id=2531), properties={Weight: 1}, layer(s)=[Grunting-Lipsmacking]))"
```
!!! Output
@@ -236,8 +235,8 @@ assert str(grunting_graph.edges) == "Edges(Edge(source=ANGELE, target=FELIPE, ea
13 [1560526320000]
14 [1561110180000]
- Graph(number_of_nodes=2, number_of_edges=2, number_of_temporal_edges=6, earliest_time=1560526320000, latest_time=1562253540000)
- Edges(Edge(source=ANGELE, target=FELIPE, earliest_time=1560526320000, latest_time=1561042620000, properties={Weight: 1}, layer(s)=[Grunting-Lipsmacking]), Edge(source=FELIPE, target=ANGELE, earliest_time=1560526320000, latest_time=1562253540000, properties={Weight: 1}, layer(s)=[Grunting-Lipsmacking]))
+ Graph(number_of_nodes=2, number_of_edges=2, number_of_temporal_edges=6, earliest_time=EventTime(timestamp=1560526320000, event_id=365), latest_time=EventTime(timestamp=1562253540000, event_id=2531))
+ Edges(Edge(source=ANGELE, target=FELIPE, earliest_time=EventTime(timestamp=1560526320000, event_id=365), latest_time=EventTime(timestamp=1561042620000, event_id=871), properties={Weight: 1}, layer(s)=[Grunting-Lipsmacking]), Edge(source=FELIPE, target=ANGELE, earliest_time=EventTime(timestamp=1560526320000, event_id=366), latest_time=EventTime(timestamp=1562253540000, event_id=2531), properties={Weight: 1}, layer(s)=[Grunting-Lipsmacking]))
Exploding the grunting-Lipsmacking layer
src dst layer \
0 ANGELE FELIPE Grunting-Lipsmacking
diff --git a/docs/user-guide/getting-started/20_intro.md b/docs/user-guide/getting-started/20_intro.md
index b7881ccfd2..dcd589fba3 100644
--- a/docs/user-guide/getting-started/20_intro.md
+++ b/docs/user-guide/getting-started/20_intro.md
@@ -50,15 +50,15 @@ print(g)
///
```{.python continuation hide}
-assert str(g) == "Graph(number_of_nodes=22, number_of_edges=290, number_of_temporal_edges=3196, earliest_time=1560419400000, latest_time=1562756700000)"
+assert str(g) == "Graph(number_of_nodes=22, number_of_edges=290, number_of_temporal_edges=3196, earliest_time=EventTime(timestamp=1560419400000, event_id=0), latest_time=EventTime(timestamp=1562756700000, event_id=18446744073709551615))"
```
-You can print the state of the graph object to verify it exists.
+You can print the state of the graph object to verify it exists. Note that the `earliest_time` and `latest_time` are given in Raphtory's `EventTime` format.
!!! Output
```output
- Graph(number_of_nodes=22, number_of_edges=290, number_of_temporal_edges=3196, earliest_time=1560419400000, latest_time=1562756700000)
+ Graph(number_of_nodes=22, number_of_edges=290, number_of_temporal_edges=3196, earliest_time=EventTime(timestamp=1560419400000, event_id=0), latest_time=EventTime(timestamp=1562756700000, event_id=18446744073709551615))
```
For more details, see [Creating a graph](../ingestion/1_intro.md).
diff --git a/docs/user-guide/ingestion/2_direct-updates.md b/docs/user-guide/ingestion/2_direct-updates.md
index ebde52148d..4046701891 100644
--- a/docs/user-guide/ingestion/2_direct-updates.md
+++ b/docs/user-guide/ingestion/2_direct-updates.md
@@ -25,18 +25,18 @@ print(v)
///
```{.python continuation hide}
-assert str(g) == "Graph(number_of_nodes=1, number_of_edges=0, number_of_temporal_edges=0, earliest_time=1, latest_time=1)"
-assert str(v) == "Node(name=10, earliest_time=1, latest_time=1)"
+assert str(g) == "Graph(number_of_nodes=1, number_of_edges=0, number_of_temporal_edges=0, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=18446744073709551615))"
+assert str(v) == "Node(name=10, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=0))"
```
-Printing out the graph and the returned node we can see the update was successful and the earliest/latest time has been
-updated.
+Printing out the graph and the returned node you can see the update was successful and the earliest and latest times have been
+updated. The timestamp you specified is used as the primary index of the `EventTime` object.
!!! Output
```output
- Graph(number_of_nodes=1, number_of_edges=0, number_of_temporal_edges=0, earliest_time=1, latest_time=1)
- Node(name=10, earliest_time=1, latest_time=1)
+ Graph(number_of_nodes=1, number_of_edges=0, number_of_temporal_edges=0, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=18446744073709551615))
+ Node(name=10, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=0))
```
## Adding edges
@@ -62,15 +62,15 @@ print(e)
///
```{.python continuation hide}
-assert str(g) == "Graph(number_of_nodes=2, number_of_edges=1, number_of_temporal_edges=1, earliest_time=1, latest_time=1)"
-assert str(e) == "Edge(source=15, target=16, earliest_time=1, latest_time=1, layer(s)=[_default])"
+assert str(g) == "Graph(number_of_nodes=2, number_of_edges=1, number_of_temporal_edges=1, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=18446744073709551615))"
+assert str(e) == "Edge(source=15, target=16, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=0), layer(s)=[_default])"
```
!!! Output
```output
- Graph(number_of_nodes=2, number_of_edges=1, number_of_temporal_edges=1, earliest_time=1, latest_time=1)
- Edge(source=15, target=16, earliest_time=1, latest_time=1, layer(s)=[_default])
+ Graph(number_of_nodes=2, number_of_edges=1, number_of_temporal_edges=1, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=18446744073709551615))
+ Edge(source=15, target=16, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=0), layer(s)=[_default])
```
You will notice in the output that the graph has two nodes as well as the edge. Raphtory automatically creates the source and destination nodes at the same time if they do not currently exist in the graph. This is to keep the graph consistent and avoid `hanging edges`. These nodes are empty other than the history of their edges, therefore if you apply filters to exclude all edges these empty nodes will also be excluded from your graph.
@@ -102,17 +102,17 @@ print(g.edge("User 1", "User 2"))
///
```{.python continuation hide}
-assert str(g.node("User 1")) == "Node(name=User 1, earliest_time=123, latest_time=789)"
-assert str(g.node("User 2")) == "Node(name=User 2, earliest_time=456, latest_time=789)"
-assert str(g.edge("User 1", "User 2")) == "Edge(source=User 1, target=User 2, earliest_time=789, latest_time=789, layer(s)=[_default])"
+assert str(g.node("User 1")) == "Node(name=User 1, earliest_time=EventTime(timestamp=123, event_id=0), latest_time=EventTime(timestamp=789, event_id=2))"
+assert str(g.node("User 2")) == "Node(name=User 2, earliest_time=EventTime(timestamp=456, event_id=1), latest_time=EventTime(timestamp=789, event_id=2))"
+assert str(g.edge("User 1", "User 2")) == "Edge(source=User 1, target=User 2, earliest_time=EventTime(timestamp=789, event_id=2), latest_time=EventTime(timestamp=789, event_id=2), layer(s)=[_default])"
```
!!! Output
```output
- Node(name=User 1, earliest_time=123, latest_time=789)
- Node(name=User 2, earliest_time=456, latest_time=789)
- Edge(source=User 1, target=User 2, earliest_time=789, latest_time=789, layer(s)=[_default])
+ Node(name=User 1, earliest_time=EventTime(timestamp=123, event_id=0), latest_time=EventTime(timestamp=789, event_id=2))
+ Node(name=User 2, earliest_time=EventTime(timestamp=456, event_id=1), latest_time=EventTime(timestamp=789, event_id=2))
+ Edge(source=User 1, target=User 2, earliest_time=EventTime(timestamp=789, event_id=2), latest_time=EventTime(timestamp=789, event_id=2), layer(s)=[_default])
```
!!! warning
@@ -146,22 +146,22 @@ datetime_obj = datetime(2021, 1, 1, 12, 32, 0, 0)
g.add_node(timestamp=datetime_obj, id=10)
print(g)
-print(g.node(id=10).history())
-print(g.node(id=10).history_date_time())
+print(g.node(id=10).history.t.collect())
+print(g.node(id=10).history.dt.collect())
```
///
```{.python continuation hide}
-assert str(g) == "Graph(number_of_nodes=1, number_of_edges=0, number_of_temporal_edges=0, earliest_time=1609504320000, latest_time=1612360860000)"
-assert str(g.node(id=10).history()) == "[1609504320000 1612360860000]"
-assert str(g.node(id=10).history_date_time()) == "[datetime.datetime(2021, 1, 1, 12, 32, tzinfo=datetime.timezone.utc), datetime.datetime(2021, 2, 3, 14, 1, tzinfo=datetime.timezone.utc)]"
+assert str(g) == "Graph(number_of_nodes=1, number_of_edges=0, number_of_temporal_edges=0, earliest_time=EventTime(timestamp=1609504320000, event_id=0), latest_time=EventTime(timestamp=1612360860000, event_id=18446744073709551615))"
+assert str(g.node(id=10).history.t.collect()) == "[1609504320000 1612360860000]"
+assert str(g.node(id=10).history.dt.collect()) == "[datetime.datetime(2021, 1, 1, 12, 32, tzinfo=datetime.timezone.utc), datetime.datetime(2021, 2, 3, 14, 1, tzinfo=datetime.timezone.utc)]"
```
!!! Output
```output
- Graph(number_of_nodes=1, number_of_edges=0, number_of_temporal_edges=0, earliest_time=1609504320000, latest_time=1612360860000)
+ Graph(number_of_nodes=1, number_of_edges=0, number_of_temporal_edges=0, earliest_time=EventTime(timestamp=1609504320000, event_id=0), latest_time=EventTime(timestamp=1612360860000, event_id=18446744073709551615))
[1609504320000 1612360860000]
[datetime.datetime(2021, 1, 1, 12, 32, tzinfo=datetime.timezone.utc), datetime.datetime(2021, 2, 3, 14, 1, tzinfo=datetime.timezone.utc)]
```
@@ -169,11 +169,11 @@ assert str(g.node(id=10).history_date_time()) == "[datetime.datetime(2021, 1, 1,
In the output we can see the `history` of node `10` contains the two times at which we have added it into the graph (
maintained in ascending order), returned in both unix epoch (integer) and datetime format.
+Internally, the [History][raphtory.History] of events is tracked using [EventTime][raphtory.EventTime] objects. However, in this example we use the `.t` and `.dt` methods to get iterables of epochs and datetimes directly from the `History` object and collect the iterables to display simple lists.
+
## Properties
-Alongside the structural update history, Raphtory can maintain the changing value of [`Properties`][raphtory.Properties] associated with nodes
-and edges. Both the `add_node()` and `add_edge()` functions have an optional parameter `properties` which takes a
-dictionary of key value pairs to be stored at the given timestamp.
+Alongside the structural update history, Raphtory can maintain the changing value of [`Properties`][raphtory.Properties] associated with nodes and edges. Both the `add_node()` and `add_edge()` functions have an optional parameter `properties` which takes a dictionary of key value pairs to be stored at the given timestamp.
The graph itself may also have its own `global properties` added using the `add_properties()` function which takes only
a `timestamp` and a `properties` dictionary.
@@ -182,7 +182,7 @@ Properties can consist of primitives (`Integer`, `Float`, `String`, `Boolean`, `
`List`). This allows you to store both basic values as well as do complex hierarchical modelling depending on your use
case.
-In the example below, we are using all of these functions to add a mixture of properties to a node, an edge, and the
+In the example below, we use all of these functions to add a mixture of properties to a node, an edge, and the
graph.
!!! warning
@@ -249,15 +249,15 @@ print(e)
///
```{.python continuation hide}
-assert str(e) == "Edge(source=User 1, target=User 2, earliest_time=4, latest_time=4, properties={weights: [1, 2, 3]}, layer(s)=[_default])"
+assert str(e) == "Edge(source=User 1, target=User 2, earliest_time=EventTime(timestamp=4, event_id=5), latest_time=EventTime(timestamp=4, event_id=5), properties={weights: [1, 2, 3]}, layer(s)=[_default])"
```
!!! Output
```output
- Graph(number_of_nodes=2, number_of_edges=1, number_of_temporal_edges=1, earliest_time=1, latest_time=4, properties=Properties({inner data: {fruits: {apple: 5, banana: 3}, date of birth: 2021-01-01 12:32:00}, favourite greetings: [hi, hello, howdy]}))
- Node(name=User 1, earliest_time=1, latest_time=4, properties=Properties({count: 2, greeting: hello, encrypted: true, balance: 0.9}))
- Edge(source=User 1, target=User 2, earliest_time=4, latest_time=4, properties={weights: [1, 2, 3]}, layer(s)=[_default])
+ Graph(number_of_nodes=2, number_of_edges=1, number_of_temporal_edges=1, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=4, event_id=18446744073709551615), properties=Properties({inner data: {fruits: {apple: 5, banana: 3}, date of birth: 2021-01-01 12:32:00}, favourite greetings: [hi, hello, howdy]}))
+ Node(name=User 1, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=4, event_id=5), properties=Properties({count: 2, greeting: hello, encrypted: true, balance: 0.9}))
+ Edge(source=User 1, target=User 2, earliest_time=EventTime(timestamp=4, event_id=5), latest_time=EventTime(timestamp=4, event_id=5), properties={weights: [1, 2, 3]}, layer(s)=[_default])
```
!!! info
@@ -268,7 +268,7 @@ assert str(e) == "Edge(source=User 1, target=User 2, earliest_time=4, latest_tim
## Metadata
-Raphtory also provides [`metadata`][raphtory.Metadata] associated with nodes and edges which have immutable values. These are useful when you know a value won't change or is not associated with a specific time.
+Raphtory also provides [`metadata`][raphtory.Metadata] associated with nodes and edges which have immutable values. These are useful when you know a value won't change or is not associated with a specific time.
You can use the `add_metadata()` function, which takes a single `dictionary` argument, to add metadata to a `graph`, `node` and `edge` as demonstrated below.
@@ -296,17 +296,17 @@ print(e)
///
```{.python continuation hide}
-assert str(g) == "Graph(number_of_nodes=2, number_of_edges=1, number_of_temporal_edges=1, earliest_time=1, latest_time=2)"
-assert str(v) == "Node(name=User 1, earliest_time=1, latest_time=2)"
-assert str(e) == "Edge(source=User 1, target=User 2, earliest_time=2, latest_time=2, layer(s)=[_default])"
+assert str(g) == "Graph(number_of_nodes=2, number_of_edges=1, number_of_temporal_edges=1, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=2, event_id=18446744073709551615))"
+assert str(v) == "Node(name=User 1, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=2, event_id=1))"
+assert str(e) == "Edge(source=User 1, target=User 2, earliest_time=EventTime(timestamp=2, event_id=1), latest_time=EventTime(timestamp=2, event_id=1), layer(s)=[_default])"
```
!!! output
```output
- Graph(number_of_nodes=2, number_of_edges=1, number_of_temporal_edges=1, earliest_time=1, latest_time=2)
- Node(name=User 1, earliest_time=1, latest_time=2)
- Edge(source=User 1, target=User 2, earliest_time=2, latest_time=2, layer(s)=[_default])
+ Graph(number_of_nodes=2, number_of_edges=1, number_of_temporal_edges=1, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=2, event_id=18446744073709551615))
+ Node(name=User 1, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=2, event_id=1))
+ Edge(source=User 1, target=User 2, earliest_time=EventTime(timestamp=2, event_id=1), latest_time=EventTime(timestamp=2, event_id=1), layer(s)=[_default])
```
## Edge Layers
@@ -387,4 +387,4 @@ assert str(layered_edge.properties.temporal.get("weight").values()) == "[20 35]"
```output
[10 13 20 17 35]
[20 35]
- ```
\ No newline at end of file
+ ```
diff --git a/docs/user-guide/ingestion/3_dataframes.md b/docs/user-guide/ingestion/3_dataframes.md
index 47db370e2e..f7c08f1352 100644
--- a/docs/user-guide/ingestion/3_dataframes.md
+++ b/docs/user-guide/ingestion/3_dataframes.md
@@ -124,16 +124,16 @@ print(g.edge("ServerA", "ServerB"))
///
```{.python continuation hide}
-assert str(g) == "Graph(number_of_nodes=5, number_of_edges=7, number_of_temporal_edges=7, earliest_time=1693555200000, latest_time=1693557000000)"
+assert str(g) == "Graph(number_of_nodes=5, number_of_edges=7, number_of_temporal_edges=7, earliest_time=EventTime(timestamp=1693555200000, event_id=0), latest_time=EventTime(timestamp=1693557000000, event_id=18446744073709551615))"
```
!!! Output
```output
The resulting graphs and example node/edge:
- Graph(number_of_nodes=5, number_of_edges=7, number_of_temporal_edges=7, earliest_time=1693555200000, latest_time=1693557000000)
- Node(name=ServerA, earliest_time=1693555200000, latest_time=1693556400000, properties=Properties({OS_version: Ubuntu 20.04, primary_function: Database, uptime_days: 120, datasource: docs/data/network_traffic_edges.csv, server_name: Alpha, hardware_type: Blade Server}))
- Edge(source=ServerA, target=ServerB, earliest_time=1693555200000, latest_time=1693555200000, properties={data_size_MB: 5.6, datasource: {Critical System Request: docs/data/network_traffic_edges.csv}, is_encrypted: {Critical System Request: true}}, layer(s)=[Critical System Request])
+ Graph(number_of_nodes=5, number_of_edges=7, number_of_temporal_edges=7, earliest_time=EventTime(timestamp=1693555200000, event_id=0), latest_time=EventTime(timestamp=1693557000000, event_id=18446744073709551615))
+ Node(name=ServerA, earliest_time=EventTime(timestamp=1693555200000, event_id=0), latest_time=EventTime(timestamp=1693556400000, event_id=4), properties=Properties({OS_version: Ubuntu 20.04, primary_function: Database, uptime_days: 120}))
+ Edge(source=ServerA, target=ServerB, earliest_time=EventTime(timestamp=1693555200000, event_id=0), latest_time=EventTime(timestamp=1693555200000, event_id=0), properties={data_size_MB: 5.6}, layer(s)=[Critical System Request])
```
## Creating a graph from a Parquet file
@@ -171,13 +171,14 @@ print(h)
///
```{.python continuation hide}
-assert str(h) == "Graph(number_of_nodes=5, number_of_edges=7, number_of_temporal_edges=7, earliest_time=1693555200000, latest_time=1693557000000)"
+
+assert str(h) == "Graph(number_of_nodes=5, number_of_edges=7, number_of_temporal_edges=7, earliest_time=EventTime(timestamp=1693555200000, event_id=0), latest_time=EventTime(timestamp=1693557000000, event_id=18446744073709551615))"
```
!!! Output
```output
- Graph(number_of_nodes=5, number_of_edges=7, number_of_temporal_edges=7, earliest_time=1693555200000, latest_time=1693557000000)
+ Graph(number_of_nodes=5, number_of_edges=7, number_of_temporal_edges=7, earliest_time=EventTime(timestamp=1693555200000, event_id=0), latest_time=EventTime(timestamp=1693557000000, event_id=18446744073709551615))
```
## Adding metadata via dataframes
@@ -187,7 +188,7 @@ same two dataframes for brevity but in real instances these would probably be fo
function call.
There may be instances where you are adding a dataset which has no timestamps. To handle this when ingesting via
-dataframes, the graph has the `load_edge_props_from_pandas()` and `load_node_props_from_pandas()` functions which are shown in this example.
+dataframes, the graph has the `load_edge_props_from_pandas()` and `load_node_props_from_pandas()` functions which are shown in this example.
!!! warning
Metadata can only be added to nodes and edges which are part of the graph. If you attempt to add a metadata without first adding the node/edge then Raphtory will throw an error.
@@ -245,13 +246,13 @@ print(g.edge("ServerA", "ServerB"))
///
```{.python continuation hide}
-assert str(g) == "Graph(number_of_nodes=5, number_of_edges=7, number_of_temporal_edges=7, earliest_time=1693555200000, latest_time=1693557000000)"
+assert str(g) == "Graph(number_of_nodes=5, number_of_edges=7, number_of_temporal_edges=7, earliest_time=EventTime(timestamp=1693555200000, event_id=0), latest_time=EventTime(timestamp=1693557000000, event_id=18446744073709551615))"
```
!!! Output
```output
- Graph(number_of_nodes=5, number_of_edges=7, number_of_temporal_edges=7, earliest_time=1693555200000, latest_time=1693557000000)
- Node(name=ServerA, earliest_time=1693555200000, latest_time=1693556400000, properties=Properties({OS_version: Ubuntu 20.04, primary_function: Database, uptime_days: 120, datasource: docs/data/network_traffic_edges.csv, server_name: Alpha, hardware_type: Blade Server}))
- Edge(source=ServerA, target=ServerB, earliest_time=1693555200000, latest_time=1693555200000, properties={data_size_MB: 5.6, datasource: {Critical System Request: docs/data/network_traffic_edges.csv}, is_encrypted: {Critical System Request: true}}, layer(s)=[Critical System Request])
+ Graph(number_of_nodes=5, number_of_edges=7, number_of_temporal_edges=7, earliest_time=EventTime(timestamp=1693555200000, event_id=0), latest_time=EventTime(timestamp=1693557000000, event_id=18446744073709551615))
+ Node(name=ServerA, earliest_time=EventTime(timestamp=1693555200000, event_id=0), latest_time=EventTime(timestamp=1693556400000, event_id=4), properties=Properties({OS_version: Ubuntu 20.04, primary_function: Database, uptime_days: 120}))
+ Edge(source=ServerA, target=ServerB, earliest_time=EventTime(timestamp=1693555200000, event_id=0), latest_time=EventTime(timestamp=1693555200000, event_id=0), properties={data_size_MB: 5.6}, layer(s)=[Critical System Request])
```
diff --git a/docs/user-guide/persistent-graph/1_intro.md b/docs/user-guide/persistent-graph/1_intro.md
index fe8471b012..aea46d70df 100644
--- a/docs/user-guide/persistent-graph/1_intro.md
+++ b/docs/user-guide/persistent-graph/1_intro.md
@@ -2,11 +2,11 @@
Up to now, we have made an implicit assumption that a temporal graph is made up of events (messages, citations, transactions) instantiated from one node to another that can be thought of as instantaneous. In the temporal graph literature, such representations of graphs are known as *link streams*. This representation can also capture temporal graphs that can be considered a sequence of static network snapshots by giving edges integer timestamps `1, 2, 3, ...` depending on the snapshot(s) in which they are found.
-However, there is another family of temporal graphs which don't fit into this format. What about instead of an instantaneous event, edges could be present for a defined amount of time? Such occasions might include proximity networks where an edge between two individuals is continuously present whenever they are close together, or a 'following' network where edges are established and later potentially removed.
+However, there is another family of temporal graphs which don't fit into this format. What about instead of an instantaneous event, edges could be present for a defined amount of time? Such occasions might include proximity networks where an edge between two individuals is continuously present whenever they are close together, or a 'following' network where edges are established and later potentially removed.
-To enable these types of interactions to be represented, we provide an additional graph representation where edges can be added, removed and added back again called the *PersistentGraph*.
+To enable these types of interactions to be represented, we provide an additional graph representation where edges can be added, removed and added back again called the [PersistentGraph][raphtory.PersistentGraph].
-The example below shows how to create and manipulate a *PersistentGraph* in Raphtory.
+The example below shows how to create and manipulate a `PersistentGraph` in Raphtory.
/// tab | :fontawesome-brands-python: Python
```python
@@ -32,17 +32,17 @@ print(f"G's exploded edges are {G.edges.explode()}")
///
```{.python continuation hide}
-assert str(f"G's edges are {G.edges}") == "G's edges are Edges(Edge(source=Alice, target=Bob, earliest_time=1, latest_time=10, layer(s)=[_default]), Edge(source=Bob, target=Charlie, earliest_time=3, latest_time=3, layer(s)=[_default]))"
-assert str(f"G's exploded edges are {G.edges.explode()}") == "G's exploded edges are Edges(Edge(source=Alice, target=Bob, earliest_time=1, latest_time=5, layer(s)=[_default]), Edge(source=Alice, target=Bob, earliest_time=10, latest_time=10, layer(s)=[_default]), Edge(source=Bob, target=Charlie, earliest_time=3, latest_time=10, layer(s)=[_default]))"
+assert str(f"G's edges are {G.edges}") == "G's edges are Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=10, event_id=3), layer(s)=[_default]), Edge(source=Bob, target=Charlie, earliest_time=EventTime(timestamp=3, event_id=1), latest_time=EventTime(timestamp=3, event_id=1), layer(s)=[_default]))"
+assert str(f"G's exploded edges are {G.edges.explode()}") == "G's exploded edges are Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=5, event_id=2), layer(s)=[_default]), Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=10, event_id=3), latest_time=EventTime(timestamp=10, event_id=18446744073709551615), layer(s)=[_default]), Edge(source=Bob, target=Charlie, earliest_time=EventTime(timestamp=3, event_id=1), latest_time=EventTime(timestamp=10, event_id=18446744073709551615), layer(s)=[_default]))"
```
!!! Output
```output
- G's edges are Edges(Edge(source=Alice, target=Bob, earliest_time=1, latest_time=10, layer(s)=[_default]), Edge(source=Bob, target=Charlie, earliest_time=3, latest_time=10, layer(s)=[_default]))
- G's exploded edges are Edges(Edge(source=Alice, target=Bob, earliest_time=1, latest_time=5, layer(s)=[_default]), Edge(source=Alice, target=Bob, earliest_time=10, latest_time=10, layer(s)=[_default]), Edge(source=Bob, target=Charlie, earliest_time=3, latest_time=10, layer(s)=[_default]))
+ G's edges are Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=10, event_id=3), layer(s)=[_default]), Edge(source=Bob, target=Charlie, earliest_time=EventTime(timestamp=3, event_id=1), latest_time=EventTime(timestamp=3, event_id=1), layer(s)=[_default]))
+ G's exploded edges are Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=5, event_id=2), layer(s)=[_default]), Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=10, event_id=3), latest_time=EventTime(timestamp=10, event_id=18446744073709551615), layer(s)=[_default]), Edge(source=Bob, target=Charlie, earliest_time=EventTime(timestamp=3, event_id=1), latest_time=EventTime(timestamp=10, event_id=18446744073709551615), layer(s)=[_default]))
```
-Here we have a graph with two edges: one connecting Alice and Bob, and one connecting Bob and Charlie, and three _exploded edges_, one for each activation of Alice and Bob's edge and the activation of Bob and Charlie's edge. If an edge is not explicitly deleted, it is assumed to last forever (or at least until an integer max value).
+Here we have a graph with two edges: one connecting Alice and Bob, and one connecting Bob and Charlie, and three exploded edges, one for each activation of Alice and Bob's edge and the activation of Bob and Charlie's edge. If an edge is not explicitly deleted, it is assumed to last forever (or at least until an integer max value).
-Over the next few pages, we will explore how the persistent graph works to understand its behaviour and semantics and how it can unlock some interesting analysis.
\ No newline at end of file
+Over the next few pages, we will explore how the persistent graph works to understand its behaviour and semantics and how it can unlock some interesting analysis.
diff --git a/docs/user-guide/persistent-graph/2_ambiguity.md b/docs/user-guide/persistent-graph/2_ambiguity.md
index 9bee528753..732e2b7926 100644
--- a/docs/user-guide/persistent-graph/2_ambiguity.md
+++ b/docs/user-guide/persistent-graph/2_ambiguity.md
@@ -1,6 +1,6 @@
# Handling of ambiguous updates
-For a *link-stream* graph there is a natural way to construct the graph regardless of the order the updates come in. However, for a *PersistentGraph* where deletions are possible this becomes more difficult. The following examples illustrate this.
+For a link-stream graph there is a natural way to construct the graph regardless of the order the updates come in. However, for a `PersistentGraph `where deletions are possible this becomes more difficult. The following examples illustrate this.
## Order of resolving additions and deletions
@@ -20,22 +20,22 @@ print(G.edges.explode())
```{.python continuation hide}
-assert str(G.edges.explode()) == "Edges(Edge(source=Alice, target=Bob, earliest_time=1, latest_time=3, layer(s)=[_default]), Edge(source=Alice, target=Bob, earliest_time=3, latest_time=5, layer(s)=[_default]))"
+assert str(G.edges.explode()) == "Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=3, event_id=2), layer(s)=[_default]), Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=3, event_id=2), latest_time=EventTime(timestamp=5, event_id=1), layer(s)=[_default]))"
```
-Here two edges between Alice and Bob overlap in time: one starting at time 1 and ending at time 5, another starting at time 3 and ending at time 7.
+Here two edges between Alice and Bob overlap in time: one starting at time 1 and ending at time 5, another starting at time 3 and ending at time 7.
-For *link-stream* graphs in Raphtory are allowed to have edges between the same pair of nodes happening at the same instant. However, when we look at the exploded edges of this *PersistentGraph* graph, the following is returned:
+For link-stream graphs in Raphtory are allowed to have edges between the same pair of nodes happening at the same instant. However, when we look at the exploded edges of this `PersistentGraph` graph, the following is returned:
!!! Output
```output
- Edges(Edge(source=Alice, target=Bob, earliest_time=1, latest_time=3, layer(s)=[_default]), Edge(source=Alice, target=Bob, earliest_time=3, latest_time=5, layer(s)=[_default]))
+ Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=3, event_id=2), layer(s)=[_default]), Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=3, event_id=2), latest_time=EventTime(timestamp=5, event_id=1), layer(s)=[_default]))
```
-Two edges are created, one that exists from time 1 to time 3 and another that exists from time 3 to time 5. The second deletion at time 7 is ignored.
+Two edges are created, one that exists from time 1 to time 3 and another that exists from time 3 to time 5. The second deletion at time 7 is ignored.
-The reason for this is that Raphtory's graph updates are inserted in chronological order, so that the same graph is constructed regardless of the order in which the updates are made. With an exception for events which have the same timestamp, which will be covered shortly.
+The reason for this is that Raphtory's graph updates are inserted in chronological order, so that the same graph is constructed regardless of the order in which the updates are made. With an exception for events which have the same timestamp, which will be covered shortly.
In this example, the order is: edge addition at time 1, edge addition at time 3, edge deletion at time 5 and edge deletion at time 7. This second edge deletion is now redundant.
@@ -56,7 +56,7 @@ print(f"G's edges are {G.edges.explode()}")
///
```{.python continuation hide}
-assert str(f"G's edges are {G.edges.explode()}") == "G's edges are Edges(Edge(source=Alice, target=Bob, earliest_time=1, latest_time=5, layer(s)=[_default]))"
+assert str(f"G's edges are {G.edges.explode()}") == "G's edges are Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=1, event_id=1), latest_time=EventTime(timestamp=5, event_id=0), layer(s)=[_default]))"
```
Which results in the following:
@@ -64,7 +64,7 @@ Which results in the following:
```output
G's edges are Edges()
- G's edges are Edges(Edge(source=Alice, target=Bob, earliest_time=1, latest_time=5, layer(s)=[_default]))
+ G's edges are Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=1, event_id=1), latest_time=EventTime(timestamp=5, event_id=0), layer(s)=[_default]))
```
## Additions and deletions in the same instant
@@ -86,20 +86,20 @@ print(f"G1's edges are {G1.edges.explode()}")
///
```{.python continuation hide}
-assert str(f"G1's edges are {G1.edges.explode()}") == "G1's edges are Edges(Edge(source=1, target=2, earliest_time=1, latest_time=1, properties={message: hi}, layer(s)=[_default]))"
+assert str(f"G1's edges are {G1.edges.explode()}") == "G1's edges are Edges(Edge(source=1, target=2, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=1), properties={message: hi}, layer(s)=[_default]))"
```
!!! Output
```output
- G1's edges are Edges(Edge(source=1, target=2, earliest_time=1, latest_time=1, properties={message: hi}, layer(s)=[_default]))
+ G1's edges are Edges(Edge(source=1, target=2, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=1), properties={message: hi}, layer(s)=[_default]))
```
This graph has an edge which instantaneously appears and disappears at time 1 and therefore the order of its history is determined by the execution order.
## Interaction with layers
-Layering allows different types of interaction to exist, and edges on different layers can have overlapping times in a way that doesn't make sense for edges in the same layer or for edges with no layer.
+Layering allows different types of interaction to exist, and edges on different layers can have overlapping times in a way that doesn't make sense for edges in the same layer or for edges with no layer.
Consider an example without layers:
@@ -118,13 +118,13 @@ print(G.edges.explode())
///
```{.python continuation hide}
-assert str(G.edges.explode()) == "Edges(Edge(source=Alice, target=Bob, earliest_time=1, latest_time=3, layer(s)=[_default]), Edge(source=Alice, target=Bob, earliest_time=3, latest_time=5, layer(s)=[_default]))"
+assert str(G.edges.explode()) == "Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=3, event_id=2), layer(s)=[_default]), Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=3, event_id=2), latest_time=EventTime(timestamp=5, event_id=1), layer(s)=[_default]))"
```
!!! Output
```output
- Edges(Edge(source=Alice, target=Bob, earliest_time=1, latest_time=3, layer(s)=[_default]), Edge(source=Alice, target=Bob, earliest_time=3, latest_time=5, layer(s)=[_default]))
+ Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=3, event_id=2), layer(s)=[_default]), Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=3, event_id=2), latest_time=EventTime(timestamp=5, event_id=1), layer(s)=[_default]))
```
Now take a look at a modified example with layers:
@@ -144,15 +144,15 @@ print(G.edges.explode())
///
```{.python continuation hide}
-assert str(G.edges.explode()) == "Edges(Edge(source=Alice, target=Bob, earliest_time=1, latest_time=5, layer(s)=[colleagues]), Edge(source=Alice, target=Bob, earliest_time=3, latest_time=7, layer(s)=[friends]))"
+assert str(G.edges.explode()) == "Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=5, event_id=1), layer(s)=[colleagues]), Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=3, event_id=2), latest_time=EventTime(timestamp=7, event_id=3), layer(s)=[friends]))"
```
!!! Output
```output
- Edges(Edge(source=Alice, target=Bob, earliest_time=1, latest_time=5, layer(s)=[colleagues]), Edge(source=Alice, target=Bob, earliest_time=3, latest_time=7, layer(s)=[friends]))
+ Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=5, event_id=1), layer(s)=[colleagues]), Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=3, event_id=2), latest_time=EventTime(timestamp=7, event_id=3), layer(s)=[friends]))
```
By adding layer names to the different edge instances we produce a different result.
-Here we have two edges, one starting and ending at 1 and 5 respectively with the 'colleague' layer, the other starting and ending at 3 and 7 on the 'friends' layer.
+Here we have two edges, one starting and ending at 1 and 5 respectively with the 'colleague' layer, the other starting and ending at 3 and 7 on the 'friends' layer.
diff --git a/docs/user-guide/persistent-graph/3_views.md b/docs/user-guide/persistent-graph/3_views.md
index 2e877d2d9c..b490e52e45 100644
--- a/docs/user-guide/persistent-graph/3_views.md
+++ b/docs/user-guide/persistent-graph/3_views.md
@@ -5,9 +5,9 @@
type="text/javascript">
-When dealing with *link-stream* graphs where edges are formed from instantaneous event streams, views were used to create a temporal bound on the graph to ultimately see how the graph changes over time. Single views were created using `at`, `before`, `after` and `window`, and iterators of windows were created using `expanding` and `rolling`.
+When dealing with link-stream graphs where edges are formed from instantaneous event streams, views were used to create a temporal bound on the graph to ultimately see how the graph changes over time. Single views were created using `at`, `before`, `after` and `window`, and iterators of windows were created using `expanding` and `rolling`.
-Functionality with the same name is available for the *PersistentGraph*. This shares similarities with the functionality for *link-stream* graphs but has some important differences. This page covers the differences in time-bounding behavior on the Raphtory `PersistentGraph`.
+Functionality with the same name is available for the `PersistentGraph`. This shares similarities with the functionality for link-stream graphs but has some important differences. This page covers the differences in time-bounding behaviour on the Raphtory `PersistentGraph`.
## Querying an instant of the graph with `at()`
@@ -44,15 +44,15 @@ assert str(f"At time 6: {G.at(6).nodes} {G.at(6).edges.explode()}") == "At time
```output
At time 0: Nodes() Edges()
- At time 2: Nodes(Node(name=Alice, earliest_time=2, latest_time=2), Node(name=Bob, earliest_time=2, latest_time=2)) Edges(Edge(source=Alice, target=Bob, earliest_time=2, latest_time=3, layer(s)=[_default]))
- At time 3: Nodes(Node(name=Alice, earliest_time=3, latest_time=3), Node(name=Bob, earliest_time=3, latest_time=3)) Edges(Edge(source=Alice, target=Bob, earliest_time=3, latest_time=4, layer(s)=[_default]))
+ At time 2: Nodes(Node(name=Alice, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=2, event_id=0)), Node(name=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=2, event_id=0))) Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=3, event_id=0), layer(s)=[_default]))
+ At time 3: Nodes(Node(name=Alice, earliest_time=EventTime(timestamp=3, event_id=0), latest_time=EventTime(timestamp=3, event_id=0)), Node(name=Bob, earliest_time=EventTime(timestamp=3, event_id=0), latest_time=EventTime(timestamp=3, event_id=0))) Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=3, event_id=0), latest_time=EventTime(timestamp=4, event_id=0), layer(s)=[_default]))
At time 5: Nodes() Edges()
At time 6: Nodes() Edges()
```
As we can see, the edge's presence in the graph is _inclusive_ of the timestamp at which it was added, but _exclusive_ of the timestamp at which it was deleted. Equivalently, it is present on a interval \\(1 \leq t < 5 \subseteq \mathbb{Z}\\). The earliest and latest times for each edge is adjusted to the time bound in the query.
-While nodes are not present until they are added (see example at time 1), once they are added they are in the graph forever (see example at time 6). This differs from the `Graph` equivalent where nodes are present only when they contain an update within the time bounds.
+While nodes are not present until they are added (see example at time 1), once they are added they are in the graph forever (see example at time 6). This differs from the `Graph` equivalent where nodes are present only when they contain an update within the time bounds.
Crucially, this means that while performing a node count on a `Graph` will count the nodes who have activity (a property update, an adjacent edge added) within the time bounds specified. The same is not true for `PersistentGraph`s.
@@ -84,7 +84,7 @@ print(f"Before time 6: {G.before(6).nodes} {G.before(6).edges.explode()}")
///
```{.python continuation hide}
-assert str(f"Before time 6: {G.before(6).nodes} {G.before(6).edges.explode()}") == "Before time 6: Nodes(Node(name=Alice, earliest_time=2, latest_time=5), Node(name=Bob, earliest_time=2, latest_time=5)) Edges(Edge(source=Alice, target=Bob, earliest_time=2, latest_time=5, layer(s)=[_default]))"
+assert str(f"Before time 6: {G.before(6).nodes} {G.before(6).edges.explode()}") == "Before time 6: Nodes(Node(name=Alice, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=1)), Node(name=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=1))) Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=1), layer(s)=[_default]))"
```
!!! Output
@@ -92,9 +92,9 @@ assert str(f"Before time 6: {G.before(6).nodes} {G.before(6).edges.explode()}")
```output
Before time 1: Nodes() Edges()
Before time 2: Nodes() Edges()
- Before time 3: Nodes(Node(name=Alice, earliest_time=2, latest_time=2), Node(name=Bob, earliest_time=2, latest_time=2)) Edges(Edge(source=Alice, target=Bob, earliest_time=2, latest_time=3, layer(s)=[_default]))
- Before time 5: Nodes(Node(name=Alice, earliest_time=2, latest_time=2), Node(name=Bob, earliest_time=2, latest_time=2)) Edges(Edge(source=Alice, target=Bob, earliest_time=2, latest_time=5, layer(s)=[_default]))
- Before time 6: Nodes(Node(name=Alice, earliest_time=2, latest_time=5), Node(name=Bob, earliest_time=2, latest_time=5)) Edges(Edge(source=Alice, target=Bob, earliest_time=2, latest_time=5, layer(s)=[_default]))
+ Before time 3: Nodes(Node(name=Alice, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=2, event_id=0)), Node(name=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=2, event_id=0))) Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=3, event_id=0), layer(s)=[_default]))
+ Before time 5: Nodes(Node(name=Alice, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=2, event_id=0)), Node(name=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=2, event_id=0))) Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=0), layer(s)=[_default]))
+ Before time 6: Nodes(Node(name=Alice, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=1)), Node(name=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=1))) Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=1), layer(s)=[_default]))
```
Here we see that the `before(T)` bound is exclusive of the end point \\(T\\), creating an intersection between the time interval \\(-\infty < t < T\\) and \\(2 \leq t < 5\\) where \\(T\\) is the argument of `before`.
@@ -133,9 +133,9 @@ assert str(f"After time 6: {G.after(6).nodes} {G.after(6).edges.explode()}") ==
!!! Output
```output
- After time 1: Nodes(Node(name=Alice, earliest_time=2, latest_time=5), Node(name=Bob, earliest_time=2, latest_time=5)) Edges(Edge(source=Alice, target=Bob, earliest_time=2, latest_time=5, layer(s)=[_default]))
- After time 2: Nodes(Node(name=Alice, earliest_time=3, latest_time=5), Node(name=Bob, earliest_time=3, latest_time=5)) Edges(Edge(source=Alice, target=Bob, earliest_time=3, latest_time=5, layer(s)=[_default]))
- After time 3: Nodes(Node(name=Alice, earliest_time=4, latest_time=5), Node(name=Bob, earliest_time=4, latest_time=5)) Edges(Edge(source=Alice, target=Bob, earliest_time=4, latest_time=5, layer(s)=[_default]))
+ After time 1: Nodes(Node(name=Alice, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=1)), Node(name=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=1))) Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=1), layer(s)=[_default]))
+ After time 2: Nodes(Node(name=Alice, earliest_time=EventTime(timestamp=3, event_id=0), latest_time=EventTime(timestamp=5, event_id=1)), Node(name=Bob, earliest_time=EventTime(timestamp=3, event_id=0), latest_time=EventTime(timestamp=5, event_id=1))) Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=3, event_id=0), latest_time=EventTime(timestamp=5, event_id=1), layer(s)=[_default]))
+ After time 3: Nodes(Node(name=Alice, earliest_time=EventTime(timestamp=4, event_id=0), latest_time=EventTime(timestamp=5, event_id=1)), Node(name=Bob, earliest_time=EventTime(timestamp=4, event_id=0), latest_time=EventTime(timestamp=5, event_id=1))) Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=4, event_id=0), latest_time=EventTime(timestamp=5, event_id=1), layer(s)=[_default]))
After time 5: Nodes() Edges()
After time 6: Nodes() Edges()
```
@@ -180,11 +180,11 @@ assert str(f"Window 6,10: {G.window(6,10).nodes} {G.window(6,10).edges.explode()
```output
Window 0,2: Nodes() Edges()
- Window 0,4: Nodes(Node(name=Alice, earliest_time=2, latest_time=2), Node(name=Bob, earliest_time=2, latest_time=2)) Edges(Edge(source=Alice, target=Bob, earliest_time=2, latest_time=4, layer(s)=[_default]))
- Window 3,4: Nodes(Node(name=Alice, earliest_time=3, latest_time=3), Node(name=Bob, earliest_time=3, latest_time=3)) Edges(Edge(source=Alice, target=Bob, earliest_time=3, latest_time=4, layer(s)=[_default]))
+ Window 0,4: Nodes(Node(name=Alice, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=2, event_id=0)), Node(name=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=2, event_id=0))) Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=4, event_id=0), layer(s)=[_default]))
+ Window 3,4: Nodes(Node(name=Alice, earliest_time=EventTime(timestamp=3, event_id=0), latest_time=EventTime(timestamp=3, event_id=0)), Node(name=Bob, earliest_time=EventTime(timestamp=3, event_id=0), latest_time=EventTime(timestamp=3, event_id=0))) Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=3, event_id=0), latest_time=EventTime(timestamp=4, event_id=0), layer(s)=[_default]))
Window 5,8: Nodes() Edges()
- Window 1,8: Nodes(Node(name=Alice, earliest_time=2, latest_time=5), Node(name=Bob, earliest_time=2, latest_time=5)) Edges(Edge(source=Alice, target=Bob, earliest_time=2, latest_time=5, layer(s)=[_default]))
+ Window 1,8: Nodes(Node(name=Alice, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=1)), Node(name=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=1))) Edges(Edge(source=Alice, target=Bob, earliest_time=EventTime(timestamp=2, event_id=0), latest_time=EventTime(timestamp=5, event_id=1), layer(s)=[_default]))
Window 6,10: Nodes() Edges()
```
-A `window(T1, T2)` creates a half-open interval \\(T_1 \leq t < T_2\\) intersecting the edge's active time ( \\(2 \leq t < 5 \\) in this case). When the window is completely inside the edge active time and when the edge's active time is strictly inside the window. In both cases, the edge is treated as present in the graph.
\ No newline at end of file
+A `window(T1, T2)` creates a half-open interval \\(T_1 \leq t < T_2\\) intersecting the edge's active time ( \\(2 \leq t < 5 \\) in this case). When the window is completely inside the edge active time and when the edge's active time is strictly inside the window. In both cases, the edge is treated as present in the graph.
diff --git a/docs/user-guide/querying/1_intro.md b/docs/user-guide/querying/1_intro.md
index 5817381905..ad9dccb135 100644
--- a/docs/user-guide/querying/1_intro.md
+++ b/docs/user-guide/querying/1_intro.md
@@ -1,6 +1,6 @@
# Introduction and dataset
-After reading data into Raphtory we can now make use of the graph representation to ask some interesting questions. This example will use a dataset from [SocioPatterns](http://www.sociopatterns.org/datasets/baboons-interactions/), comprising different behavioral interactions between a group of 22 baboons over a month.
+After reading data into Raphtory we can now make use of the graph representation to ask some interesting questions. This example will use a dataset from [SocioPatterns](http://www.sociopatterns.org/datasets/baboons-interactions/), comprising different behavioral interactions between a group of 22 baboons over a month.
!!! info
@@ -9,7 +9,7 @@ After reading data into Raphtory we can now make use of the graph representation
In the below code loads this dataset into a dataframe and does a small amount of preprocessing to prepare it for loading into Raphtory. This includes dropping rows with blank fields and mapping the values of the `behavior category` into a `weight` which can be aggregated. The mapping consists of the following conversions:
- Affiliative (positive interaction) → `+1`
-- Agonistic (negative interaction) → `-1`
+- Agonistic (negative interaction) → `-1`
- Other (neutral interaction) → `0`
/// tab | :fontawesome-brands-python: Python
@@ -42,7 +42,7 @@ print(edges_df.head())
21 2019-06-13 09:51:00 ANGELE FELIPE Grooming Affiliative 1
```
-Next we load this into Raphtory using the `load_edges_from_pandas` function, modelling it as a weighted multi-layer graph, with a layer per unique `behavior`.
+Next we load this into Raphtory using the `load_edges_from_pandas` function, modelling it as a weighted multi-layer graph, with a layer per unique `behavior`.
/// tab | :fontawesome-brands-python: Python
```{.python continuation}
@@ -62,12 +62,11 @@ print(g)
///
```{.python continuation hide}
-assert str(g) == "Graph(number_of_nodes=22, number_of_edges=290, number_of_temporal_edges=3196, earliest_time=1560419400000, latest_time=1562756700000)"
+assert str(g) == "Graph(number_of_nodes=22, number_of_edges=290, number_of_temporal_edges=3196, earliest_time=EventTime(timestamp=1560419400000, event_id=0), latest_time=EventTime(timestamp=1562756700000, event_id=18446744073709551615))"
```
!!! Output
```output
- Graph(number_of_nodes=22, number_of_edges=290, number_of_temporal_edges=3196, earliest_time=1560419400000, latest_time=1562756700000)
+ Graph(number_of_nodes=22, number_of_edges=290, number_of_temporal_edges=3196, earliest_time=EventTime(timestamp=1560419400000, event_id=0), latest_time=EventTime(timestamp=1562756700000, event_id=18446744073709551615))
```
-
\ No newline at end of file
diff --git a/docs/user-guide/querying/2_graph-metrics.md b/docs/user-guide/querying/2_graph-metrics.md
index 066fe7cde1..1a56c2cd2d 100644
--- a/docs/user-guide/querying/2_graph-metrics.md
+++ b/docs/user-guide/querying/2_graph-metrics.md
@@ -2,10 +2,11 @@
# Graph metrics and functions
## Basic metrics
-Begining with the [previous graph](1_intro.md) we can start probing it for some basic metrics, such as how many nodes and edges it contains and the time range over which it exists.
-In the below code example the functions `count_edges()` and `count_temporal_edges()` are called and return different results. This is because `count_edges()` returns the number of unique edges and `count_temporal_edges()` returns the total edge updates which have occurred.
-
+Beginning with the [previous graph](1_intro.md) we can start probing it for some basic metrics, such as how many nodes and edges it contains and the time range over which it exists.
+
+In the below code example the functions `count_edges()` and `count_temporal_edges()` are called and return different results. This is because `count_edges()` returns the number of unique edges and `count_temporal_edges()` returns the total edge updates which have occurred.
+
Using `count_temporal_edges()` is useful if you want to imagine each edge update as a separate connection between the two nodes. The edges can be accessed in this manner via `edge.explode()`, as is discussed in [edge metrics and functions](../querying/4_edge-metrics.md).
!!! info
@@ -52,10 +53,10 @@ print("Unique layers:", unique_layers, "\n")
print("Stats on the graphs time range:")
-earliest_datetime = g.earliest_date_time
-latest_datetime = g.latest_date_time
-earliest_epoch = g.earliest_time
-latest_epoch = g.latest_time
+earliest_datetime = g.earliest_time.dt
+latest_datetime = g.latest_time.dt
+earliest_epoch = g.earliest_time.t
+latest_epoch = g.latest_time.t
print("Earliest datetime:", earliest_datetime)
print("Latest datetime:", latest_datetime)
@@ -86,12 +87,13 @@ assert total_interactions == 3196
Latest time (Unix Epoch): 1562756700000
```
-## Accessing nodes and edges
-Three types of functions are provided for accessing the nodes and edges within a graph:
+## Accessing nodes and edges
+
+Three types of functions are provided for accessing the nodes and edges within a graph:
-* **Existence check:** using `has_node()` and `has_edge()` you can check if an entity is present within the graph.
-* **Direct access:** `node()` and `edge()` will return a node or edge object if the entity is present and `None` if it is not.
-* **Iterable access:** `nodes` and `edges` will return iterables for all nodes/edges which can be used within a for loop or as part of a [function chain](../querying/6_chaining.md).
+- **Existence check:** using `has_node()` and `has_edge()` you can check if an entity is present within the graph.
+- **Direct access:** `node()` and `edge()` will return a node or edge object if the entity is present and `None` if it is not.
+- **Iterable access:** `nodes` and `edges` will return iterables for all nodes/edges which can be used within a for loop or as part of a [function chain](../querying/6_chaining.md).
All of these functions are shown in the code below and will appear in several other examples throughout this tutorial.
@@ -137,7 +139,7 @@ print(g.edges)
///
```{.python continuation hide}
-assert str(g.node("LOME")) == "Node(name=LOME, earliest_time=1560419520000, latest_time=1562756100000)"
+assert str(g.node("LOME")) == "Node(name=LOME, earliest_time=EventTime(timestamp=1560419520000, event_id=7), latest_time=EventTime(timestamp=1562756100000, event_id=3189))"
```
!!! Output
@@ -145,13 +147,13 @@ assert str(g.node("LOME")) == "Node(name=LOME, earliest_time=1560419520000, late
```output
Checking if specific nodes and edges are in the graph:
Lomme is in the graph
- Lomme has played with Nekke
-
+ Lomme has played with Nekke
+
Getting individual nodes and edges:
- Node(name=LOME, earliest_time=1560419520000, latest_time=1562756100000)
- Edge(source=LOME, target=NEKKE, earliest_time=1560421080000, latest_time=1562755980000, properties={Weight: 1}, layer(s)=[Touching, Grooming, Resting, Playing with, Carrying])
-
+ Node(name=LOME, earliest_time=EventTime(timestamp=1560419520000, event_id=7), latest_time=EventTime(timestamp=1562756100000, event_id=3189))
+ Edge(source=LOME, target=NEKKE, earliest_time=EventTime(timestamp=1560421080000, event_id=22), latest_time=EventTime(timestamp=1562755980000, event_id=3185), properties={Weight: 1}, layer(s)=[Playing with, Resting, Grooming, Touching, Carrying])
+
Getting iterators over all nodes and edges:
- Nodes(Node(name=MALI, earliest_time=1560422040000, latest_time=1562755320000), Node(name=LOME, earliest_time=1560419520000, latest_time=1562756100000), Node(name=NEKKE, earliest_time=1560419520000, latest_time=1562756700000), Node(name=PETOULETTE, earliest_time=1560422520000, latest_time=1562754420000), Node(name=EWINE, earliest_time=1560442020000, latest_time=1562754600000), Node(name=ANGELE, earliest_time=1560419400000, latest_time=1562754600000), Node(name=VIOLETTE, earliest_time=1560423600000, latest_time=1562754900000), Node(name=BOBO, earliest_time=1560419520000, latest_time=1562755500000), Node(name=MAKO, earliest_time=1560421620000, latest_time=1562756100000), Node(name=FEYA, earliest_time=1560420000000, latest_time=1562756040000), ...)
- Edges(Edge(source=ANGELE, target=FELIPE, earliest_time=1560419400000, latest_time=1562753640000, properties={Weight: 1}, layer(s)=[Grooming, Resting, Presenting, Grunting-Lipsmacking, Submission, Copulating]), Edge(source=PIPO, target=KALI, earliest_time=1560420660000, latest_time=1562752560000, properties={Weight: 1}, layer(s)=[Touching, Grooming, Resting, Grunting-Lipsmacking, Chasing, Mounting, Copulating]), Edge(source=LOME, target=NEKKE, earliest_time=1560421080000, latest_time=1562755980000, properties={Weight: 1}, layer(s)=[Touching, Grooming, Resting, Playing with, Carrying]), Edge(source=LOME, target=MUSE, earliest_time=1560421080000, latest_time=1562584200000, properties={Weight: 1}, layer(s)=[Resting, Playing with]), Edge(source=ATMOSPHERE, target=LIPS, earliest_time=1560420000000, latest_time=1560420000000, properties={Weight: 1}, layer(s)=[Playing with]), Edge(source=PIPO, target=FELIPE, earliest_time=1560420720000, latest_time=1562151240000, properties={Weight: 1}, layer(s)=[Resting, Presenting, Grunting-Lipsmacking, Avoiding]), Edge(source=MUSE, target=NEKKE, earliest_time=1560421620000, latest_time=1562755380000, properties={Weight: 1}, layer(s)=[Touching, Grooming, Resting, Playing with, Embracing, Mounting]), Edge(source=MUSE, target=MAKO, earliest_time=1560421620000, latest_time=1562251620000, properties={Weight: 1}, layer(s)=[Touching, Grooming, Resting, Playing with]), Edge(source=PIPO, target=LIPS, earliest_time=1560420720000, latest_time=1560420720000, properties={Weight: 1}, layer(s)=[Resting]), Edge(source=MUSE, target=LOME, earliest_time=1560421620000, latest_time=1562251620000, properties={Weight: 1}, layer(s)=[Playing with, Submission]), ...)
+ Nodes(Node(name=BOBO, earliest_time=EventTime(timestamp=1560419520000, event_id=8), latest_time=EventTime(timestamp=1562755500000, event_id=3182)), Node(name=PIPO, earliest_time=EventTime(timestamp=1560420660000, event_id=16), latest_time=EventTime(timestamp=1562752560000, event_id=3143)), Node(name=MUSE, earliest_time=EventTime(timestamp=1560421080000, event_id=23), latest_time=EventTime(timestamp=1562755500000, event_id=3182)), Node(name=VIOLETTE, earliest_time=EventTime(timestamp=1560423600000, event_id=112), latest_time=EventTime(timestamp=1562754900000, event_id=3172)), Node(name=FELIPE, earliest_time=EventTime(timestamp=1560419400000, event_id=0), latest_time=EventTime(timestamp=1562756700000, event_id=3195)), Node(name=MAKO, earliest_time=EventTime(timestamp=1560421620000, event_id=38), latest_time=EventTime(timestamp=1562756100000, event_id=3189)), Node(name=EWINE, earliest_time=EventTime(timestamp=1560442020000, event_id=192), latest_time=EventTime(timestamp=1562754600000, event_id=3169)), Node(name=LIPS, earliest_time=EventTime(timestamp=1560419460000, event_id=3), latest_time=EventTime(timestamp=1562756700000, event_id=3195)), Node(name=KALI, earliest_time=EventTime(timestamp=1560420660000, event_id=16), latest_time=EventTime(timestamp=1562752560000, event_id=3143)), Node(name=ANGELE, earliest_time=EventTime(timestamp=1560419400000, event_id=0), latest_time=EventTime(timestamp=1562754600000, event_id=3170)), ...)
+ Edges(Edge(source=ANGELE, target=FELIPE, earliest_time=EventTime(timestamp=1560419400000, event_id=0), latest_time=EventTime(timestamp=1562753640000, event_id=3151), properties={Weight: 1}, layer(s)=[Resting, Grunting-Lipsmacking, Presenting, Grooming, Copulating, Submission]), Edge(source=ATMOSPHERE, target=LIPS, earliest_time=EventTime(timestamp=1560420000000, event_id=14), latest_time=EventTime(timestamp=1560420000000, event_id=14), properties={Weight: 1}, layer(s)=[Playing with]), Edge(source=PIPO, target=KALI, earliest_time=EventTime(timestamp=1560420660000, event_id=16), latest_time=EventTime(timestamp=1562752560000, event_id=3143), properties={Weight: 1}, layer(s)=[Resting, Grunting-Lipsmacking, Grooming, Copulating, Touching, Chasing, Mounting]), Edge(source=PIPO, target=FELIPE, earliest_time=EventTime(timestamp=1560420720000, event_id=19), latest_time=EventTime(timestamp=1562151240000, event_id=2244), properties={Weight: 1}, layer(s)=[Resting, Grunting-Lipsmacking, Presenting, Avoiding]), Edge(source=PIPO, target=LIPS, earliest_time=EventTime(timestamp=1560420720000, event_id=21), latest_time=EventTime(timestamp=1560420720000, event_id=21), properties={Weight: 1}, layer(s)=[Resting]), Edge(source=NEKKE, target=LIPS, earliest_time=EventTime(timestamp=1560421980000, event_id=70), latest_time=EventTime(timestamp=1562668860000, event_id=2965), properties={Weight: 1}, layer(s)=[Playing with, Resting, Embracing]), Edge(source=MUSE, target=NEKKE, earliest_time=EventTime(timestamp=1560421620000, event_id=37), latest_time=EventTime(timestamp=1562755380000, event_id=3179), properties={Weight: 1}, layer(s)=[Playing with, Resting, Embracing, Grooming, Touching, Mounting]), Edge(source=NEKKE, target=MAKO, earliest_time=EventTime(timestamp=1560421980000, event_id=71), latest_time=EventTime(timestamp=1562751120000, event_id=3118), properties={Weight: 1}, layer(s)=[Playing with, Resting, Embracing, Presenting, Grooming]), Edge(source=FELIPE, target=ANGELE, earliest_time=EventTime(timestamp=1560419460000, event_id=2), latest_time=EventTime(timestamp=1562754600000, event_id=3170), properties={Weight: 1}, layer(s)=[Resting, Embracing, Grunting-Lipsmacking, Presenting, Touching, Chasing, Mounting, Supplanting, Submission]), Edge(source=NEKKE, target=MUSE, earliest_time=EventTime(timestamp=1560421980000, event_id=72), latest_time=EventTime(timestamp=1562682060000, event_id=3088), properties={Weight: 1}, layer(s)=[Playing with, Resting, Embracing]), ...)
```
diff --git a/docs/user-guide/querying/3_node-metrics.md b/docs/user-guide/querying/3_node-metrics.md
index 460dfcefe0..ea9b5ef38a 100644
--- a/docs/user-guide/querying/3_node-metrics.md
+++ b/docs/user-guide/querying/3_node-metrics.md
@@ -1,10 +1,10 @@
-
# Node metrics and functions
-Nodes can be accessed by storing the object returned from a call to `add_node()`, by directly asking for a specific entity via `node()`, or by iterating over all entities via `nodes`. Once you have a node, you can ask it some questions.
-## Update history
+Nodes can be accessed by storing the object returned from a call to `add_node()`, by directly asking for a specific entity via `node()`, or by iterating over all entities via `nodes`. Once you have a node, you can ask it some questions.
+
+## Update history
-Nodes have functions for querying their earliest and latest update time (as an epoch or datetime) as well as for accessing their full history (using `history()` or `history_date_time()`). In the code below we create a node object for the monkey `Felipe` and see when their updates occurred.
+Nodes have functions for querying their earliest and latest update time (as an epoch or datetime) as well as for accessing their full history (using `history` or `history.dt`). In the code below we create a node object for the monkey `Felipe` and see when their updates occurred.
/// tab | :fontawesome-brands-python: Python
```python
@@ -33,9 +33,9 @@ g.load_edges_from_pandas(
v = g.node("FELIPE")
print(
- f"{v.name}'s first interaction was at {v.earliest_date_time} and their last interaction was at {v.latest_date_time}\n"
+ f"{v.name}'s first interaction was at {v.earliest_time.dt} and their last interaction was at {v.latest_time.dt}\n"
)
-history = v.history_date_time()
+history = v.history.dt
# We format the returned datetime objects here to make the list more readable
history_formatted = [date.strftime("%Y-%m-%d %H:%M:%S") for date in history]
@@ -44,7 +44,7 @@ print(f"{v.name} had interactions at the following times: {history_formatted}\n"
///
```{.python continuation hide}
-assert str(f"{v.name}'s first interaction was at {v.earliest_date_time} and their last interaction was at {v.latest_date_time}") == "FELIPE's first interaction was at 2019-06-13 09:50:00+00:00 and their last interaction was at 2019-07-10 11:05:00+00:00"
+assert str(f"{v.name}'s first interaction was at {v.earliest_time.dt} and their last interaction was at {v.latest_time.dt}") == "FELIPE's first interaction was at 2019-06-13 09:50:00+00:00 and their last interaction was at 2019-07-10 11:05:00+00:00"
```
@@ -53,21 +53,21 @@ assert str(f"{v.name}'s first interaction was at {v.earliest_date_time} and thei
```output
FELIPE's first interaction was at 2019-06-13 09:50:00+00:00 and their last interaction was at 2019-07-10 11:05:00+00:00
- FELIPE had interactions at the following times: ['2019-06-13 09:50:00', '2019-06-13 09:51:00', '2019-06-13 09:52:00', '2019-06-13 09:53:00', '2019-06-13 09:54:00', '2019-06-13 10:12:00', '2019-06-13 10:21:00', '2019-06-13 10:43:00', '2019-06-13 10:56:00', '2019-06-13 10:57:00', '2019-06-13 11:00:00', '2019-06-13 14:50:00', '2019-06-13 14:51:00', '2019-06-13 14:54:00', '2019-06-13 15:32:00', '2019-06-13 15:33:00', '2019-06-13 15:36:00', '2019-06-13 15:44:00', '2019-06-13 16:03:00', '2019-06-13 16:04:00', '2019-06-13 16:06:00', '2019-06-13 16:24:00', '2019-06-14 09:20:00', '2019-06-14 09:29:00', '2019-06-14 10:06:00', '2019-06-14 10:19:00', '2019-06-14 10:32:00', '2019-06-14 10:33:00', '2019-06-14 10:35:00', '2019-06-14 14:41:00', '2019-06-14 14:47:00', '2019-06-14 14:48:00', '2019-06-14 14:49:00', '2019-06-14 14:51:00', '2019-06-14 15:08:00', '2019-06-14 15:20:00', '2019-06-14 15:22:00', '2019-06-14 15:29:00', '2019-06-14 15:31:00', '2019-06-14 15:32:00', '2019-06-17 11:02:00', '2019-06-17 11:04:00', '2019-06-17 12:49:00', '2019-06-17 14:01:00', '2019-06-17 14:02:00', '2019-06-18 10:25:00', '2019-06-18 10:36:00', '2019-06-18 10:40:00', '2019-06-18 11:01:00', '2019-06-18 13:42:00', '2019-06-18 13:44:00', '2019-06-18 13:45:00', '2019-06-19 08:56:00', '2019-06-19 08:58:00', '2019-06-19 08:59:00', '2019-06-19 09:11:00', '2019-06-19 09:12:00', '2019-06-19 09:14:00', '2019-06-19 09:26:00', '2019-06-19 09:31:00', '2019-06-19 09:42:00', '2019-06-19 10:25:00', '2019-06-19 10:26:00', '2019-06-19 10:27:00', '2019-06-19 11:25:00', '2019-06-19 12:01:00', '2019-06-19 12:05:00', '2019-06-19 12:06:00', '2019-06-19 12:23:00', '2019-06-19 12:34:00', '2019-06-19 12:43:00', '2019-06-19 12:48:00', '2019-06-19 12:49:00', '2019-06-19 12:50:00', '2019-06-20 10:05:00', '2019-06-20 10:15:00', '2019-06-20 10:36:00', '2019-06-20 10:51:00', '2019-06-20 11:17:00', '2019-06-20 14:56:00', '2019-06-20 14:57:00', '2019-06-20 14:59:00', '2019-06-20 15:08:00', '2019-06-20 15:09:00', '2019-06-20 15:20:00', '2019-06-20 15:27:00', '2019-06-20 15:30:00', '2019-06-20 15:55:00', '2019-06-21 09:43:00', '2019-06-21 10:38:00', '2019-06-21 10:39:00', '2019-06-21 11:11:00', '2019-06-21 11:14:00', '2019-06-21 11:39:00', '2019-06-21 11:40:00', '2019-06-21 11:46:00', '2019-06-21 11:47:00', '2019-06-21 11:48:00', '2019-06-21 11:49:00', '2019-06-21 12:03:00', '2019-06-21 12:41:00', '2019-06-21 12:57:00', '2019-06-21 13:00:00', '2019-06-21 13:01:00', '2019-06-21 13:02:00', '2019-06-24 10:54:00', '2019-06-24 10:56:00', '2019-06-24 10:57:00', '2019-06-24 10:58:00', '2019-06-24 10:59:00', '2019-06-24 11:01:00', '2019-06-24 11:08:00', '2019-06-24 11:09:00', '2019-06-24 11:14:00', '2019-06-24 11:22:00', '2019-06-24 15:40:00', '2019-06-24 15:41:00', '2019-06-24 15:42:00', '2019-06-24 15:43:00', '2019-06-24 16:09:00', '2019-06-24 16:11:00', '2019-06-25 10:25:00', '2019-06-25 10:30:00', '2019-06-25 10:48:00', '2019-06-25 10:49:00', '2019-06-25 11:13:00', '2019-06-25 11:14:00', '2019-06-25 11:26:00', '2019-06-25 15:11:00', '2019-06-25 15:17:00', '2019-06-25 15:52:00', '2019-06-25 15:54:00', '2019-06-25 16:03:00', '2019-06-25 16:04:00', '2019-06-25 16:09:00', '2019-06-26 09:10:00', '2019-06-26 09:11:00', '2019-06-26 09:32:00', '2019-06-26 09:33:00', '2019-06-26 09:53:00', '2019-06-26 09:54:00', '2019-06-26 09:58:00', '2019-06-26 10:05:00', '2019-06-26 10:06:00', '2019-06-26 10:37:00', '2019-06-26 10:39:00', '2019-06-26 13:22:00', '2019-06-26 13:35:00', '2019-06-26 13:39:00', '2019-06-26 13:40:00', '2019-06-26 14:17:00', '2019-06-26 14:18:00', '2019-06-27 09:31:00', '2019-06-27 09:37:00', '2019-06-27 12:29:00', '2019-06-27 12:46:00', '2019-06-27 13:03:00', '2019-06-27 13:49:00', '2019-06-27 13:52:00', '2019-06-27 13:53:00', '2019-06-28 10:15:00', '2019-06-28 10:16:00', '2019-06-28 10:17:00', '2019-06-28 10:18:00', '2019-06-28 10:19:00', '2019-06-28 10:20:00', '2019-06-28 11:12:00', '2019-06-28 11:13:00', '2019-07-01 08:44:00', '2019-07-01 08:46:00', '2019-07-01 08:49:00', '2019-07-01 08:51:00', '2019-07-01 09:06:00', '2019-07-01 09:21:00', '2019-07-01 10:13:00', '2019-07-01 13:20:00', '2019-07-01 14:45:00', '2019-07-02 08:52:00', '2019-07-02 13:32:00', '2019-07-02 13:33:00', '2019-07-02 13:34:00', '2019-07-02 13:35:00', '2019-07-02 13:36:00', '2019-07-02 14:35:00', '2019-07-02 14:39:00', '2019-07-03 09:27:00', '2019-07-03 09:39:00', '2019-07-03 09:41:00', '2019-07-03 10:14:00', '2019-07-03 10:15:00', '2019-07-03 10:16:00', '2019-07-03 10:17:00', '2019-07-03 10:18:00', '2019-07-03 10:54:00', '2019-07-03 11:16:00', '2019-07-03 11:17:00', '2019-07-03 11:37:00', '2019-07-03 12:03:00', '2019-07-03 12:05:00', '2019-07-04 09:30:00', '2019-07-04 09:31:00', '2019-07-04 09:39:00', '2019-07-04 09:45:00', '2019-07-04 09:46:00', '2019-07-04 10:00:00', '2019-07-04 10:25:00', '2019-07-04 10:55:00', '2019-07-04 10:59:00', '2019-07-04 14:02:00', '2019-07-04 14:04:00', '2019-07-04 14:05:00', '2019-07-04 14:30:00', '2019-07-04 14:38:00', '2019-07-04 15:09:00', '2019-07-04 15:19:00', '2019-07-05 10:10:00', '2019-07-05 10:12:00', '2019-07-05 10:13:00', '2019-07-05 10:20:00', '2019-07-05 10:46:00', '2019-07-05 11:05:00', '2019-07-05 11:40:00', '2019-07-05 11:41:00', '2019-07-05 11:57:00', '2019-07-05 12:57:00', '2019-07-05 13:02:00', '2019-07-05 13:07:00', '2019-07-05 13:13:00', '2019-07-08 11:34:00', '2019-07-08 11:39:00', '2019-07-08 11:40:00', '2019-07-08 11:46:00', '2019-07-08 11:50:00', '2019-07-08 12:37:00', '2019-07-08 14:32:00', '2019-07-08 14:33:00', '2019-07-08 15:42:00', '2019-07-08 15:48:00', '2019-07-08 15:51:00', '2019-07-09 10:57:00', '2019-07-09 10:59:00', '2019-07-09 11:01:00', '2019-07-09 11:17:00', '2019-07-09 11:20:00', '2019-07-09 13:37:00', '2019-07-09 13:38:00', '2019-07-09 13:39:00', '2019-07-09 13:40:00', '2019-07-09 14:34:00', '2019-07-09 14:36:00', '2019-07-09 14:37:00', '2019-07-10 10:00:00', '2019-07-10 10:06:00', '2019-07-10 10:14:00', '2019-07-10 10:30:00', '2019-07-10 10:44:00', '2019-07-10 11:05:00']
+ FELIPE had interactions at the following times: ['2019-06-13 09:50:00', '2019-06-13 09:50:00', '2019-06-13 09:51:00', '2019-06-13 09:51:00', '2019-06-13 09:51:00', '2019-06-13 09:52:00', '2019-06-13 09:52:00', '2019-06-13 09:52:00', '2019-06-13 09:52:00', '2019-06-13 09:53:00', '2019-06-13 09:53:00', '2019-06-13 09:53:00', '2019-06-13 09:54:00', '2019-06-13 10:12:00', '2019-06-13 10:12:00', '2019-06-13 10:12:00', '2019-06-13 10:21:00', '2019-06-13 10:21:00', '2019-06-13 10:43:00', '2019-06-13 10:56:00', '2019-06-13 10:56:00', '2019-06-13 10:57:00', '2019-06-13 10:57:00', '2019-06-13 10:57:00', '2019-06-13 11:00:00', '2019-06-13 14:50:00', '2019-06-13 14:50:00', '2019-06-13 14:50:00', '2019-06-13 14:50:00', '2019-06-13 14:51:00', '2019-06-13 14:54:00', '2019-06-13 14:54:00', '2019-06-13 15:32:00', '2019-06-13 15:32:00', '2019-06-13 15:33:00', '2019-06-13 15:36:00', '2019-06-13 15:44:00', '2019-06-13 16:03:00', '2019-06-13 16:03:00', '2019-06-13 16:04:00', '2019-06-13 16:06:00', '2019-06-13 16:24:00', '2019-06-13 16:24:00', '2019-06-14 09:20:00', '2019-06-14 09:20:00', '2019-06-14 09:29:00', '2019-06-14 09:29:00', '2019-06-14 10:06:00', '2019-06-14 10:19:00', '2019-06-14 10:32:00', '2019-06-14 10:32:00', '2019-06-14 10:33:00', '2019-06-14 10:35:00', '2019-06-14 10:35:00', '2019-06-14 14:41:00', '2019-06-14 14:47:00', '2019-06-14 14:47:00', '2019-06-14 14:48:00', '2019-06-14 14:49:00', '2019-06-14 14:49:00', '2019-06-14 14:49:00', '2019-06-14 14:51:00', '2019-06-14 15:08:00', '2019-06-14 15:20:00', '2019-06-14 15:22:00', '2019-06-14 15:29:00', '2019-06-14 15:29:00', '2019-06-14 15:29:00', '2019-06-14 15:31:00', '2019-06-14 15:32:00', '2019-06-14 15:32:00', '2019-06-14 15:32:00', '2019-06-14 15:32:00', '2019-06-17 11:02:00', '2019-06-17 11:04:00', '2019-06-17 11:04:00', '2019-06-17 12:49:00', '2019-06-17 14:01:00', '2019-06-17 14:02:00', '2019-06-17 14:02:00', '2019-06-18 10:25:00', '2019-06-18 10:25:00', '2019-06-18 10:25:00', '2019-06-18 10:36:00', '2019-06-18 10:40:00', '2019-06-18 11:01:00', '2019-06-18 11:01:00', '2019-06-18 13:42:00', '2019-06-18 13:42:00', '2019-06-18 13:42:00', '2019-06-18 13:44:00', '2019-06-18 13:44:00', '2019-06-18 13:44:00', '2019-06-18 13:45:00', '2019-06-18 13:45:00', '2019-06-18 13:45:00', '2019-06-19 08:56:00', '2019-06-19 08:58:00', '2019-06-19 08:58:00', '2019-06-19 08:58:00', '2019-06-19 08:59:00', '2019-06-19 08:59:00', '2019-06-19 09:11:00', '2019-06-19 09:11:00', '2019-06-19 09:12:00', '2019-06-19 09:14:00', '2019-06-19 09:14:00', '2019-06-19 09:14:00', '2019-06-19 09:26:00', '2019-06-19 09:31:00', '2019-06-19 09:42:00', '2019-06-19 10:25:00', '2019-06-19 10:26:00', '2019-06-19 10:26:00', '2019-06-19 10:27:00', '2019-06-19 11:25:00', '2019-06-19 12:01:00', '2019-06-19 12:05:00', '2019-06-19 12:06:00', '2019-06-19 12:23:00', '2019-06-19 12:34:00', '2019-06-19 12:34:00', '2019-06-19 12:43:00', '2019-06-19 12:48:00', '2019-06-19 12:48:00', '2019-06-19 12:48:00', '2019-06-19 12:48:00', '2019-06-19 12:48:00', '2019-06-19 12:48:00', '2019-06-19 12:49:00', '2019-06-19 12:49:00', '2019-06-19 12:49:00', '2019-06-19 12:49:00', '2019-06-19 12:50:00', '2019-06-19 12:50:00', '2019-06-20 10:05:00', '2019-06-20 10:15:00', '2019-06-20 10:36:00', '2019-06-20 10:51:00', '2019-06-20 11:17:00', '2019-06-20 14:56:00', '2019-06-20 14:56:00', '2019-06-20 14:57:00', '2019-06-20 14:59:00', '2019-06-20 15:08:00', '2019-06-20 15:08:00', '2019-06-20 15:08:00', '2019-06-20 15:08:00', '2019-06-20 15:08:00', '2019-06-20 15:09:00', '2019-06-20 15:20:00', '2019-06-20 15:27:00', '2019-06-20 15:27:00', '2019-06-20 15:30:00', '2019-06-20 15:55:00', '2019-06-21 09:43:00', '2019-06-21 10:38:00', '2019-06-21 10:38:00', '2019-06-21 10:39:00', '2019-06-21 11:11:00', '2019-06-21 11:11:00', '2019-06-21 11:14:00', '2019-06-21 11:39:00', '2019-06-21 11:40:00', '2019-06-21 11:46:00', '2019-06-21 11:46:00', '2019-06-21 11:46:00', '2019-06-21 11:46:00', '2019-06-21 11:46:00', '2019-06-21 11:47:00', '2019-06-21 11:47:00', '2019-06-21 11:47:00', '2019-06-21 11:48:00', '2019-06-21 11:48:00', '2019-06-21 11:49:00', '2019-06-21 12:03:00', '2019-06-21 12:41:00', '2019-06-21 12:57:00', '2019-06-21 13:00:00', '2019-06-21 13:00:00', '2019-06-21 13:01:00', '2019-06-21 13:02:00', '2019-06-24 10:54:00', '2019-06-24 10:56:00', '2019-06-24 10:57:00', '2019-06-24 10:58:00', '2019-06-24 10:58:00', '2019-06-24 10:58:00', '2019-06-24 10:58:00', '2019-06-24 10:59:00', '2019-06-24 10:59:00', '2019-06-24 10:59:00', '2019-06-24 10:59:00', '2019-06-24 10:59:00', '2019-06-24 11:01:00', '2019-06-24 11:08:00', '2019-06-24 11:08:00', '2019-06-24 11:09:00', '2019-06-24 11:14:00', '2019-06-24 11:14:00', '2019-06-24 11:22:00', '2019-06-24 15:40:00', '2019-06-24 15:40:00', '2019-06-24 15:40:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-24 15:42:00', '2019-06-24 15:42:00', '2019-06-24 15:42:00', '2019-06-24 15:42:00', '2019-06-24 15:42:00', '2019-06-24 15:42:00', '2019-06-24 15:43:00', '2019-06-24 15:43:00', '2019-06-24 16:09:00', '2019-06-24 16:11:00', '2019-06-25 10:25:00', '2019-06-25 10:30:00', '2019-06-25 10:30:00', '2019-06-25 10:48:00', '2019-06-25 10:49:00', '2019-06-25 11:13:00', '2019-06-25 11:14:00', '2019-06-25 11:14:00', '2019-06-25 11:14:00', '2019-06-25 11:26:00', '2019-06-25 15:11:00', '2019-06-25 15:11:00', '2019-06-25 15:11:00', '2019-06-25 15:17:00', '2019-06-25 15:17:00', '2019-06-25 15:17:00', '2019-06-25 15:52:00', '2019-06-25 15:52:00', '2019-06-25 15:54:00', '2019-06-25 16:03:00', '2019-06-25 16:03:00', '2019-06-25 16:04:00', '2019-06-25 16:04:00', '2019-06-25 16:09:00', '2019-06-26 09:10:00', '2019-06-26 09:11:00', '2019-06-26 09:32:00', '2019-06-26 09:32:00', '2019-06-26 09:33:00', '2019-06-26 09:53:00', '2019-06-26 09:53:00', '2019-06-26 09:54:00', '2019-06-26 09:58:00', '2019-06-26 10:05:00', '2019-06-26 10:06:00', '2019-06-26 10:37:00', '2019-06-26 10:37:00', '2019-06-26 10:39:00', '2019-06-26 13:22:00', '2019-06-26 13:35:00', '2019-06-26 13:39:00', '2019-06-26 13:40:00', '2019-06-26 13:40:00', '2019-06-26 14:17:00', '2019-06-26 14:18:00', '2019-06-27 09:31:00', '2019-06-27 09:31:00', '2019-06-27 09:37:00', '2019-06-27 12:29:00', '2019-06-27 12:46:00', '2019-06-27 13:03:00', '2019-06-27 13:49:00', '2019-06-27 13:52:00', '2019-06-27 13:52:00', '2019-06-27 13:52:00', '2019-06-27 13:52:00', '2019-06-27 13:53:00', '2019-06-27 13:53:00', '2019-06-27 13:53:00', '2019-06-27 13:53:00', '2019-06-28 10:15:00', '2019-06-28 10:16:00', '2019-06-28 10:17:00', '2019-06-28 10:18:00', '2019-06-28 10:18:00', '2019-06-28 10:19:00', '2019-06-28 10:19:00', '2019-06-28 10:19:00', '2019-06-28 10:19:00', '2019-06-28 10:19:00', '2019-06-28 10:19:00', '2019-06-28 10:20:00', '2019-06-28 11:12:00', '2019-06-28 11:13:00', '2019-06-28 11:13:00', '2019-07-01 08:44:00', '2019-07-01 08:46:00', '2019-07-01 08:46:00', '2019-07-01 08:49:00', '2019-07-01 08:51:00', '2019-07-01 09:06:00', '2019-07-01 09:21:00', '2019-07-01 09:21:00', '2019-07-01 10:13:00', '2019-07-01 13:20:00', '2019-07-01 14:45:00', '2019-07-01 14:45:00', '2019-07-01 14:45:00', '2019-07-02 08:52:00', '2019-07-02 08:52:00', '2019-07-02 13:32:00', '2019-07-02 13:33:00', '2019-07-02 13:34:00', '2019-07-02 13:34:00', '2019-07-02 13:35:00', '2019-07-02 13:35:00', '2019-07-02 13:36:00', '2019-07-02 14:35:00', '2019-07-02 14:39:00', '2019-07-03 09:27:00', '2019-07-03 09:39:00', '2019-07-03 09:41:00', '2019-07-03 10:14:00', '2019-07-03 10:14:00', '2019-07-03 10:14:00', '2019-07-03 10:15:00', '2019-07-03 10:15:00', '2019-07-03 10:15:00', '2019-07-03 10:16:00', '2019-07-03 10:16:00', '2019-07-03 10:16:00', '2019-07-03 10:16:00', '2019-07-03 10:16:00', '2019-07-03 10:17:00', '2019-07-03 10:17:00', '2019-07-03 10:17:00', '2019-07-03 10:17:00', '2019-07-03 10:17:00', '2019-07-03 10:18:00', '2019-07-03 10:18:00', '2019-07-03 10:18:00', '2019-07-03 10:18:00', '2019-07-03 10:18:00', '2019-07-03 10:54:00', '2019-07-03 11:16:00', '2019-07-03 11:17:00', '2019-07-03 11:17:00', '2019-07-03 11:37:00', '2019-07-03 12:03:00', '2019-07-03 12:05:00', '2019-07-04 09:30:00', '2019-07-04 09:30:00', '2019-07-04 09:30:00', '2019-07-04 09:31:00', '2019-07-04 09:39:00', '2019-07-04 09:45:00', '2019-07-04 09:45:00', '2019-07-04 09:46:00', '2019-07-04 10:00:00', '2019-07-04 10:25:00', '2019-07-04 10:55:00', '2019-07-04 10:59:00', '2019-07-04 14:02:00', '2019-07-04 14:04:00', '2019-07-04 14:05:00', '2019-07-04 14:05:00', '2019-07-04 14:05:00', '2019-07-04 14:30:00', '2019-07-04 14:30:00', '2019-07-04 14:38:00', '2019-07-04 14:38:00', '2019-07-04 15:09:00', '2019-07-04 15:19:00', '2019-07-04 15:19:00', '2019-07-04 15:19:00', '2019-07-04 15:19:00', '2019-07-04 15:19:00', '2019-07-05 10:10:00', '2019-07-05 10:12:00', '2019-07-05 10:12:00', '2019-07-05 10:13:00', '2019-07-05 10:13:00', '2019-07-05 10:13:00', '2019-07-05 10:13:00', '2019-07-05 10:20:00', '2019-07-05 10:46:00', '2019-07-05 10:46:00', '2019-07-05 10:46:00', '2019-07-05 11:05:00', '2019-07-05 11:05:00', '2019-07-05 11:05:00', '2019-07-05 11:05:00', '2019-07-05 11:05:00', '2019-07-05 11:40:00', '2019-07-05 11:40:00', '2019-07-05 11:40:00', '2019-07-05 11:41:00', '2019-07-05 11:57:00', '2019-07-05 11:57:00', '2019-07-05 12:57:00', '2019-07-05 13:02:00', '2019-07-05 13:02:00', '2019-07-05 13:07:00', '2019-07-05 13:13:00', '2019-07-05 13:13:00', '2019-07-08 11:34:00', '2019-07-08 11:34:00', '2019-07-08 11:34:00', '2019-07-08 11:39:00', '2019-07-08 11:39:00', '2019-07-08 11:40:00', '2019-07-08 11:40:00', '2019-07-08 11:40:00', '2019-07-08 11:46:00', '2019-07-08 11:50:00', '2019-07-08 11:50:00', '2019-07-08 12:37:00', '2019-07-08 12:37:00', '2019-07-08 14:32:00', '2019-07-08 14:32:00', '2019-07-08 14:32:00', '2019-07-08 14:32:00', '2019-07-08 14:33:00', '2019-07-08 15:42:00', '2019-07-08 15:48:00', '2019-07-08 15:48:00', '2019-07-08 15:48:00', '2019-07-08 15:51:00', '2019-07-09 10:57:00', '2019-07-09 10:57:00', '2019-07-09 10:59:00', '2019-07-09 11:01:00', '2019-07-09 11:01:00', '2019-07-09 11:17:00', '2019-07-09 11:17:00', '2019-07-09 11:17:00', '2019-07-09 11:17:00', '2019-07-09 11:17:00', '2019-07-09 11:17:00', '2019-07-09 11:17:00', '2019-07-09 11:17:00', '2019-07-09 11:20:00', '2019-07-09 13:37:00', '2019-07-09 13:37:00', '2019-07-09 13:37:00', '2019-07-09 13:38:00', '2019-07-09 13:39:00', '2019-07-09 13:39:00', '2019-07-09 13:40:00', '2019-07-09 14:34:00', '2019-07-09 14:34:00', '2019-07-09 14:36:00', '2019-07-09 14:37:00', '2019-07-10 10:00:00', '2019-07-10 10:06:00', '2019-07-10 10:14:00', '2019-07-10 10:30:00', '2019-07-10 10:30:00', '2019-07-10 10:44:00', '2019-07-10 11:05:00', '2019-07-10 11:05:00']
```
## Neighbours, edges and paths
+
To investigate who a node is connected with we can ask for its `degree()`, `edges`, or `neighbours`. As Raphtory graphs are directed, all of these functions also have an `in_` and `out_` variation, allowing you get only incoming and outgoing connections respectively. These functions return the following:
-* **degree:** A count of the number of unique connections a node has
-* **edges:** An `Edges` iterable of edge objects, one for each unique `(src,dst)` pair
-* **neighbours:** A `PathFromNode` iterable of node objects, one for each entity the original node shares an edge with
+- **degree:** A count of the number of unique connections a node has
+- **edges:** An `Edges` iterable of edge objects, one for each unique `(src,dst)` pair
+- **neighbours:** A `PathFromNode` iterable of node objects, one for each entity the original node shares an edge with
In the code below we call a selection of these functions to show the sort of questions you may ask.
!!! info
-
- The final section of the code makes use of `v.neighbours.name.collect()` - this is a chain of functions which are run on each node in the `PathFromNode` iterable. We will discuss these sort of operations more in [Chaining functions](../querying/6_chaining.md).
+ The final section of the code makes use of `v.neighbours.name.collect()` - this is a chain of functions which are run on each node in the `PathFromNode` iterable. We will discuss these sort of operations more in [Chaining functions](../querying/6_chaining.md).
/// tab | :fontawesome-brands-python: Python
```python
@@ -119,9 +119,9 @@ assert str(f"{v_name} has {in_degree} incoming interactions and {out_degree} out
```output
FELIPE has 17 incoming interactions and 18 outgoing interactions.
-
- Edges(Edge(source=MALI, target=FELIPE, earliest_time=1561117140000, latest_time=1562753160000, properties={Weight: 1}, layer(s)=[Grooming, Resting, Presenting]), Edge(source=LOME, target=FELIPE, earliest_time=1560421260000, latest_time=1562149080000, properties={Weight: 1}, layer(s)=[Grooming, Resting, Playing with, Presenting, Chasing]), Edge(source=NEKKE, target=FELIPE, earliest_time=1560443040000, latest_time=1562596380000, properties={Weight: 1}, layer(s)=[Touching, Resting, Presenting, Grunting-Lipsmacking, Embracing]), Edge(source=PETOULETTE, target=FELIPE, earliest_time=1561628220000, latest_time=1562252940000, properties={Weight: 1}, layer(s)=[Resting]), Edge(source=EWINE, target=FELIPE, earliest_time=1560523260000, latest_time=1562585640000, properties={Weight: 1}, layer(s)=[Grooming, Resting, Presenting, Avoiding]), Edge(source=ANGELE, target=FELIPE, earliest_time=1560419400000, latest_time=1562753640000, properties={Weight: 1}, layer(s)=[Grooming, Resting, Presenting, Grunting-Lipsmacking, Submission, Copulating]), Edge(source=VIOLETTE, target=FELIPE, earliest_time=1560439920000, latest_time=1561373760000, properties={Weight: 1}, layer(s)=[Grooming, Resting, Presenting]), Edge(source=BOBO, target=FELIPE, earliest_time=1560423360000, latest_time=1561543080000, properties={Weight: -1}, layer(s)=[Presenting, Grunting-Lipsmacking, Avoiding]), Edge(source=MAKO, target=FELIPE, earliest_time=1560937320000, latest_time=1562679600000, properties={Weight: 1}, layer(s)=[Grooming, Resting, Playing with, Presenting, Grunting-Lipsmacking, Embracing]), Edge(source=FEYA, target=FELIPE, earliest_time=1560853500000, latest_time=1562586000000, properties={Weight: 1}, layer(s)=[Resting, Presenting]), ...)
- PathFromNode(Node(name=MALI, earliest_time=1560422040000, latest_time=1562755320000), Node(name=LOME, earliest_time=1560419520000, latest_time=1562756100000), Node(name=NEKKE, earliest_time=1560419520000, latest_time=1562756700000), Node(name=PETOULETTE, earliest_time=1560422520000, latest_time=1562754420000), Node(name=EWINE, earliest_time=1560442020000, latest_time=1562754600000), Node(name=ANGELE, earliest_time=1560419400000, latest_time=1562754600000), Node(name=VIOLETTE, earliest_time=1560423600000, latest_time=1562754900000), Node(name=BOBO, earliest_time=1560419520000, latest_time=1562755500000), Node(name=MAKO, earliest_time=1560421620000, latest_time=1562756100000), Node(name=FEYA, earliest_time=1560420000000, latest_time=1562756040000), ...)
- FELIPE interacted with the following baboons ['MALI', 'LOME', 'NEKKE', 'PETOULETTE', 'EWINE', 'ANGELE', 'VIOLETTE', 'BOBO', 'MAKO', 'FEYA', 'LIPS', 'ATMOSPHERE', 'FANA', 'MUSE', 'HARLEM', 'PIPO', 'ARIELLE', 'SELF']
- ```
\ No newline at end of file
+ Edges(Edge(source=ANGELE, target=FELIPE, earliest_time=EventTime[1560419400000, 0], latest_time=EventTime[1562753640000, 3151], properties={Weight: 1}, layer(s)=[Resting, Presenting, Grooming, Grunting-Lipsmacking, Submission, Copulating]), Edge(source=MAKO, target=FELIPE, earliest_time=EventTime[1560937320000, 692], latest_time=EventTime[1562679600000, 3056], properties={Weight: 1}, layer(s)=[Resting, Presenting, Grooming, Playing with, Embracing, Grunting-Lipsmacking]), Edge(source=NEKKE, target=FELIPE, earliest_time=EventTime[1560443040000, 199], latest_time=EventTime[1562596380000, 2839], properties={Weight: 1}, layer(s)=[Resting, Touching, Presenting, Embracing, Grunting-Lipsmacking]), Edge(source=MALI, target=FELIPE, earliest_time=EventTime[1561117140000, 1060], latest_time=EventTime[1562753160000, 3148], properties={Weight: 1}, layer(s)=[Resting, Presenting, Grooming]), Edge(source=ARIELLE, target=FELIPE, earliest_time=EventTime[1561046100000, 923], latest_time=EventTime[1562586600000, 2805], properties={Weight: 1}, layer(s)=[Resting, Presenting, Grooming, Submission]), Edge(source=MUSE, target=FELIPE, earliest_time=EventTime[1560504540000, 213], latest_time=EventTime[1562755440000, 3181], properties={Weight: 1}, layer(s)=[Resting, Presenting, Grooming, Grunting-Lipsmacking, Submission]), Edge(source=FEYA, target=FELIPE, earliest_time=EventTime[1560853500000, 530], latest_time=EventTime[1562586000000, 2800], properties={Weight: 1}, layer(s)=[Resting, Presenting]), Edge(source=LOME, target=FELIPE, earliest_time=EventTime[1560421260000, 33], latest_time=EventTime[1562149080000, 2228], properties={Weight: 1}, layer(s)=[Resting, Presenting, Grooming, Playing with, Chasing]), Edge(source=VIOLETTE, target=FELIPE, earliest_time=EventTime[1560439920000, 156], latest_time=EventTime[1561373760000, 1208], properties={Weight: 1}, layer(s)=[Resting, Presenting, Grooming]), Edge(source=FANA, target=FELIPE, earliest_time=EventTime[1560526140000, 360], latest_time=EventTime[1562752800000, 3144], properties={Weight: 1}, layer(s)=[Resting, Touching, Presenting, Grooming, Submission, Copulating]), ...)
+ PathFromNode(Node(name=ANGELE, earliest_time=EventTime[1560419400000, 0], latest_time=EventTime[1562754600000, 3170]), Node(name=MAKO, earliest_time=EventTime[1560421620000, 38], latest_time=EventTime[1562756100000, 3189]), Node(name=NEKKE, earliest_time=EventTime[1560419520000, 6], latest_time=EventTime[1562756700000, 3194]), Node(name=MALI, earliest_time=EventTime[1560422040000, 81], latest_time=EventTime[1562755320000, 3177]), Node(name=ARIELLE, earliest_time=EventTime[1560422520000, 102], latest_time=EventTime[1562756520000, 3191]), Node(name=MUSE, earliest_time=EventTime[1560421080000, 23], latest_time=EventTime[1562755500000, 3182]), Node(name=FEYA, earliest_time=EventTime[1560420000000, 13], latest_time=EventTime[1562756040000, 3187]), Node(name=LOME, earliest_time=EventTime[1560419520000, 7], latest_time=EventTime[1562756100000, 3189]), Node(name=VIOLETTE, earliest_time=EventTime[1560423600000, 112], latest_time=EventTime[1562754900000, 3172]), Node(name=FANA, earliest_time=EventTime[1560420000000, 13], latest_time=EventTime[1562754600000, 3171]), ...)
+
+ FELIPE interacted with the following baboons ['ANGELE', 'MAKO', 'NEKKE', 'MALI', 'ARIELLE', 'MUSE', 'FEYA', 'LOME', 'VIOLETTE', 'FANA', 'HARLEM', 'EWINE', 'BOBO', 'PIPO', 'ATMOSPHERE', 'LIPS', 'PETOULETTE', 'SELF']
+ ```
diff --git a/docs/user-guide/querying/4_edge-metrics.md b/docs/user-guide/querying/4_edge-metrics.md
index 5205884df5..eb1860b6f1 100644
--- a/docs/user-guide/querying/4_edge-metrics.md
+++ b/docs/user-guide/querying/4_edge-metrics.md
@@ -1,12 +1,14 @@
-# Edge metrics and functions
-Edges can be accessed by storing the object returned from a call to `add_edge()`, by directly asking for a specific edge via `edge()`, or by iterating over all edges with `in-edges`, `out-edges`, or `edges`.
+# Edge metrics and functions
+
+Edges can be accessed by storing the object returned from a call to `add_edge()`, by directly asking for a specific edge via `edge()`, or by iterating over all edges with `in-edges`, `out-edges`, or `edges`.
## Edge structure and update history
-By default an edge object in Raphtory will contain all updates over all layers between the given source and destination nodes. As an example, we can look at the two edges between `FELIPE` and `MAKO` (one for each direction).
+
+By default an edge object in Raphtory will contain all updates over all layers between the given source and destination nodes. As an example, we can look at the two edges between `FELIPE` and `MAKO` (one for each direction).
In the code below we create the two edge objects by requesting them from the graph and then print out the layers each is involved in with `layer_names`. We can see that there are multiple behaviors in each direction represented within the edges.
-Following this we access the history to get the earliest and latest update times. This update history consists all interactions across all layers.
+Following this we access the history to get the earliest and latest update times. This update [history][raphtory.History] consists all interactions across all layers.
!!!info
@@ -39,22 +41,22 @@ g.load_edges_from_pandas(
e = g.edge("FELIPE", "MAKO")
e_reversed = g.edge("MAKO", "FELIPE")
-e_history = [date.strftime("%Y-%m-%d %H:%M:%S") for date in e.history_date_time()]
+e_history = [date.strftime("%Y-%m-%d %H:%M:%S") for date in e.history.dt]
e_reversed_history = [
- date.strftime("%Y-%m-%d %H:%M:%S") for date in e_reversed.history_date_time()
+ date.strftime("%Y-%m-%d %H:%M:%S") for date in e_reversed.history.dt
]
print(
f"The edge from {e.src.name} to {e.dst.name} covers the following layers: {e.layer_names}"
)
print(
- f"and has updates between {e.earliest_date_time} and {e.latest_date_time} at the following times: {e_history}\n"
+ f"and has updates between {e.earliest_time.dt} and {e.latest_time.dt} at the following times: {e_history}\n"
)
print(
f"The edge from {e_reversed.src.name} to {e_reversed.dst.name} covers the following layers: {e_reversed.layer_names}"
)
print(
- f"and has updates between {e_reversed.earliest_date_time} and {e_reversed.latest_date_time} at the following times: {e_reversed_history}"
+ f"and has updates between {e_reversed.earliest_time.dt} and {e_reversed.latest_time.dt} at the following times: {e_reversed_history}"
)
```
///
@@ -66,27 +68,28 @@ assert str(f"The edge from {e.src.name} to {e.dst.name}") == "The edge from FELI
!!! Output
```output
- The edge from FELIPE to MAKO covers the following layers: ['Touching', 'Grooming', 'Resting', 'Playing with', 'Grunting-Lipsmacking', 'Embracing', 'Carrying']
+ The edge from FELIPE to MAKO covers the following layers: ['Resting', 'Grooming', 'Playing with', 'Embracing', 'Touching', 'Carrying', 'Grunting-Lipsmacking']
and has updates between 2019-06-13 14:50:00+00:00 and 2019-07-09 11:17:00+00:00 at the following times: ['2019-06-13 14:50:00', '2019-06-13 14:54:00', '2019-06-19 09:11:00', '2019-06-20 15:08:00', '2019-06-20 15:08:00', '2019-06-20 15:09:00', '2019-06-21 11:47:00', '2019-06-24 10:58:00', '2019-06-24 10:58:00', '2019-06-24 10:59:00', '2019-06-24 10:59:00', '2019-06-24 10:59:00', '2019-06-24 10:59:00', '2019-06-24 10:59:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-24 15:42:00', '2019-06-27 13:53:00', '2019-06-28 10:18:00', '2019-06-28 10:19:00', '2019-07-01 08:46:00', '2019-07-03 10:16:00', '2019-07-03 10:16:00', '2019-07-03 10:17:00', '2019-07-03 10:17:00', '2019-07-03 10:18:00', '2019-07-09 11:17:00']
-
- The edge from MAKO to FELIPE covers the following layers: ['Grooming', 'Resting', 'Playing with', 'Presenting', 'Grunting-Lipsmacking', 'Embracing']
+
+ The edge from MAKO to FELIPE covers the following layers: ['Resting', 'Grooming', 'Playing with', 'Embracing', 'Presenting', 'Grunting-Lipsmacking']
and has updates between 2019-06-19 09:42:00+00:00 and 2019-07-09 13:40:00+00:00 at the following times: ['2019-06-19 09:42:00', '2019-06-20 15:08:00', '2019-06-20 15:08:00', '2019-06-20 15:08:00', '2019-06-24 10:58:00', '2019-06-24 11:14:00', '2019-06-24 11:14:00', '2019-06-24 16:09:00', '2019-06-24 16:11:00', '2019-06-26 09:10:00', '2019-06-26 09:11:00', '2019-06-28 10:17:00', '2019-07-02 14:35:00', '2019-07-04 14:30:00', '2019-07-04 14:30:00', '2019-07-09 13:37:00', '2019-07-09 13:37:00', '2019-07-09 13:37:00', '2019-07-09 13:38:00', '2019-07-09 13:39:00', '2019-07-09 13:39:00', '2019-07-09 13:40:00']
```
## Exploded edges
+
Raphtory offers you three different ways to split an edge by layer, depending on your use case:
- `.layers()`: takes a list of layer names and returns a new `Edge View` which contains updates for only the specified layers. This is discussed in more detail in the [Layer views](../views/3_layer.md) chapter
- `.explode_layers()`: returns an iterable of `Edge Views`, each containing the updates for one layer
-- `.explode()`: returns an `Exploded Edge` containing only the information from one call to `add_edge()`, in this case an edge object for each update.
+- `.explode()`: returns an `Exploded Edge` containing only the information from one call to `add_edge()`, in this case an edge object for each update.
-In the code below you can see an example of each of these functions. We first call `explode_layers()`, to see which layer each edge object represents and output its update history. Next we fully `explode()` the edge and see each update as an individual object. Thirdly we use the `layer()` function to look at only the `Touching` and `Carrying` layers and chain this with a call to `explode()` to see the separate updates.
+In the code below you can see an example of each of these functions. We first call `explode_layers()`, to see which layer each edge object represents and output its update history. Next we fully `explode()` the edge and see each update as an individual object. Thirdly we use the `layer()` function to look at only the `Touching` and `Carrying` layers and chain this with a call to `explode()` to see the separate updates.
!!! info
Within the examples and in the API documentation you will see singular and plural versions what appear to be the same function. For example `.layer_names` and `.layer_name`.
- Singular functions such as `.layer_name` or `.time` can be called on exploded edges and plural functions such as `.layer_names` and `.history()` can be called on standard edges.
+ Singular functions such as `.layer_name` or `.time` can be called on exploded edges and plural functions such as `.layer_names` and `.history` can be called on standard edges.
/// tab | :fontawesome-brands-python: Python
```python
@@ -115,7 +118,7 @@ g.load_edges_from_pandas(
print("Update history per layer:")
for e in g.edge("FELIPE", "MAKO").explode_layers():
- e_history = [date.strftime("%Y-%m-%d %H:%M:%S") for date in e.history_date_time()]
+ e_history = [date.strftime("%Y-%m-%d %H:%M:%S") for date in e.history.dt]
print(
f"{e.src.name} interacted with {e.dst.name} with the following behaviour '{e.layer_name}' at this times: {e_history}"
)
@@ -124,14 +127,14 @@ print()
print("Individual updates as edges:")
for e in g.edge("FELIPE", "MAKO").explode():
print(
- f"At {e.date_time} {e.src.name} interacted with {e.dst.name} in the following manner: '{e.layer_name}'"
+ f"At {e.time.dt} {e.src.name} interacted with {e.dst.name} in the following manner: '{e.layer_name}'"
)
print()
print("Individual updates for 'Touching' and 'Carrying:")
for e in g.edge("FELIPE", "MAKO").layers(["Touching", "Carrying"]).explode():
print(
- f"At {e.date_time} {e.src.name} interacted with {e.dst.name} in the following manner: '{e.layer_name}'"
+ f"At {e.time.dt} {e.src.name} interacted with {e.dst.name} in the following manner: '{e.layer_name}'"
)
```
///
@@ -141,13 +144,13 @@ for e in g.edge("FELIPE", "MAKO").layers(["Touching", "Carrying"]).explode():
```output
Update history per layer:
FELIPE interacted with MAKO with the following behaviour 'Touching' at this times: ['2019-07-03 10:17:00']
+ FELIPE interacted with MAKO with the following behaviour 'Grunting-Lipsmacking' at this times: ['2019-06-24 10:59:00', '2019-06-28 10:18:00', '2019-06-28 10:19:00']
+ FELIPE interacted with MAKO with the following behaviour 'Carrying' at this times: ['2019-06-20 15:08:00', '2019-06-24 10:59:00']
FELIPE interacted with MAKO with the following behaviour 'Grooming' at this times: ['2019-06-20 15:08:00', '2019-06-20 15:09:00']
FELIPE interacted with MAKO with the following behaviour 'Resting' at this times: ['2019-06-13 14:50:00', '2019-06-13 14:54:00', '2019-06-19 09:11:00', '2019-06-21 11:47:00', '2019-06-24 10:58:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-24 15:41:00', '2019-06-27 13:53:00', '2019-07-01 08:46:00', '2019-07-03 10:17:00', '2019-07-09 11:17:00']
FELIPE interacted with MAKO with the following behaviour 'Playing with' at this times: ['2019-06-24 10:58:00', '2019-06-24 10:59:00', '2019-06-24 10:59:00', '2019-06-24 15:42:00', '2019-07-03 10:16:00', '2019-07-03 10:16:00', '2019-07-03 10:18:00']
- FELIPE interacted with MAKO with the following behaviour 'Grunting-Lipsmacking' at this times: ['2019-06-24 10:59:00', '2019-06-28 10:18:00', '2019-06-28 10:19:00']
FELIPE interacted with MAKO with the following behaviour 'Embracing' at this times: ['2019-06-24 10:59:00']
- FELIPE interacted with MAKO with the following behaviour 'Carrying' at this times: ['2019-06-20 15:08:00', '2019-06-24 10:59:00']
-
+
Individual updates as edges:
At 2019-06-13 14:50:00+00:00 FELIPE interacted with MAKO in the following manner: 'Resting'
At 2019-06-13 14:54:00+00:00 FELIPE interacted with MAKO in the following manner: 'Resting'
@@ -177,7 +180,7 @@ for e in g.edge("FELIPE", "MAKO").layers(["Touching", "Carrying"]).explode():
At 2019-07-03 10:17:00+00:00 FELIPE interacted with MAKO in the following manner: 'Resting'
At 2019-07-03 10:18:00+00:00 FELIPE interacted with MAKO in the following manner: 'Playing with'
At 2019-07-09 11:17:00+00:00 FELIPE interacted with MAKO in the following manner: 'Resting'
-
+
Individual updates for 'Touching' and 'Carrying:
At 2019-06-20 15:08:00+00:00 FELIPE interacted with MAKO in the following manner: 'Carrying'
At 2019-06-24 10:59:00+00:00 FELIPE interacted with MAKO in the following manner: 'Carrying'
diff --git a/docs/user-guide/views/2_time.md b/docs/user-guide/views/2_time.md
index e6f0105a43..1eebcf1232 100644
--- a/docs/user-guide/views/2_time.md
+++ b/docs/user-guide/views/2_time.md
@@ -71,12 +71,12 @@ print(f"Across the full dataset {v.name} interacted with {v.degree()} other monk
v_before = g.before(1560428239000).node("LOME") # 13/06/2019 12:17:19 as epoch
print(
- f"Between {v_before.start_date_time} and {v_before.end_date_time}, {v_before.name} interacted with {v_before.degree()} other monkeys."
+ f"Between {v_before.start} and {v_before.end.dt}, {v_before.name} interacted with {v_before.degree()} other monkeys."
)
v_after = g.node("LOME").after("2019-06-30 9:07:31")
print(
- f"Between {v_after.start_date_time} and {v_after.end_date_time}, {v_after.name} interacted with {v_after.degree()} other monkeys."
+ f"Between {v_after.start.dt} and {v_after.end}, {v_after.name} interacted with {v_after.degree()} other monkeys."
)
```
///
@@ -125,22 +125,22 @@ start_day = datetime.strptime("2019-06-13", "%Y-%m-%d")
end_day = datetime.strptime("2019-06-14", "%Y-%m-%d")
e = g.edge("LOME", "NEKKE")
print(
- f"Across the full dataset {e.src.name} interacted with {e.dst.name} {len(e.history())} times"
+ f"Across the full dataset {e.src.name} interacted with {e.dst.name} {len(e.history)} times"
)
w = e.window(start_day, end_day)
print(
- f"Between {w.start_date_time} and {w.end_date_time}, {w.src.name} interacted with {w.dst.name} {len(w.history())} times"
+ f"Between {w.start.dt} and {w.end.dt}, {w.src.name} interacted with {w.dst.name} {len(w.history)} times"
)
print(
- f"Window start: {w.start_date_time}, First update: {w.earliest_date_time}, Last update: {w.latest_date_time}, Window End: {w.end_date_time}"
+ f"Window start: {w.start.dt}, First update: {w.earliest_time.dt}, Last update: {w.latest_time.dt}, Window End: {w.end.dt}"
)
```
///
```{.python continuation hide}
-assert str(f"Across the full dataset {e.src.name} interacted with {e.dst.name} {len(e.history())} times.") == "Across the full dataset LOME interacted with NEKKE 41 times."
-assert str(f"Between {w.start_date_time} and {w.end_date_time}, {w.src.name} interacted with {w.dst.name} {len(w.history())} times") == "Between 2019-06-13 00:00:00+00:00 and 2019-06-14 00:00:00+00:00, LOME interacted with NEKKE 8 times"
-assert str(f"Window start: {w.start_date_time}, First update: {w.earliest_date_time}, Last update: {w.latest_date_time}, Window End: {w.end_date_time}") == "Window start: 2019-06-13 00:00:00+00:00, First update: 2019-06-13 10:18:00+00:00, Last update: 2019-06-13 15:05:00+00:00, Window End: 2019-06-14 00:00:00+00:00"
+assert str(f"Across the full dataset {e.src.name} interacted with {e.dst.name} {len(e.history)} times.") == "Across the full dataset LOME interacted with NEKKE 41 times."
+assert str(f"Between {w.start.dt} and {w.end.dt}, {w.src.name} interacted with {w.dst.name} {len(w.history)} times") == "Between 2019-06-13 00:00:00+00:00 and 2019-06-14 00:00:00+00:00, LOME interacted with NEKKE 8 times"
+assert str(f"Window start: {w.start.dt}, First update: {w.earliest_time.dt}, Last update: {w.latest_time.dt}, Window End: {w.end.dt}") == "Window start: 2019-06-13 00:00:00+00:00, First update: 2019-06-13 10:18:00+00:00, Last update: 2019-06-13 15:05:00+00:00, Window End: 2019-06-14 00:00:00+00:00"
```
!!! Output
@@ -270,12 +270,12 @@ g.load_edges_from_pandas(
)
print(
- f"The full range of time in the graph is {g.earliest_date_time} to {g.latest_date_time}\n"
+ f"The full range of time in the graph is {g.earliest_time.dt} to {g.latest_time.dt}\n"
)
for expanding_g in g.expanding("1 week"):
print(
- f"From {expanding_g.start_date_time} to {expanding_g.end_date_time} there were {expanding_g.count_temporal_edges()} monkey interactions"
+ f"From {expanding_g.start} to {expanding_g.end.dt} there were {expanding_g.count_temporal_edges()} monkey interactions"
)
print()
@@ -285,13 +285,13 @@ for expanding_g in g.window(start_day, end_day).expanding(
"2 days, 3 hours, 12 minutes and 6 seconds"
):
print(
- f"From {expanding_g.start_date_time} to {expanding_g.end_date_time} there were {expanding_g.count_temporal_edges()} monkey interactions"
+ f"From {expanding_g.start.dt} to {expanding_g.end.dt} there were {expanding_g.count_temporal_edges()} monkey interactions"
)
```
///
```{.python continuation hide}
-assert str(f"The full range of time in the graph is {g.earliest_date_time} to {g.latest_date_time}") == "The full range of time in the graph is 2019-06-13 09:50:00+00:00 to 2019-07-10 11:05:00+00:00"
+assert str(f"The full range of time in the graph is {g.earliest_time.dt} to {g.latest_time.dt}") == "The full range of time in the graph is 2019-06-13 09:50:00+00:00 to 2019-07-10 11:05:00+00:00"
```
!!! Output
@@ -348,7 +348,7 @@ g.load_edges_from_pandas(
print("Rolling 1 week")
for rolling_g in g.rolling(window="1 week"):
print(
- f"From {rolling_g.start_date_time} to {rolling_g.end_date_time} there were {rolling_g.count_temporal_edges()} monkey interactions"
+ f"From {rolling_g.start.dt} to {rolling_g.end.dt} there were {rolling_g.count_temporal_edges()} monkey interactions"
)
```
///
@@ -399,7 +399,7 @@ importance = []
time = []
for rolling_lome in g.node("LOME").rolling("1 day"):
importance.append(rolling_lome.degree())
- time.append(rolling_lome.end_date_time)
+ time.append(rolling_lome.end.dt)
plt.plot(time, importance, marker="o")
plt.xlabel("Date")
diff --git a/docs/user-guide/views/6_filtering.md b/docs/user-guide/views/6_filtering.md
index 53bf735c09..ea0ad5965d 100644
--- a/docs/user-guide/views/6_filtering.md
+++ b/docs/user-guide/views/6_filtering.md
@@ -71,9 +71,9 @@ traffic_graph.load_nodes_from_pandas(
shared_metadata={"datasource": "../data/network_traffic_edges.csv"},
)
-my_filter = filter.Property("OS_version").is_in(["Ubuntu 20.04", "Red Hat 8.1"]) & filter.Property("primary_function").is_in(["Web Server", "Application Server"])
+my_filter = filter.Node.property("OS_version").is_in(["Ubuntu 20.04", "Red Hat 8.1"]) & filter.Node.property("primary_function").is_in(["Web Server", "Application Server"])
-cve_view = traffic_graph.filter_nodes(my_filter)
+cve_view = traffic_graph.filter(my_filter)
print(cve_view.nodes)
diff --git a/examples/netflow/src/netflow_one_path_node.rs b/examples/netflow/src/netflow_one_path_node.rs
index 295adda11f..c6567436c6 100644
--- a/examples/netflow/src/netflow_one_path_node.rs
+++ b/examples/netflow/src/netflow_one_path_node.rs
@@ -1,7 +1,10 @@
use raphtory::{
- core::state::{
- accumulator_id::accumulators::sum,
- compute_state::{ComputeState, ComputeStateVec},
+ core::{
+ state::{
+ accumulator_id::accumulators::sum,
+ compute_state::{ComputeState, ComputeStateVec},
+ },
+ storage::timeindex::AsTime,
},
db::{
api::view::{GraphViewOps, NodeViewOps, StaticGraphViewOps},
@@ -64,7 +67,7 @@ fn one_path_algorithm<'graph, G: GraphViewOps<'graph>, CS: ComputeState>(
}
// Now we save the time of nf1
- let nf1_time = nf_e_edge_expl.time().unwrap_or_default();
+ let nf1_time = nf_e_edge_expl.time().map(|t| t.t()).unwrap_or_default();
let mut time_bound = nf1_time.saturating_sub(30);
if no_time {
time_bound = 0;
@@ -85,7 +88,7 @@ fn one_path_algorithm<'graph, G: GraphViewOps<'graph>, CS: ComputeState>(
.flat_map(|login_exp| {
login_exp
.dst()
- .window(login_exp.time().unwrap().saturating_add(1), nf1_time)
+ .window(login_exp.time().unwrap().t().saturating_add(1), nf1_time)
.layers("Events1v4688")
})
.flat_map(|v| {
diff --git a/examples/python/socio-patterns/example.ipynb b/examples/python/socio-patterns/example.ipynb
index 6d76cd9cab..c9d8c42501 100644
--- a/examples/python/socio-patterns/example.ipynb
+++ b/examples/python/socio-patterns/example.ipynb
@@ -149,7 +149,7 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": null,
"metadata": {},
"outputs": [
{
@@ -186,8 +186,8 @@
"\n",
"print(\"Stats on the graphs time range:\")\n",
"\n",
- "earliest_datetime = g.earliest_date_time\n",
- "latest_datetime = g.latest_date_time\n",
+ "earliest_datetime = g.earliest_time.dt\n",
+ "latest_datetime = g.latest_time.dt\n",
"earliest_epoch = g.earliest_time\n",
"latest_epoch = g.latest_time\n",
"\n",
@@ -263,7 +263,7 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": null,
"metadata": {},
"outputs": [
{
@@ -281,9 +281,9 @@
"source": [
"v = g.node(\"FELIPE\")\n",
"print(\n",
- " f\"{v.name}'s first interaction was at {v.earliest_date_time} and their last interaction was at {v.latest_date_time}\\n\"\n",
+ " f\"{v.name}'s first interaction was at {v.earliest_time.dt} and their last interaction was at {v.latest_time.dt}\\n\"\n",
")\n",
- "print(f\"{v.name} had interactions at the following times: {v.history()[:10]}\\n\")"
+ "print(f\"{v.name} had interactions at the following times: {v.history.t.collect()[:10]}\\n\")"
]
},
{
@@ -362,7 +362,7 @@
},
{
"cell_type": "code",
- "execution_count": 8,
+ "execution_count": null,
"metadata": {
"scrolled": true
},
@@ -401,18 +401,18 @@
"source": [
"print(\"Update history per layer:\")\n",
"for e in list(g.edge(\"FELIPE\", \"MAKO\").explode_layers())[:5]:\n",
- " print(f\"{e.src.name} interacted with {e.dst.name} with the following behaviour '{e.layer_name}' at this times: {e.history()}\")\n",
+ " print(f\"{e.src.name} interacted with {e.dst.name} with the following behaviour '{e.layer_name}' at this times: {e.history}\")\n",
"\n",
"\n",
"print()\n",
"print(\"Individual updates as edges:\")\n",
"for e in list(g.edge(\"FELIPE\", \"MAKO\").explode())[:5]:\n",
- " print(f\"At {e.date_time} {e.src.name} interacted with {e.dst.name} in the following manner: '{e.layer_name}'\")\n",
+ " print(f\"At {e.time.dt} {e.src.name} interacted with {e.dst.name} in the following manner: '{e.layer_name}'\")\n",
"\n",
"print(\"...\\n\")\n",
"print(\"Individual updates for 'Touching' and 'Carrying:\")\n",
"for e in list(g.edge(\"FELIPE\", \"MAKO\").layers([\"Touching\", \"Carrying\"]).explode())[:5]:\n",
- " print(f\"At {e.date_time} {e.src.name} interacted with {e.dst.name} in the following manner: '{e.layer_name}'\")\n",
+ " print(f\"At {e.time.dt} {e.src.name} interacted with {e.dst.name} in the following manner: '{e.layer_name}'\")\n",
"print(\"...\\n\")"
]
},
@@ -551,7 +551,7 @@
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": null,
"metadata": {},
"outputs": [
{
@@ -572,14 +572,14 @@
"v_at_2 = g.before(1560428239000).node(\"LOME\") # 13/06/2019 12:17:19 as epoch\n",
"e = g.edge(\"LOME\", \"NEKKE\")\n",
"print(\n",
- " f\"Across the full dataset {e.src.name} interacted with {e.dst.name} {len(e.history())} times\"\n",
+ " f\"Across the full dataset {e.src.name} interacted with {e.dst.name} {len(e.history)} times\"\n",
")\n",
"e = e.window(start_day, end_day)\n",
"print(\n",
- " f\"Between {v_at_2.start_date_time} and {v_at_2.end_date_time}, {e.src.name} interacted with {e.dst.name} {len(e.history())} times\"\n",
+ " f\"Between {v_at_2.start} and {v_at_2.end.dt}, {e.src.name} interacted with {e.dst.name} {len(e.history)} times\"\n",
")\n",
"print(\n",
- " f\"Window start: {e.start_date_time}, First update: {e.earliest_date_time}, Last update: {e.latest_date_time}, Window End: {e.end_date_time}\"\n",
+ " f\"Window start: {e.start.dt}, First update: {e.earliest_time.dt}, Last update: {e.latest_time.dt}, Window End: {e.end.dt}\"\n",
")\n"
]
},
@@ -605,7 +605,7 @@
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": null,
"metadata": {},
"outputs": [
{
@@ -625,7 +625,7 @@
"time = []\n",
"for rolling_lome in g.node(\"LOME\").rolling(\"1 day\"):\n",
" importance.append(rolling_lome.degree())\n",
- " time.append(rolling_lome.end_date_time)\n",
+ " time.append(rolling_lome.end.dt)\n",
"\n",
"plt.plot(time, importance, marker=\"o\")\n",
"plt.xlabel(\"Date\")\n",
@@ -852,7 +852,7 @@
},
{
"cell_type": "code",
- "execution_count": 17,
+ "execution_count": null,
"metadata": {},
"outputs": [
{
@@ -875,7 +875,7 @@
"for windowed_graph in g.rolling(window=\"3 days\"):\n",
" result = rp.pagerank(windowed_graph)\n",
" importance.append(result.get(\"NEKKE\"))\n",
- " time.append(windowed_graph.earliest_date_time)\n",
+ " time.append(windowed_graph.earliest_time.dt)\n",
"\n",
"plt.plot(time, importance, marker=\"o\")\n",
"plt.xlabel(\"Date\")\n",
diff --git a/graphql-bench/package-lock.json b/graphql-bench/package-lock.json
new file mode 100644
index 0000000000..92b113a10a
--- /dev/null
+++ b/graphql-bench/package-lock.json
@@ -0,0 +1,4899 @@
+{
+ "name": "bench",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "bench",
+ "version": "0.0.0",
+ "dependencies": {
+ "@google-cloud/compute": "^5.2.0",
+ "dotenv": "^16.5.0",
+ "node-ssh": "^13.2.1",
+ "zod": "^3.23.8"
+ },
+ "devDependencies": {
+ "@genql/cli": "^6.3.2",
+ "@types/k6": "^1.0.2",
+ "@types/node": "^22.10.2",
+ "concurrently": "^9.1.2",
+ "tsup": "8.3.0",
+ "typescript": "^5.8.3"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
+ "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz",
+ "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz",
+ "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz",
+ "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz",
+ "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz",
+ "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz",
+ "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz",
+ "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz",
+ "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz",
+ "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz",
+ "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz",
+ "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz",
+ "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz",
+ "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz",
+ "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz",
+ "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz",
+ "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz",
+ "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz",
+ "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz",
+ "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz",
+ "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz",
+ "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz",
+ "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz",
+ "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@fastify/busboy": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
+ "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@genql/cli": {
+ "version": "6.3.3",
+ "resolved": "https://registry.npmjs.org/@genql/cli/-/cli-6.3.3.tgz",
+ "integrity": "sha512-Etrdo9uPGXokGWml6agJsxoalz6eOicJ6X82YnL61WBHcJKFZ871BoSfCyCT1dxmXH60q+DDYxgZBCEbMivj4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@graphql-tools/graphql-file-loader": "^7.5.17",
+ "@graphql-tools/load": "^7.8.14",
+ "fs-extra": "^10.1.0",
+ "graphql": "^16.6.0",
+ "kleur": "^4.1.5",
+ "listr2": "^6.3.1",
+ "lodash": "^4.17.21",
+ "mkdirp": "^0.5.1",
+ "native-fetch": "^4.0.2",
+ "prettier": "^2.8.0",
+ "qs": "^6.11.0",
+ "rimraf": "^2.6.3",
+ "undici": "^5.22.0",
+ "yargs": "^15.3.1"
+ },
+ "bin": {
+ "genql": "dist/cli.js"
+ }
+ },
+ "node_modules/@google-cloud/compute": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/compute/-/compute-5.3.0.tgz",
+ "integrity": "sha512-ETGljLELIq71g5iXVJOYa1SWSRLzeGCgGzPxM36RAZF9VYcgZ/019hmtT2QNcS8cNLtuQ4yJyq+cDQzNIUj67g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "google-gax": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@graphql-tools/graphql-file-loader": {
+ "version": "7.5.17",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.5.17.tgz",
+ "integrity": "sha512-hVwwxPf41zOYgm4gdaZILCYnKB9Zap7Ys9OhY1hbwuAuC4MMNY9GpUjoTU3CQc3zUiPoYStyRtUGkHSJZ3HxBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@graphql-tools/import": "6.7.18",
+ "@graphql-tools/utils": "^9.2.1",
+ "globby": "^11.0.3",
+ "tslib": "^2.4.0",
+ "unixify": "^1.0.0"
+ },
+ "peerDependencies": {
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@graphql-tools/import": {
+ "version": "6.7.18",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-6.7.18.tgz",
+ "integrity": "sha512-XQDdyZTp+FYmT7as3xRWH/x8dx0QZA2WZqfMF5EWb36a0PiH7WwlRQYIdyYXj8YCLpiWkeBXgBRHmMnwEYR8iQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@graphql-tools/utils": "^9.2.1",
+ "resolve-from": "5.0.0",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@graphql-tools/load": {
+ "version": "7.8.14",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-7.8.14.tgz",
+ "integrity": "sha512-ASQvP+snHMYm+FhIaLxxFgVdRaM0vrN9wW2BKInQpktwWTXVyk+yP5nQUCEGmn0RTdlPKrffBaigxepkEAJPrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@graphql-tools/schema": "^9.0.18",
+ "@graphql-tools/utils": "^9.2.1",
+ "p-limit": "3.1.0",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@graphql-tools/merge": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.4.2.tgz",
+ "integrity": "sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@graphql-tools/utils": "^9.2.1",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@graphql-tools/schema": {
+ "version": "9.0.19",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz",
+ "integrity": "sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@graphql-tools/merge": "^8.4.1",
+ "@graphql-tools/utils": "^9.2.1",
+ "tslib": "^2.4.0",
+ "value-or-promise": "^1.0.12"
+ },
+ "peerDependencies": {
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@graphql-tools/utils": {
+ "version": "9.2.1",
+ "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz",
+ "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@graphql-typed-document-node/core": "^3.1.1",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@graphql-typed-document-node/core": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
+ "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+ }
+ },
+ "node_modules/@grpc/grpc-js": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz",
+ "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@grpc/proto-loader": "^0.8.0",
+ "@js-sdsl/ordered-map": "^4.4.2"
+ },
+ "engines": {
+ "node": ">=12.10.0"
+ }
+ },
+ "node_modules/@grpc/proto-loader": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz",
+ "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "lodash.camelcase": "^4.3.0",
+ "long": "^5.0.0",
+ "protobufjs": "^7.5.3",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/@grpc/proto-loader/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@grpc/proto-loader/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@js-sdsl/ordered-map": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
+ "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/js-sdsl"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz",
+ "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz",
+ "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz",
+ "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz",
+ "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz",
+ "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz",
+ "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz",
+ "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz",
+ "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz",
+ "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz",
+ "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz",
+ "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz",
+ "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz",
+ "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz",
+ "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz",
+ "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz",
+ "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz",
+ "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz",
+ "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz",
+ "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz",
+ "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz",
+ "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz",
+ "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz",
+ "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz",
+ "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz",
+ "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/k6": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@types/k6/-/k6-1.5.0.tgz",
+ "integrity": "sha512-eQmSjjgYO1irlOmoZzGSuoYsK2uMrX3m/flcnqt5IDLA9D/vkPcRsAKfe9D6GrjXATD/Oo9ynQ6vc13V02WRmQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "22.19.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz",
+ "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz",
+ "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "license": "MIT"
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "node_modules/bignumber.js": {
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
+ "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/buildcheck": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz",
+ "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==",
+ "optional": true,
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/bundle-require": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz",
+ "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "load-tsconfig": "^0.2.3"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "esbuild": ">=0.18"
+ }
+ },
+ "node_modules/cac": {
+ "version": "6.7.14",
+ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+ "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
+ "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz",
+ "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^5.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cliui/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concurrently": {
+ "version": "9.2.1",
+ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz",
+ "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "4.1.2",
+ "rxjs": "7.8.2",
+ "shell-quote": "1.8.3",
+ "supports-color": "8.1.1",
+ "tree-kill": "1.2.2",
+ "yargs": "17.7.2"
+ },
+ "bin": {
+ "conc": "dist/bin/concurrently.js",
+ "concurrently": "dist/bin/concurrently.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
+ }
+ },
+ "node_modules/concurrently/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/concurrently/node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/concurrently/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concurrently/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/concurrently/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/concurrently/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/concurrently/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/concurrently/node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/concurrently/node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/concurrently/node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/consola": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz",
+ "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.18.0 || >=16.10.0"
+ }
+ },
+ "node_modules/cpu-features": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
+ "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==",
+ "hasInstallScript": true,
+ "optional": true,
+ "dependencies": {
+ "buildcheck": "~0.0.6",
+ "nan": "^2.19.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/data-uri-to-buffer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.6.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/duplexify": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
+ "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.4.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1",
+ "stream-shift": "^1.0.2"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "license": "MIT"
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "license": "MIT"
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
+ "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.23.1",
+ "@esbuild/android-arm": "0.23.1",
+ "@esbuild/android-arm64": "0.23.1",
+ "@esbuild/android-x64": "0.23.1",
+ "@esbuild/darwin-arm64": "0.23.1",
+ "@esbuild/darwin-x64": "0.23.1",
+ "@esbuild/freebsd-arm64": "0.23.1",
+ "@esbuild/freebsd-x64": "0.23.1",
+ "@esbuild/linux-arm": "0.23.1",
+ "@esbuild/linux-arm64": "0.23.1",
+ "@esbuild/linux-ia32": "0.23.1",
+ "@esbuild/linux-loong64": "0.23.1",
+ "@esbuild/linux-mips64el": "0.23.1",
+ "@esbuild/linux-ppc64": "0.23.1",
+ "@esbuild/linux-riscv64": "0.23.1",
+ "@esbuild/linux-s390x": "0.23.1",
+ "@esbuild/linux-x64": "0.23.1",
+ "@esbuild/netbsd-x64": "0.23.1",
+ "@esbuild/openbsd-arm64": "0.23.1",
+ "@esbuild/openbsd-x64": "0.23.1",
+ "@esbuild/sunos-x64": "0.23.1",
+ "@esbuild/win32-arm64": "0.23.1",
+ "@esbuild/win32-ia32": "0.23.1",
+ "@esbuild/win32-x64": "0.23.1"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
+ },
+ "engines": {
+ "node": "^12.20 || >= 14.13"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/foreground-child/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "license": "MIT",
+ "dependencies": {
+ "fetch-blob": "^3.1.2"
+ },
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gaxios": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz",
+ "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "node-fetch": "^3.3.2",
+ "rimraf": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/gaxios/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/gaxios/node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/gaxios/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/gaxios/node_modules/rimraf": {
+ "version": "5.0.10",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
+ "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^10.3.7"
+ },
+ "bin": {
+ "rimraf": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/gcp-metadata": {
+ "version": "8.1.2",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz",
+ "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "gaxios": "^7.0.0",
+ "google-logging-utils": "^1.0.0",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/google-auth-library": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz",
+ "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^7.0.0",
+ "gcp-metadata": "^8.0.0",
+ "google-logging-utils": "^1.0.0",
+ "gtoken": "^8.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/google-gax": {
+ "version": "5.0.6",
+ "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-5.0.6.tgz",
+ "integrity": "sha512-1kGbqVQBZPAAu4+/R1XxPQKP0ydbNYoLAr4l0ZO2bMV0kLyLW4I1gAk++qBLWt7DPORTzmWRMsCZe86gDjShJA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@grpc/grpc-js": "^1.12.6",
+ "@grpc/proto-loader": "^0.8.0",
+ "duplexify": "^4.1.3",
+ "google-auth-library": "^10.1.0",
+ "google-logging-utils": "^1.1.1",
+ "node-fetch": "^3.3.2",
+ "object-hash": "^3.0.0",
+ "proto3-json-serializer": "^3.0.0",
+ "protobufjs": "^7.5.3",
+ "retry-request": "^8.0.0",
+ "rimraf": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/google-gax/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/google-gax/node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/google-gax/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/google-gax/node_modules/rimraf": {
+ "version": "5.0.10",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
+ "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^10.3.7"
+ },
+ "bin": {
+ "rimraf": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/google-logging-utils": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
+ "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/graphql": {
+ "version": "16.12.0",
+ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz",
+ "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
+ }
+ },
+ "node_modules/gtoken": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz",
+ "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==",
+ "license": "MIT",
+ "dependencies": {
+ "gaxios": "^7.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "license": "MIT",
+ "dependencies": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/http-proxy-agent/node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/joycon": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
+ "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/json-bigint": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "bignumber.js": "^9.0.0"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
+ "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/listr2": {
+ "version": "6.6.1",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz",
+ "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cli-truncate": "^3.1.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^5.0.1",
+ "rfdc": "^1.3.0",
+ "wrap-ansi": "^8.1.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "peerDependencies": {
+ "enquirer": ">= 2.3.0 < 3"
+ },
+ "peerDependenciesMeta": {
+ "enquirer": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/load-tsconfig": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz",
+ "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.sortby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+ "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-update": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz",
+ "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^5.0.0",
+ "cli-cursor": "^4.0.0",
+ "slice-ansi": "^5.0.0",
+ "strip-ansi": "^7.0.1",
+ "wrap-ansi": "^8.0.1"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "license": "ISC"
+ },
+ "node_modules/make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nan": {
+ "version": "2.24.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz",
+ "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/native-fetch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/native-fetch/-/native-fetch-4.0.2.tgz",
+ "integrity": "sha512-4QcVlKFtv2EYVS5MBgsGX5+NWKtbDbIECdUXDBGDMAZXq3Jkv9zf+y8iS7Ub8fEdga3GpYeazp9gauNqXHJOCg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "undici": "*"
+ }
+ },
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "deprecated": "Use your platform's native DOMException instead",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "license": "MIT",
+ "dependencies": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
+ }
+ },
+ "node_modules/node-ssh": {
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/node-ssh/-/node-ssh-13.2.1.tgz",
+ "integrity": "sha512-rfl4GWMygQfzlExPkQ2LWyya5n2jOBm5vhEnup+4mdw7tQhNpJWbP5ldr09Jfj93k5SfY5lxcn8od5qrQ/6mBg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-stream": "^2.0.0",
+ "make-dir": "^3.1.0",
+ "sb-promise-queue": "^2.1.0",
+ "sb-scandir": "^3.1.0",
+ "shell-escape": "^0.2.0",
+ "ssh2": "^1.14.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-locate/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
+ "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz",
+ "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^3.1.1"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "jiti": ">=1.21.0",
+ "postcss": ">=8.0.9",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ },
+ "postcss": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/prettier": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
+ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/proto3-json-serializer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-3.0.4.tgz",
+ "integrity": "sha512-E1sbAYg3aEbXrq0n1ojJkRHQJGE1kaE/O6GLA94y8rnJBfgvOPTOd1b9hOceQK1FFZI9qMh1vBERCyO2ifubcw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "protobufjs": "^7.4.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/protobufjs": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
+ "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/node": ">=13.7.0",
+ "long": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.14.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
+ "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
+ "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/retry-request": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-8.0.2.tgz",
+ "integrity": "sha512-JzFPAfklk1kjR1w76f0QOIhoDkNkSqW8wYKT08n9yysTmZfB+RQ2QoXoTAeOi1HD9ZipTyTAZg3c4pM/jeqgSw==",
+ "license": "MIT",
+ "dependencies": {
+ "extend": "^3.0.2",
+ "teeny-request": "^10.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.55.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz",
+ "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.55.1",
+ "@rollup/rollup-android-arm64": "4.55.1",
+ "@rollup/rollup-darwin-arm64": "4.55.1",
+ "@rollup/rollup-darwin-x64": "4.55.1",
+ "@rollup/rollup-freebsd-arm64": "4.55.1",
+ "@rollup/rollup-freebsd-x64": "4.55.1",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.55.1",
+ "@rollup/rollup-linux-arm-musleabihf": "4.55.1",
+ "@rollup/rollup-linux-arm64-gnu": "4.55.1",
+ "@rollup/rollup-linux-arm64-musl": "4.55.1",
+ "@rollup/rollup-linux-loong64-gnu": "4.55.1",
+ "@rollup/rollup-linux-loong64-musl": "4.55.1",
+ "@rollup/rollup-linux-ppc64-gnu": "4.55.1",
+ "@rollup/rollup-linux-ppc64-musl": "4.55.1",
+ "@rollup/rollup-linux-riscv64-gnu": "4.55.1",
+ "@rollup/rollup-linux-riscv64-musl": "4.55.1",
+ "@rollup/rollup-linux-s390x-gnu": "4.55.1",
+ "@rollup/rollup-linux-x64-gnu": "4.55.1",
+ "@rollup/rollup-linux-x64-musl": "4.55.1",
+ "@rollup/rollup-openbsd-x64": "4.55.1",
+ "@rollup/rollup-openharmony-arm64": "4.55.1",
+ "@rollup/rollup-win32-arm64-msvc": "4.55.1",
+ "@rollup/rollup-win32-ia32-msvc": "4.55.1",
+ "@rollup/rollup-win32-x64-gnu": "4.55.1",
+ "@rollup/rollup-win32-x64-msvc": "4.55.1",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/sb-promise-queue": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/sb-promise-queue/-/sb-promise-queue-2.1.1.tgz",
+ "integrity": "sha512-qXfdcJQMxMljxmPprn4Q4hl3pJmoljSCzUvvEBa9Kscewnv56n0KqrO6yWSrGLOL9E021wcGdPa39CHGKA6G0w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/sb-scandir": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/sb-scandir/-/sb-scandir-3.1.1.tgz",
+ "integrity": "sha512-Q5xiQMtoragW9z8YsVYTAZcew+cRzdVBefPbb9theaIKw6cBo34WonP9qOCTKgyAmn/Ch5gmtAxT/krUgMILpA==",
+ "license": "MIT",
+ "dependencies": {
+ "sb-promise-queue": "^2.1.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shell-escape": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/shell-escape/-/shell-escape-0.2.0.tgz",
+ "integrity": "sha512-uRRBT2MfEOyxuECseCZd28jC1AJ8hmqqneWQ4VWUTgCAFvb3wKU1jLqj6egC4Exrr88ogg3dp+zroH4wJuaXzw==",
+ "license": "MIT"
+ },
+ "node_modules/shell-quote": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
+ "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.8.0-beta.0",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",
+ "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==",
+ "deprecated": "The work that was done in this beta branch won't be included in future versions",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "whatwg-url": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/ssh2": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz",
+ "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "asn1": "^0.2.6",
+ "bcrypt-pbkdf": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ },
+ "optionalDependencies": {
+ "cpu-features": "~0.0.10",
+ "nan": "^2.23.0"
+ }
+ },
+ "node_modules/stream-events": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
+ "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+ "license": "MIT",
+ "dependencies": {
+ "stubs": "^3.0.0"
+ }
+ },
+ "node_modules/stream-shift": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
+ "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
+ "license": "MIT"
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/stubs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
+ "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==",
+ "license": "MIT"
+ },
+ "node_modules/sucrase": {
+ "version": "3.35.1",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
+ "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "tinyglobby": "^0.2.11",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/teeny-request": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-10.1.0.tgz",
+ "integrity": "sha512-3ZnLvgWF29jikg1sAQ1g0o+lr5JX6sVgYvfUJazn7ZjJroDBUTWp44/+cFVX0bULjv4vci+rBD+oGVAkWqhUbw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "node-fetch": "^3.3.2",
+ "stream-events": "^1.0.5"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/teeny-request/node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/teeny-request/node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+ "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "tree-kill": "cli.js"
+ }
+ },
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/tsup": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.3.0.tgz",
+ "integrity": "sha512-ALscEeyS03IomcuNdFdc0YWGVIkwH1Ws7nfTbAPuoILvEV2hpGQAY72LIOjglGo4ShWpZfpBqP/jpQVCzqYQag==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bundle-require": "^5.0.0",
+ "cac": "^6.7.14",
+ "chokidar": "^3.6.0",
+ "consola": "^3.2.3",
+ "debug": "^4.3.5",
+ "esbuild": "^0.23.0",
+ "execa": "^5.1.1",
+ "joycon": "^3.1.1",
+ "picocolors": "^1.0.1",
+ "postcss-load-config": "^6.0.1",
+ "resolve-from": "^5.0.0",
+ "rollup": "^4.19.0",
+ "source-map": "0.8.0-beta.0",
+ "sucrase": "^3.35.0",
+ "tinyglobby": "^0.2.1",
+ "tree-kill": "^1.2.2"
+ },
+ "bin": {
+ "tsup": "dist/cli-default.js",
+ "tsup-node": "dist/cli-node.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@microsoft/api-extractor": "^7.36.0",
+ "@swc/core": "^1",
+ "postcss": "^8.4.12",
+ "typescript": ">=4.5.0"
+ },
+ "peerDependenciesMeta": {
+ "@microsoft/api-extractor": {
+ "optional": true
+ },
+ "@swc/core": {
+ "optional": true
+ },
+ "postcss": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+ "license": "Unlicense"
+ },
+ "node_modules/type-fest": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
+ "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici": {
+ "version": "5.29.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
+ "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@fastify/busboy": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "license": "MIT"
+ },
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/unixify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz",
+ "integrity": "sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "normalize-path": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unixify/node_modules/normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "remove-trailing-separator": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/value-or-promise": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz",
+ "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/web-streams-polyfill": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+ "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/whatwg-url": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+ "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash.sortby": "^4.7.0",
+ "tr46": "^1.0.1",
+ "webidl-conversions": "^4.0.2"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-module": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
+ "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ },
+ "node_modules/y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yargs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/yargs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/graphql-bench/src/bench.ts b/graphql-bench/src/bench.ts
index 6470f8e8c0..5b370114b1 100644
--- a/graphql-bench/src/bench.ts
+++ b/graphql-bench/src/bench.ts
@@ -151,7 +151,11 @@ export function randomEdgePage(input: SetupData) {
explodeLayers: {
count: true,
},
- history: true,
+ history: {
+ list: {
+ timestamp: true,
+ },
+ },
src: { name: true },
dst: { name: true },
},
@@ -231,7 +235,7 @@ export function readAndWriteNodeProperties(input: SetupData) {
},
at: {
__args: {
- time,
+ time: { simpleTime: time },
},
properties: {
get: {
diff --git a/graphql-bench/src/stress-test.ts b/graphql-bench/src/stress-test.ts
index ec8f100ec2..c3a33b208d 100644
--- a/graphql-bench/src/stress-test.ts
+++ b/graphql-bench/src/stress-test.ts
@@ -422,7 +422,7 @@ function randomView(rate: ViewRate) {
const [start, end] = [randomTime(), randomTime()].sort()
const views: PathFromNodeViewCollection[] = [
...randomAppend(rate.latest, { latest: true }),
- ...randomAppend(rate.layer, { layer: randomLayer() }),
+ ...randomAppend(rate.layer, { layers: [randomLayer()] }),
...randomAppend(rate.window, { window: { start, end } }),
]
// TODO: add more kind of filters
@@ -572,7 +572,11 @@ function randomPropertyQuery(rates: PropertyRates): PropertyGenqlSelection {
values: {
key: true,
values: true,
- history: true,
+ history: {
+ list: {
+ timestamp: true,
+ },
+ },
}
}
}
diff --git a/pometry-storage-private b/pometry-storage-private
index 3cc719a6b2..f28bd721ea 160000
--- a/pometry-storage-private
+++ b/pometry-storage-private
@@ -1 +1 @@
-Subproject commit 3cc719a6b2e373d069be868be3a0134a436e87a1
+Subproject commit f28bd721ea91a59f80f08af3d760a33725eca481
diff --git a/python/pyproject.toml b/python/pyproject.toml
index fdb61bdc9b..27e1952a0a 100644
--- a/python/pyproject.toml
+++ b/python/pyproject.toml
@@ -15,6 +15,7 @@ dependencies = [
"pandas >= 2.0.3",
"pyarrow >=18",
"numpy >= 1.26.0",
+ "ipywidgets",
]
@@ -33,7 +34,7 @@ networkx = ["networkx >= 2.6.3"]
export = ["raphtory[pyvis,networkx]"]
all = ["raphtory[export,plot]"]
dev = ["docstring_parser >= 0.16", "pandas-stubs", "maturin>=1.8.3", "tox>=4.25"]
-test = ["raphtory[all]", "requests >= 2.31.0", "pyjwt[crypto] >= 2.10.1", "pytest >= 8", "pytest-benchmark >= 5.1.0"]
+test = ["raphtory[all]", "requests >= 2.31.0", "pyjwt[crypto] >= 2.10.1", "pytest >= 8", "pytest-benchmark >= 5.1.0", "polars >= 1.35.2", "fireducks; sys_platform != 'win32' and python_version < '3.14'", "duckdb >= 1.4.2"]
tox = ["nbmake"]
[tool.maturin]
diff --git a/python/python/raphtory/__init__.pyi b/python/python/raphtory/__init__.pyi
index 1561298686..e76fbb9a54 100644
--- a/python/python/raphtory/__init__.pyi
+++ b/python/python/raphtory/__init__.pyi
@@ -20,6 +20,8 @@ from raphtory.vectors import *
from raphtory.node_state import *
from raphtory.graphql import *
from raphtory.typing import *
+import numpy as np
+from numpy.typing import NDArray
from datetime import datetime
from pandas import DataFrame
from os import PathLike
@@ -43,9 +45,17 @@ __all__ = [
"Properties",
"PyPropValueList",
"Metadata",
+ "MetadataView",
"TemporalProperties",
"PropertiesView",
- "TemporalProp",
+ "TemporalProperty",
+ "EventTime",
+ "OptionalEventTime",
+ "History",
+ "HistoryTimestamp",
+ "HistoryDateTime",
+ "HistoryEventId",
+ "Intervals",
"WindowSet",
"IndexSpecBuilder",
"IndexSpec",
@@ -162,21 +172,12 @@ class GraphView(object):
"""
@property
- def earliest_date_time(self) -> Optional[datetime]:
+ def earliest_time(self) -> OptionalEventTime:
"""
- DateTime of earliest activity in the graph
+ Time entry of the earliest activity in the graph
Returns:
- Optional[datetime]: the datetime of the earliest activity in the graph
- """
-
- @property
- def earliest_time(self) -> Optional[int]:
- """
- Timestamp of earliest activity in the graph
-
- Returns:
- Optional[int]: the timestamp of the earliest activity in the graph
+ OptionalEventTime: the time entry of the earliest activity in the graph
"""
def edge(self, src: NodeInput, dst: NodeInput) -> Optional[Edge]:
@@ -201,21 +202,12 @@ class GraphView(object):
"""
@property
- def end(self) -> Optional[int]:
+ def end(self) -> OptionalEventTime:
"""
Gets the latest time that this GraphView is valid.
Returns:
- Optional[int]: The latest time that this GraphView is valid or None if the GraphView is valid for all times.
- """
-
- @property
- def end_date_time(self) -> Optional[datetime]:
- """
- Gets the latest datetime that this GraphView is valid
-
- Returns:
- Optional[datetime]: The latest datetime that this GraphView is valid or None if the GraphView is valid for all times.
+ OptionalEventTime: The latest time that this GraphView is valid or None if the GraphView is valid for all times.
"""
def exclude_layer(self, name: str) -> GraphView:
@@ -374,21 +366,12 @@ class GraphView(object):
"""
@property
- def latest_date_time(self) -> Optional[datetime]:
- """
- DateTime of latest activity in the graph
-
- Returns:
- Optional[datetime]: the datetime of the latest activity in the graph
- """
-
- @property
- def latest_time(self) -> Optional[int]:
+ def latest_time(self) -> OptionalEventTime:
"""
- Timestamp of latest activity in the graph
+ Time entry of the latest activity in the graph
Returns:
- Optional[int]: the timestamp of the latest activity in the graph
+ OptionalEventTime: the time entry of the latest activity in the graph
"""
def layer(self, name: str) -> GraphView:
@@ -575,21 +558,12 @@ class GraphView(object):
"""
@property
- def start(self) -> Optional[int]:
+ def start(self) -> OptionalEventTime:
"""
Gets the start time for rolling and expanding windows for this GraphView
Returns:
- Optional[int]: The earliest time that this GraphView is valid or None if the GraphView is valid for all times.
- """
-
- @property
- def start_date_time(self) -> Optional[datetime]:
- """
- Gets the earliest datetime that this GraphView is valid
-
- Returns:
- Optional[datetime]: The earliest datetime that this GraphView is valid or None if the GraphView is valid for all times.
+ OptionalEventTime: The earliest time that this GraphView is valid or None if the GraphView is valid for all times.
"""
def subgraph(self, nodes: list[NodeInput]) -> GraphView:
@@ -775,7 +749,7 @@ class Graph(GraphView):
dst: str | int,
properties: Optional[PropInput] = None,
layer: Optional[str] = None,
- secondary_index: Optional[int] = None,
+ event_id: Optional[int] = None,
) -> MutableEdge:
"""
Adds a new edge with the given source and destination nodes and properties to the graph.
@@ -786,7 +760,7 @@ class Graph(GraphView):
dst (str|int): The id of the destination node.
properties (PropInput, optional): The properties of the edge, as a dict of string and properties.
layer (str, optional): The layer of the edge.
- secondary_index (int, optional): The optional integer which will be used as a secondary index
+ event_id (int, optional): The optional integer which will be used as an event id.
Returns:
MutableEdge: The added edge.
@@ -815,7 +789,7 @@ class Graph(GraphView):
id: str | int,
properties: Optional[PropInput] = None,
node_type: Optional[str] = None,
- secondary_index: Optional[int] = None,
+ event_id: Optional[int] = None,
) -> MutableNode:
"""
Adds a new node with the given id and properties to the graph.
@@ -824,8 +798,8 @@ class Graph(GraphView):
timestamp (TimeInput): The timestamp of the node.
id (str|int): The id of the node.
properties (PropInput, optional): The properties of the node.
- node_type (str, optional): The optional string which will be used as a node type
- secondary_index (int, optional): The optional integer which will be used as a secondary index
+ node_type (str, optional): The optional string which will be used as a node type.
+ event_id (int, optional): The optional integer which will be used as an event id.
Returns:
MutableNode: The added node.
@@ -838,7 +812,7 @@ class Graph(GraphView):
self,
timestamp: TimeInput,
properties: PropInput,
- secondary_index: Optional[int] = None,
+ event_id: Optional[int] = None,
) -> None:
"""
Adds properties to the graph.
@@ -846,7 +820,7 @@ class Graph(GraphView):
Arguments:
timestamp (TimeInput): The timestamp of the temporal property.
properties (PropInput): The temporal properties of the graph.
- secondary_index (int, optional): The optional integer which will be used as a secondary index
+ event_id (int, optional): The optional integer which will be used as an event id.
Returns:
None: This function does not return a value, if the operation is successful.
@@ -922,7 +896,7 @@ class Graph(GraphView):
id: str | int,
properties: Optional[PropInput] = None,
node_type: Optional[str] = None,
- secondary_index: Optional[int] = None,
+ event_id: Optional[int] = None,
) -> MutableNode:
"""
Creates a new node with the given id and properties to the graph. It fails if the node already exists.
@@ -931,8 +905,8 @@ class Graph(GraphView):
timestamp (TimeInput): The timestamp of the node.
id (str|int): The id of the node.
properties (PropInput, optional): The properties of the node.
- node_type (str, optional): The optional string which will be used as a node type
- secondary_index (int, optional): The optional integer which will be used as a secondary index
+ node_type (str, optional): The optional string which will be used as a node type.
+ event_id (int, optional): The optional integer which will be used as an event id.
Returns:
MutableNode: The created node.
@@ -1167,6 +1141,37 @@ class Graph(GraphView):
Graph: the loaded graph with initialised cache
"""
+ def load_edge_metadata_from_df(
+ self,
+ data: Any,
+ src: str,
+ dst: str,
+ metadata: Optional[List[str]] = None,
+ shared_metadata: Optional[PropInput] = None,
+ layer: Optional[str] = None,
+ layer_col: Optional[str] = None,
+ ) -> None:
+ """
+ Load edge metadata into the graph from any data source that supports the ArrowStreamExportable protocol (by providing an __arrow_c_stream__() method).
+ This includes, but is not limited to: Pandas dataframes, FireDucks(.pandas) dataframes,
+ Polars dataframes, Arrow tables, DuckDB (eg. DuckDBPyRelation obtained from running an SQL query)
+
+ Arguments:
+ data (Any): The data source containing edge information.
+ src (str): The column name for the source node.
+ dst (str): The column name for the destination node.
+ metadata (List[str], optional): List of edge metadata column names. Defaults to None.
+ shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every edge. Defaults to None.
+ layer (str, optional): The edge layer name. Defaults to None.
+ layer_col (str, optional): The edge layer column name in a dataframe. Defaults to None.
+
+ Returns:
+ None: This function does not return a value if the operation is successful.
+
+ Raises:
+ GraphError: If the operation fails.
+ """
+
def load_edge_props_from_pandas(
self,
df: DataFrame,
@@ -1225,6 +1230,41 @@ class Graph(GraphView):
GraphError: If the operation fails.
"""
+ def load_edges_from_df(
+ self,
+ data: Any,
+ time: str,
+ src: str,
+ dst: str,
+ properties: Optional[List[str]] = None,
+ metadata: Optional[List[str]] = None,
+ shared_metadata: Optional[PropInput] = None,
+ layer: Optional[str] = None,
+ layer_col: Optional[str] = None,
+ ) -> None:
+ """
+ Load edges into the graph from any data source that supports the ArrowStreamExportable protocol (by providing an __arrow_c_stream__() method).
+ This includes, but is not limited to: Pandas dataframes, FireDucks(.pandas) dataframes,
+ Polars dataframes, Arrow tables, DuckDB (eg. DuckDBPyRelation obtained from running an SQL query)
+
+ Arguments:
+ data (Any): The data source containing the edges.
+ time (str): The column name for the update timestamps.
+ src (str): The column name for the source node ids.
+ dst (str): The column name for the destination node ids.
+ properties (List[str], optional): List of edge property column names. Defaults to None.
+ metadata (List[str], optional): List of edge metadata column names. Defaults to None.
+ shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every edge. Defaults to None.
+ layer (str, optional): A value to use as the layer for all edges. Cannot be used in combination with layer_col. Defaults to None.
+ layer_col (str, optional): The edge layer column name in a dataframe. Cannot be used in combination with layer. Defaults to None.
+
+ Returns:
+ None: This function does not return a value if the operation is successful.
+
+ Raises:
+ GraphError: If the operation fails.
+ """
+
def load_edges_from_pandas(
self,
df: DataFrame,
@@ -1248,8 +1288,8 @@ class Graph(GraphView):
properties (List[str], optional): List of edge property column names. Defaults to None.
metadata (List[str], optional): List of edge metadata column names. Defaults to None.
shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every edge. Defaults to None.
- layer (str, optional): A value to use as the layer for all edges. Defaults to None. (cannot be used in combination with layer_col)
- layer_col (str, optional): The edge layer col name in dataframe. Defaults to None. (cannot be used in combination with layer)
+ layer (str, optional): A value to use as the layer for all edges. Cannot be used in combination with layer_col. Defaults to None.
+ layer_col (str, optional): The edge layer col name in dataframe. Cannot be used in combination with layer. Defaults to None.
Returns:
None: This function does not return a value, if the operation is successful.
@@ -1281,8 +1321,8 @@ class Graph(GraphView):
properties (List[str], optional): List of edge property column names. Defaults to None.
metadata (List[str], optional): List of edge metadata column names. Defaults to None.
shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every edge. Defaults to None.
- layer (str, optional): A value to use as the layer for all edges. Defaults to None. (cannot be used in combination with layer_col)
- layer_col (str, optional): The edge layer col name in dataframe. Defaults to None. (cannot be used in combination with layer)
+ layer (str, optional): A value to use as the layer for all edges. Cannot be used in combination with layer_col. Defaults to None.
+ layer_col (str, optional): The edge layer col name in dataframe. Cannot be used in combination with layer. Defaults to None.
Returns:
None: This function does not return a value, if the operation is successful.
@@ -1303,6 +1343,35 @@ class Graph(GraphView):
Graph:
"""
+ def load_node_metadata_from_df(
+ self,
+ data: Any,
+ id: str,
+ node_type: Optional[str] = None,
+ node_type_col: Optional[str] = None,
+ metadata: Optional[List[str]] = None,
+ shared_metadata: Optional[PropInput] = None,
+ ) -> None:
+ """
+ Load node metadata into the graph from any data source that supports the ArrowStreamExportable protocol (by providing an __arrow_c_stream__() method).
+ This includes, but is not limited to: Pandas dataframes, FireDucks(.pandas) dataframes,
+ Polars dataframes, Arrow tables, DuckDB (eg. DuckDBPyRelation obtained from running an SQL query)
+
+ Arguments:
+ data (Any): The data source containing node information.
+ id(str): The column name for the node IDs.
+ node_type (str, optional): A value to use as the node type for all nodes. Cannot be used in combination with node_type_col. Defaults to None.
+ node_type_col (str, optional): The node type column name in a dataframe. Cannot be used in combination with node_type. Defaults to None.
+ metadata (List[str], optional): List of node metadata column names. Defaults to None.
+ shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every node. Defaults to None.
+
+ Returns:
+ None: This function does not return a value if the operation is successful.
+
+ Raises:
+ GraphError: If the operation fails.
+ """
+
def load_node_props_from_pandas(
self,
df: DataFrame,
@@ -1318,8 +1387,8 @@ class Graph(GraphView):
Arguments:
df (DataFrame): The Pandas DataFrame containing node information.
id(str): The column name for the node IDs.
- node_type (str, optional): A value to use as the node type for all nodes. Defaults to None. (cannot be used in combination with node_type_col)
- node_type_col (str, optional): The node type col name in dataframe. Defaults to None. (cannot be used in combination with node_type)
+ node_type (str, optional): A value to use as the node type for all nodes. Cannot be used in combination with node_type_col. Defaults to None.
+ node_type_col (str, optional): The node type col name in dataframe. Cannot be used in combination with node_type. Defaults to None.
metadata (List[str], optional): List of node metadata column names. Defaults to None.
shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every node. Defaults to None.
@@ -1345,8 +1414,8 @@ class Graph(GraphView):
Arguments:
parquet_path (str): Parquet file or directory of Parquet files path containing node information.
id(str): The column name for the node IDs.
- node_type (str, optional): A value to use as the node type for all nodes. Defaults to None. (cannot be used in combination with node_type_col)
- node_type_col (str, optional): The node type col name in dataframe. Defaults to None. (cannot be used in combination with node_type)
+ node_type (str, optional): A value to use as the node type for all nodes. Cannot be used in combination with node_type_col. Defaults to None.
+ node_type_col (str, optional): The node type col name in dataframe. Cannot be used in combination with node_type. Defaults to None.
metadata (List[str], optional): List of node metadata column names. Defaults to None.
shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every node. Defaults to None.
@@ -1357,6 +1426,39 @@ class Graph(GraphView):
GraphError: If the operation fails.
"""
+ def load_nodes_from_df(
+ self,
+ data: Any,
+ time: str,
+ id: str,
+ node_type: Optional[str] = None,
+ node_type_col: Optional[str] = None,
+ properties: Optional[List[str]] = None,
+ metadata: Optional[List[str]] = None,
+ shared_metadata: Optional[PropInput] = None,
+ ) -> None:
+ """
+ Load nodes into the graph from any data source that supports the ArrowStreamExportable protocol (by providing an __arrow_c_stream__() method).
+ This includes, but is not limited to: Pandas dataframes, FireDucks(.pandas) dataframes,
+ Polars dataframes, Arrow tables, DuckDB (eg. DuckDBPyRelation obtained from running an SQL query)
+
+ Arguments:
+ data (Any): The data source containing the nodes.
+ time (str): The column name for the timestamps.
+ id (str): The column name for the node IDs.
+ node_type (str, optional): A value to use as the node type for all nodes. Cannot be used in combination with node_type_col. Defaults to None.
+ node_type_col (str, optional): The node type column name in a dataframe. Cannot be used in combination with node_type. Defaults to None.
+ properties (List[str], optional): List of node property column names. Defaults to None.
+ metadata (List[str], optional): List of node metadata column names. Defaults to None.
+ shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every node. Defaults to None.
+
+ Returns:
+ None: This function does not return a value if the operation is successful.
+
+ Raises:
+ GraphError: If the operation fails.
+ """
+
def load_nodes_from_pandas(
self,
df: DataFrame,
@@ -1375,8 +1477,8 @@ class Graph(GraphView):
df (DataFrame): The Pandas DataFrame containing the nodes.
time (str): The column name for the timestamps.
id (str): The column name for the node IDs.
- node_type (str, optional): A value to use as the node type for all nodes. Defaults to None. (cannot be used in combination with node_type_col)
- node_type_col (str, optional): The node type col name in dataframe. Defaults to None. (cannot be used in combination with node_type)
+ node_type (str, optional): A value to use as the node type for all nodes. Cannot be used in combination with node_type_col. Defaults to None.
+ node_type_col (str, optional): The node type col name in dataframe. Cannot be used in combination with node_type. Defaults to None.
properties (List[str], optional): List of node property column names. Defaults to None.
metadata (List[str], optional): List of node metadata column names. Defaults to None.
shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every node. Defaults to None.
@@ -1406,8 +1508,8 @@ class Graph(GraphView):
parquet_path (str): Parquet file or directory of Parquet files containing the nodes
time (str): The column name for the timestamps.
id (str): The column name for the node IDs.
- node_type (str, optional): A value to use as the node type for all nodes. Defaults to None. (cannot be used in combination with node_type_col)
- node_type_col (str, optional): The node type col name in dataframe. Defaults to None. (cannot be used in combination with node_type)
+ node_type (str, optional): A value to use as the node type for all nodes. Cannot be used in combination with node_type_col. Defaults to None.
+ node_type_col (str, optional): The node type col name in dataframe. Cannot be used in combination with node_type. Defaults to None.
properties (List[str], optional): List of node property column names. Defaults to None.
metadata (List[str], optional): List of node metadata column names. Defaults to None.
shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every node. Defaults to None.
@@ -1514,7 +1616,7 @@ class PersistentGraph(GraphView):
dst: str | int,
properties: Optional[PropInput] = None,
layer: Optional[str] = None,
- secondary_index: Optional[int] = None,
+ event_id: Optional[int] = None,
) -> None:
"""
Adds a new edge with the given source and destination nodes and properties to the graph.
@@ -1523,9 +1625,9 @@ class PersistentGraph(GraphView):
timestamp (int): The timestamp of the edge.
src (str | int): The id of the source node.
dst (str | int): The id of the destination node.
- properties (PropInput, optional): The properties of the edge, as a dict of string and properties
+ properties (PropInput, optional): The properties of the edge, as a dict of string and properties.
layer (str, optional): The layer of the edge.
- secondary_index (int, optional): The optional integer which will be used as a secondary index
+ event_id (int, optional): The optional integer which will be used as an event id.
Returns:
None: This function does not return a value, if the operation is successful.
@@ -1554,7 +1656,7 @@ class PersistentGraph(GraphView):
id: str | int,
properties: Optional[PropInput] = None,
node_type: Optional[str] = None,
- secondary_index: Optional[int] = None,
+ event_id: Optional[int] = None,
) -> None:
"""
Adds a new node with the given id and properties to the graph.
@@ -1563,8 +1665,8 @@ class PersistentGraph(GraphView):
timestamp (TimeInput): The timestamp of the node.
id (str | int): The id of the node.
properties (PropInput, optional): The properties of the node.
- node_type (str, optional) : The optional string which will be used as a node type
- secondary_index (int, optional): The optional integer which will be used as a secondary index
+ node_type (str, optional) : The optional string which will be used as a node type.
+ event_id (int, optional): The optional integer which will be used as an event id.
Returns:
None: This function does not return a value, if the operation is successful.
@@ -1574,10 +1676,7 @@ class PersistentGraph(GraphView):
"""
def add_properties(
- self,
- timestamp: TimeInput,
- properties: dict,
- secondary_index: Optional[int] = None,
+ self, timestamp: TimeInput, properties: dict, event_id: Optional[int] = None
) -> None:
"""
Adds properties to the graph.
@@ -1585,7 +1684,7 @@ class PersistentGraph(GraphView):
Arguments:
timestamp (TimeInput): The timestamp of the temporal property.
properties (dict): The temporal properties of the graph.
- secondary_index (int, optional): The optional integer which will be used as a secondary index
+ event_id (int, optional): The optional integer which will be used as an event id.
Returns:
None: This function does not return a value, if the operation is successful.
@@ -1660,7 +1759,7 @@ class PersistentGraph(GraphView):
id: str | int,
properties: Optional[PropInput] = None,
node_type: Optional[str] = None,
- secondary_index: Optional[int] = None,
+ event_id: Optional[int] = None,
) -> MutableNode:
"""
Creates a new node with the given id and properties to the graph. It fails if the node already exists.
@@ -1669,8 +1768,8 @@ class PersistentGraph(GraphView):
timestamp (TimeInput): The timestamp of the node.
id (str | int): The id of the node.
properties (PropInput, optional): The properties of the node.
- node_type (str, optional) : The optional string which will be used as a node type
- secondary_index (int, optional): The optional integer which will be used as a secondary index
+ node_type (str, optional) : The optional string which will be used as a node type.
+ event_id (int, optional): The optional integer which will be used as an event id.
Returns:
MutableNode: the newly created node.
@@ -1685,17 +1784,17 @@ class PersistentGraph(GraphView):
src: str | int,
dst: str | int,
layer: Optional[str] = None,
- secondary_index: Optional[int] = None,
+ event_id: Optional[int] = None,
) -> MutableEdge:
"""
- Deletes an edge given the timestamp, src and dst nodes and layer (optional)
+ Deletes an edge given the timestamp, src and dst nodes and layer (optional).
Arguments:
timestamp (int): The timestamp of the edge.
src (str | int): The id of the source node.
dst (str | int): The id of the destination node.
layer (str, optional): The layer of the edge.
- secondary_index (int, optional): The optional integer which will be used as a secondary index.
+ event_id (int, optional): The optional integer which will be used as an event id.
Returns:
MutableEdge: The deleted edge
@@ -1913,6 +2012,35 @@ class PersistentGraph(GraphView):
PersistentGraph: the loaded graph with initialised cache
"""
+ def load_edge_deletions_from_df(
+ self,
+ data: Any,
+ time: str,
+ src: str,
+ dst: str,
+ layer: Optional[str] = None,
+ layer_col: Optional[str] = None,
+ ) -> None:
+ """
+ Load edge deletions into the graph from any data source that supports the ArrowStreamExportable protocol (by providing an __arrow_c_stream__() method).
+ This includes, but is not limited to: Pandas dataframes, FireDucks(.pandas) dataframes,
+ Polars dataframes, Arrow tables, DuckDB (eg. DuckDBPyRelation obtained from running an SQL query)
+
+ Arguments:
+ data (Any): The data source containing the edges.
+ time (str): The column name for the update timestamps.
+ src (str): The column name for the source node ids.
+ dst (str): The column name for the destination node ids.
+ layer (str, optional): A value to use as the layer for all edges. Cannot be used in combination with layer_col. Defaults to None.
+ layer_col (str, optional): The edge layer col name in the data source. Cannot be used in combination with layer. Defaults to None.
+
+ Returns:
+ None: This function does not return a value, if the operation is successful.
+
+ Raises:
+ GraphError: If the operation fails.
+ """
+
def load_edge_deletions_from_pandas(
self,
df: DataFrame,
@@ -1930,8 +2058,8 @@ class PersistentGraph(GraphView):
time (str): The column name for the update timestamps.
src (str): The column name for the source node ids.
dst (str): The column name for the destination node ids.
- layer (str, optional): A value to use as the layer for all edges. Defaults to None. (cannot be used in combination with layer_col)
- layer_col (str, optional): The edge layer col name in dataframe. Defaults to None. (cannot be used in combination with layer)
+ layer (str, optional): A value to use as the layer for all edges. Cannot be used in combination with layer_col. Defaults to None.
+ layer_col (str, optional): The edge layer col name in dataframe. Cannot be used in combination with layer. Defaults to None.
Returns:
None: This function does not return a value, if the operation is successful.
@@ -1957,8 +2085,8 @@ class PersistentGraph(GraphView):
src (str): The column name for the source node ids.
dst (str): The column name for the destination node ids.
time (str): The column name for the update timestamps.
- layer (str, optional): A value to use as the layer for all edges. Defaults to None. (cannot be used in combination with layer_col)
- layer_col (str, optional): The edge layer col name in dataframe. Defaults to None. (cannot be used in combination with layer)
+ layer (str, optional): A value to use as the layer for all edges. Cannot be used in combination with layer_col. Defaults to None.
+ layer_col (str, optional): The edge layer col name in dataframe. Cannot be used in combination with layer. Defaults to None.
Returns:
None: This function does not return a value, if the operation is successful.
@@ -1967,6 +2095,37 @@ class PersistentGraph(GraphView):
GraphError: If the operation fails.
"""
+ def load_edge_metadata_from_df(
+ self,
+ data: Any,
+ src: str,
+ dst: str,
+ metadata: Optional[List[str]] = None,
+ shared_metadata: Optional[PropInput] = None,
+ layer: Optional[str] = None,
+ layer_col: Optional[str] = None,
+ ) -> None:
+ """
+ Load edge metadata into the graph from any data source that supports the ArrowStreamExportable protocol (by providing an __arrow_c_stream__() method).
+ This includes, but is not limited to: Pandas dataframes, FireDucks(.pandas) dataframes,
+ Polars dataframes, Arrow tables, DuckDB (eg. DuckDBPyRelation obtained from running an SQL query)
+
+ Arguments:
+ data (Any): The data source containing edge information.
+ src (str): The column name for the source node.
+ dst (str): The column name for the destination node.
+ metadata (List[str], optional): List of edge metadata column names. Defaults to None.
+ shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every edge. Defaults to None.
+ layer (str, optional): The edge layer name. Defaults to None.
+ layer_col (str, optional): The edge layer column name in a dataframe. Defaults to None.
+
+ Returns:
+ None: This function does not return a value if the operation is successful.
+
+ Raises:
+ GraphError: If the operation fails.
+ """
+
def load_edge_props_from_pandas(
self,
df: DataFrame,
@@ -2025,6 +2184,41 @@ class PersistentGraph(GraphView):
GraphError: If the operation fails.
"""
+ def load_edges_from_df(
+ self,
+ data: Any,
+ time: str,
+ src: str,
+ dst: str,
+ properties: Optional[List[str]] = None,
+ metadata: Optional[List[str]] = None,
+ shared_metadata: Optional[PropInput] = None,
+ layer: Optional[str] = None,
+ layer_col: Optional[str] = None,
+ ) -> None:
+ """
+ Load edges into the graph from any data source that supports the ArrowStreamExportable protocol (by providing an __arrow_c_stream__() method).
+ This includes, but is not limited to: Pandas dataframes, FireDucks(.pandas) dataframes,
+ Polars dataframes, Arrow tables, DuckDB (eg. DuckDBPyRelation obtained from running an SQL query)
+
+ Arguments:
+ data (Any): The data source containing the edges.
+ time (str): The column name for the update timestamps.
+ src (str): The column name for the source node ids.
+ dst (str): The column name for the destination node ids.
+ properties (List[str], optional): List of edge property column names. Defaults to None.
+ metadata (List[str], optional): List of edge metadata column names. Defaults to None.
+ shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every edge. Defaults to None.
+ layer (str, optional): A value to use as the layer for all edges. Cannot be used in combination with layer_col. Defaults to None.
+ layer_col (str, optional): The edge layer column name in a dataframe. Cannot be used in combination with layer. Defaults to None.
+
+ Returns:
+ None: This function does not return a value if the operation is successful.
+
+ Raises:
+ GraphError: If the operation fails.
+ """
+
def load_edges_from_pandas(
self,
df: DataFrame,
@@ -2048,8 +2242,8 @@ class PersistentGraph(GraphView):
properties (List[str], optional): List of edge property column names. Defaults to None.
metadata (List[str], optional): List of edge metadata column names. Defaults to None.
shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every edge. Defaults to None.
- layer (str, optional): A value to use as the layer for all edges. Defaults to None. (cannot be used in combination with layer_col)
- layer_col (str, optional): The edge layer col name in dataframe. Defaults to None. (cannot be used in combination with layer)
+ layer (str, optional): A value to use as the layer for all edges. Cannot be used in combination with layer_col. Defaults to None.
+ layer_col (str, optional): The edge layer col name in dataframe. Cannot be used in combination with layer. Defaults to None.
Returns:
None: This function does not return a value, if the operation is successful.
@@ -2081,8 +2275,8 @@ class PersistentGraph(GraphView):
properties (List[str], optional): List of edge property column names. Defaults to None.
metadata (List[str], optional): List of edge metadata column names. Defaults to None.
shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every edge. Defaults to None.
- layer (str, optional): A value to use as the layer for all edges. Defaults to None. (cannot be used in combination with layer_col)
- layer_col (str, optional): The edge layer col name in dataframe. Defaults to None. (cannot be used in combination with layer)
+ layer (str, optional): A value to use as the layer for all edges. Cannot be used in combination with layer_col. Defaults to None.
+ layer_col (str, optional): The edge layer col name in dataframe. Cannot be used in combination with layer. Defaults to None.
Returns:
None: This function does not return a value, if the operation is successful.
@@ -2103,6 +2297,35 @@ class PersistentGraph(GraphView):
PersistentGraph:
"""
+ def load_node_metadata_from_df(
+ self,
+ data: Any,
+ id: str,
+ node_type: Optional[str] = None,
+ node_type_col: Optional[str] = None,
+ metadata: Optional[List[str]] = None,
+ shared_metadata: Optional[PropInput] = None,
+ ) -> None:
+ """
+ Load node metadata into the graph from any data source that supports the ArrowStreamExportable protocol (by providing an __arrow_c_stream__() method).
+ This includes, but is not limited to: Pandas dataframes, FireDucks(.pandas) dataframes,
+ Polars dataframes, Arrow tables, DuckDB (eg. DuckDBPyRelation obtained from running an SQL query)
+
+ Arguments:
+ data (Any): The data source containing node information.
+ id(str): The column name for the node IDs.
+ node_type (str, optional): A value to use as the node type for all nodes. Cannot be used in combination with node_type_col. Defaults to None.
+ node_type_col (str, optional): The node type column name in a dataframe. Cannot be used in combination with node_type. Defaults to None.
+ metadata (List[str], optional): List of node metadata column names. Defaults to None.
+ shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every node. Defaults to None.
+
+ Returns:
+ None: This function does not return a value if the operation is successful.
+
+ Raises:
+ GraphError: If the operation fails.
+ """
+
def load_node_props_from_pandas(
self,
df: DataFrame,
@@ -2118,8 +2341,8 @@ class PersistentGraph(GraphView):
Arguments:
df (DataFrame): The Pandas DataFrame containing node information.
id(str): The column name for the node IDs.
- node_type (str, optional): A value to use as the node type for all nodes. Defaults to None. (cannot be used in combination with node_type_col)
- node_type_col (str, optional): The node type col name in dataframe. Defaults to None. (cannot be used in combination with node_type)
+ node_type (str, optional): A value to use as the node type for all nodes. Cannot be used in combination with node_type_col. Defaults to None.
+ node_type_col (str, optional): The node type col name in dataframe. Cannot be used in combination with node_type. Defaults to None.
metadata (List[str], optional): List of node metadata column names. Defaults to None.
shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every node. Defaults to None.
@@ -2145,8 +2368,8 @@ class PersistentGraph(GraphView):
Arguments:
parquet_path (str): Parquet file or directory of Parquet files path containing node information.
id(str): The column name for the node IDs.
- node_type (str, optional): A value to use as the node type for all nodes. Defaults to None. (cannot be used in combination with node_type_col)
- node_type_col (str, optional): The node type col name in dataframe. Defaults to None. (cannot be used in combination with node_type)
+ node_type (str, optional): A value to use as the node type for all nodes. Cannot be used in combination with node_type_col. Defaults to None.
+ node_type_col (str, optional): The node type col name in dataframe. Cannot be used in combination with node_type. Defaults to None.
metadata (List[str], optional): List of node metadata column names. Defaults to None.
shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every node. Defaults to None.
@@ -2157,6 +2380,39 @@ class PersistentGraph(GraphView):
GraphError: If the operation fails.
"""
+ def load_nodes_from_df(
+ self,
+ data: Any,
+ time: str,
+ id: str,
+ node_type: Optional[str] = None,
+ node_type_col: Optional[str] = None,
+ properties: Optional[List[str]] = None,
+ metadata: Optional[List[str]] = None,
+ shared_metadata: Optional[PropInput] = None,
+ ) -> None:
+ """
+ Load nodes into the graph from any data source that supports the ArrowStreamExportable protocol (by providing an __arrow_c_stream__() method).
+ This includes, but is not limited to: Pandas dataframes, FireDucks(.pandas) dataframes,
+ Polars dataframes, Arrow tables, DuckDB (eg. DuckDBPyRelation obtained from running an SQL query)
+
+ Arguments:
+ data (Any): The data source containing the nodes.
+ time (str): The column name for the timestamps.
+ id (str): The column name for the node IDs.
+ node_type (str, optional): A value to use as the node type for all nodes. Cannot be used in combination with node_type_col. Defaults to None.
+ node_type_col (str, optional): The node type column name in a dataframe. Cannot be used in combination with node_type. Defaults to None.
+ properties (List[str], optional): List of node property column names. Defaults to None.
+ metadata (List[str], optional): List of node metadata column names. Defaults to None.
+ shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every node. Defaults to None.
+
+ Returns:
+ None: This function does not return a value if the operation is successful.
+
+ Raises:
+ GraphError: If the operation fails.
+ """
+
def load_nodes_from_pandas(
self,
df: DataFrame,
@@ -2175,8 +2431,8 @@ class PersistentGraph(GraphView):
df (DataFrame): The Pandas DataFrame containing the nodes.
time (str): The column name for the timestamps.
id (str): The column name for the node IDs.
- node_type (str, optional): A value to use as the node type for all nodes. Defaults to None. (cannot be used in combination with node_type_col)
- node_type_col (str, optional): The node type col name in dataframe. Defaults to None. (cannot be used in combination with node_type)
+ node_type (str, optional): A value to use as the node type for all nodes. Cannot be used in combination with node_type_col. Defaults to None.
+ node_type_col (str, optional): The node type col name in dataframe. Cannot be used in combination with node_type. Defaults to None.
properties (List[str], optional): List of node property column names. Defaults to None.
metadata (List[str], optional): List of node metadata column names. Defaults to None.
shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every node. Defaults to None.
@@ -2206,8 +2462,8 @@ class PersistentGraph(GraphView):
parquet_path (str): Parquet file or directory of Parquet files containing the nodes
time (str): The column name for the timestamps.
id (str): The column name for the node IDs.
- node_type (str, optional): A value to use as the node type for all nodes. Defaults to None. (cannot be used in combination with node_type_col)
- node_type_col (str, optional): The node type col name in dataframe. Defaults to None. (cannot be used in combination with node_type)
+ node_type (str, optional): A value to use as the node type for all nodes. Cannot be used in combination with node_type_col. Defaults to None.
+ node_type_col (str, optional): The node type col name in dataframe. Cannot be used in combination with node_type. Defaults to None.
properties (List[str], optional): List of node property column names. Defaults to None.
metadata (List[str], optional): List of node metadata column names. Defaults to None.
shared_metadata (PropInput, optional): A dictionary of metadata properties that will be added to every node. Defaults to None.
@@ -2368,21 +2624,12 @@ class Node(object):
"""
@property
- def earliest_date_time(self) -> datetime:
- """
- Returns the earliest datetime that the node exists.
-
- Returns:
- datetime: The earliest datetime that the node exists as a Datetime.
- """
-
- @property
- def earliest_time(self) -> int:
+ def earliest_time(self) -> OptionalEventTime:
"""
Returns the earliest time that the node exists.
Returns:
- int: The earliest time that the node exists as an integer.
+ OptionalEventTime: The earliest time that the node exists.
"""
def edge_history_count(self) -> int:
@@ -2403,21 +2650,12 @@ class Node(object):
"""
@property
- def end(self) -> Optional[int]:
+ def end(self) -> OptionalEventTime:
"""
Gets the latest time that this Node is valid.
Returns:
- Optional[int]: The latest time that this Node is valid or None if the Node is valid for all times.
- """
-
- @property
- def end_date_time(self) -> Optional[datetime]:
- """
- Gets the latest datetime that this Node is valid
-
- Returns:
- Optional[datetime]: The latest datetime that this Node is valid or None if the Node is valid for all times.
+ OptionalEventTime: The latest time that this Node is valid or None if the Node is valid for all times.
"""
def exclude_layer(self, name: str) -> Node:
@@ -2507,25 +2745,17 @@ class Node(object):
bool:
"""
- def history(self) -> List[int]:
+ @property
+ def history(self) -> History:
"""
Returns the history of a node, including node additions and changes made to node.
Returns:
- List[int]: A list of unix timestamps of the event history of node.
+ History: A History object for the node, providing access to time entries.
"""
- def history_date_time(self) -> List[datetime]:
- """
- Returns the history of a node, including node additions and changes made to node.
-
- Returns:
- List[datetime]: A list of timestamps of the event history of node.
-
- """
-
- @property
- def id(self) -> str | int:
+ @property
+ def id(self) -> str | int:
"""
Returns the id of the node.
This is a unique identifier for the node.
@@ -2577,21 +2807,12 @@ class Node(object):
"""
@property
- def latest_date_time(self) -> datetime:
- """
- Returns the latest datetime that the node exists.
-
- Returns:
- datetime: The latest datetime that the node exists as a Datetime.
- """
-
- @property
- def latest_time(self) -> int:
+ def latest_time(self) -> OptionalEventTime:
"""
Returns the latest time that the node exists.
Returns:
- int: The latest time that the node exists as an integer.
+ OptionalEventTime: The latest time that the node exists.
"""
def layer(self, name: str) -> Node:
@@ -2775,21 +2996,12 @@ class Node(object):
"""
@property
- def start(self) -> Optional[int]:
+ def start(self) -> OptionalEventTime:
"""
Gets the start time for rolling and expanding windows for this Node
Returns:
- Optional[int]: The earliest time that this Node is valid or None if the Node is valid for all times.
- """
-
- @property
- def start_date_time(self) -> Optional[datetime]:
- """
- Gets the earliest datetime that this Node is valid
-
- Returns:
- Optional[datetime]: The earliest datetime that this Node is valid or None if the Node is valid for all times.
+ OptionalEventTime: The earliest time that this Node is valid or None if the Node is valid for all times.
"""
def valid_layers(self, names: list[str]) -> Node:
@@ -2917,15 +3129,6 @@ class Nodes(object):
DegreeView: a view of the undirected node degrees.
"""
- @property
- def earliest_date_time(self) -> EarliestDateTimeView:
- """
- The earliest time nodes are active as datetime objects
-
- Returns:
- EarliestDateTimeView: a view of the earliest active times.
- """
-
@property
def earliest_time(self) -> EarliestTimeView:
"""
@@ -2953,21 +3156,12 @@ class Nodes(object):
"""
@property
- def end(self) -> Optional[int]:
+ def end(self) -> OptionalEventTime:
"""
Gets the latest time that this Nodes is valid.
Returns:
- Optional[int]: The latest time that this Nodes is valid or None if the Nodes is valid for all times.
- """
-
- @property
- def end_date_time(self) -> Optional[datetime]:
- """
- Gets the latest datetime that this Nodes is valid
-
- Returns:
- Optional[datetime]: The latest datetime that this Nodes is valid or None if the Nodes is valid for all times.
+ OptionalEventTime: The latest time that this Nodes is valid or None if the Nodes is valid for all times.
"""
def exclude_layer(self, name: str) -> Nodes:
@@ -3057,22 +3251,13 @@ class Nodes(object):
bool:
"""
+ @property
def history(self) -> HistoryView:
"""
- Returns all timestamps of nodes, when a node is added or change to a node is made.
+ Returns all history objects of nodes, with information on when a node is added or change to a node is made.
Returns:
HistoryView: a view of the node histories
-
- """
-
- def history_date_time(self) -> HistoryDateTimeView:
- """
- Returns all timestamps of nodes, when a node is added or change to a node is made.
-
- Returns:
- HistoryDateTimeView: a view of the node histories as datetime objects.
-
"""
@property
@@ -3118,15 +3303,6 @@ class Nodes(object):
Nodes:
"""
- @property
- def latest_date_time(self) -> LatestDateTimeView:
- """
- The latest time nodes are active as datetime objects
-
- Returns:
- LatestDateTimeView: a view of the latest active times
- """
-
@property
def latest_time(self) -> LatestTimeView:
"""
@@ -3161,7 +3337,7 @@ class Nodes(object):
"""
@property
- def metadata(self):
+ def metadata(self) -> MetadataView:
"""
The metadata of the nodes.
@@ -3317,21 +3493,12 @@ class Nodes(object):
"""
@property
- def start(self) -> Optional[int]:
+ def start(self) -> OptionalEventTime:
"""
Gets the start time for rolling and expanding windows for this Nodes
Returns:
- Optional[int]: The earliest time that this Nodes is valid or None if the Nodes is valid for all times.
- """
-
- @property
- def start_date_time(self) -> Optional[datetime]:
- """
- Gets the earliest datetime that this Nodes is valid
-
- Returns:
- Optional[datetime]: The earliest datetime that this Nodes is valid or None if the Nodes is valid for all times.
+ OptionalEventTime: The earliest time that this Nodes is valid or None if the Nodes is valid for all times.
"""
def to_df(
@@ -3454,6 +3621,14 @@ class PathFromNode(object):
list[Node]: the list of nodes
"""
+ def combined_history(self) -> History:
+ """
+ Returns a single history object containing time entries for all nodes in the path.
+
+ Returns:
+ History: History object with all time entries for the nodes.
+ """
+
def default_layer(self) -> PathFromNode:
"""
Return a view of PathFromNode containing only the default edge layer
@@ -3470,12 +3645,12 @@ class PathFromNode(object):
"""
@property
- def earliest_time(self) -> OptionI64Iterable:
+ def earliest_time(self) -> OptionEventTimeIterable:
"""
- The node earliest time.
+ The earliest time of each node.
Returns:
- OptionI64Iterable:
+ OptionEventTimeIterable: An iterable of `EventTime`s.
"""
def edge_history_count(self) -> UsizeIterable:
@@ -3496,21 +3671,12 @@ class PathFromNode(object):
"""
@property
- def end(self) -> Optional[int]:
+ def end(self) -> OptionalEventTime:
"""
Gets the latest time that this PathFromNode is valid.
Returns:
- Optional[int]: The latest time that this PathFromNode is valid or None if the PathFromNode is valid for all times.
- """
-
- @property
- def end_date_time(self) -> Optional[datetime]:
- """
- Gets the latest datetime that this PathFromNode is valid
-
- Returns:
- Optional[datetime]: The latest datetime that this PathFromNode is valid or None if the PathFromNode is valid for all times.
+ OptionalEventTime: The latest time that this PathFromNode is valid or None if the PathFromNode is valid for all times.
"""
def exclude_layer(self, name: str) -> PathFromNode:
@@ -3644,12 +3810,12 @@ class PathFromNode(object):
"""
@property
- def latest_time(self) -> OptionI64Iterable:
+ def latest_time(self) -> OptionEventTimeIterable:
"""
- The node latest time.
+ The latest time of each node.
Returns:
- OptionI64Iterable:
+ OptionEventTimeIterable: An iterable of `EventTime`s.
"""
def layer(self, name: str) -> PathFromNode:
@@ -3677,7 +3843,7 @@ class PathFromNode(object):
"""
@property
- def metadata(self):
+ def metadata(self) -> MetadataView:
"""
The node metadata.
@@ -3833,21 +3999,12 @@ class PathFromNode(object):
"""
@property
- def start(self) -> Optional[int]:
+ def start(self) -> OptionalEventTime:
"""
Gets the start time for rolling and expanding windows for this PathFromNode
Returns:
- Optional[int]: The earliest time that this PathFromNode is valid or None if the PathFromNode is valid for all times.
- """
-
- @property
- def start_date_time(self) -> Optional[datetime]:
- """
- Gets the earliest datetime that this PathFromNode is valid
-
- Returns:
- Optional[datetime]: The earliest datetime that this PathFromNode is valid or None if the PathFromNode is valid for all times.
+ OptionalEventTime: The earliest time that this PathFromNode is valid or None if the PathFromNode is valid for all times.
"""
def type_filter(self, node_types: list[str]) -> PathFromNode:
@@ -3951,6 +4108,14 @@ class PathFromGraph(object):
list[list[Node]]: the list of nodes
"""
+ def combined_history(self) -> History:
+ """
+ Returns a single history object containing time entries for all nodes in the path.
+
+ Returns:
+ History: A history object with all time entries associated with the nodes.
+ """
+
def default_layer(self) -> PathFromGraph:
"""
Return a view of PathFromGraph containing only the default edge layer
@@ -3967,21 +4132,12 @@ class PathFromGraph(object):
"""
@property
- def earliest_date_time(self) -> NestedUtcDateTimeIterable:
- """
- Returns the earliest date time of the nodes.
-
- Returns:
- NestedUtcDateTimeIterable:
- """
-
- @property
- def earliest_time(self) -> NestedOptionI64Iterable:
+ def earliest_time(self) -> NestedOptionEventTimeIterable:
"""
The node earliest times.
Returns:
- NestedOptionI64Iterable:
+ NestedOptionEventTimeIterable:
"""
def edge_history_count(self) -> NestedUsizeIterable:
@@ -4002,21 +4158,12 @@ class PathFromGraph(object):
"""
@property
- def end(self) -> Optional[int]:
+ def end(self) -> OptionalEventTime:
"""
Gets the latest time that this PathFromGraph is valid.
Returns:
- Optional[int]: The latest time that this PathFromGraph is valid or None if the PathFromGraph is valid for all times.
- """
-
- @property
- def end_date_time(self) -> Optional[datetime]:
- """
- Gets the latest datetime that this PathFromGraph is valid
-
- Returns:
- Optional[datetime]: The latest datetime that this PathFromGraph is valid or None if the PathFromGraph is valid for all times.
+ OptionalEventTime: The latest time that this PathFromGraph is valid or None if the PathFromGraph is valid for all times.
"""
def exclude_layer(self, name: str) -> PathFromGraph:
@@ -4106,20 +4253,13 @@ class PathFromGraph(object):
bool:
"""
- def history(self) -> NestedI64VecIterable:
- """
- Returns all timestamps of nodes, when an node is added or change to an node is made.
-
- Returns:
- NestedI64VecIterable:
- """
-
- def history_date_time(self) -> NestedVecUtcDateTimeIterable:
+ @property
+ def history(self) -> NestedHistoryIterable:
"""
- Returns all timestamps of nodes, when an node is added or change to an node is made.
+ Returns a history object for each node with time entries for when a node is added or change to a node is made.
Returns:
- NestedVecUtcDateTimeIterable:
+ NestedHistoryIterable: A nested iterable of history objects, one for each node.
"""
@property
@@ -4166,21 +4306,12 @@ class PathFromGraph(object):
"""
@property
- def latest_date_time(self) -> NestedUtcDateTimeIterable:
- """
- Returns the latest date time of the nodes.
-
- Returns:
- NestedUtcDateTimeIterable:
- """
-
- @property
- def latest_time(self) -> NestedOptionI64Iterable:
+ def latest_time(self) -> NestedOptionEventTimeIterable:
"""
The node latest times.
Returns:
- NestedOptionI64Iterable:
+ NestedOptionEventTimeIterable:
"""
def layer(self, name: str) -> PathFromGraph:
@@ -4364,21 +4495,12 @@ class PathFromGraph(object):
"""
@property
- def start(self) -> Optional[int]:
+ def start(self) -> OptionalEventTime:
"""
Gets the start time for rolling and expanding windows for this PathFromGraph
Returns:
- Optional[int]: The earliest time that this PathFromGraph is valid or None if the PathFromGraph is valid for all times.
- """
-
- @property
- def start_date_time(self) -> Optional[datetime]:
- """
- Gets the earliest datetime that this PathFromGraph is valid
-
- Returns:
- Optional[datetime]: The earliest datetime that this PathFromGraph is valid or None if the PathFromGraph is valid for all times.
+ OptionalEventTime: The earliest time that this PathFromGraph is valid or None if the PathFromGraph is valid for all times.
"""
def type_filter(self, node_types: list[str]) -> PathFromGraph:
@@ -4446,7 +4568,7 @@ class MutableNode(Node):
self,
t: TimeInput,
properties: Optional[PropInput] = None,
- secondary_index: Optional[int] = None,
+ event_id: Optional[int] = None,
) -> None:
"""
Add updates to a node in the graph at a specified time.
@@ -4458,7 +4580,7 @@ class MutableNode(Node):
string representing the property name, and each value
is of type Prop representing the property value.
If None, no properties are updated.
- secondary_index (int, optional): The optional integer which will be used as a secondary index
+ event_id (int, optional): The optional integer which will be used as an event id.
Returns:
None: This function does not return a value, if the operation is successful.
@@ -4558,15 +4680,6 @@ class Edge(object):
Edge:
"""
- @property
- def date_time(self) -> datetime:
- """
- Gets the datetime of an exploded edge.
-
- Returns:
- datetime: the datetime of an exploded edge
- """
-
def default_layer(self) -> Edge:
"""
Return a view of Edge containing only the default edge layer
@@ -4574,20 +4687,13 @@ class Edge(object):
Edge: The layered view
"""
- def deletions(self) -> List[int]:
- """
- Returns a list of timestamps of when an edge is deleted.
-
- Returns:
- List[int]: A list of unix timestamps
- """
-
- def deletions_data_time(self) -> List[datetime]:
+ @property
+ def deletions(self) -> History:
"""
- Returns a list of timestamps of when an edge is deleted.
+ Returns a history object with EventTime entries for an edge's deletion times.
Returns:
- List[datetime]:
+ History: A history object containing time entries about the edge's deletions
"""
@property
@@ -4600,39 +4706,21 @@ class Edge(object):
"""
@property
- def earliest_date_time(self) -> datetime:
- """
- Gets of earliest datetime of an edge.
-
- Returns:
- datetime: the earliest datetime of an edge
- """
-
- @property
- def earliest_time(self) -> int:
+ def earliest_time(self) -> OptionalEventTime:
"""
Gets the earliest time of an edge.
Returns:
- int: The earliest time of an edge
+ OptionalEventTime: The earliest time of an edge
"""
@property
- def end(self) -> Optional[int]:
+ def end(self) -> OptionalEventTime:
"""
Gets the latest time that this Edge is valid.
Returns:
- Optional[int]: The latest time that this Edge is valid or None if the Edge is valid for all times.
- """
-
- @property
- def end_date_time(self) -> Optional[datetime]:
- """
- Gets the latest datetime that this Edge is valid
-
- Returns:
- Optional[datetime]: The latest datetime that this Edge is valid or None if the Edge is valid for all times.
+ OptionalEventTime: The latest time that this Edge is valid or None if the Edge is valid for all times.
"""
def exclude_layer(self, name: str) -> Edge:
@@ -4727,28 +4815,13 @@ class Edge(object):
bool:
"""
- def history(self) -> List[int]:
- """
- Returns a list of timestamps of when an edge is added or change to an edge is made.
-
- Returns:
- List[int]:
- """
-
- def history_counts(self) -> int:
- """
- Returns the number of times an edge was added or change to an edge was made.
-
- Returns:
- int: The number of times an edge was added or change to an edge was made.
- """
-
- def history_date_time(self) -> Optional[List[datetime]]:
+ @property
+ def history(self) -> History:
"""
- Returns a list of timestamps of when an edge is added or change to an edge is made.
+ Returns a history object with EventTime entries for when an edge is added or change to an edge is made.
Returns:
- Optional[List[datetime]]:
+ History: A history object containing temporal entries about the edge
"""
@property
@@ -4797,21 +4870,12 @@ class Edge(object):
"""
@property
- def latest_date_time(self) -> datetime:
- """
- Gets of latest datetime of an edge.
-
- Returns:
- datetime: the latest datetime of an edge
- """
-
- @property
- def latest_time(self) -> int:
+ def latest_time(self) -> OptionalEventTime:
"""
Gets the latest time of an edge.
Returns:
- int: The latest time of an edge
+ OptionalEventTime: The latest time of an edge
"""
def layer(self, name: str) -> Edge:
@@ -4978,21 +5042,12 @@ class Edge(object):
"""
@property
- def start(self) -> Optional[int]:
+ def start(self) -> OptionalEventTime:
"""
Gets the start time for rolling and expanding windows for this Edge
Returns:
- Optional[int]: The earliest time that this Edge is valid or None if the Edge is valid for all times.
- """
-
- @property
- def start_date_time(self) -> Optional[datetime]:
- """
- Gets the earliest datetime that this Edge is valid
-
- Returns:
- Optional[datetime]: The earliest datetime that this Edge is valid or None if the Edge is valid for all times.
+ OptionalEventTime: The earliest time that this Edge is valid or None if the Edge is valid for all times.
"""
@property
@@ -5104,15 +5159,6 @@ class Edges(object):
int:
"""
- @property
- def date_time(self) -> OptionUtcDateTimeIterable:
- """
- Returns the date times of exploded edges
-
- Returns:
- OptionUtcDateTimeIterable:
- """
-
def default_layer(self) -> Edges:
"""
Return a view of Edges containing only the default edge layer
@@ -5120,20 +5166,13 @@ class Edges(object):
Edges: The layered view
"""
- def deletions(self):
- """
- Returns all timestamps of edges where an edge is deleted
-
- Returns:
- PyGenericIterable:
- """
-
- def deletions_date_time(self) -> OptionVecUtcDateTimeIterable:
+ @property
+ def deletions(self) -> HistoryIterable:
"""
- Returns all timestamps of edges where an edge is deleted
+ Returns a history object for each edge containing their deletion times.
Returns:
- OptionVecUtcDateTimeIterable:
+ HistoryIterable: An iterable of history objects, one for each edge.
"""
@property
@@ -5146,39 +5185,21 @@ class Edges(object):
"""
@property
- def earliest_date_time(self) -> OptionUtcDateTimeIterable:
- """
- Returns the earliest date time of the edges.
-
- Returns:
- OptionUtcDateTimeIterable:
- """
-
- @property
- def earliest_time(self) -> OptionI64Iterable:
+ def earliest_time(self) -> OptionEventTimeIterable:
"""
Returns the earliest time of the edges.
Returns:
- OptionI64Iterable:
+ OptionEventTimeIterable: Iterable of `EventTime`s.
"""
@property
- def end(self) -> Optional[int]:
+ def end(self) -> OptionalEventTime:
"""
Gets the latest time that this Edges is valid.
Returns:
- Optional[int]: The latest time that this Edges is valid or None if the Edges is valid for all times.
- """
-
- @property
- def end_date_time(self) -> Optional[datetime]:
- """
- Gets the latest datetime that this Edges is valid
-
- Returns:
- Optional[datetime]: The latest datetime that this Edges is valid or None if the Edges is valid for all times.
+ OptionalEventTime: The latest time that this Edges is valid or None if the Edges is valid for all times.
"""
def exclude_layer(self, name: str) -> Edges:
@@ -5273,29 +5294,13 @@ class Edges(object):
bool:
"""
- def history(self):
- """
- Returns all timestamps of edges, when an edge is added or change to an edge is made.
-
- Returns:
- PyGenericIterable:
-
- """
-
- def history_counts(self) -> U64Iterable:
- """
- Returns the number of times any edge was added or change to an edge was been made.
-
- Returns:
- U64Iterable:
- """
-
- def history_date_time(self) -> OptionVecUtcDateTimeIterable:
+ @property
+ def history(self) -> HistoryIterable:
"""
- Returns all timestamps of edges, when an edge is added or change to an edge is made.
+ Returns a history object for each edge containing time entries for when the edge is added or change to the edge is made.
Returns:
- OptionVecUtcDateTimeIterable:
+ HistoryIterable: An iterable of history objects, one for each edge.
"""
@property
@@ -5348,21 +5353,12 @@ class Edges(object):
"""
@property
- def latest_date_time(self) -> OptionUtcDateTimeIterable:
- """
- Returns the latest date time of the edges.
-
- Returns:
- OptionUtcDateTimeIterable:
- """
-
- @property
- def latest_time(self) -> OptionI64Iterable:
+ def latest_time(self) -> OptionEventTimeIterable:
"""
- Returns the latest time of the edges.
+ Returns the latest times of the edges.
Returns:
- OptionI64Iterable:
+ OptionEventTimeIterable: Iterable of `EventTime`s.
"""
def layer(self, name: str) -> Edges:
@@ -5408,7 +5404,7 @@ class Edges(object):
"""
@property
- def metadata(self):
+ def metadata(self) -> MetadataView:
"""
Returns all the metadata of the edges
@@ -5529,30 +5525,21 @@ class Edges(object):
"""
@property
- def start(self) -> Optional[int]:
+ def start(self) -> OptionalEventTime:
"""
Gets the start time for rolling and expanding windows for this Edges
Returns:
- Optional[int]: The earliest time that this Edges is valid or None if the Edges is valid for all times.
- """
-
- @property
- def start_date_time(self) -> Optional[datetime]:
- """
- Gets the earliest datetime that this Edges is valid
-
- Returns:
- Optional[datetime]: The earliest datetime that this Edges is valid or None if the Edges is valid for all times.
+ OptionalEventTime: The earliest time that this Edges is valid or None if the Edges is valid for all times.
"""
@property
- def time(self):
+ def time(self) -> EventTimeIterable:
"""
- Returns the times of exploded edges.
+ Returns the times of exploded edges
Returns:
- I64Iterable:
+ EventTimeIterable: Iterable of `EventTime`s.
"""
def to_df(
@@ -5670,15 +5657,6 @@ class NestedEdges(object):
list[list[Edges]]: the list of edges
"""
- @property
- def date_time(self) -> NestedUtcDateTimeIterable:
- """
- Get the date times of exploded edges.
-
- Returns:
- NestedUtcDateTimeIterable:
- """
-
def default_layer(self) -> NestedEdges:
"""
Return a view of NestedEdges containing only the default edge layer
@@ -5686,20 +5664,13 @@ class NestedEdges(object):
NestedEdges: The layered view
"""
- def deletions(self) -> NestedI64VecIterable:
- """
- Returns all timestamps of edges, where an edge is deleted.
-
- Returns:
- NestedI64VecIterable: A list of lists of lists of unix timestamps
- """
-
- def deletions_date_time(self) -> NestedVecUtcDateTimeIterable:
+ @property
+ def deletions(self) -> NestedHistoryIterable:
"""
- Returns all timestamps of edges, where an edge is deleted.
+ Returns a history object for each edge containing their deletion times.
Returns:
- NestedVecUtcDateTimeIterable: A list of lists of lists of DateTime objects
+ NestedHistoryIterable: A nested iterable of history objects, one for each edge.
"""
@property
@@ -5712,39 +5683,21 @@ class NestedEdges(object):
"""
@property
- def earliest_date_time(self) -> NestedUtcDateTimeIterable:
- """
- Returns the earliest date time of the edges.
-
- Returns:
- NestedUtcDateTimeIterable:
- """
-
- @property
- def earliest_time(self) -> NestedOptionI64Iterable:
+ def earliest_time(self) -> NestedOptionEventTimeIterable:
"""
Returns the earliest time of the edges.
Returns:
- NestedOptionI64Iterable:
+ NestedOptionEventTimeIterable: A nested iterable of `EventTime`s.
"""
@property
- def end(self) -> Optional[int]:
+ def end(self) -> OptionalEventTime:
"""
Gets the latest time that this NestedEdges is valid.
Returns:
- Optional[int]: The latest time that this NestedEdges is valid or None if the NestedEdges is valid for all times.
- """
-
- @property
- def end_date_time(self) -> Optional[datetime]:
- """
- Gets the latest datetime that this NestedEdges is valid
-
- Returns:
- Optional[datetime]: The latest datetime that this NestedEdges is valid or None if the NestedEdges is valid for all times.
+ OptionalEventTime: The latest time that this NestedEdges is valid or None if the NestedEdges is valid for all times.
"""
def exclude_layer(self, name: str) -> NestedEdges:
@@ -5839,20 +5792,13 @@ class NestedEdges(object):
bool:
"""
- def history(self) -> NestedI64VecIterable:
- """
- Returns all timestamps of edges, when an edge is added or change to an edge is made.
-
- Returns:
- NestedI64VecIterable:
- """
-
- def history_date_time(self) -> NestedVecUtcDateTimeIterable:
+ @property
+ def history(self) -> NestedHistoryIterable:
"""
- Returns all timestamps of edges, when an edge is added or change to an edge is made.
+ Returns a history object for each edge containing time entries for when the edge is added or change to the edge is made.
Returns:
- NestedVecUtcDateTimeIterable:
+ NestedHistoryIterable: A nested iterable of history objects, one for each edge.
"""
@property
@@ -5905,21 +5851,12 @@ class NestedEdges(object):
"""
@property
- def latest_date_time(self) -> NestedUtcDateTimeIterable:
- """
- Returns the latest date time of the edges.
-
- Returns:
- NestedUtcDateTimeIterable:
- """
-
- @property
- def latest_time(self) -> NestedOptionI64Iterable:
+ def latest_time(self) -> NestedOptionEventTimeIterable:
"""
Returns the latest time of the edges.
Returns:
- NestedOptionI64Iterable:
+ NestedOptionEventTimeIterable: A nested iterable of `EventTime`s.
"""
def layer(self, name: str) -> NestedEdges:
@@ -5935,7 +5872,7 @@ class NestedEdges(object):
"""
@property
- def layer_name(self):
+ def layer_name(self) -> NestedArcStringIterable:
"""
Returns the name of the layer the edges belong to - assuming they only belong to one layer.
@@ -6086,30 +6023,24 @@ class NestedEdges(object):
"""
@property
- def start(self) -> Optional[int]:
+ def start(self) -> OptionalEventTime:
"""
Gets the start time for rolling and expanding windows for this NestedEdges
Returns:
- Optional[int]: The earliest time that this NestedEdges is valid or None if the NestedEdges is valid for all times.
- """
-
- @property
- def start_date_time(self) -> Optional[datetime]:
- """
- Gets the earliest datetime that this NestedEdges is valid
-
- Returns:
- Optional[datetime]: The earliest datetime that this NestedEdges is valid or None if the NestedEdges is valid for all times.
+ OptionalEventTime: The earliest time that this NestedEdges is valid or None if the NestedEdges is valid for all times.
"""
@property
- def time(self) -> NestedOptionI64Iterable:
+ def time(self) -> NestedEventTimeIterable:
"""
Returns the times of exploded edges.
Returns:
- NestedOptionI64Iterable:
+ NestedEventTimeIterable: A nested iterable of `EventTime`s.
+
+ Raises:
+ GraphError: If a graph error occurs (e.g. the edges are not exploded).
"""
def valid_layers(self, names: list[str]) -> NestedEdges:
@@ -6168,7 +6099,7 @@ class MutableEdge(Edge):
t: TimeInput,
properties: Optional[PropInput] = None,
layer: Optional[str] = None,
- secondary_index: Optional[int] = None,
+ event_id: Optional[int] = None,
) -> None:
"""
Add updates to an edge in the graph at a specified time.
@@ -6178,7 +6109,7 @@ class MutableEdge(Edge):
t (TimeInput): The timestamp at which the updates should be applied.
properties (PropInput, optional): A dictionary of properties to update.
layer (str, optional): The layer you want these properties to be added on to.
- secondary_index (int, optional): The optional integer which will be used as a secondary index
+ event_id (int, optional): The optional integer which will be used as an event id
Returns:
None: This function does not return a value, if the operation is successful.
@@ -6187,13 +6118,16 @@ class MutableEdge(Edge):
GraphError: If the operation fails.
"""
- def delete(self, t: TimeInput, layer: Optional[str] = None) -> None:
+ def delete(
+ self, t: TimeInput, layer: Optional[str] = None, event_id: Optional[int] = None
+ ) -> None:
"""
Mark the edge as deleted at the specified time.
Arguments:
t (TimeInput): The timestamp at which the deletion should be applied.
layer (str, optional): The layer you want the deletion applied to.
+ event_id (int, optional): The event id for the deletion's time entry.
Returns:
None:
@@ -6291,7 +6225,7 @@ class Properties(object):
"""
@property
- def temporal(self) -> TemporalProp:
+ def temporal(self):
"""
Get a view of the temporal properties only.
@@ -6474,9 +6408,7 @@ class Metadata(object):
list[PropValue]:
"""
-class TemporalProperties(object):
- """A view of the temporal properties of an entity"""
-
+class MetadataView(object):
def __contains__(self, key):
"""Return bool(key in self)."""
@@ -6498,9 +6430,45 @@ class TemporalProperties(object):
def __le__(self, value):
"""Return self<=value."""
- def __len__(self):
- """Return len(self)."""
-
+ def __lt__(self, value):
+ """Return self=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
def __lt__(self, value):
"""Return self TemporalProp:
+ def get(self, key: str) -> TemporalProperty:
"""
Get property value for `key` if it exists.
@@ -6518,31 +6486,23 @@ class TemporalProperties(object):
key (str): the name of the property.
Returns:
- TemporalProp: the property view if it exists, otherwise `None`
- """
-
- def histories(self) -> dict[str, list[Tuple[int, PropValue]]]:
- """
- Get the histories of all properties
-
- Returns:
- dict[str, list[Tuple[int, PropValue]]]: the mapping of property keys to histories
+ TemporalProperty: the property view if it exists, otherwise `None`
"""
- def histories_date_time(self) -> dict[str, list[Tuple[datetime, PropValue]]]:
+ def histories(self) -> dict[str, list[Tuple[EventTime, PropValue]]]:
"""
Get the histories of all properties
Returns:
- dict[str, list[Tuple[datetime, PropValue]]]: the mapping of property keys to histories
+ dict[str, list[Tuple[EventTime, PropValue]]]: the mapping of property keys to histories
"""
- def items(self) -> List[Tuple[str, TemporalProp]]:
+ def items(self) -> List[Tuple[str, TemporalProperty]]:
"""
List the property keys together with the corresponding values
Returns:
- List[Tuple[str, TemporalProp]]:
+ List[Tuple[str, TemporalProperty]]:
"""
def keys(self) -> list[str]:
@@ -6561,12 +6521,12 @@ class TemporalProperties(object):
dict[str, PropValue]: the mapping of property keys to latest values
"""
- def values(self) -> list[TemporalProp]:
+ def values(self) -> list[TemporalProperty]:
"""
List the values of the properties
Returns:
- list[TemporalProp]: the list of property views
+ list[TemporalProperty]: the list of property views
"""
class PropertiesView(object):
@@ -6636,7 +6596,7 @@ class PropertiesView(object):
"""
@property
- def temporal(self) -> List[TemporalProp]:
+ def temporal(self):
"""
Get a view of the temporal properties only.
@@ -6652,7 +6612,7 @@ class PropertiesView(object):
list[list[PropValue]]:
"""
-class TemporalProp(object):
+class TemporalProperty(object):
"""A view of a temporal property"""
def __eq__(self, value):
@@ -6679,12 +6639,12 @@ class TemporalProp(object):
def __repr__(self):
"""Return repr(self)."""
- def at(self, t: Any) -> Optional[PropValue]:
+ def at(self, t: TimeInput) -> Optional[PropValue]:
"""
Get the value of the property at a specified time.
Arguments:
- t (time): time
+ t (TimeInput): time
Returns:
Optional[PropValue]:
@@ -6706,44 +6666,29 @@ class TemporalProp(object):
int: The number of properties.
"""
- def history(self):
- """
- Get the timestamps at which the property was updated.
-
- Returns:
- NumpyArray:
- """
-
- def history_date_time(self) -> Optional[List[datetime]]:
- """
- Get the timestamps at which the property was updated.
-
- Returns:
- Optional[List[datetime]]:
- """
-
- def items(self) -> List[Tuple[int, PropValue]]:
+ @property
+ def history(self) -> History:
"""
- List update timestamps and corresponding property values.
+ Returns a history object which contains time entries for when the property was updated.
Returns:
- List[Tuple[int, PropValue]]:
+ History:
"""
- def items_date_time(self) -> list[Tuple[datetime, PropValue]]:
+ def items(self) -> List[Tuple[EventTime, PropValue]]:
"""
- List update timestamps and corresponding property values.
+ List update times and corresponding property values.
Returns:
- list[Tuple[datetime, PropValue]]:
+ List[Tuple[EventTime, PropValue]]:
"""
- def max(self) -> Tuple[int, PropValue]:
+ def max(self) -> Tuple[EventTime, PropValue]:
"""
Find the maximum property value and its associated time.
Returns:
- Tuple[int, PropValue]: A tuple containing the time and the maximum property value.
+ Tuple[EventTime, PropValue]: A tuple containing the time and the maximum property value.
"""
def mean(self) -> PropValue:
@@ -6754,31 +6699,31 @@ class TemporalProp(object):
PropValue: The mean of each property values, or None if count is zero.
"""
- def median(self) -> Tuple[int, PropValue]:
+ def median(self) -> Tuple[EventTime, PropValue]:
"""
Compute the median of all property values.
Returns:
- Tuple[int, PropValue]: A tuple containing the time and the median property value, or None if empty
+ Tuple[EventTime, PropValue]: A tuple containing the time and the median property value, or None if empty
"""
- def min(self) -> Tuple[int, PropValue]:
+ def min(self) -> Tuple[EventTime, PropValue]:
"""
Find the minimum property value and its associated time.
Returns:
- Tuple[int, PropValue]: A tuple containing the time and the minimum property value.
+ Tuple[EventTime, PropValue]: A tuple containing the time and the minimum property value.
"""
- def ordered_dedupe(self, latest_time: Any) -> List[int]:
+ def ordered_dedupe(self, latest_time: bool) -> List[Tuple[EventTime, PropValue]]:
"""
List of ordered deduplicated property values.
Arguments:
- latest_time: Enable to check only latest time.
+ latest_time (bool): Enable to check the latest time only.
Returns:
- List[int]:
+ List[Tuple[EventTime, PropValue]]:
"""
def sum(self) -> PropValue:
@@ -6813,6 +6758,652 @@ class TemporalProp(object):
NumpyArray:
"""
+class EventTime(object):
+ """
+ Raphtory’s EventTime.
+ Represents a unique timepoint in the graph’s history as (timestamp, event_id).
+
+ - timestamp: Number of milliseconds since the Unix epoch.
+ - event_id: ID used for ordering between equal timestamps.
+
+ Unless specified manually, the event ids are generated automatically by Raphtory to
+ maintain a unique ordering of events.
+ EventTime can be converted into a timestamp or a Python datetime, and compared
+ either by timestamp (against ints/floats/datetimes/strings), by tuple of (timestamp, event_id),
+ or against another EventTime.
+ """
+
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __hash__(self):
+ """Return hash(self)."""
+
+ def __int__(self):
+ """int(self)"""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __lt__(self, value):
+ """Return self EventTime:
+ """Create and return a new object. See help(type) for accurate signature."""
+
+ def __repr__(self):
+ """Return repr(self)."""
+
+ @property
+ def as_tuple(self) -> tuple[int, int]:
+ """
+ Return this entry as a tuple of (timestamp, event_id), where the timestamp is in milliseconds.
+
+ Returns:
+ tuple[int,int]: (timestamp, event_id).
+ """
+
+ @property
+ def dt(self) -> datetime:
+ """
+ Returns the UTC datetime representation of this EventTime's timestamp.
+
+ Returns:
+ datetime: The UTC datetime.
+
+ Raises:
+ TimeError: Returns TimeError on timestamp conversion errors (e.g. out-of-range timestamp).
+ """
+
+ @property
+ def event_id(self) -> int:
+ """
+ Returns the event id used to order events within the same timestamp.
+
+ Returns:
+ int: The event id.
+ """
+
+ @property
+ def t(self) -> int:
+ """
+ Returns the timestamp in milliseconds since the Unix epoch.
+
+ Returns:
+ int: Milliseconds since the Unix epoch.
+ """
+
+class OptionalEventTime(object):
+ """
+ Raphtory’s optional EventTime type. Instances of OptionalEventTime may contain an EventTime, or be empty.
+ This is used for functions that may not return data (such as earliest_time and latest_time) because the data is unavailable.
+
+ If data is contained, OptionalEventTime instances can be used similarly to EventTime.
+ If empty, time operations (such as .t, .dt, .event_id) will return None.
+ An empty OptionalEventTime is considered smaller than (<) any EventTime or OptionalEventTime with data.
+ """
+
+ def __bool__(self):
+ """True if self else False"""
+
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __lt__(self, value):
+ """Return self bool:
+ """
+ Returns true if the OptionalEventTime doesn't contain an EventTime.
+
+ Returns:
+ bool:
+ """
+
+ def is_some(self) -> bool:
+ """
+ Returns true if the OptionalEventTime contains an EventTime.
+
+ Returns:
+ bool:
+ """
+
+ @property
+ def t(self):
+ """
+ Returns the timestamp in milliseconds since the Unix epoch if an EventTime is contained, or else None.
+
+ Returns:
+ int | None: Milliseconds since the Unix epoch.
+ """
+
+class History(object):
+ """History of updates for an object. Provides access to time entries and derived views such as timestamps, datetimes, event ids, and intervals."""
+
+ def __contains__(self, key):
+ """Return bool(key in self)."""
+
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self Iterator[EventTime]:
+ """
+ Iterate over all time entries in reverse chronological order.
+
+ Returns:
+ Iterator[EventTime]: Iterator over time entries in reverse order.
+ """
+
+ def collect(self) -> list[EventTime]:
+ """
+ Collect all time entries in chronological order.
+
+ Returns:
+ list[EventTime]: Collected time entries.
+ """
+
+ def collect_rev(self) -> list[EventTime]:
+ """
+ Collect all time entries in reverse chronological order.
+
+ Returns:
+ list[EventTime]: Collected time entries in reverse order.
+ """
+
+ @staticmethod
+ def compose_histories(objects: Iterable[History]) -> History:
+ """
+ Compose multiple History objects into a single History by fusing their time entries in chronological order.
+
+ Arguments:
+ objects (Iterable[History]): History objects to compose.
+
+ Returns:
+ History: Composed History object containing entries from all inputs.
+ """
+
+ @property
+ def dt(self) -> HistoryDateTime:
+ """
+ Access history events as UTC datetimes.
+
+ Returns:
+ HistoryDateTime: Datetime view of this history.
+ """
+
+ def earliest_time(self) -> OptionalEventTime:
+ """
+ Get the earliest time entry.
+
+ Returns:
+ OptionalEventTime: Earliest time entry, or None if empty.
+ """
+
+ @property
+ def event_id(self) -> HistoryEventId:
+ """
+ Access the unique event id of each time entry.
+
+ Returns:
+ HistoryEventId: Event id view of this history.
+ """
+
+ @property
+ def intervals(self) -> Intervals:
+ """
+ Access the intervals between consecutive timestamps in milliseconds.
+
+ Returns:
+ Intervals: Intervals view of this history.
+ """
+
+ def is_empty(self) -> bool:
+ """
+ Check whether the history has no entries.
+
+ Returns:
+ bool: True if empty, otherwise False.
+ """
+
+ def latest_time(self) -> OptionalEventTime:
+ """
+ Get the latest time entry.
+
+ Returns:
+ OptionalEventTime: Latest time entry, or None if empty.
+ """
+
+ def merge(self, other: History) -> History:
+ """
+ Merge this History with another by interleaving entries in time order.
+
+ Arguments:
+ other (History): Right-hand history to merge.
+
+ Returns:
+ History: Merged history containing entries from both inputs.
+ """
+
+ def reverse(self) -> History:
+ """
+ Return a History where iteration order is reversed.
+
+ Returns:
+ History: History that yields items in reverse chronological order.
+ """
+
+ @property
+ def t(self) -> HistoryTimestamp:
+ """
+ Access history events as timestamps (milliseconds since Unix the epoch).
+
+ Returns:
+ HistoryTimestamp: Timestamp (as int) view of this history.
+ """
+
+class HistoryTimestamp(object):
+ """History view that exposes timestamps in milliseconds since the Unix epoch."""
+
+ def __contains__(self, key):
+ """Return bool(key in self)."""
+
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __lt__(self, value):
+ """Return self Iterator[int]:
+ """
+ Iterate over all timestamps in reverse order.
+
+ Returns:
+ Iterator[int]: Iterator over timestamps (milliseconds since the Unix epoch) in reverse order.
+ """
+
+ def collect(self) -> NDArray[np.int64]:
+ """
+ Collect all timestamps into a NumPy ndarray.
+
+ Returns:
+ NDArray[np.int64]: Timestamps in milliseconds since the Unix epoch.
+ """
+
+ def collect_rev(self) -> NDArray[np.int64]:
+ """
+ Collect all timestamps into a NumPy ndarray in reverse order.
+
+ Returns:
+ NDArray[np.int64]: Timestamps in milliseconds since the Unix epoch in reverse order.
+ """
+
+ def to_list(self) -> list[int]:
+ """
+ Collect all timestamps into a list.
+
+ Returns:
+ list[int]: List of timestamps.
+ """
+
+ def to_list_rev(self) -> list[int]:
+ """
+ Collect all timestamps into a list in reverse order.
+
+ Returns:
+ list[int]: List of timestamps.
+ """
+
+class HistoryDateTime(object):
+ """History view that exposes UTC datetimes."""
+
+ def __contains__(self, key):
+ """Return bool(key in self)."""
+
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __lt__(self, value):
+ """Return self Iterator[datetime]:
+ """
+ Iterate over all datetimes in reverse order.
+
+ Returns:
+ Iterator[datetime]: Iterator over UTC datetimes in reverse order.
+
+ Raises:
+ TimeError: May be raised during iteration if a timestamp cannot be converted.
+ """
+
+ def collect(self) -> list[datetime]:
+ """
+ Collect all datetimes.
+
+ Returns:
+ list[datetime]: Collected UTC datetimes.
+
+ Raises:
+ TimeError: If a timestamp cannot be converted to a datetime.
+ """
+
+ def collect_rev(self) -> list[datetime]:
+ """
+ Collect all datetimes in reverse order.
+
+ Returns:
+ list[datetime]: Collected UTC datetimes in reverse order.
+
+ Raises:
+ TimeError: If a timestamp cannot be converted to a datetime.
+ """
+
+class HistoryEventId(object):
+ """History view that exposes event ids of time entries. They are used for ordering within the same timestamp."""
+
+ def __contains__(self, key):
+ """Return bool(key in self)."""
+
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __lt__(self, value):
+ """Return self Iterator[int]:
+ """
+ Iterate over all event ids in reverse order.
+
+ Returns:
+ Iterator[int]: Iterator over event ids in reverse order.
+ """
+
+ def collect(self) -> NDArray[np.uintp]:
+ """
+ Collect all event ids.
+
+ Returns:
+ NDArray[np.uintp]: Event ids.
+ """
+
+ def collect_rev(self) -> NDArray[np.uintp]:
+ """
+ Collect all event ids in reverse order.
+
+ Returns:
+ NDArray[np.uintp]: Event ids in reverse order.
+ """
+
+ def to_list(self) -> list[int]:
+ """
+ Collect all event ids into a list.
+
+ Returns:
+ list[int]: List of event ids.
+ """
+
+ def to_list_rev(self) -> list[int]:
+ """
+ Collect all event ids into a list in reverse order.
+
+ Returns:
+ list[int]: List of event ids.
+ """
+
+class Intervals(object):
+ """View over the intervals between consecutive timestamps, expressed in milliseconds."""
+
+ def __contains__(self, key):
+ """Return bool(key in self)."""
+
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __lt__(self, value):
+ """Return self Iterator[int]:
+ """
+ Iterate over all intervals in reverse order.
+
+ Returns:
+ Iterator[int]: Iterator over intervals in reverse order.
+ """
+
+ def collect(self) -> NDArray[np.int64]:
+ """
+ Collect all interval values in milliseconds.
+
+ Returns:
+ NDArray[np.int64]: NumPy NDArray of interval values in milliseconds.
+ """
+
+ def collect_rev(self) -> NDArray[np.int64]:
+ """
+ Collect all interval values in reverse order.
+
+ Returns:
+ NDArray[np.int64]: Intervals in reverse order.
+ """
+
+ def max(self) -> Optional[int]:
+ """
+ Calculate the maximum interval in milliseconds.
+
+ Returns:
+ Optional[int]: Maximum interval, or None if fewer than 1 interval.
+ """
+
+ def mean(self) -> Optional[float]:
+ """
+ Calculate the mean interval in milliseconds.
+
+ Returns:
+ Optional[float]: Mean interval, or None if fewer than 1 interval.
+ """
+
+ def median(self) -> Optional[int]:
+ """
+ Calculate the median interval in milliseconds.
+
+ Returns:
+ Optional[int]: Median interval, or None if fewer than 1 interval.
+ """
+
+ def min(self) -> Optional[int]:
+ """
+ Calculate the minimum interval in milliseconds.
+
+ Returns:
+ Optional[int]: Minimum interval, or None if fewer than 1 interval.
+ """
+
+ def to_list(self) -> list[int]:
+ """
+ Collect all interval values in milliseconds into a list.
+
+ Returns:
+ list[int]: List of intervals in milliseconds.
+ """
+
+ def to_list_rev(self) -> list[int]:
+ """
+ Collect all interval values in milliseconds into a list in reverse order.
+
+ Returns:
+ list[int]: List of intervals in milliseconds.
+ """
+
class WindowSet(object):
def __iter__(self):
"""Implement iter(self)."""
diff --git a/python/python/raphtory/algorithms/__init__.pyi b/python/python/raphtory/algorithms/__init__.pyi
index ae2892f399..3beca213a3 100644
--- a/python/python/raphtory/algorithms/__init__.pyi
+++ b/python/python/raphtory/algorithms/__init__.pyi
@@ -20,6 +20,8 @@ from raphtory.vectors import *
from raphtory.node_state import *
from raphtory.graphql import *
from raphtory.typing import *
+import numpy as np
+from numpy.typing import NDArray
from datetime import datetime
from pandas import DataFrame
from os import PathLike
diff --git a/python/python/raphtory/filter/__init__.pyi b/python/python/raphtory/filter/__init__.pyi
index c8ce692f56..b234f9069a 100644
--- a/python/python/raphtory/filter/__init__.pyi
+++ b/python/python/raphtory/filter/__init__.pyi
@@ -16,6 +16,8 @@ from raphtory.vectors import *
from raphtory.node_state import *
from raphtory.graphql import *
from raphtory.typing import *
+import numpy as np
+from numpy.typing import NDArray
from datetime import datetime
from pandas import DataFrame
from os import PathLike
diff --git a/python/python/raphtory/graph_gen/__init__.pyi b/python/python/raphtory/graph_gen/__init__.pyi
index 3a9f849f05..9a77fd5dfe 100644
--- a/python/python/raphtory/graph_gen/__init__.pyi
+++ b/python/python/raphtory/graph_gen/__init__.pyi
@@ -21,6 +21,8 @@ from raphtory.vectors import *
from raphtory.node_state import *
from raphtory.graphql import *
from raphtory.typing import *
+import numpy as np
+from numpy.typing import NDArray
from datetime import datetime
from pandas import DataFrame
from os import PathLike
diff --git a/python/python/raphtory/graph_loader/__init__.pyi b/python/python/raphtory/graph_loader/__init__.pyi
index e0b31f720f..192b78ebdd 100644
--- a/python/python/raphtory/graph_loader/__init__.pyi
+++ b/python/python/raphtory/graph_loader/__init__.pyi
@@ -21,6 +21,8 @@ from raphtory.vectors import *
from raphtory.node_state import *
from raphtory.graphql import *
from raphtory.typing import *
+import numpy as np
+from numpy.typing import NDArray
from datetime import datetime
from pandas import DataFrame
from os import PathLike
diff --git a/python/python/raphtory/graphql/__init__.pyi b/python/python/raphtory/graphql/__init__.pyi
index 84808a34db..a95c9f6125 100644
--- a/python/python/raphtory/graphql/__init__.pyi
+++ b/python/python/raphtory/graphql/__init__.pyi
@@ -16,6 +16,8 @@ from raphtory.algorithms import *
from raphtory.vectors import *
from raphtory.node_state import *
from raphtory.typing import *
+import numpy as np
+from numpy.typing import NDArray
from datetime import datetime
from pandas import DataFrame
from os import PathLike
diff --git a/python/python/raphtory/iterables/__init__.pyi b/python/python/raphtory/iterables/__init__.pyi
index 2a80bbc5cb..271dfa8518 100644
--- a/python/python/raphtory/iterables/__init__.pyi
+++ b/python/python/raphtory/iterables/__init__.pyi
@@ -17,6 +17,8 @@ from raphtory.vectors import *
from raphtory.node_state import *
from raphtory.graphql import *
from raphtory.typing import *
+import numpy as np
+from numpy.typing import NDArray
from datetime import datetime
from pandas import DataFrame
from os import PathLike
@@ -47,6 +49,29 @@ __all__ = [
"OptionUtcDateTimeIterable",
"ArcStringVecIterable",
"NestedArcStringVecIterable",
+ "NestedEventTimeIterable",
+ "NestedArcStringIterable",
+ "NestedOptionEventTimeIterable",
+ "NestedHistoryIterable",
+ "EventTimeIterable",
+ "OptionEventTimeIterable",
+ "HistoryIterable",
+ "HistoryTimestampIterable",
+ "IntervalsIterable",
+ "HistoryEventIdIterable",
+ "HistoryDateTimeIterable",
+ "OptionUsizeIterable",
+ "ResultOptionUtcDateTimeIterable",
+ "I64Iterable",
+ "ResultUtcDateTimeIterable",
+ "NestedHistoryTimestampIterable",
+ "NestedIntervalsIterable",
+ "NestedHistoryEventIdIterable",
+ "NestedHistoryDateTimeIterable",
+ "NestedOptionUsizeIterable",
+ "NestedResultOptionUtcDateTimeIterable",
+ "NestedI64Iterable",
+ "NestedResultUtcDateTimeIterable",
]
class NestedUtcDateTimeIterable(object):
@@ -744,3 +769,820 @@ class NestedArcStringVecIterable(object):
"""Return repr(self)."""
def collect(self): ...
+
+class NestedEventTimeIterable(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self NestedResultUtcDateTimeIterable:
+ """
+ Change this nested Iterable of EventTime into a nested Iterable of corresponding UTC DateTimes.
+
+ Returns:
+ NestedResultUtcDateTimeIterable: Nested iterable of UTC datetimes for each EventTime.
+
+ Raises:
+ TimeError: Returns TimeError on timestamp conversion errors (e.g. out-of-range timestamp).
+ """
+
+ @property
+ def event_id(self) -> NestedUsizeIterable:
+ """
+ Change this nested Iterable of EventTime into a nested Iterable of their associated event ids.
+
+ Returns:
+ NestedUsizeIterable: Nested iterable of event ids associated to each EventTime.
+ """
+
+ def max(self): ...
+ def min(self): ...
+ @property
+ def t(self) -> NestedI64Iterable:
+ """
+ Change this nested Iterable of EventTime into a nested Iterable of corresponding Unix timestamps in milliseconds.
+
+ Returns:
+ NestedI64Iterable: Nested iterable of millisecond timestamps since the Unix epoch for each EventTime.
+ """
+
+class NestedArcStringIterable(object):
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __repr__(self):
+ """Return repr(self)."""
+
+ def collect(self): ...
+
+class NestedOptionEventTimeIterable(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self NestedResultOptionUtcDateTimeIterable:
+ """
+ Change this nested Iterable of Optional[EventTime] into a nested Iterable of corresponding UTC DateTimes.
+
+ Returns:
+ NestedResultOptionUtcDateTimeIterable: Nested iterable of UTC datetimes for each EventTime, if available.
+
+ Raises:
+ TimeError: Returns TimeError on timestamp conversion errors (e.g. out-of-range timestamp).
+ """
+
+ @property
+ def event_id(self) -> NestedOptionUsizeIterable:
+ """
+ Change this nested Iterable of Optional[EventTime] into a nested Iterable of their associated event ids.
+
+ Returns:
+ NestedOptionUsizeIterable: Nested iterable of event ids associated to each EventTime, if available.
+ """
+
+ def max(self): ...
+ def min(self): ...
+ @property
+ def t(self) -> NestedOptionI64Iterable:
+ """
+ Change this nested Iterable of Optional[EventTime] into a nested Iterable of corresponding Unix timestamps in milliseconds.
+
+ Returns:
+ NestedOptionI64Iterable: Nested iterable of millisecond timestamps since the Unix epoch for each EventTime, if available.
+ """
+
+class NestedHistoryIterable(object):
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __repr__(self):
+ """Return repr(self)."""
+
+ def collect(self) -> list[list[list[EventTime]]]:
+ """
+ Collect time entries from each history within each nested iterable.
+
+ Returns:
+ list[list[list[EventTime]]]: Collected entries per nested history.
+ """
+
+ @property
+ def dt(self) -> NestedHistoryDateTimeIterable:
+ """
+ Access nested histories as datetime views.
+
+ Returns:
+ NestedHistoryDateTimeIterable: Iterable of iterables of HistoryDateTime objects.
+ """
+
+ @property
+ def event_id(self) -> NestedHistoryEventIdIterable:
+ """
+ Access nested histories as event id views.
+
+ Returns:
+ NestedHistoryEventIdIterable: Iterable of iterables of HistoryEventId objects.
+ """
+
+ def flatten(self) -> list[EventTime]:
+ """
+ Flatten the nested iterable of history objects into a single list of all contained time entries.
+
+ Returns:
+ list[EventTime]: List of time entries.
+ """
+
+ @property
+ def intervals(self) -> NestedIntervalsIterable:
+ """
+ Access nested histories as intervals views.
+
+ Returns:
+ NestedIntervalsIterable: Iterable of iterables of Intervals objects.
+ """
+
+ @property
+ def t(self) -> NestedHistoryTimestampIterable:
+ """
+ Access nested histories as timestamp views.
+
+ Returns:
+ NestedHistoryTimestampIterable: Iterable of iterables of HistoryTimestamp objects.
+ """
+
+class EventTimeIterable(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self ResultUtcDateTimeIterable:
+ """
+ Change this Iterable of EventTime into an Iterable of corresponding UTC DateTimes.
+
+ Returns:
+ ResultUtcDateTimeIterable: Iterable of UTC datetimes for each EventTime.
+
+ Raises:
+ TimeError: Returns TimeError on timestamp conversion errors (e.g. out-of-range timestamp).
+ """
+
+ @property
+ def event_id(self) -> UsizeIterable:
+ """
+ Change this Iterable of EventTime into an Iterable of their associated event ids.
+
+ Returns:
+ UsizeIterable: Iterable of event ids associated to each EventTime.
+ """
+
+ def max(self): ...
+ def min(self): ...
+ @property
+ def t(self) -> I64Iterable:
+ """
+ Change this Iterable of EventTime into an Iterable of corresponding Unix timestamps in milliseconds.
+
+ Returns:
+ I64Iterable: Iterable of millisecond timestamps since the Unix epoch for each EventTime.
+ """
+
+class OptionEventTimeIterable(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self ResultOptionUtcDateTimeIterable:
+ """
+ Change this Iterable of Optional[EventTime] into an Iterable of corresponding UTC DateTimes.
+
+ Returns:
+ ResultOptionUtcDateTimeIterable: Iterable of UTC datetimes for each EventTime, if available.
+
+ Raises:
+ TimeError: Returns TimeError on timestamp conversion errors (e.g. out-of-range timestamp).
+ """
+
+ @property
+ def event_id(self) -> OptionUsizeIterable:
+ """
+ Change this Iterable of Optional[EventTime] into an Iterable of their associated event ids.
+
+ Returns:
+ OptionUsizeIterable: Iterable of event ids associated to each EventTime, if available.
+ """
+
+ def max(self): ...
+ def min(self): ...
+ @property
+ def t(self) -> OptionI64Iterable:
+ """
+ Change this Iterable of Optional[EventTime] into an Iterable of corresponding Unix timestamps in milliseconds.
+
+ Returns:
+ OptionI64Iterable: Iterable of millisecond timestamps since the Unix epoch for each EventTime, if available.
+ """
+
+class HistoryIterable(object):
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __repr__(self):
+ """Return repr(self)."""
+
+ def collect(self) -> list[list[EventTime]]:
+ """
+ Collect time entries from each history in the iterable.
+
+ Returns:
+ list[list[EventTime]]: Collected entries per history.
+ """
+
+ @property
+ def dt(self) -> HistoryDateTimeIterable:
+ """
+ Access history items as UTC datetimes.
+
+ Returns:
+ HistoryDateTimeIterable: Iterable of HistoryDateTime objects, one for each item.
+ """
+
+ @property
+ def event_id(self) -> HistoryEventIdIterable:
+ """
+ Access event ids of history items.
+
+ Returns:
+ HistoryEventIdIterable: Iterable of HistoryEventId objects, one for each item.
+ """
+
+ def flatten(self) -> list[EventTime]:
+ """
+ Flatten the iterable of history objects into a single list of all contained time entries.
+
+ Returns:
+ list[EventTime]: List of time entries.
+ """
+
+ @property
+ def intervals(self) -> IntervalsIterable:
+ """
+ Access intervals between consecutive timestamps in milliseconds.
+
+ Returns:
+ IntervalsIterable: Iterable of Intervals objects, one for each item.
+ """
+
+ @property
+ def t(self) -> HistoryTimestampIterable:
+ """
+ Access history items as timestamps (milliseconds since the Unix epoch).
+
+ Returns:
+ HistoryTimestampIterable: Iterable of HistoryTimestamp objects, one for each item.
+ """
+
+class HistoryTimestampIterable(object):
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __repr__(self):
+ """Return repr(self)."""
+
+ def collect(self) -> list[NDArray[np.int64]]:
+ """
+ Collect timestamps for each history into a NumPy array.
+
+ Returns:
+ list[NDArray[np.int64]]: NumPy NDArray of timestamps in milliseconds per history.
+ """
+
+ def to_list(self) -> list[list[int]]:
+ """
+ Collect timestamps for each history into a list.
+
+ Returns:
+ list[list[int]]: List of timestamps in milliseconds per history.
+ """
+
+class IntervalsIterable(object):
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __repr__(self):
+ """Return repr(self)."""
+
+ def collect(self) -> list[NDArray[np.int64]]:
+ """
+ Collect intervals between each history's consecutive timestamps in milliseconds into a NumPy array.
+
+ Returns:
+ list[NDArray[np.int64]]: NumPy NDArray of intervals per history.
+ """
+
+ def to_list(self) -> list[list[int]]:
+ """
+ Collect intervals between each history's consecutive timestamps in milliseconds into a list.
+
+ Returns:
+ list[list[int]]: List of intervals per history.
+ """
+
+class HistoryEventIdIterable(object):
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __repr__(self):
+ """Return repr(self)."""
+
+ def collect(self) -> list[NDArray[np.uintp]]:
+ """
+ Collect event ids for each history into a NumPy array.
+
+ Returns:
+ list[NDArray[np.uintp]]: NumPy NDArray of event ids per history.
+ """
+
+ def to_list(self) -> list[list[int]]:
+ """
+ Collect event ids for each history into a list.
+
+ Returns:
+ list[list[int]]: List of event ids per history.
+ """
+
+class HistoryDateTimeIterable(object):
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __repr__(self):
+ """Return repr(self)."""
+
+ def collect(self) -> list[list[datetime]]:
+ """
+ Collect datetimes for each history.
+
+ Returns:
+ list[list[datetime]]: UTC datetimes per history.
+
+ Raises:
+ TimeError: If a timestamp cannot be converted to a datetime.
+ """
+
+class OptionUsizeIterable(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self=value."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self list[list[NDArray[np.int64]]]:
+ """
+ Collect timestamps for each history in each nested iterable into a NumPy array.
+
+ Returns:
+ list[list[NDArray[np.int64]]]: NumPy NDArray of timestamps in milliseconds per nested history.
+ """
+
+ def flatten(self) -> NDArray[np.int64]:
+ """
+ Flatten the nested iterable of history objects into a single NumPy NDArray of all contained timestamps.
+
+ Returns:
+ NDArray[np.int64]: NumPy NDArray of timestamps in milliseconds.
+ """
+
+ def flattened_list(self) -> list[int]:
+ """
+ Flatten the nested iterable of history objects into a single list of all contained timestamps.
+
+ Returns:
+ list[int]: List of timestamps in milliseconds.
+ """
+
+ def to_list(self) -> list[list[list[int]]]:
+ """
+ Collect timestamps for each history in each nested iterable into a list.
+
+ Returns:
+ list[list[list[int]]]: List of timestamps in milliseconds per nested history.
+ """
+
+class NestedIntervalsIterable(object):
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __repr__(self):
+ """Return repr(self)."""
+
+ def collect(self) -> list[list[NDArray[np.int64]]]:
+ """
+ Collect intervals between each nested history's consecutive timestamps in milliseconds into a NumPy array.
+
+ Returns:
+ list[list[NDArray[np.int64]]]: NumPy NDArray of intervals per nested history.
+ """
+
+ def flatten(self) -> NDArray[np.int64]:
+ """
+ Collect intervals between each nested history's consecutive timestamps in milliseconds into a single NumPy array.
+
+ Returns:
+ NDArray[np.int64]: NumPy NDArray of intervals.
+ """
+
+ def flattened_list(self) -> list[int]:
+ """
+ Collect intervals between each nested history's consecutive timestamps in milliseconds into a single list.
+
+ Returns:
+ list[int]: List of intervals.
+ """
+
+ def to_list(self) -> list[list[list[int]]]:
+ """
+ Collect intervals between each nested history's consecutive timestamps in milliseconds into a list.
+
+ Returns:
+ list[list[list[int]]]: List of intervals per nested history.
+ """
+
+class NestedHistoryEventIdIterable(object):
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __repr__(self):
+ """Return repr(self)."""
+
+ def collect(self) -> list[list[NDArray[np.uintp]]]:
+ """
+ Collect event ids for each history in each nested iterable into a NumPy array.
+
+ Returns:
+ list[list[NDArray[np.uintp]]]: NumPy NDArray of event ids per nested history.
+ """
+
+ def flatten(self) -> NDArray[np.uintp]:
+ """
+ Flatten the nested iterable of history objects into a single NumPy NDArray of all contained event ids.
+
+ Returns:
+ NDArray[np.uintp]: NumPy NDArray of event ids.
+ """
+
+ def flattened_list(self) -> list[int]:
+ """
+ Flatten the nested iterable of history objects into a single list of all contained event ids.
+
+ Returns:
+ list[int]: List of timestamps in milliseconds.
+ """
+
+ def to_list(self) -> list[list[list[int]]]:
+ """
+ Collect event ids for each history in each nested iterable into a list.
+
+ Returns:
+ list[list[list[int]]]: List of event ids per nested history.
+ """
+
+class NestedHistoryDateTimeIterable(object):
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __repr__(self):
+ """Return repr(self)."""
+
+ def collect(self) -> list[list[list[datetime]]]:
+ """
+ Collect datetimes for each history in each nested iterable.
+
+ Returns:
+ list[list[list[datetime]]]: UTC datetimes per nested history.
+
+ Raises:
+ TimeError: If a timestamp cannot be converted to a datetime.
+ """
+
+ def flatten(self) -> list[datetime]:
+ """
+ Flatten the nested iterable of history objects into a single list of all contained datetimes.
+
+ Returns:
+ list[datetime]: List of UTC datetimes.
+
+ Raises:
+ TimeError: If a timestamp cannot be converted to a datetime.
+ """
+
+class NestedOptionUsizeIterable(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self=value."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self NodeStateOptionUsize:
+ """
+ Compute the k smallest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateOptionUsize: The k smallest values as a node state
+ """
+
+ def get(
+ self, node: NodeInput, default: Optional[Optional[int]] = None
+ ) -> Optional[Optional[int]]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[Optional[int]]): the default value. Defaults to None.
+
+ Returns:
+ Optional[Optional[int]]: the value for the node or the default value
+ """
+
+ def groups(self) -> NodeGroups:
+ """
+ Group by value
+
+ Returns:
+ NodeGroups: The grouped nodes
+ """
+
+ def items(self) -> Iterator[Tuple[Node, Optional[int]]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, Optional[int]]]: Iterator over items
+ """
+
+ def max(self) -> Optional[Optional[int]]:
+ """
+ Return the maximum value
+
+ Returns:
+ Optional[Optional[int]]: The maximum value or `None` if empty
+ """
+
+ def max_item(self) -> Optional[Tuple[Node, Optional[int]]]:
+ """
+ Return largest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[int]]]: The Node and maximum value or `None` if empty
+ """
+
+ def median(self) -> PropValue:
+ """
+ Return the median value
+
+ Returns:
+ PropValue:
+ Optional[Optional[int]]:
+ """
+
+ def median_item(self) -> Optional[Tuple[Node, Optional[int]]]:
+ """
+ Return median value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[int]]]: The median value or `None` if empty
+ """
+
+ def min(self) -> Optional[Optional[int]]:
+ """
+ Return the minimum value
+
+ Returns:
+ Optional[Optional[int]]: The minimum value or `None` if empty
+ """
+
+ def min_item(self) -> Optional[Tuple[Node, Optional[int]]]:
+ """
+ Return smallest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[int]]]: The Node and minimum value or `None` if empty
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted(self, reverse: bool = False) -> NodeStateOptionUsize:
+ """
+ Sort by value
+
+ Arguments:
+ reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
+
+ Returns:
+ NodeStateOptionUsize: Sorted node state
+ """
+
+ def sorted_by_id(self) -> NodeStateOptionUsize:
+ """
+ Sort results by node id
+
+ Returns:
+ NodeStateOptionUsize: The sorted node state
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def top_k(self, k: int) -> NodeStateOptionUsize:
+ """
+ Compute the k largest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateOptionUsize: The k largest values as a node state
+ """
+
+ def values(self) -> Iterator[Optional[int]]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[Optional[int]]: Iterator over values
+ """
+
class NodeStateU64(object):
def __eq__(self, value):
"""Return self==value."""
@@ -868,9 +1064,7 @@ class NodeStateOptionI64(object):
Iterator[Optional[int]]: Iterator over values
"""
-class IdView(object):
- """A lazy view over node values"""
-
+class NodeStateOptionEventTime(object):
def __eq__(self, value):
"""Return self==value."""
@@ -901,7 +1095,7 @@ class IdView(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateGID:
+ def bottom_k(self, k: int) -> NodeStateOptionEventTime:
"""
Compute the k smallest values
@@ -909,59 +1103,53 @@ class IdView(object):
k (int): The number of values to return
Returns:
- NodeStateGID: The k smallest values as a node state
- """
-
- def collect(self) -> list[GID]:
+ NodeStateOptionEventTime: The k smallest values as a node state
"""
- Compute all values and return the result as a list
- Returns:
- list[GID]: all values as a list
+ def get(
+ self, node: NodeInput, default: Optional[Optional[EventTime]] = None
+ ) -> Optional[Optional[EventTime]]:
"""
+ Get value for node
- def compute(self) -> NodeStateGID:
- """
- Compute all values and return the result as a node view
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[Optional[EventTime]]): the default value. Defaults to None.
Returns:
- NodeStateGID: the computed `NodeState`
+ Optional[Optional[EventTime]]: the value for the node or the default value
"""
- def get(self, node: NodeInput, default: Optional[GID] = None) -> Optional[GID]:
+ def groups(self) -> NodeGroups:
"""
- Get value for node
-
- Arguments:
- node (NodeInput): the node
- default (Optional[GID]): the default value. Defaults to None.
+ Group by value
Returns:
- Optional[GID]: the value for the node or the default value
+ NodeGroups: The grouped nodes
"""
- def items(self) -> Iterator[Tuple[Node, GID]]:
+ def items(self) -> Iterator[Tuple[Node, Optional[EventTime]]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, GID]]: Iterator over items
+ Iterator[Tuple[Node, Optional[EventTime]]]: Iterator over items
"""
- def max(self) -> Optional[GID]:
+ def max(self) -> Optional[Optional[EventTime]]:
"""
Return the maximum value
Returns:
- Optional[GID]: The maximum value or `None` if empty
+ Optional[Optional[EventTime]]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, GID]]:
+ def max_item(self) -> Optional[Tuple[Node, Optional[EventTime]]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, GID]]: The Node and maximum value or `None` if empty
+ Optional[Tuple[Node, Optional[EventTime]]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -970,31 +1158,31 @@ class IdView(object):
Returns:
PropValue:
- Optional[GID]:
+ Optional[Optional[EventTime]]:
"""
- def median_item(self) -> Optional[Tuple[Node, GID]]:
+ def median_item(self) -> Optional[Tuple[Node, Optional[EventTime]]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, GID]]: The median value or `None` if empty
+ Optional[Tuple[Node, Optional[EventTime]]]: The median value or `None` if empty
"""
- def min(self) -> Optional[GID]:
+ def min(self) -> Optional[Optional[EventTime]]:
"""
Return the minimum value
Returns:
- Optional[GID]: The minimum value or `None` if empty
+ Optional[Optional[EventTime]]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, GID]]:
+ def min_item(self) -> Optional[Tuple[Node, Optional[EventTime]]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, GID]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, Optional[EventTime]]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -1005,7 +1193,7 @@ class IdView(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateGID:
+ def sorted(self, reverse: bool = False) -> NodeStateOptionEventTime:
"""
Sort by value
@@ -1013,15 +1201,15 @@ class IdView(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateGID: Sorted node state
+ NodeStateOptionEventTime: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateGID:
+ def sorted_by_id(self) -> NodeStateOptionEventTime:
"""
Sort results by node id
Returns:
- NodeStateGID: The sorted node state
+ NodeStateOptionEventTime: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -1035,7 +1223,7 @@ class IdView(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateGID:
+ def top_k(self, k: int) -> NodeStateOptionEventTime:
"""
Compute the k largest values
@@ -1043,18 +1231,18 @@ class IdView(object):
k (int): The number of values to return
Returns:
- NodeStateGID: The k largest values as a node state
+ NodeStateOptionEventTime: The k largest values as a node state
"""
- def values(self) -> Iterator[GID]:
+ def values(self) -> Iterator[Optional[EventTime]]:
"""
Iterate over values
Returns:
- Iterator[GID]: Iterator over values
+ Iterator[Optional[EventTime]]: Iterator over values
"""
-class NodeStateGID(object):
+class NodeStateOptionDateTime(object):
def __eq__(self, value):
"""Return self==value."""
@@ -1085,7 +1273,7 @@ class NodeStateGID(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateGID:
+ def bottom_k(self, k: int) -> NodeStateOptionDateTime:
"""
Compute the k smallest values
@@ -1093,43 +1281,53 @@ class NodeStateGID(object):
k (int): The number of values to return
Returns:
- NodeStateGID: The k smallest values as a node state
+ NodeStateOptionDateTime: The k smallest values as a node state
"""
- def get(self, node: NodeInput, default: Optional[GID] = None) -> Optional[GID]:
+ def get(
+ self, node: NodeInput, default: Optional[Optional[datetime]] = None
+ ) -> Optional[Optional[datetime]]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[GID]): the default value. Defaults to None.
+ default (Optional[Optional[datetime]]): the default value. Defaults to None.
Returns:
- Optional[GID]: the value for the node or the default value
+ Optional[Optional[datetime]]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, GID]]:
+ def groups(self) -> NodeGroups:
"""
- Iterate over items
+ Group by value
Returns:
- Iterator[Tuple[Node, GID]]: Iterator over items
+ NodeGroups: The grouped nodes
"""
- def max(self) -> Optional[GID]:
+ def items(self) -> Iterator[Tuple[Node, Optional[datetime]]]:
"""
- Return the maximum value
+ Iterate over items
Returns:
- Optional[GID]: The maximum value or `None` if empty
+ Iterator[Tuple[Node, Optional[datetime]]]: Iterator over items
"""
- def max_item(self) -> Optional[Tuple[Node, GID]]:
+ def max(self) -> Optional[Optional[datetime]]:
"""
- Return largest value and corresponding node
+ Return the maximum value
Returns:
- Optional[Tuple[Node, GID]]: The Node and maximum value or `None` if empty
+ Optional[Optional[datetime]]: The maximum value or `None` if empty
+ """
+
+ def max_item(self) -> Optional[Tuple[Node, Optional[datetime]]]:
+ """
+ Return largest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[datetime]]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -1138,31 +1336,31 @@ class NodeStateGID(object):
Returns:
PropValue:
- Optional[GID]:
+ Optional[Optional[datetime]]:
"""
- def median_item(self) -> Optional[Tuple[Node, GID]]:
+ def median_item(self) -> Optional[Tuple[Node, Optional[datetime]]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, GID]]: The median value or `None` if empty
+ Optional[Tuple[Node, Optional[datetime]]]: The median value or `None` if empty
"""
- def min(self) -> Optional[GID]:
+ def min(self) -> Optional[Optional[datetime]]:
"""
Return the minimum value
Returns:
- Optional[GID]: The minimum value or `None` if empty
+ Optional[Optional[datetime]]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, GID]]:
+ def min_item(self) -> Optional[Tuple[Node, Optional[datetime]]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, GID]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, Optional[datetime]]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -1173,7 +1371,7 @@ class NodeStateGID(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateGID:
+ def sorted(self, reverse: bool = False) -> NodeStateOptionDateTime:
"""
Sort by value
@@ -1181,15 +1379,15 @@ class NodeStateGID(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateGID: Sorted node state
+ NodeStateOptionDateTime: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateGID:
+ def sorted_by_id(self) -> NodeStateOptionDateTime:
"""
Sort results by node id
Returns:
- NodeStateGID: The sorted node state
+ NodeStateOptionDateTime: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -1203,7 +1401,7 @@ class NodeStateGID(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateGID:
+ def top_k(self, k: int) -> NodeStateOptionDateTime:
"""
Compute the k largest values
@@ -1211,18 +1409,18 @@ class NodeStateGID(object):
k (int): The number of values to return
Returns:
- NodeStateGID: The k largest values as a node state
+ NodeStateOptionDateTime: The k largest values as a node state
"""
- def values(self) -> Iterator[GID]:
+ def values(self) -> Iterator[Optional[datetime]]:
"""
Iterate over values
Returns:
- Iterator[GID]: Iterator over values
+ Iterator[Optional[datetime]]: Iterator over values
"""
-class EarliestTimeView(object):
+class IdView(object):
"""A lazy view over node values"""
def __eq__(self, value):
@@ -1255,7 +1453,7 @@ class EarliestTimeView(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateOptionI64:
+ def bottom_k(self, k: int) -> NodeStateGID:
"""
Compute the k smallest values
@@ -1263,69 +1461,59 @@ class EarliestTimeView(object):
k (int): The number of values to return
Returns:
- NodeStateOptionI64: The k smallest values as a node state
+ NodeStateGID: The k smallest values as a node state
"""
- def collect(self) -> list[Optional[int]]:
+ def collect(self) -> list[GID]:
"""
Compute all values and return the result as a list
Returns:
- list[Optional[int]]: all values as a list
+ list[GID]: all values as a list
"""
- def compute(self) -> NodeStateOptionI64:
+ def compute(self) -> NodeStateGID:
"""
Compute all values and return the result as a node view
Returns:
- NodeStateOptionI64: the computed `NodeState`
+ NodeStateGID: the computed `NodeState`
"""
- def get(
- self, node: NodeInput, default: Optional[Optional[int]] = None
- ) -> Optional[Optional[int]]:
+ def get(self, node: NodeInput, default: Optional[GID] = None) -> Optional[GID]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[Optional[int]]): the default value. Defaults to None.
-
- Returns:
- Optional[Optional[int]]: the value for the node or the default value
- """
-
- def groups(self) -> NodeGroups:
- """
- Group by value
+ default (Optional[GID]): the default value. Defaults to None.
Returns:
- NodeGroups: The grouped nodes
+ Optional[GID]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, Optional[int]]]:
+ def items(self) -> Iterator[Tuple[Node, GID]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, Optional[int]]]: Iterator over items
+ Iterator[Tuple[Node, GID]]: Iterator over items
"""
- def max(self) -> Optional[Optional[int]]:
+ def max(self) -> Optional[GID]:
"""
Return the maximum value
Returns:
- Optional[Optional[int]]: The maximum value or `None` if empty
+ Optional[GID]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, Optional[int]]]:
+ def max_item(self) -> Optional[Tuple[Node, GID]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[int]]]: The Node and maximum value or `None` if empty
+ Optional[Tuple[Node, GID]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -1334,31 +1522,31 @@ class EarliestTimeView(object):
Returns:
PropValue:
- Optional[Optional[int]]:
+ Optional[GID]:
"""
- def median_item(self) -> Optional[Tuple[Node, Optional[int]]]:
+ def median_item(self) -> Optional[Tuple[Node, GID]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[int]]]: The median value or `None` if empty
+ Optional[Tuple[Node, GID]]: The median value or `None` if empty
"""
- def min(self) -> Optional[Optional[int]]:
+ def min(self) -> Optional[GID]:
"""
Return the minimum value
Returns:
- Optional[Optional[int]]: The minimum value or `None` if empty
+ Optional[GID]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, Optional[int]]]:
+ def min_item(self) -> Optional[Tuple[Node, GID]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[int]]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, GID]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -1369,7 +1557,7 @@ class EarliestTimeView(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateOptionI64:
+ def sorted(self, reverse: bool = False) -> NodeStateGID:
"""
Sort by value
@@ -1377,15 +1565,15 @@ class EarliestTimeView(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateOptionI64: Sorted node state
+ NodeStateGID: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateOptionI64:
+ def sorted_by_id(self) -> NodeStateGID:
"""
Sort results by node id
Returns:
- NodeStateOptionI64: The sorted node state
+ NodeStateGID: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -1399,7 +1587,7 @@ class EarliestTimeView(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateOptionI64:
+ def top_k(self, k: int) -> NodeStateGID:
"""
Compute the k largest values
@@ -1407,20 +1595,18 @@ class EarliestTimeView(object):
k (int): The number of values to return
Returns:
- NodeStateOptionI64: The k largest values as a node state
+ NodeStateGID: The k largest values as a node state
"""
- def values(self) -> Iterator[Optional[int]]:
+ def values(self) -> Iterator[GID]:
"""
Iterate over values
Returns:
- Iterator[Optional[int]]: Iterator over values
+ Iterator[GID]: Iterator over values
"""
-class LatestTimeView(object):
- """A lazy view over node values"""
-
+class NodeStateGID(object):
def __eq__(self, value):
"""Return self==value."""
@@ -1451,7 +1637,7 @@ class LatestTimeView(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateOptionI64:
+ def bottom_k(self, k: int) -> NodeStateGID:
"""
Compute the k smallest values
@@ -1459,69 +1645,43 @@ class LatestTimeView(object):
k (int): The number of values to return
Returns:
- NodeStateOptionI64: The k smallest values as a node state
- """
-
- def collect(self) -> list[Optional[int]]:
- """
- Compute all values and return the result as a list
-
- Returns:
- list[Optional[int]]: all values as a list
- """
-
- def compute(self) -> NodeStateOptionI64:
- """
- Compute all values and return the result as a node view
-
- Returns:
- NodeStateOptionI64: the computed `NodeState`
+ NodeStateGID: The k smallest values as a node state
"""
- def get(
- self, node: NodeInput, default: Optional[Optional[int]] = None
- ) -> Optional[Optional[int]]:
+ def get(self, node: NodeInput, default: Optional[GID] = None) -> Optional[GID]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[Optional[int]]): the default value. Defaults to None.
-
- Returns:
- Optional[Optional[int]]: the value for the node or the default value
- """
-
- def groups(self) -> NodeGroups:
- """
- Group by value
+ default (Optional[GID]): the default value. Defaults to None.
Returns:
- NodeGroups: The grouped nodes
+ Optional[GID]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, Optional[int]]]:
+ def items(self) -> Iterator[Tuple[Node, GID]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, Optional[int]]]: Iterator over items
+ Iterator[Tuple[Node, GID]]: Iterator over items
"""
- def max(self) -> Optional[Optional[int]]:
+ def max(self) -> Optional[GID]:
"""
Return the maximum value
Returns:
- Optional[Optional[int]]: The maximum value or `None` if empty
+ Optional[GID]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, Optional[int]]]:
+ def max_item(self) -> Optional[Tuple[Node, GID]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[int]]]: The Node and maximum value or `None` if empty
+ Optional[Tuple[Node, GID]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -1530,31 +1690,31 @@ class LatestTimeView(object):
Returns:
PropValue:
- Optional[Optional[int]]:
+ Optional[GID]:
"""
- def median_item(self) -> Optional[Tuple[Node, Optional[int]]]:
+ def median_item(self) -> Optional[Tuple[Node, GID]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[int]]]: The median value or `None` if empty
+ Optional[Tuple[Node, GID]]: The median value or `None` if empty
"""
- def min(self) -> Optional[Optional[int]]:
+ def min(self) -> Optional[GID]:
"""
Return the minimum value
Returns:
- Optional[Optional[int]]: The minimum value or `None` if empty
+ Optional[GID]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, Optional[int]]]:
+ def min_item(self) -> Optional[Tuple[Node, GID]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[int]]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, GID]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -1565,7 +1725,7 @@ class LatestTimeView(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateOptionI64:
+ def sorted(self, reverse: bool = False) -> NodeStateGID:
"""
Sort by value
@@ -1573,15 +1733,15 @@ class LatestTimeView(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateOptionI64: Sorted node state
+ NodeStateGID: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateOptionI64:
+ def sorted_by_id(self) -> NodeStateGID:
"""
Sort results by node id
Returns:
- NodeStateOptionI64: The sorted node state
+ NodeStateGID: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -1595,7 +1755,7 @@ class LatestTimeView(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateOptionI64:
+ def top_k(self, k: int) -> NodeStateGID:
"""
Compute the k largest values
@@ -1603,18 +1763,18 @@ class LatestTimeView(object):
k (int): The number of values to return
Returns:
- NodeStateOptionI64: The k largest values as a node state
+ NodeStateGID: The k largest values as a node state
"""
- def values(self) -> Iterator[Optional[int]]:
+ def values(self) -> Iterator[GID]:
"""
Iterate over values
Returns:
- Iterator[Optional[int]]: Iterator over values
+ Iterator[GID]: Iterator over values
"""
-class NameView(object):
+class EarliestTimeView(object):
"""A lazy view over node values"""
def __eq__(self, value):
@@ -1647,7 +1807,7 @@ class NameView(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateString:
+ def bottom_k(self, k: int) -> NodeStateOptionEventTime:
"""
Compute the k smallest values
@@ -1655,35 +1815,55 @@ class NameView(object):
k (int): The number of values to return
Returns:
- NodeStateString: The k smallest values as a node state
+ NodeStateOptionEventTime: The k smallest values as a node state
"""
- def collect(self) -> list[str]:
+ def collect(self) -> list[Optional[EventTime]]:
"""
Compute all values and return the result as a list
Returns:
- list[str]: all values as a list
+ list[Optional[EventTime]]: all values as a list
"""
- def compute(self) -> NodeStateString:
+ def compute(self) -> NodeStateOptionEventTime:
"""
Compute all values and return the result as a node view
Returns:
- NodeStateString: the computed `NodeState`
+ NodeStateOptionEventTime: the computed `NodeState`
"""
- def get(self, node: NodeInput, default: Optional[str] = None) -> Optional[str]:
+ @property
+ def dt(self) -> EarliestDateTimeView:
+ """
+ Access earliest times as UTC DateTimes.
+
+ Returns:
+ EarliestDateTimeView: A lazy view over the earliest times for each node as datetimes.
+ """
+
+ @property
+ def event_id(self) -> EarliestEventIdView:
+ """
+ Access the event ids of the earliest times.
+
+ Returns:
+ EarliestEventIdView: A lazy view over the event ids of the earliest times for each node.
+ """
+
+ def get(
+ self, node: NodeInput, default: Optional[Optional[EventTime]] = None
+ ) -> Optional[Optional[EventTime]]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[str]): the default value. Defaults to None.
+ default (Optional[Optional[EventTime]]): the default value. Defaults to None.
Returns:
- Optional[str]: the value for the node or the default value
+ Optional[Optional[EventTime]]: the value for the node or the default value
"""
def groups(self) -> NodeGroups:
@@ -1694,28 +1874,28 @@ class NameView(object):
NodeGroups: The grouped nodes
"""
- def items(self) -> Iterator[Tuple[Node, str]]:
+ def items(self) -> Iterator[Tuple[Node, Optional[EventTime]]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, str]]: Iterator over items
+ Iterator[Tuple[Node, Optional[EventTime]]]: Iterator over items
"""
- def max(self) -> Optional[str]:
+ def max(self) -> Optional[Optional[EventTime]]:
"""
Return the maximum value
Returns:
- Optional[str]: The maximum value or `None` if empty
+ Optional[Optional[EventTime]]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, str]]:
+ def max_item(self) -> Optional[Tuple[Node, Optional[EventTime]]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, str]]: The Node and maximum value or `None` if empty
+ Optional[Tuple[Node, Optional[EventTime]]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -1724,31 +1904,31 @@ class NameView(object):
Returns:
PropValue:
- Optional[str]:
+ Optional[Optional[EventTime]]:
"""
- def median_item(self) -> Optional[Tuple[Node, str]]:
+ def median_item(self) -> Optional[Tuple[Node, Optional[EventTime]]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, str]]: The median value or `None` if empty
+ Optional[Tuple[Node, Optional[EventTime]]]: The median value or `None` if empty
"""
- def min(self) -> Optional[str]:
+ def min(self) -> Optional[Optional[EventTime]]:
"""
Return the minimum value
Returns:
- Optional[str]: The minimum value or `None` if empty
+ Optional[Optional[EventTime]]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, str]]:
+ def min_item(self) -> Optional[Tuple[Node, Optional[EventTime]]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, str]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, Optional[EventTime]]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -1759,7 +1939,7 @@ class NameView(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateString:
+ def sorted(self, reverse: bool = False) -> NodeStateOptionEventTime:
"""
Sort by value
@@ -1767,15 +1947,24 @@ class NameView(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateString: Sorted node state
+ NodeStateOptionEventTime: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateString:
+ def sorted_by_id(self) -> NodeStateOptionEventTime:
"""
Sort results by node id
Returns:
- NodeStateString: The sorted node state
+ NodeStateOptionEventTime: The sorted node state
+ """
+
+ @property
+ def t(self) -> EarliestTimestampView:
+ """
+ Access earliest times as timestamps (milliseconds since the Unix epoch).
+
+ Returns:
+ EarliestTimestampView: A lazy view over the earliest times for each node as timestamps.
"""
def to_df(self) -> DataFrame:
@@ -1789,7 +1978,7 @@ class NameView(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateString:
+ def top_k(self, k: int) -> NodeStateOptionEventTime:
"""
Compute the k largest values
@@ -1797,18 +1986,20 @@ class NameView(object):
k (int): The number of values to return
Returns:
- NodeStateString: The k largest values as a node state
+ NodeStateOptionEventTime: The k largest values as a node state
"""
- def values(self) -> Iterator[str]:
+ def values(self) -> Iterator[Optional[EventTime]]:
"""
Iterate over values
Returns:
- Iterator[str]: Iterator over values
+ Iterator[Optional[EventTime]]: Iterator over values
"""
-class NodeStateString(object):
+class EarliestTimestampView(object):
+ """A lazy view over node values"""
+
def __eq__(self, value):
"""Return self==value."""
@@ -1839,7 +2030,7 @@ class NodeStateString(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateString:
+ def bottom_k(self, k: int) -> NodeStateOptionI64:
"""
Compute the k smallest values
@@ -1847,19 +2038,37 @@ class NodeStateString(object):
k (int): The number of values to return
Returns:
- NodeStateString: The k smallest values as a node state
+ NodeStateOptionI64: The k smallest values as a node state
"""
- def get(self, node: NodeInput, default: Optional[str] = None) -> Optional[str]:
+ def collect(self) -> list[Optional[int]]:
"""
- Get value for node
+ Compute all values and return the result as a list
- Arguments:
- node (NodeInput): the node
- default (Optional[str]): the default value. Defaults to None.
+ Returns:
+ list[Optional[int]]: all values as a list
+ """
+
+ def compute(self) -> NodeStateOptionI64:
+ """
+ Compute all values and return the result as a node view
Returns:
- Optional[str]: the value for the node or the default value
+ NodeStateOptionI64: the computed `NodeState`
+ """
+
+ def get(
+ self, node: NodeInput, default: Optional[Optional[int]] = None
+ ) -> Optional[Optional[int]]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[Optional[int]]): the default value. Defaults to None.
+
+ Returns:
+ Optional[Optional[int]]: the value for the node or the default value
"""
def groups(self) -> NodeGroups:
@@ -1870,28 +2079,28 @@ class NodeStateString(object):
NodeGroups: The grouped nodes
"""
- def items(self) -> Iterator[Tuple[Node, str]]:
+ def items(self) -> Iterator[Tuple[Node, Optional[int]]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, str]]: Iterator over items
+ Iterator[Tuple[Node, Optional[int]]]: Iterator over items
"""
- def max(self) -> Optional[str]:
+ def max(self) -> Optional[Optional[int]]:
"""
Return the maximum value
Returns:
- Optional[str]: The maximum value or `None` if empty
+ Optional[Optional[int]]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, str]]:
+ def max_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, str]]: The Node and maximum value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -1900,31 +2109,31 @@ class NodeStateString(object):
Returns:
PropValue:
- Optional[str]:
+ Optional[Optional[int]]:
"""
- def median_item(self) -> Optional[Tuple[Node, str]]:
+ def median_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, str]]: The median value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The median value or `None` if empty
"""
- def min(self) -> Optional[str]:
+ def min(self) -> Optional[Optional[int]]:
"""
Return the minimum value
Returns:
- Optional[str]: The minimum value or `None` if empty
+ Optional[Optional[int]]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, str]]:
+ def min_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, str]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -1935,7 +2144,7 @@ class NodeStateString(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateString:
+ def sorted(self, reverse: bool = False) -> NodeStateOptionI64:
"""
Sort by value
@@ -1943,15 +2152,15 @@ class NodeStateString(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateString: Sorted node state
+ NodeStateOptionI64: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateString:
+ def sorted_by_id(self) -> NodeStateOptionI64:
"""
Sort results by node id
Returns:
- NodeStateString: The sorted node state
+ NodeStateOptionI64: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -1965,7 +2174,7 @@ class NodeStateString(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateString:
+ def top_k(self, k: int) -> NodeStateOptionI64:
"""
Compute the k largest values
@@ -1973,18 +2182,18 @@ class NodeStateString(object):
k (int): The number of values to return
Returns:
- NodeStateString: The k largest values as a node state
+ NodeStateOptionI64: The k largest values as a node state
"""
- def values(self) -> Iterator[str]:
+ def values(self) -> Iterator[Optional[int]]:
"""
Iterate over values
Returns:
- Iterator[str]: Iterator over values
+ Iterator[Optional[int]]: Iterator over values
"""
-class EarliestDateTimeView(object):
+class EarliestEventIdView(object):
"""A lazy view over node values"""
def __eq__(self, value):
@@ -2017,7 +2226,7 @@ class EarliestDateTimeView(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateOptionDateTime:
+ def bottom_k(self, k: int) -> NodeStateOptionUsize:
"""
Compute the k smallest values
@@ -2025,37 +2234,37 @@ class EarliestDateTimeView(object):
k (int): The number of values to return
Returns:
- NodeStateOptionDateTime: The k smallest values as a node state
+ NodeStateOptionUsize: The k smallest values as a node state
"""
- def collect(self) -> list[Optional[datetime]]:
+ def collect(self) -> list[Optional[int]]:
"""
Compute all values and return the result as a list
Returns:
- list[Optional[datetime]]: all values as a list
+ list[Optional[int]]: all values as a list
"""
- def compute(self) -> NodeStateOptionDateTime:
+ def compute(self) -> NodeStateOptionUsize:
"""
Compute all values and return the result as a node view
Returns:
- NodeStateOptionDateTime: the computed `NodeState`
+ NodeStateOptionUsize: the computed `NodeState`
"""
def get(
- self, node: NodeInput, default: Optional[Optional[datetime]] = None
- ) -> Optional[Optional[datetime]]:
+ self, node: NodeInput, default: Optional[Optional[int]] = None
+ ) -> Optional[Optional[int]]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[Optional[datetime]]): the default value. Defaults to None.
+ default (Optional[Optional[int]]): the default value. Defaults to None.
Returns:
- Optional[Optional[datetime]]: the value for the node or the default value
+ Optional[Optional[int]]: the value for the node or the default value
"""
def groups(self) -> NodeGroups:
@@ -2066,28 +2275,28 @@ class EarliestDateTimeView(object):
NodeGroups: The grouped nodes
"""
- def items(self) -> Iterator[Tuple[Node, Optional[datetime]]]:
+ def items(self) -> Iterator[Tuple[Node, Optional[int]]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, Optional[datetime]]]: Iterator over items
+ Iterator[Tuple[Node, Optional[int]]]: Iterator over items
"""
- def max(self) -> Optional[Optional[datetime]]:
+ def max(self) -> Optional[Optional[int]]:
"""
Return the maximum value
Returns:
- Optional[Optional[datetime]]: The maximum value or `None` if empty
+ Optional[Optional[int]]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, Optional[datetime]]]:
+ def max_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[datetime]]]: The Node and maximum value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -2096,31 +2305,31 @@ class EarliestDateTimeView(object):
Returns:
PropValue:
- Optional[Optional[datetime]]:
+ Optional[Optional[int]]:
"""
- def median_item(self) -> Optional[Tuple[Node, Optional[datetime]]]:
+ def median_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[datetime]]]: The median value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The median value or `None` if empty
"""
- def min(self) -> Optional[Optional[datetime]]:
+ def min(self) -> Optional[Optional[int]]:
"""
Return the minimum value
Returns:
- Optional[Optional[datetime]]: The minimum value or `None` if empty
+ Optional[Optional[int]]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, Optional[datetime]]]:
+ def min_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[datetime]]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -2131,7 +2340,7 @@ class EarliestDateTimeView(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateOptionDateTime:
+ def sorted(self, reverse: bool = False) -> NodeStateOptionUsize:
"""
Sort by value
@@ -2139,15 +2348,15 @@ class EarliestDateTimeView(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateOptionDateTime: Sorted node state
+ NodeStateOptionUsize: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateOptionDateTime:
+ def sorted_by_id(self) -> NodeStateOptionUsize:
"""
Sort results by node id
Returns:
- NodeStateOptionDateTime: The sorted node state
+ NodeStateOptionUsize: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -2161,7 +2370,7 @@ class EarliestDateTimeView(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateOptionDateTime:
+ def top_k(self, k: int) -> NodeStateOptionUsize:
"""
Compute the k largest values
@@ -2169,19 +2378,19 @@ class EarliestDateTimeView(object):
k (int): The number of values to return
Returns:
- NodeStateOptionDateTime: The k largest values as a node state
+ NodeStateOptionUsize: The k largest values as a node state
"""
- def values(self) -> Iterator[Optional[datetime]]:
+ def values(self) -> Iterator[Optional[int]]:
"""
Iterate over values
Returns:
- Iterator[Optional[datetime]]: Iterator over values
+ Iterator[Optional[int]]: Iterator over values
"""
-class LatestDateTimeView(object):
- """A lazy view over node values"""
+class EarliestDateTimeView(object):
+ """A lazy view over EarliestDateTime values for each node."""
def __eq__(self, value):
"""Return self==value."""
@@ -2221,37 +2430,51 @@ class LatestDateTimeView(object):
k (int): The number of values to return
Returns:
- NodeStateOptionDateTime: The k smallest values as a node state
+ NodeStateOptionDateTime: The k smallest values as a node state
"""
def collect(self) -> list[Optional[datetime]]:
"""
- Compute all values and return the result as a list
+ Compute all DateTime values and return the result as a list
+
+ Returns:
+ list[Optional[datetime]]: all values as a list
+ """
+
+ def collect_valid(self) -> list[datetime]:
+ """
+ Compute all DateTime values and return the valid results as a list. Conversion errors and empty values are ignored
Returns:
- list[Optional[datetime]]: all values as a list
+ list[datetime]: all values as a list
"""
def compute(self) -> NodeStateOptionDateTime:
"""
- Compute all values and return the result as a node view
+ Compute all DateTime values and return the result as a NodeState. Fails if any DateTime error is encountered.
Returns:
- NodeStateOptionDateTime: the computed `NodeState`
+ NodeStateOptionDateTime: the computed `NodeState`
"""
- def get(
- self, node: NodeInput, default: Optional[Optional[datetime]] = None
- ) -> Optional[Optional[datetime]]:
+ def compute_valid(self) -> NodeStateOptionDateTime:
+ """
+ Compute all values and only return the valid results as a NodeState. DateTime errors are ignored.
+
+ Returns:
+ NodeStateOptionDateTime: the computed `NodeState`
+ """
+
+ def get(self, node: NodeInput, default=...) -> Optional[datetime]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[Optional[datetime]]): the default value. Defaults to None.
+ default (Optional[datetime]): the default value. Defaults to None.
Returns:
- Optional[Optional[datetime]]: the value for the node or the default value
+ Optional[datetime]: the value for the node or the default value
"""
def groups(self) -> NodeGroups:
@@ -2264,59 +2487,74 @@ class LatestDateTimeView(object):
def items(self) -> Iterator[Tuple[Node, Optional[datetime]]]:
"""
- Iterate over items
+ Iterate over DateTimes
Returns:
- Iterator[Tuple[Node, Optional[datetime]]]: Iterator over items
+ Iterator[Tuple[Node, Optional[datetime]]]: Iterator over items
"""
- def max(self) -> Optional[Optional[datetime]]:
+ def items_valid(self) -> Iterator[Tuple[Node, datetime]]:
+ """
+ Iterate over valid DateTimes only. Ignore error and None values.
+
+ Returns:
+ Iterator[Tuple[Node, datetime]]: Iterator over items
+ """
+
+ def iter_valid(self) -> Iterator[datetime]:
+ """
+ Returns an iterator over all valid DateTime values. Conversion errors and empty values are ignored
+
+ Returns:
+ Iterator[datetime]: Valid datetime values.
+ """
+
+ def max(self) -> Optional[datetime]:
"""
Return the maximum value
Returns:
- Optional[Optional[datetime]]: The maximum value or `None` if empty
+ Optional[datetime]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, Optional[datetime]]]:
+ def max_item(self) -> Optional[Tuple[Node, datetime]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[datetime]]]: The Node and maximum value or `None` if empty
+ Optional[Tuple[Node, datetime]]: The Node and maximum value or `None` if empty
"""
- def median(self) -> PropValue:
+ def median(self) -> Optional[datetime]:
"""
Return the median value
Returns:
- PropValue:
- Optional[Optional[datetime]]:
+ Optional[datetime]: The median value or `None` if empty
"""
- def median_item(self) -> Optional[Tuple[Node, Optional[datetime]]]:
+ def median_item(self) -> Optional[Tuple[Node, datetime]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[datetime]]]: The median value or `None` if empty
+ Optional[Tuple[Node, datetime]]: The median value or `None` if empty
"""
- def min(self) -> Optional[Optional[datetime]]:
+ def min(self) -> Optional[datetime]:
"""
Return the minimum value
Returns:
- Optional[Optional[datetime]]: The minimum value or `None` if empty
+ Optional[datetime]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, Optional[datetime]]]:
+ def min_item(self) -> Optional[Tuple[Node, datetime]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[datetime]]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, datetime]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -2329,21 +2567,29 @@ class LatestDateTimeView(object):
def sorted(self, reverse: bool = False) -> NodeStateOptionDateTime:
"""
- Sort by value
+ Sort by value. Note that 'None' values will always come after valid DateTime values
Arguments:
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateOptionDateTime: Sorted node state
+ NodeStateOptionDateTime: Sorted node state
"""
def sorted_by_id(self) -> NodeStateOptionDateTime:
"""
- Sort results by node id
+ Sort results by node id. Fails if any DateTime error is encountered.
Returns:
- NodeStateOptionDateTime: The sorted node state
+ NodeStateOptionDateTime: The sorted node state
+ """
+
+ def sorted_by_id_valid(self) -> NodeStateOptionDateTime:
+ """
+ Sort only non-error DateTimes by node id. DateTime errors are ignored.
+
+ Returns:
+ NodeStateOptionDateTime: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -2354,7 +2600,7 @@ class LatestDateTimeView(object):
the corresponding values.
Returns:
- DataFrame: the pandas DataFrame
+ DataFrame: A Pandas DataFrame.
"""
def top_k(self, k: int) -> NodeStateOptionDateTime:
@@ -2365,18 +2611,28 @@ class LatestDateTimeView(object):
k (int): The number of values to return
Returns:
- NodeStateOptionDateTime: The k largest values as a node state
+ NodeStateOptionDateTime: The k largest values as a node state
"""
def values(self) -> Iterator[Optional[datetime]]:
"""
- Iterate over values
+ Iterate over DateTimes
Returns:
- Iterator[Optional[datetime]]: Iterator over values
+ Iterator[Optional[datetime]]: Iterator over datetimes
"""
-class NodeStateOptionDateTime(object):
+ def values_valid(self) -> Iterator[datetime]:
+ """
+ Iterate over valid DateTime values only. Ignore error and None values.
+
+ Returns:
+ Iterator[datetime]: Iterator over values
+ """
+
+class LatestTimeView(object):
+ """A lazy view over node values"""
+
def __eq__(self, value):
"""Return self==value."""
@@ -2407,7 +2663,7 @@ class NodeStateOptionDateTime(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateOptionDateTime:
+ def bottom_k(self, k: int) -> NodeStateOptionI64:
"""
Compute the k smallest values
@@ -2415,21 +2671,55 @@ class NodeStateOptionDateTime(object):
k (int): The number of values to return
Returns:
- NodeStateOptionDateTime: The k smallest values as a node state
+ NodeStateOptionI64: The k smallest values as a node state
+ """
+
+ def collect(self) -> list[Optional[int]]:
+ """
+ Compute all values and return the result as a list
+
+ Returns:
+ list[Optional[int]]: all values as a list
+ """
+
+ def compute(self) -> NodeStateOptionI64:
+ """
+ Compute all values and return the result as a node view
+
+ Returns:
+ NodeStateOptionI64: the computed `NodeState`
+ """
+
+ @property
+ def dt(self) -> LatestDateTimeView:
+ """
+ Access latest times as UTC DateTimes.
+
+ Returns:
+ LatestDateTimeView: A lazy view over the latest times for each node as datetimes.
+ """
+
+ @property
+ def event_id(self) -> LatestEventIdView:
+ """
+ Access the event ids of the latest times.
+
+ Returns:
+ LatestEventIdView: A lazy view over the event ids of the latest times for each node.
"""
def get(
- self, node: NodeInput, default: Optional[Optional[datetime]] = None
- ) -> Optional[Optional[datetime]]:
+ self, node: NodeInput, default: Optional[Optional[int]] = None
+ ) -> Optional[Optional[int]]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[Optional[datetime]]): the default value. Defaults to None.
+ default (Optional[Optional[int]]): the default value. Defaults to None.
Returns:
- Optional[Optional[datetime]]: the value for the node or the default value
+ Optional[Optional[int]]: the value for the node or the default value
"""
def groups(self) -> NodeGroups:
@@ -2440,28 +2730,28 @@ class NodeStateOptionDateTime(object):
NodeGroups: The grouped nodes
"""
- def items(self) -> Iterator[Tuple[Node, Optional[datetime]]]:
+ def items(self) -> Iterator[Tuple[Node, Optional[int]]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, Optional[datetime]]]: Iterator over items
+ Iterator[Tuple[Node, Optional[int]]]: Iterator over items
"""
- def max(self) -> Optional[Optional[datetime]]:
+ def max(self) -> Optional[Optional[int]]:
"""
Return the maximum value
Returns:
- Optional[Optional[datetime]]: The maximum value or `None` if empty
+ Optional[Optional[int]]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, Optional[datetime]]]:
+ def max_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[datetime]]]: The Node and maximum value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -2470,31 +2760,31 @@ class NodeStateOptionDateTime(object):
Returns:
PropValue:
- Optional[Optional[datetime]]:
+ Optional[Optional[int]]:
"""
- def median_item(self) -> Optional[Tuple[Node, Optional[datetime]]]:
+ def median_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[datetime]]]: The median value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The median value or `None` if empty
"""
- def min(self) -> Optional[Optional[datetime]]:
+ def min(self) -> Optional[Optional[int]]:
"""
Return the minimum value
Returns:
- Optional[Optional[datetime]]: The minimum value or `None` if empty
+ Optional[Optional[int]]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, Optional[datetime]]]:
+ def min_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[datetime]]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -2505,7 +2795,7 @@ class NodeStateOptionDateTime(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateOptionDateTime:
+ def sorted(self, reverse: bool = False) -> NodeStateOptionI64:
"""
Sort by value
@@ -2513,15 +2803,24 @@ class NodeStateOptionDateTime(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateOptionDateTime: Sorted node state
+ NodeStateOptionI64: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateOptionDateTime:
+ def sorted_by_id(self) -> NodeStateOptionI64:
"""
Sort results by node id
Returns:
- NodeStateOptionDateTime: The sorted node state
+ NodeStateOptionI64: The sorted node state
+ """
+
+ @property
+ def t(self) -> LatestTimestampView:
+ """
+ Access latest times as timestamps (milliseconds since the Unix epoch).
+
+ Returns:
+ LatestTimestampView: A lazy view over the latest times for each node as timestamps.
"""
def to_df(self) -> DataFrame:
@@ -2535,7 +2834,7 @@ class NodeStateOptionDateTime(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateOptionDateTime:
+ def top_k(self, k: int) -> NodeStateOptionI64:
"""
Compute the k largest values
@@ -2543,18 +2842,18 @@ class NodeStateOptionDateTime(object):
k (int): The number of values to return
Returns:
- NodeStateOptionDateTime: The k largest values as a node state
+ NodeStateOptionI64: The k largest values as a node state
"""
- def values(self) -> Iterator[Optional[datetime]]:
+ def values(self) -> Iterator[Optional[int]]:
"""
Iterate over values
Returns:
- Iterator[Optional[datetime]]: Iterator over values
+ Iterator[Optional[int]]: Iterator over values
"""
-class HistoryView(object):
+class LatestTimestampView(object):
"""A lazy view over node values"""
def __eq__(self, value):
@@ -2587,7 +2886,7 @@ class HistoryView(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateListI64:
+ def bottom_k(self, k: int) -> NodeStateOptionI64:
"""
Compute the k smallest values
@@ -2595,61 +2894,69 @@ class HistoryView(object):
k (int): The number of values to return
Returns:
- NodeStateListI64: The k smallest values as a node state
+ NodeStateOptionI64: The k smallest values as a node state
"""
- def collect(self) -> list[list[int]]:
+ def collect(self) -> list[Optional[int]]:
"""
Compute all values and return the result as a list
Returns:
- list[list[int]]: all values as a list
+ list[Optional[int]]: all values as a list
"""
- def compute(self) -> NodeStateListI64:
+ def compute(self) -> NodeStateOptionI64:
"""
Compute all values and return the result as a node view
Returns:
- NodeStateListI64: the computed `NodeState`
+ NodeStateOptionI64: the computed `NodeState`
"""
def get(
- self, node: NodeInput, default: Optional[list[int]] = None
- ) -> Optional[list[int]]:
+ self, node: NodeInput, default: Optional[Optional[int]] = None
+ ) -> Optional[Optional[int]]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[list[int]]): the default value. Defaults to None.
+ default (Optional[Optional[int]]): the default value. Defaults to None.
Returns:
- Optional[list[int]]: the value for the node or the default value
+ Optional[Optional[int]]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, list[int]]]:
+ def groups(self) -> NodeGroups:
+ """
+ Group by value
+
+ Returns:
+ NodeGroups: The grouped nodes
+ """
+
+ def items(self) -> Iterator[Tuple[Node, Optional[int]]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, list[int]]]: Iterator over items
+ Iterator[Tuple[Node, Optional[int]]]: Iterator over items
"""
- def max(self) -> Optional[list[int]]:
+ def max(self) -> Optional[Optional[int]]:
"""
Return the maximum value
Returns:
- Optional[list[int]]: The maximum value or `None` if empty
+ Optional[Optional[int]]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, list[int]]]:
+ def max_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, list[int]]]: The Node and maximum value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -2658,31 +2965,31 @@ class HistoryView(object):
Returns:
PropValue:
- Optional[list[int]]:
+ Optional[Optional[int]]:
"""
- def median_item(self) -> Optional[Tuple[Node, list[int]]]:
+ def median_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, list[int]]]: The median value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The median value or `None` if empty
"""
- def min(self) -> Optional[list[int]]:
+ def min(self) -> Optional[Optional[int]]:
"""
Return the minimum value
Returns:
- Optional[list[int]]: The minimum value or `None` if empty
+ Optional[Optional[int]]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, list[int]]]:
+ def min_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, list[int]]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -2693,7 +3000,7 @@ class HistoryView(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateListI64:
+ def sorted(self, reverse: bool = False) -> NodeStateOptionI64:
"""
Sort by value
@@ -2701,15 +3008,15 @@ class HistoryView(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateListI64: Sorted node state
+ NodeStateOptionI64: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateListI64:
+ def sorted_by_id(self) -> NodeStateOptionI64:
"""
Sort results by node id
Returns:
- NodeStateListI64: The sorted node state
+ NodeStateOptionI64: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -2723,7 +3030,7 @@ class HistoryView(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateListI64:
+ def top_k(self, k: int) -> NodeStateOptionI64:
"""
Compute the k largest values
@@ -2731,18 +3038,18 @@ class HistoryView(object):
k (int): The number of values to return
Returns:
- NodeStateListI64: The k largest values as a node state
+ NodeStateOptionI64: The k largest values as a node state
"""
- def values(self) -> Iterator[list[int]]:
+ def values(self) -> Iterator[Optional[int]]:
"""
Iterate over values
Returns:
- Iterator[list[int]]: Iterator over values
+ Iterator[Optional[int]]: Iterator over values
"""
-class EdgeHistoryCountView(object):
+class LatestEventIdView(object):
"""A lazy view over node values"""
def __eq__(self, value):
@@ -2775,7 +3082,7 @@ class EdgeHistoryCountView(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> EdgeHistoryCountView:
+ def bottom_k(self, k: int) -> NodeStateOptionUsize:
"""
Compute the k smallest values
@@ -2783,67 +3090,69 @@ class EdgeHistoryCountView(object):
k (int): The number of values to return
Returns:
- EdgeHistoryCountView: The k smallest values as a node state
+ NodeStateOptionUsize: The k smallest values as a node state
"""
- def collect(self) -> list[int]:
+ def collect(self) -> list[Optional[int]]:
"""
Compute all values and return the result as a list
Returns:
- list[int]: all values as a list
+ list[Optional[int]]: all values as a list
"""
- def compute(self) -> EdgeHistoryCountView:
+ def compute(self) -> NodeStateOptionUsize:
"""
Compute all values and return the result as a node view
Returns:
- EdgeHistoryCountView: the computed `NodeState`
+ NodeStateOptionUsize: the computed `NodeState`
"""
- def get(self, node: NodeInput, default: Optional[int] = None) -> Optional[int]:
+ def get(
+ self, node: NodeInput, default: Optional[Optional[int]] = None
+ ) -> Optional[Optional[int]]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[int]): the default value. Defaults to None.
+ default (Optional[Optional[int]]): the default value. Defaults to None.
Returns:
- Optional[int]: the value for the node or the default value
+ Optional[Optional[int]]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, int]]:
+ def groups(self) -> NodeGroups:
"""
- Iterate over items
+ Group by value
Returns:
- Iterator[Tuple[Node, int]]: Iterator over items
+ NodeGroups: The grouped nodes
"""
- def max(self) -> Optional[int]:
+ def items(self) -> Iterator[Tuple[Node, Optional[int]]]:
"""
- Return the maximum value
+ Iterate over items
Returns:
- Optional[int]: The maximum value or `None` if empty
+ Iterator[Tuple[Node, Optional[int]]]: Iterator over items
"""
- def max_item(self) -> Optional[Tuple[Node, int]]:
+ def max(self) -> Optional[Optional[int]]:
"""
- Return largest value and corresponding node
+ Return the maximum value
Returns:
- Optional[Tuple[Node, int]]: The Node and maximum value or `None` if empty
+ Optional[Optional[int]]: The maximum value or `None` if empty
"""
- def mean(self) -> float:
+ def max_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
- mean of values over all nodes
+ Return largest value and corresponding node
Returns:
- float: mean value
+ Optional[Tuple[Node, Optional[int]]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -2852,31 +3161,31 @@ class EdgeHistoryCountView(object):
Returns:
PropValue:
- Optional[int]:
+ Optional[Optional[int]]:
"""
- def median_item(self) -> Optional[Tuple[Node, int]]:
+ def median_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, int]]: The median value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The median value or `None` if empty
"""
- def min(self) -> Optional[int]:
+ def min(self) -> Optional[Optional[int]]:
"""
Return the minimum value
Returns:
- Optional[int]: The minimum value or `None` if empty
+ Optional[Optional[int]]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, int]]:
+ def min_item(self) -> Optional[Tuple[Node, Optional[int]]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, int]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, Optional[int]]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -2887,7 +3196,7 @@ class EdgeHistoryCountView(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> EdgeHistoryCountView:
+ def sorted(self, reverse: bool = False) -> NodeStateOptionUsize:
"""
Sort by value
@@ -2895,24 +3204,15 @@ class EdgeHistoryCountView(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- EdgeHistoryCountView: Sorted node state
+ NodeStateOptionUsize: Sorted node state
"""
- def sorted_by_id(self) -> EdgeHistoryCountView:
+ def sorted_by_id(self) -> NodeStateOptionUsize:
"""
Sort results by node id
Returns:
- EdgeHistoryCountView: The sorted node state
- """
-
- def sum(self) -> PropValue:
- """
- sum of values over all nodes
-
- Returns:
- PropValue:
- int: the sum
+ NodeStateOptionUsize: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -2926,7 +3226,7 @@ class EdgeHistoryCountView(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> EdgeHistoryCountView:
+ def top_k(self, k: int) -> NodeStateOptionUsize:
"""
Compute the k largest values
@@ -2934,18 +3234,20 @@ class EdgeHistoryCountView(object):
k (int): The number of values to return
Returns:
- EdgeHistoryCountView: The k largest values as a node state
+ NodeStateOptionUsize: The k largest values as a node state
"""
- def values(self) -> Iterator[int]:
+ def values(self) -> Iterator[Optional[int]]:
"""
Iterate over values
Returns:
- Iterator[int]: Iterator over values
+ Iterator[Optional[int]]: Iterator over values
"""
-class NodeStateListI64(object):
+class LatestDateTimeView(object):
+ """A lazy view over EarliestDateTime values for each node."""
+
def __eq__(self, value):
"""Return self==value."""
@@ -2976,7 +3278,7 @@ class NodeStateListI64(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateListI64:
+ def bottom_k(self, k: int) -> NodeStateOptionDateTime:
"""
Compute the k smallest values
@@ -2984,78 +3286,133 @@ class NodeStateListI64(object):
k (int): The number of values to return
Returns:
- NodeStateListI64: The k smallest values as a node state
+ NodeStateOptionDateTime: The k smallest values as a node state
+ """
+
+ def collect(self) -> list[Optional[datetime]]:
+ """
+ Compute all DateTime values and return the result as a list
+
+ Returns:
+ list[Optional[datetime]]: all values as a list
+ """
+
+ def collect_valid(self) -> list[datetime]:
+ """
+ Compute all DateTime values and return the valid results as a list. Conversion errors and empty values are ignored
+
+ Returns:
+ list[datetime]: all values as a list
+ """
+
+ def compute(self) -> NodeStateOptionDateTime:
+ """
+ Compute all DateTime values and return the result as a NodeState. Fails if any DateTime error is encountered.
+
+ Returns:
+ NodeStateOptionDateTime: the computed `NodeState`
+ """
+
+ def compute_valid(self) -> NodeStateOptionDateTime:
+ """
+ Compute all DateTime values and only return the valid results as a NodeState. DateTime errors are ignored.
+
+ Returns:
+ NodeStateOptionDateTime: the computed `NodeState`
"""
def get(
- self, node: NodeInput, default: Optional[list[int]] = None
- ) -> Optional[list[int]]:
+ self, node: NodeInput, default: Optional[datetime] = None
+ ) -> Optional[datetime]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[list[int]]): the default value. Defaults to None.
+ default (Optional[datetime]): the default value. Defaults to None.
Returns:
- Optional[list[int]]: the value for the node or the default value
+ Optional[datetime]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, list[int]]]:
+ def groups(self) -> NodeGroups:
+ """
+ Group by value
+
+ Returns:
+ NodeGroups: The grouped nodes
+ """
+
+ def items(self) -> Iterator[Tuple[Node, Optional[datetime]]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, list[int]]]: Iterator over items
+ Iterator[Tuple[Node, Optional[datetime]]]: Iterator over items
"""
- def max(self) -> Optional[list[int]]:
+ def items_valid(self) -> Iterator[Tuple[Node, datetime]]:
+ """
+ Iterate over valid DateTime items only. Ignore error and None values.
+
+ Returns:
+ Iterator[Tuple[Node, datetime]]: Iterator over items
+ """
+
+ def iter_valid(self) -> Iterator[datetime]:
+ """
+ Returns an iterator over all valid DateTime values. Conversion errors and empty values are ignored
+
+ Returns:
+ Iterator[datetime]: Valid DateTime values.
+ """
+
+ def max(self) -> Optional[datetime]:
"""
Return the maximum value
Returns:
- Optional[list[int]]: The maximum value or `None` if empty
+ Optional[datetime]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, list[int]]]:
+ def max_item(self) -> Optional[Tuple[Node, datetime]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, list[int]]]: The Node and maximum value or `None` if empty
+ Optional[Tuple[Node, datetime]]: The Node and maximum value or `None` if empty
"""
- def median(self) -> PropValue:
+ def median(self) -> Optional[datetime]:
"""
Return the median value
Returns:
- PropValue:
- Optional[list[int]]:
+ Optional[datetime]: The median value or `None` if empty
"""
- def median_item(self) -> Optional[Tuple[Node, list[int]]]:
+ def median_item(self) -> Optional[Tuple[Node, datetime]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, list[int]]]: The median value or `None` if empty
+ Optional[Tuple[Node, datetime]]: The median value or `None` if empty
"""
- def min(self) -> Optional[list[int]]:
+ def min(self) -> Optional[datetime]:
"""
Return the minimum value
Returns:
- Optional[list[int]]: The minimum value or `None` if empty
+ Optional[datetime]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, list[int]]]:
+ def min_item(self) -> Optional[Tuple[Node, datetime]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, list[int]]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, datetime]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -3066,23 +3423,31 @@ class NodeStateListI64(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateListI64:
+ def sorted(self, reverse: bool = False) -> NodeStateOptionDateTime:
"""
- Sort by value
+ Sort by value. Note that 'None' values will always come after valid DateTime values
Arguments:
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateListI64: Sorted node state
+ NodeStateOptionDateTime: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateListI64:
+ def sorted_by_id(self) -> NodeStateOptionDateTime:
"""
- Sort results by node id
+ Sort results by node id. Fails if any DateTime error is encountered.
+
+ Returns:
+ NodeStateOptionDateTime: The sorted node state
+ """
+
+ def sorted_by_id_valid(self) -> NodeStateOptionDateTime:
+ """
+ Sort only non-error DateTimes by node id. DateTime errors are ignored.
Returns:
- NodeStateListI64: The sorted node state
+ NodeStateOptionDateTime: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -3093,10 +3458,10 @@ class NodeStateListI64(object):
the corresponding values.
Returns:
- DataFrame: the pandas DataFrame
+ DataFrame: A Pandas DataFrame.
"""
- def top_k(self, k: int) -> NodeStateListI64:
+ def top_k(self, k: int) -> NodeStateOptionDateTime:
"""
Compute the k largest values
@@ -3104,18 +3469,26 @@ class NodeStateListI64(object):
k (int): The number of values to return
Returns:
- NodeStateListI64: The k largest values as a node state
+ NodeStateOptionDateTime: The k largest values as a node state
"""
- def values(self) -> Iterator[list[int]]:
+ def values(self) -> Iterator[Optional[datetime]]:
"""
- Iterate over values
+ Iterate over DateTime values
Returns:
- Iterator[list[int]]: Iterator over values
+ Iterator[Optional[datetime]]: Iterator over values
"""
-class HistoryDateTimeView(object):
+ def values_valid(self) -> Iterator[datetime]:
+ """
+ Iterate over valid DateTime values only. Ignore error and None values.
+
+ Returns:
+ Iterator[datetime]: Iterator over values
+ """
+
+class NameView(object):
"""A lazy view over node values"""
def __eq__(self, value):
@@ -3148,7 +3521,7 @@ class HistoryDateTimeView(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateOptionListDateTime:
+ def bottom_k(self, k: int) -> NodeStateString:
"""
Compute the k smallest values
@@ -3156,61 +3529,67 @@ class HistoryDateTimeView(object):
k (int): The number of values to return
Returns:
- NodeStateOptionListDateTime: The k smallest values as a node state
+ NodeStateString: The k smallest values as a node state
"""
- def collect(self) -> list[Optional[list[datetime]]]:
+ def collect(self) -> list[str]:
"""
Compute all values and return the result as a list
Returns:
- list[Optional[list[datetime]]]: all values as a list
+ list[str]: all values as a list
"""
- def compute(self) -> NodeStateOptionListDateTime:
+ def compute(self) -> NodeStateString:
"""
Compute all values and return the result as a node view
Returns:
- NodeStateOptionListDateTime: the computed `NodeState`
+ NodeStateString: the computed `NodeState`
"""
- def get(
- self, node: NodeInput, default: Optional[Optional[list[datetime]]] = None
- ) -> Optional[Optional[list[datetime]]]:
+ def get(self, node: NodeInput, default: Optional[str] = None) -> Optional[str]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[Optional[list[datetime]]]): the default value. Defaults to None.
+ default (Optional[str]): the default value. Defaults to None.
+
+ Returns:
+ Optional[str]: the value for the node or the default value
+ """
+
+ def groups(self) -> NodeGroups:
+ """
+ Group by value
Returns:
- Optional[Optional[list[datetime]]]: the value for the node or the default value
+ NodeGroups: The grouped nodes
"""
- def items(self) -> Iterator[Tuple[Node, Optional[list[datetime]]]]:
+ def items(self) -> Iterator[Tuple[Node, str]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, Optional[list[datetime]]]]: Iterator over items
+ Iterator[Tuple[Node, str]]: Iterator over items
"""
- def max(self) -> Optional[Optional[list[datetime]]]:
+ def max(self) -> Optional[str]:
"""
Return the maximum value
Returns:
- Optional[Optional[list[datetime]]]: The maximum value or `None` if empty
+ Optional[str]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, Optional[list[datetime]]]]:
+ def max_item(self) -> Optional[Tuple[Node, str]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[list[datetime]]]]: The Node and maximum value or `None` if empty
+ Optional[Tuple[Node, str]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -3219,31 +3598,31 @@ class HistoryDateTimeView(object):
Returns:
PropValue:
- Optional[Optional[list[datetime]]]:
+ Optional[str]:
"""
- def median_item(self) -> Optional[Tuple[Node, Optional[list[datetime]]]]:
+ def median_item(self) -> Optional[Tuple[Node, str]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[list[datetime]]]]: The median value or `None` if empty
+ Optional[Tuple[Node, str]]: The median value or `None` if empty
"""
- def min(self) -> Optional[Optional[list[datetime]]]:
+ def min(self) -> Optional[str]:
"""
Return the minimum value
Returns:
- Optional[Optional[list[datetime]]]: The minimum value or `None` if empty
+ Optional[str]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, Optional[list[datetime]]]]:
+ def min_item(self) -> Optional[Tuple[Node, str]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[list[datetime]]]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, str]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -3254,7 +3633,7 @@ class HistoryDateTimeView(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateOptionListDateTime:
+ def sorted(self, reverse: bool = False) -> NodeStateString:
"""
Sort by value
@@ -3262,15 +3641,15 @@ class HistoryDateTimeView(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateOptionListDateTime: Sorted node state
+ NodeStateString: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateOptionListDateTime:
+ def sorted_by_id(self) -> NodeStateString:
"""
Sort results by node id
Returns:
- NodeStateOptionListDateTime: The sorted node state
+ NodeStateString: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -3284,7 +3663,7 @@ class HistoryDateTimeView(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateOptionListDateTime:
+ def top_k(self, k: int) -> NodeStateString:
"""
Compute the k largest values
@@ -3292,18 +3671,18 @@ class HistoryDateTimeView(object):
k (int): The number of values to return
Returns:
- NodeStateOptionListDateTime: The k largest values as a node state
+ NodeStateString: The k largest values as a node state
"""
- def values(self) -> Iterator[Optional[list[datetime]]]:
+ def values(self) -> Iterator[str]:
"""
Iterate over values
Returns:
- Iterator[Optional[list[datetime]]]: Iterator over values
+ Iterator[str]: Iterator over values
"""
-class NodeStateOptionListDateTime(object):
+class NodeStateString(object):
def __eq__(self, value):
"""Return self==value."""
@@ -3334,7 +3713,7 @@ class NodeStateOptionListDateTime(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateOptionListDateTime:
+ def bottom_k(self, k: int) -> NodeStateString:
"""
Compute the k smallest values
@@ -3342,45 +3721,51 @@ class NodeStateOptionListDateTime(object):
k (int): The number of values to return
Returns:
- NodeStateOptionListDateTime: The k smallest values as a node state
+ NodeStateString: The k smallest values as a node state
"""
- def get(
- self, node: NodeInput, default: Optional[Optional[list[datetime]]] = None
- ) -> Optional[Optional[list[datetime]]]:
+ def get(self, node: NodeInput, default: Optional[str] = None) -> Optional[str]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[Optional[list[datetime]]]): the default value. Defaults to None.
+ default (Optional[str]): the default value. Defaults to None.
+
+ Returns:
+ Optional[str]: the value for the node or the default value
+ """
+
+ def groups(self) -> NodeGroups:
+ """
+ Group by value
Returns:
- Optional[Optional[list[datetime]]]: the value for the node or the default value
+ NodeGroups: The grouped nodes
"""
- def items(self) -> Iterator[Tuple[Node, Optional[list[datetime]]]]:
+ def items(self) -> Iterator[Tuple[Node, str]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, Optional[list[datetime]]]]: Iterator over items
+ Iterator[Tuple[Node, str]]: Iterator over items
"""
- def max(self) -> Optional[Optional[list[datetime]]]:
+ def max(self) -> Optional[str]:
"""
Return the maximum value
Returns:
- Optional[Optional[list[datetime]]]: The maximum value or `None` if empty
+ Optional[str]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, Optional[list[datetime]]]]:
+ def max_item(self) -> Optional[Tuple[Node, str]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[list[datetime]]]]: The Node and maximum value or `None` if empty
+ Optional[Tuple[Node, str]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -3389,31 +3774,31 @@ class NodeStateOptionListDateTime(object):
Returns:
PropValue:
- Optional[Optional[list[datetime]]]:
+ Optional[str]:
"""
- def median_item(self) -> Optional[Tuple[Node, Optional[list[datetime]]]]:
+ def median_item(self) -> Optional[Tuple[Node, str]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[list[datetime]]]]: The median value or `None` if empty
+ Optional[Tuple[Node, str]]: The median value or `None` if empty
"""
- def min(self) -> Optional[Optional[list[datetime]]]:
+ def min(self) -> Optional[str]:
"""
Return the minimum value
Returns:
- Optional[Optional[list[datetime]]]: The minimum value or `None` if empty
+ Optional[str]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, Optional[list[datetime]]]]:
+ def min_item(self) -> Optional[Tuple[Node, str]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, Optional[list[datetime]]]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, str]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -3424,7 +3809,7 @@ class NodeStateOptionListDateTime(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateOptionListDateTime:
+ def sorted(self, reverse: bool = False) -> NodeStateString:
"""
Sort by value
@@ -3432,15 +3817,15 @@ class NodeStateOptionListDateTime(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateOptionListDateTime: Sorted node state
+ NodeStateString: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateOptionListDateTime:
+ def sorted_by_id(self) -> NodeStateString:
"""
Sort results by node id
Returns:
- NodeStateOptionListDateTime: The sorted node state
+ NodeStateString: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -3454,7 +3839,7 @@ class NodeStateOptionListDateTime(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateOptionListDateTime:
+ def top_k(self, k: int) -> NodeStateString:
"""
Compute the k largest values
@@ -3462,19 +3847,19 @@ class NodeStateOptionListDateTime(object):
k (int): The number of values to return
Returns:
- NodeStateOptionListDateTime: The k largest values as a node state
+ NodeStateString: The k largest values as a node state
"""
- def values(self) -> Iterator[Optional[list[datetime]]]:
+ def values(self) -> Iterator[str]:
"""
Iterate over values
Returns:
- Iterator[Optional[list[datetime]]]: Iterator over values
+ Iterator[str]: Iterator over values
"""
-class NodeTypeView(object):
- """A lazy view over node values"""
+class HistoryView(object):
+ """A lazy view over History objects for each node."""
def __eq__(self, value):
"""Return self==value."""
@@ -3506,110 +3891,100 @@ class NodeTypeView(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateOptionStr:
+ def collect(self) -> list[History]:
"""
- Compute the k smallest values
-
- Arguments:
- k (int): The number of values to return
+ Compute all History objects and return the result as a list
Returns:
- NodeStateOptionStr: The k smallest values as a node state
+ list[History]: all History objects as a list
"""
- def collect(self) -> list[Optional[str]]:
+ def collect_time_entries(self) -> list[EventTime]:
"""
- Compute all values and return the result as a list
+ Compute all History objects and return the contained time entries as a sorted list
Returns:
- list[Optional[str]]: all values as a list
+ list[EventTime]: all time entries as a list
"""
- def compute(self) -> NodeStateOptionStr:
+ def compute(self) -> NodeStateHistory:
"""
Compute all values and return the result as a node view
Returns:
- NodeStateOptionStr: the computed `NodeState`
+ NodeStateHistory: the computed `NodeState`
"""
- def get(
- self, node: NodeInput, default: Optional[Optional[str]] = None
- ) -> Optional[Optional[str]]:
+ @property
+ def dt(self) -> HistoryDateTimeView:
"""
- Get value for node
-
- Arguments:
- node (NodeInput): the node
- default (Optional[Optional[str]]): the default value. Defaults to None.
+ Access history events as UTC datetimes.
Returns:
- Optional[Optional[str]]: the value for the node or the default value
+ HistoryDateTimeView: A lazy view over HistoryDateTime objects for each node.
"""
- def groups(self) -> NodeGroups:
+ def earliest_time(self) -> EarliestTimeView:
"""
- Group by value
+ Get the earliest time entry.
Returns:
- NodeGroups: The grouped nodes
+ EarliestTimeView: A lazy view over the earliest time of each node as an EventTime.
"""
- def items(self) -> Iterator[Tuple[Node, Optional[str]]]:
+ @property
+ def event_id(self) -> HistoryEventIdView:
"""
- Iterate over items
+ Access the unique event id of each time entry.
Returns:
- Iterator[Tuple[Node, Optional[str]]]: Iterator over items
+ HistoryEventIdView: A lazy view over HistoryEventId objects for each node.
"""
- def max(self) -> Optional[Optional[str]]:
+ def flatten(self) -> History:
"""
- Return the maximum value
+ Flattens all history objects into a single history with all time entries ordered.
Returns:
- Optional[Optional[str]]: The maximum value or `None` if empty
- """
-
- def max_item(self) -> Optional[Tuple[Node, Optional[str]]]:
+ History: a history object containing all time entries
"""
- Return largest value and corresponding node
- Returns:
- Optional[Tuple[Node, Optional[str]]]: The Node and maximum value or `None` if empty
+ def get(
+ self, node: NodeInput, default: Optional[History] = None
+ ) -> Optional[History]:
"""
+ Get value for node
- def median(self) -> PropValue:
- """
- Return the median value
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[History]): the default value. Defaults to None.
Returns:
- PropValue:
- Optional[Optional[str]]:
+ Optional[History]: the History object for the node or the default value
"""
- def median_item(self) -> Optional[Tuple[Node, Optional[str]]]:
+ @property
+ def intervals(self) -> IntervalsView:
"""
- Return median value and corresponding node
+ Access the intervals between consecutive timestamps in milliseconds.
Returns:
- Optional[Tuple[Node, Optional[str]]]: The median value or `None` if empty
+ IntervalsView: A lazy view over Intervals objects for each node.
"""
- def min(self) -> Optional[Optional[str]]:
+ def items(self) -> Iterator[Tuple[Node, History]]:
"""
- Return the minimum value
+ Iterate over History objects
Returns:
- Optional[Optional[str]]: The minimum value or `None` if empty
+ Iterator[Tuple[Node, History]]: Iterator over histories
"""
- def min_item(self) -> Optional[Tuple[Node, Optional[str]]]:
+ def latest_time(self) -> LatestTimeView:
"""
- Return smallest value and corresponding node
-
+ Get the latest time entry.
Returns:
- Optional[Tuple[Node, Optional[str]]]: The Node and minimum value or `None` if empty
+ LatestTimeView: A lazy view over the latest time of each node as an EventTime.
"""
def nodes(self) -> Nodes:
@@ -3620,23 +3995,21 @@ class NodeTypeView(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateOptionStr:
+ def sorted_by_id(self) -> NodeStateHistory:
"""
- Sort by value
-
- Arguments:
- reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
+ Sort results by node id
Returns:
- NodeStateOptionStr: Sorted node state
+ NodeStateHistory: The sorted node state
"""
- def sorted_by_id(self) -> NodeStateOptionStr:
+ @property
+ def t(self) -> HistoryTimestampView:
"""
- Sort results by node id
+ Access history events as timestamps (milliseconds since the Unix epoch).
Returns:
- NodeStateOptionStr: The sorted node state
+ HistoryTimestampView: A lazy view over HistoryTimestamp objects for each node.
"""
def to_df(self) -> DataFrame:
@@ -3647,29 +4020,20 @@ class NodeTypeView(object):
the corresponding values.
Returns:
- DataFrame: the pandas DataFrame
+ DataFrame: A Pandas DataFrame.
"""
- def top_k(self, k: int) -> NodeStateOptionStr:
+ def values(self) -> Iterator[History]:
"""
- Compute the k largest values
-
- Arguments:
- k (int): The number of values to return
+ Iterate over History objects
Returns:
- NodeStateOptionStr: The k largest values as a node state
- """
-
- def values(self) -> Iterator[Optional[str]]:
+ Iterator[History]: Iterator over histories
"""
- Iterate over values
- Returns:
- Iterator[Optional[str]]: Iterator over values
- """
+class HistoryTimestampView(object):
+ """A lazy view over node values"""
-class NodeStateOptionStr(object):
def __eq__(self, value):
"""Return self==value."""
@@ -3700,94 +4064,42 @@ class NodeStateOptionStr(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateOptionStr:
+ def collect(self) -> list[HistoryTimestamp]:
"""
- Compute the k smallest values
+ Compute all values and return the result as a list
- Arguments:
- k (int): The number of values to return
+ Returns:
+ list[HistoryTimestamp]: all values as a list
+ """
+
+ def compute(self) -> NodeStateHistoryTimestamp:
+ """
+ Compute all values and return the result as a node view
Returns:
- NodeStateOptionStr: The k smallest values as a node state
+ NodeStateHistoryTimestamp: the computed `NodeState`
"""
def get(
- self, node: NodeInput, default: Optional[Optional[str]] = None
- ) -> Optional[Optional[str]]:
+ self, node: NodeInput, default: Optional[HistoryTimestamp] = None
+ ) -> Optional[HistoryTimestamp]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[Optional[str]]): the default value. Defaults to None.
-
- Returns:
- Optional[Optional[str]]: the value for the node or the default value
- """
-
- def groups(self) -> NodeGroups:
- """
- Group by value
+ default (Optional[HistoryTimestamp]): the default value. Defaults to None.
Returns:
- NodeGroups: The grouped nodes
+ Optional[HistoryTimestamp]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, Optional[str]]]:
+ def items(self) -> Iterator[Tuple[Node, HistoryTimestamp]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, Optional[str]]]: Iterator over items
- """
-
- def max(self) -> Optional[Optional[str]]:
- """
- Return the maximum value
-
- Returns:
- Optional[Optional[str]]: The maximum value or `None` if empty
- """
-
- def max_item(self) -> Optional[Tuple[Node, Optional[str]]]:
- """
- Return largest value and corresponding node
-
- Returns:
- Optional[Tuple[Node, Optional[str]]]: The Node and maximum value or `None` if empty
- """
-
- def median(self) -> PropValue:
- """
- Return the median value
-
- Returns:
- PropValue:
- Optional[Optional[str]]:
- """
-
- def median_item(self) -> Optional[Tuple[Node, Optional[str]]]:
- """
- Return median value and corresponding node
-
- Returns:
- Optional[Tuple[Node, Optional[str]]]: The median value or `None` if empty
- """
-
- def min(self) -> Optional[Optional[str]]:
- """
- Return the minimum value
-
- Returns:
- Optional[Optional[str]]: The minimum value or `None` if empty
- """
-
- def min_item(self) -> Optional[Tuple[Node, Optional[str]]]:
- """
- Return smallest value and corresponding node
-
- Returns:
- Optional[Tuple[Node, Optional[str]]]: The Node and minimum value or `None` if empty
+ Iterator[Tuple[Node, HistoryTimestamp]]: Iterator over items
"""
def nodes(self) -> Nodes:
@@ -3798,23 +4110,12 @@ class NodeStateOptionStr(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateOptionStr:
- """
- Sort by value
-
- Arguments:
- reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
-
- Returns:
- NodeStateOptionStr: Sorted node state
- """
-
- def sorted_by_id(self) -> NodeStateOptionStr:
+ def sorted_by_id(self) -> NodeStateHistoryTimestamp:
"""
Sort results by node id
Returns:
- NodeStateOptionStr: The sorted node state
+ NodeStateHistoryTimestamp: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -3828,26 +4129,17 @@ class NodeStateOptionStr(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateOptionStr:
- """
- Compute the k largest values
-
- Arguments:
- k (int): The number of values to return
-
- Returns:
- NodeStateOptionStr: The k largest values as a node state
- """
-
- def values(self) -> Iterator[Optional[str]]:
+ def values(self) -> Iterator[HistoryTimestamp]:
"""
Iterate over values
Returns:
- Iterator[Optional[str]]: Iterator over values
+ Iterator[HistoryTimestamp]: Iterator over values
"""
-class NodeStateListDateTime(object):
+class HistoryDateTimeView(object):
+ """A lazy view over node values"""
+
def __eq__(self, value):
"""Return self==value."""
@@ -3878,113 +4170,164 @@ class NodeStateListDateTime(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateListDateTime:
+ def collect(self) -> list[HistoryDateTime]:
"""
- Compute the k smallest values
+ Compute all values and return the result as a list
- Arguments:
- k (int): The number of values to return
+ Returns:
+ list[HistoryDateTime]: all values as a list
+ """
+
+ def compute(self) -> NodeStateHistoryDateTime:
+ """
+ Compute all values and return the result as a node view
Returns:
- NodeStateListDateTime: The k smallest values as a node state
+ NodeStateHistoryDateTime: the computed `NodeState`
"""
def get(
- self, node: NodeInput, default: Optional[list[datetime]] = None
- ) -> Optional[list[datetime]]:
+ self, node: NodeInput, default: Optional[HistoryDateTime] = None
+ ) -> Optional[HistoryDateTime]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[list[datetime]]): the default value. Defaults to None.
+ default (Optional[HistoryDateTime]): the default value. Defaults to None.
Returns:
- Optional[list[datetime]]: the value for the node or the default value
+ Optional[HistoryDateTime]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, list[datetime]]]:
+ def items(self) -> Iterator[Tuple[Node, HistoryDateTime]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, list[datetime]]]: Iterator over items
+ Iterator[Tuple[Node, HistoryDateTime]]: Iterator over items
"""
- def max(self) -> Optional[list[datetime]]:
+ def nodes(self) -> Nodes:
"""
- Return the maximum value
+ Iterate over nodes
Returns:
- Optional[list[datetime]]: The maximum value or `None` if empty
+ Nodes: The nodes
"""
- def max_item(self) -> Optional[Tuple[Node, list[datetime]]]:
+ def sorted_by_id(self) -> NodeStateHistoryDateTime:
"""
- Return largest value and corresponding node
+ Sort results by node id
Returns:
- Optional[Tuple[Node, list[datetime]]]: The Node and maximum value or `None` if empty
+ NodeStateHistoryDateTime: The sorted node state
"""
- def median(self) -> PropValue:
+ def to_df(self) -> DataFrame:
"""
- Return the median value
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
Returns:
- PropValue:
- Optional[list[datetime]]:
+ DataFrame: the pandas DataFrame
"""
- def median_item(self) -> Optional[Tuple[Node, list[datetime]]]:
+ def values(self) -> Iterator[HistoryDateTime]:
"""
- Return median value and corresponding node
+ Iterate over values
Returns:
- Optional[Tuple[Node, list[datetime]]]: The median value or `None` if empty
+ Iterator[HistoryDateTime]: Iterator over values
"""
- def min(self) -> Optional[list[datetime]]:
+class HistoryEventIdView(object):
+ """A lazy view over node values"""
+
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self list[HistoryEventId]:
"""
- Return the minimum value
+ Compute all values and return the result as a list
Returns:
- Optional[list[datetime]]: The minimum value or `None` if empty
+ list[HistoryEventId]: all values as a list
"""
- def min_item(self) -> Optional[Tuple[Node, list[datetime]]]:
+ def compute(self) -> NodeStateHistoryEventId:
"""
- Return smallest value and corresponding node
+ Compute all values and return the result as a node view
Returns:
- Optional[Tuple[Node, list[datetime]]]: The Node and minimum value or `None` if empty
+ NodeStateHistoryEventId: the computed `NodeState`
"""
- def nodes(self) -> Nodes:
+ def get(
+ self, node: NodeInput, default: Optional[HistoryEventId] = None
+ ) -> Optional[HistoryEventId]:
"""
- Iterate over nodes
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[HistoryEventId]): the default value. Defaults to None.
Returns:
- Nodes: The nodes
+ Optional[HistoryEventId]: the value for the node or the default value
"""
- def sorted(self, reverse: bool = False) -> NodeStateListDateTime:
+ def items(self) -> Iterator[Tuple[Node, HistoryEventId]]:
"""
- Sort by value
+ Iterate over items
- Arguments:
- reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
+ Returns:
+ Iterator[Tuple[Node, HistoryEventId]]: Iterator over items
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
Returns:
- NodeStateListDateTime: Sorted node state
+ Nodes: The nodes
"""
- def sorted_by_id(self) -> NodeStateListDateTime:
+ def sorted_by_id(self) -> NodeStateHistoryEventId:
"""
Sort results by node id
Returns:
- NodeStateListDateTime: The sorted node state
+ NodeStateHistoryEventId: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -3998,26 +4341,17 @@ class NodeStateListDateTime(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateListDateTime:
- """
- Compute the k largest values
-
- Arguments:
- k (int): The number of values to return
-
- Returns:
- NodeStateListDateTime: The k largest values as a node state
- """
-
- def values(self) -> Iterator[list[datetime]]:
+ def values(self) -> Iterator[HistoryEventId]:
"""
Iterate over values
Returns:
- Iterator[list[datetime]]: Iterator over values
+ Iterator[HistoryEventId]: Iterator over values
"""
-class NodeStateWeightedSP(object):
+class IntervalsView(object):
+ """A lazy view over node values"""
+
def __eq__(self, value):
"""Return self==value."""
@@ -4048,26 +4382,74 @@ class NodeStateWeightedSP(object):
def __repr__(self):
"""Return repr(self)."""
+ def collect(self) -> list[Intervals]:
+ """
+ Compute all values and return the result as a list
+
+ Returns:
+ list[Intervals]: all values as a list
+ """
+
+ def compute(self) -> NodeStateIntervals:
+ """
+ Compute all values and return the result as a node view
+
+ Returns:
+ NodeStateIntervals: the computed `NodeState`
+ """
+
def get(
- self, node: NodeInput, default: Optional[Tuple[float, Nodes]] = None
- ) -> Optional[Tuple[float, Nodes]]:
+ self, node: NodeInput, default: Optional[Intervals] = None
+ ) -> Optional[Intervals]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[Tuple[float, Nodes]]): the default value. Defaults to None.
+ default (Optional[Intervals]): the default value. Defaults to None.
Returns:
- Optional[Tuple[float, Nodes]]: the value for the node or the default value
+ Optional[Intervals]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, Tuple[float, Nodes]]]:
+ def items(self) -> Iterator[Tuple[Node, Intervals]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, Tuple[float, Nodes]]]: Iterator over items
+ Iterator[Tuple[Node, Intervals]]: Iterator over items
+ """
+
+ def max(self) -> IntervalsIntegerView:
+ """
+ Calculate the maximum interval in milliseconds for each node.
+
+ Returns:
+ IntervalsIntegerView: A lazy view over the maximum interval between consecutive timestamps for each node. The maximum is None if there is fewer than 1 interval.
+ """
+
+ def mean(self) -> IntervalsFloatView:
+ """
+ Calculate the mean interval in milliseconds for each node.
+
+ Returns:
+ IntervalsFloatView: A lazy view over the mean interval between consecutive timestamps for each node. The mean is None if there is fewer than 1 interval.
+ """
+
+ def median(self) -> IntervalsIntegerView:
+ """
+ Calculate the median interval in milliseconds for each node.
+
+ Returns:
+ IntervalsIntegerView: A lazy view over the median interval between consecutive timestamps for each node. The median is None if there is fewer than 1 interval.
+ """
+
+ def min(self) -> IntervalsIntegerView:
+ """
+ Calculate the minimum interval in milliseconds for each node.
+
+ Returns:
+ IntervalsIntegerView: A lazy view over the minimum interval between consecutive timestamps for each node. The minimum is None if there is fewer than 1 interval.
"""
def nodes(self) -> Nodes:
@@ -4078,12 +4460,12 @@ class NodeStateWeightedSP(object):
Nodes: The nodes
"""
- def sorted_by_id(self) -> NodeStateWeightedSP:
+ def sorted_by_id(self) -> NodeStateIntervals:
"""
Sort results by node id
Returns:
- NodeStateWeightedSP: The sorted node state
+ NodeStateIntervals: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -4097,15 +4479,17 @@ class NodeStateWeightedSP(object):
DataFrame: the pandas DataFrame
"""
- def values(self) -> Iterator[Tuple[float, Nodes]]:
+ def values(self) -> Iterator[Intervals]:
"""
Iterate over values
Returns:
- Iterator[Tuple[float, Nodes]]: Iterator over values
+ Iterator[Intervals]: Iterator over values
"""
-class NodeStateF64(object):
+class IntervalsFloatView(object):
+ """A lazy view over node values"""
+
def __eq__(self, value):
"""Return self==value."""
@@ -4136,7 +4520,7 @@ class NodeStateF64(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateF64:
+ def bottom_k(self, k: int) -> NodeStateOptionF64:
"""
Compute the k smallest values
@@ -4144,51 +4528,61 @@ class NodeStateF64(object):
k (int): The number of values to return
Returns:
- NodeStateF64: The k smallest values as a node state
+ NodeStateOptionF64: The k smallest values as a node state
"""
- def get(self, node: NodeInput, default: Optional[float] = None) -> Optional[float]:
+ def collect(self) -> list[Optional[float]]:
+ """
+ Compute all values and return the result as a list
+
+ Returns:
+ list[Optional[float]]: all values as a list
+ """
+
+ def compute(self) -> NodeStateOptionF64:
+ """
+ Compute all values and return the result as a node view
+
+ Returns:
+ NodeStateOptionF64: the computed `NodeState`
+ """
+
+ def get(
+ self, node: NodeInput, default: Optional[Optional[float]] = None
+ ) -> Optional[Optional[float]]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[float]): the default value. Defaults to None.
+ default (Optional[Optional[float]]): the default value. Defaults to None.
Returns:
- Optional[float]: the value for the node or the default value
+ Optional[Optional[float]]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, float]]:
+ def items(self) -> Iterator[Tuple[Node, Optional[float]]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, float]]: Iterator over items
+ Iterator[Tuple[Node, Optional[float]]]: Iterator over items
"""
- def max(self) -> Optional[float]:
+ def max(self) -> Optional[Optional[float]]:
"""
Return the maximum value
Returns:
- Optional[float]: The maximum value or `None` if empty
+ Optional[Optional[float]]: The maximum value or `None` if empty
"""
- def max_item(self) -> Optional[Tuple[Node, float]]:
+ def max_item(self) -> Optional[Tuple[Node, Optional[float]]]:
"""
Return largest value and corresponding node
Returns:
- Optional[Tuple[Node, float]]: The Node and maximum value or `None` if empty
- """
-
- def mean(self) -> float:
- """
- mean of values over all nodes
-
- Returns:
- float: mean value
+ Optional[Tuple[Node, Optional[float]]]: The Node and maximum value or `None` if empty
"""
def median(self) -> PropValue:
@@ -4197,31 +4591,31 @@ class NodeStateF64(object):
Returns:
PropValue:
- Optional[float]:
+ Optional[Optional[float]]:
"""
- def median_item(self) -> Optional[Tuple[Node, float]]:
+ def median_item(self) -> Optional[Tuple[Node, Optional[float]]]:
"""
Return median value and corresponding node
Returns:
- Optional[Tuple[Node, float]]: The median value or `None` if empty
+ Optional[Tuple[Node, Optional[float]]]: The median value or `None` if empty
"""
- def min(self) -> Optional[float]:
+ def min(self) -> Optional[Optional[float]]:
"""
Return the minimum value
Returns:
- Optional[float]: The minimum value or `None` if empty
+ Optional[Optional[float]]: The minimum value or `None` if empty
"""
- def min_item(self) -> Optional[Tuple[Node, float]]:
+ def min_item(self) -> Optional[Tuple[Node, Optional[float]]]:
"""
Return smallest value and corresponding node
Returns:
- Optional[Tuple[Node, float]]: The Node and minimum value or `None` if empty
+ Optional[Tuple[Node, Optional[float]]]: The Node and minimum value or `None` if empty
"""
def nodes(self) -> Nodes:
@@ -4232,7 +4626,7 @@ class NodeStateF64(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateF64:
+ def sorted(self, reverse: bool = False) -> NodeStateOptionF64:
"""
Sort by value
@@ -4240,24 +4634,2029 @@ class NodeStateF64(object):
reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
Returns:
- NodeStateF64: Sorted node state
+ NodeStateOptionF64: Sorted node state
"""
- def sorted_by_id(self) -> NodeStateF64:
+ def sorted_by_id(self) -> NodeStateOptionF64:
"""
Sort results by node id
Returns:
- NodeStateF64: The sorted node state
+ NodeStateOptionF64: The sorted node state
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def top_k(self, k: int) -> NodeStateOptionF64:
+ """
+ Compute the k largest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateOptionF64: The k largest values as a node state
+ """
+
+ def values(self) -> Iterator[Optional[float]]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[Optional[float]]: Iterator over values
+ """
+
+class IntervalsIntegerView(object):
+ """A lazy view over node values"""
+
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self NodeStateOptionI64:
+ """
+ Compute the k smallest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateOptionI64: The k smallest values as a node state
+ """
+
+ def collect(self) -> list[Optional[int]]:
+ """
+ Compute all values and return the result as a list
+
+ Returns:
+ list[Optional[int]]: all values as a list
+ """
+
+ def compute(self) -> NodeStateOptionI64:
+ """
+ Compute all values and return the result as a node view
+
+ Returns:
+ NodeStateOptionI64: the computed `NodeState`
+ """
+
+ def get(
+ self, node: NodeInput, default: Optional[Optional[int]] = None
+ ) -> Optional[Optional[int]]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[Optional[int]]): the default value. Defaults to None.
+
+ Returns:
+ Optional[Optional[int]]: the value for the node or the default value
+ """
+
+ def items(self) -> Iterator[Tuple[Node, Optional[int]]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, Optional[int]]]: Iterator over items
+ """
+
+ def max(self) -> Optional[Optional[int]]:
+ """
+ Return the maximum value
+
+ Returns:
+ Optional[Optional[int]]: The maximum value or `None` if empty
+ """
+
+ def max_item(self) -> Optional[Tuple[Node, Optional[int]]]:
+ """
+ Return largest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[int]]]: The Node and maximum value or `None` if empty
+ """
+
+ def median(self) -> PropValue:
+ """
+ Return the median value
+
+ Returns:
+ PropValue:
+ Optional[Optional[int]]:
+ """
+
+ def median_item(self) -> Optional[Tuple[Node, Optional[int]]]:
+ """
+ Return median value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[int]]]: The median value or `None` if empty
+ """
+
+ def min(self) -> Optional[Optional[int]]:
+ """
+ Return the minimum value
+
+ Returns:
+ Optional[Optional[int]]: The minimum value or `None` if empty
+ """
+
+ def min_item(self) -> Optional[Tuple[Node, Optional[int]]]:
+ """
+ Return smallest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[int]]]: The Node and minimum value or `None` if empty
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted(self, reverse: bool = False) -> NodeStateOptionI64:
+ """
+ Sort by value
+
+ Arguments:
+ reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
+
+ Returns:
+ NodeStateOptionI64: Sorted node state
+ """
+
+ def sorted_by_id(self) -> NodeStateOptionI64:
+ """
+ Sort results by node id
+
+ Returns:
+ NodeStateOptionI64: The sorted node state
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def top_k(self, k: int) -> NodeStateOptionI64:
+ """
+ Compute the k largest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateOptionI64: The k largest values as a node state
+ """
+
+ def values(self) -> Iterator[Optional[int]]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[Optional[int]]: Iterator over values
+ """
+
+class EdgeHistoryCountView(object):
+ """A lazy view over node values"""
+
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self EdgeHistoryCountView:
+ """
+ Compute the k smallest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ EdgeHistoryCountView: The k smallest values as a node state
+ """
+
+ def collect(self) -> list[int]:
+ """
+ Compute all values and return the result as a list
+
+ Returns:
+ list[int]: all values as a list
+ """
+
+ def compute(self) -> EdgeHistoryCountView:
+ """
+ Compute all values and return the result as a node view
+
+ Returns:
+ EdgeHistoryCountView: the computed `NodeState`
+ """
+
+ def get(self, node: NodeInput, default: Optional[int] = None) -> Optional[int]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[int]): the default value. Defaults to None.
+
+ Returns:
+ Optional[int]: the value for the node or the default value
+ """
+
+ def items(self) -> Iterator[Tuple[Node, int]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, int]]: Iterator over items
+ """
+
+ def max(self) -> Optional[int]:
+ """
+ Return the maximum value
+
+ Returns:
+ Optional[int]: The maximum value or `None` if empty
+ """
+
+ def max_item(self) -> Optional[Tuple[Node, int]]:
+ """
+ Return largest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, int]]: The Node and maximum value or `None` if empty
+ """
+
+ def mean(self) -> float:
+ """
+ mean of values over all nodes
+
+ Returns:
+ float: mean value
+ """
+
+ def median(self) -> PropValue:
+ """
+ Return the median value
+
+ Returns:
+ PropValue:
+ Optional[int]:
+ """
+
+ def median_item(self) -> Optional[Tuple[Node, int]]:
+ """
+ Return median value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, int]]: The median value or `None` if empty
+ """
+
+ def min(self) -> Optional[int]:
+ """
+ Return the minimum value
+
+ Returns:
+ Optional[int]: The minimum value or `None` if empty
+ """
+
+ def min_item(self) -> Optional[Tuple[Node, int]]:
+ """
+ Return smallest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, int]]: The Node and minimum value or `None` if empty
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted(self, reverse: bool = False) -> EdgeHistoryCountView:
+ """
+ Sort by value
+
+ Arguments:
+ reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
+
+ Returns:
+ EdgeHistoryCountView: Sorted node state
+ """
+
+ def sorted_by_id(self) -> EdgeHistoryCountView:
+ """
+ Sort results by node id
+
+ Returns:
+ EdgeHistoryCountView: The sorted node state
+ """
+
+ def sum(self) -> PropValue:
+ """
+ sum of values over all nodes
+
+ Returns:
+ PropValue:
+ int: the sum
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def top_k(self, k: int) -> EdgeHistoryCountView:
+ """
+ Compute the k largest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ EdgeHistoryCountView: The k largest values as a node state
+ """
+
+ def values(self) -> Iterator[int]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[int]: Iterator over values
+ """
+
+class UsizeIterable(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self NodeStateOptionStr:
+ """
+ Compute the k smallest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateOptionStr: The k smallest values as a node state
+ """
+
+ def collect(self) -> list[Optional[str]]:
+ """
+ Compute all values and return the result as a list
+
+ Returns:
+ list[Optional[str]]: all values as a list
+ """
+
+ def compute(self) -> NodeStateOptionStr:
+ """
+ Compute all values and return the result as a node view
+
+ Returns:
+ NodeStateOptionStr: the computed `NodeState`
+ """
+
+ def get(
+ self, node: NodeInput, default: Optional[Optional[str]] = None
+ ) -> Optional[Optional[str]]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[Optional[str]]): the default value. Defaults to None.
+
+ Returns:
+ Optional[Optional[str]]: the value for the node or the default value
+ """
+
+ def groups(self) -> NodeGroups:
+ """
+ Group by value
+
+ Returns:
+ NodeGroups: The grouped nodes
+ """
+
+ def items(self) -> Iterator[Tuple[Node, Optional[str]]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, Optional[str]]]: Iterator over items
+ """
+
+ def max(self) -> Optional[Optional[str]]:
+ """
+ Return the maximum value
+
+ Returns:
+ Optional[Optional[str]]: The maximum value or `None` if empty
+ """
+
+ def max_item(self) -> Optional[Tuple[Node, Optional[str]]]:
+ """
+ Return largest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[str]]]: The Node and maximum value or `None` if empty
+ """
+
+ def median(self) -> PropValue:
+ """
+ Return the median value
+
+ Returns:
+ PropValue:
+ Optional[Optional[str]]:
+ """
+
+ def median_item(self) -> Optional[Tuple[Node, Optional[str]]]:
+ """
+ Return median value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[str]]]: The median value or `None` if empty
+ """
+
+ def min(self) -> Optional[Optional[str]]:
+ """
+ Return the minimum value
+
+ Returns:
+ Optional[Optional[str]]: The minimum value or `None` if empty
+ """
+
+ def min_item(self) -> Optional[Tuple[Node, Optional[str]]]:
+ """
+ Return smallest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[str]]]: The Node and minimum value or `None` if empty
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted(self, reverse: bool = False) -> NodeStateOptionStr:
+ """
+ Sort by value
+
+ Arguments:
+ reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
+
+ Returns:
+ NodeStateOptionStr: Sorted node state
+ """
+
+ def sorted_by_id(self) -> NodeStateOptionStr:
+ """
+ Sort results by node id
+
+ Returns:
+ NodeStateOptionStr: The sorted node state
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def top_k(self, k: int) -> NodeStateOptionStr:
+ """
+ Compute the k largest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateOptionStr: The k largest values as a node state
+ """
+
+ def values(self) -> Iterator[Optional[str]]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[Optional[str]]: Iterator over values
+ """
+
+class NodeStateOptionStr(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self NodeStateOptionStr:
+ """
+ Compute the k smallest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateOptionStr: The k smallest values as a node state
+ """
+
+ def get(
+ self, node: NodeInput, default: Optional[Optional[str]] = None
+ ) -> Optional[Optional[str]]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[Optional[str]]): the default value. Defaults to None.
+
+ Returns:
+ Optional[Optional[str]]: the value for the node or the default value
+ """
+
+ def groups(self) -> NodeGroups:
+ """
+ Group by value
+
+ Returns:
+ NodeGroups: The grouped nodes
+ """
+
+ def items(self) -> Iterator[Tuple[Node, Optional[str]]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, Optional[str]]]: Iterator over items
+ """
+
+ def max(self) -> Optional[Optional[str]]:
+ """
+ Return the maximum value
+
+ Returns:
+ Optional[Optional[str]]: The maximum value or `None` if empty
+ """
+
+ def max_item(self) -> Optional[Tuple[Node, Optional[str]]]:
+ """
+ Return largest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[str]]]: The Node and maximum value or `None` if empty
+ """
+
+ def median(self) -> PropValue:
+ """
+ Return the median value
+
+ Returns:
+ PropValue:
+ Optional[Optional[str]]:
+ """
+
+ def median_item(self) -> Optional[Tuple[Node, Optional[str]]]:
+ """
+ Return median value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[str]]]: The median value or `None` if empty
+ """
+
+ def min(self) -> Optional[Optional[str]]:
+ """
+ Return the minimum value
+
+ Returns:
+ Optional[Optional[str]]: The minimum value or `None` if empty
+ """
+
+ def min_item(self) -> Optional[Tuple[Node, Optional[str]]]:
+ """
+ Return smallest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[str]]]: The Node and minimum value or `None` if empty
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted(self, reverse: bool = False) -> NodeStateOptionStr:
+ """
+ Sort by value
+
+ Arguments:
+ reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
+
+ Returns:
+ NodeStateOptionStr: Sorted node state
+ """
+
+ def sorted_by_id(self) -> NodeStateOptionStr:
+ """
+ Sort results by node id
+
+ Returns:
+ NodeStateOptionStr: The sorted node state
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def top_k(self, k: int) -> NodeStateOptionStr:
+ """
+ Compute the k largest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateOptionStr: The k largest values as a node state
+ """
+
+ def values(self) -> Iterator[Optional[str]]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[Optional[str]]: Iterator over values
+ """
+
+class NodeStateListDateTime(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self NodeStateListDateTime:
+ """
+ Compute the k smallest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateListDateTime: The k smallest values as a node state
+ """
+
+ def get(
+ self, node: NodeInput, default: Optional[list[datetime]] = None
+ ) -> Optional[list[datetime]]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[list[datetime]]): the default value. Defaults to None.
+
+ Returns:
+ Optional[list[datetime]]: the value for the node or the default value
+ """
+
+ def items(self) -> Iterator[Tuple[Node, list[datetime]]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, list[datetime]]]: Iterator over items
+ """
+
+ def max(self) -> Optional[list[datetime]]:
+ """
+ Return the maximum value
+
+ Returns:
+ Optional[list[datetime]]: The maximum value or `None` if empty
+ """
+
+ def max_item(self) -> Optional[Tuple[Node, list[datetime]]]:
+ """
+ Return largest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, list[datetime]]]: The Node and maximum value or `None` if empty
+ """
+
+ def median(self) -> PropValue:
+ """
+ Return the median value
+
+ Returns:
+ PropValue:
+ Optional[list[datetime]]:
+ """
+
+ def median_item(self) -> Optional[Tuple[Node, list[datetime]]]:
+ """
+ Return median value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, list[datetime]]]: The median value or `None` if empty
+ """
+
+ def min(self) -> Optional[list[datetime]]:
+ """
+ Return the minimum value
+
+ Returns:
+ Optional[list[datetime]]: The minimum value or `None` if empty
+ """
+
+ def min_item(self) -> Optional[Tuple[Node, list[datetime]]]:
+ """
+ Return smallest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, list[datetime]]]: The Node and minimum value or `None` if empty
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted(self, reverse: bool = False) -> NodeStateListDateTime:
+ """
+ Sort by value
+
+ Arguments:
+ reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
+
+ Returns:
+ NodeStateListDateTime: Sorted node state
+ """
+
+ def sorted_by_id(self) -> NodeStateListDateTime:
+ """
+ Sort results by node id
+
+ Returns:
+ NodeStateListDateTime: The sorted node state
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def top_k(self, k: int) -> NodeStateListDateTime:
+ """
+ Compute the k largest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateListDateTime: The k largest values as a node state
+ """
+
+ def values(self) -> Iterator[list[datetime]]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[list[datetime]]: Iterator over values
+ """
+
+class NodeStateWeightedSP(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self Optional[Tuple[float, Nodes]]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[Tuple[float, Nodes]]): the default value. Defaults to None.
+
+ Returns:
+ Optional[Tuple[float, Nodes]]: the value for the node or the default value
+ """
+
+ def items(self) -> Iterator[Tuple[Node, Tuple[float, Nodes]]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, Tuple[float, Nodes]]]: Iterator over items
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted_by_id(self) -> NodeStateWeightedSP:
+ """
+ Sort results by node id
+
+ Returns:
+ NodeStateWeightedSP: The sorted node state
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def values(self) -> Iterator[Tuple[float, Nodes]]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[Tuple[float, Nodes]]: Iterator over values
+ """
+
+class NodeStateF64(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self NodeStateF64:
+ """
+ Compute the k smallest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateF64: The k smallest values as a node state
+ """
+
+ def get(self, node: NodeInput, default: Optional[float] = None) -> Optional[float]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[float]): the default value. Defaults to None.
+
+ Returns:
+ Optional[float]: the value for the node or the default value
+ """
+
+ def items(self) -> Iterator[Tuple[Node, float]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, float]]: Iterator over items
+ """
+
+ def max(self) -> Optional[float]:
+ """
+ Return the maximum value
+
+ Returns:
+ Optional[float]: The maximum value or `None` if empty
+ """
+
+ def max_item(self) -> Optional[Tuple[Node, float]]:
+ """
+ Return largest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, float]]: The Node and maximum value or `None` if empty
+ """
+
+ def mean(self) -> float:
+ """
+ mean of values over all nodes
+
+ Returns:
+ float: mean value
+ """
+
+ def median(self) -> PropValue:
+ """
+ Return the median value
+
+ Returns:
+ PropValue:
+ Optional[float]:
+ """
+
+ def median_item(self) -> Optional[Tuple[Node, float]]:
+ """
+ Return median value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, float]]: The median value or `None` if empty
+ """
+
+ def min(self) -> Optional[float]:
+ """
+ Return the minimum value
+
+ Returns:
+ Optional[float]: The minimum value or `None` if empty
+ """
+
+ def min_item(self) -> Optional[Tuple[Node, float]]:
+ """
+ Return smallest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, float]]: The Node and minimum value or `None` if empty
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted(self, reverse: bool = False) -> NodeStateF64:
+ """
+ Sort by value
+
+ Arguments:
+ reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
+
+ Returns:
+ NodeStateF64: Sorted node state
+ """
+
+ def sorted_by_id(self) -> NodeStateF64:
+ """
+ Sort results by node id
+
+ Returns:
+ NodeStateF64: The sorted node state
+ """
+
+ def sum(self) -> PropValue:
+ """
+ sum of values over all nodes
+
+ Returns:
+ PropValue:
+ float: the sum
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def top_k(self, k: int) -> NodeStateF64:
+ """
+ Compute the k largest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateF64: The k largest values as a node state
+ """
+
+ def values(self) -> Iterator[float]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[float]: Iterator over values
+ """
+
+class NodeStateOptionF64(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self NodeStateOptionF64:
+ """
+ Compute the k smallest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateOptionF64: The k smallest values as a node state
+ """
+
+ def get(
+ self, node: NodeInput, default: Optional[Optional[float]] = None
+ ) -> Optional[Optional[float]]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[Optional[float]]): the default value. Defaults to None.
+
+ Returns:
+ Optional[Optional[float]]: the value for the node or the default value
+ """
+
+ def items(self) -> Iterator[Tuple[Node, Optional[float]]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, Optional[float]]]: Iterator over items
+ """
+
+ def max(self) -> Optional[Optional[float]]:
+ """
+ Return the maximum value
+
+ Returns:
+ Optional[Optional[float]]: The maximum value or `None` if empty
+ """
+
+ def max_item(self) -> Optional[Tuple[Node, Optional[float]]]:
+ """
+ Return largest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[float]]]: The Node and maximum value or `None` if empty
+ """
+
+ def median(self) -> PropValue:
+ """
+ Return the median value
+
+ Returns:
+ PropValue:
+ Optional[Optional[float]]:
+ """
+
+ def median_item(self) -> Optional[Tuple[Node, Optional[float]]]:
+ """
+ Return median value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[float]]]: The median value or `None` if empty
+ """
+
+ def min(self) -> Optional[Optional[float]]:
+ """
+ Return the minimum value
+
+ Returns:
+ Optional[Optional[float]]: The minimum value or `None` if empty
+ """
+
+ def min_item(self) -> Optional[Tuple[Node, Optional[float]]]:
+ """
+ Return smallest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Optional[float]]]: The Node and minimum value or `None` if empty
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted(self, reverse: bool = False) -> NodeStateOptionF64:
+ """
+ Sort by value
+
+ Arguments:
+ reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
+
+ Returns:
+ NodeStateOptionF64: Sorted node state
+ """
+
+ def sorted_by_id(self) -> NodeStateOptionF64:
+ """
+ Sort results by node id
+
+ Returns:
+ NodeStateOptionF64: The sorted node state
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def top_k(self, k: int) -> NodeStateOptionF64:
+ """
+ Compute the k largest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateOptionF64: The k largest values as a node state
+ """
+
+ def values(self) -> Iterator[Optional[float]]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[Optional[float]]: Iterator over values
+ """
+
+class NodeStateNodes(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self Optional[Nodes]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[Nodes]): the default value. Defaults to None.
+
+ Returns:
+ Optional[Nodes]: the value for the node or the default value
+ """
+
+ def items(self) -> Iterator[Tuple[Node, Nodes]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, Nodes]]: Iterator over items
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted_by_id(self) -> NodeStateNodes:
+ """
+ Sort results by node id
+
+ Returns:
+ NodeStateNodes: The sorted node state
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def values(self) -> Iterator[Nodes]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[Nodes]: Iterator over values
+ """
+
+class NodeStateReachability(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self Optional[list[Tuple[int, str]]]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[list[Tuple[int, str]]]): the default value. Defaults to None.
+
+ Returns:
+ Optional[list[Tuple[int, str]]]: the value for the node or the default value
+ """
+
+ def items(self) -> Iterator[Tuple[Node, list[Tuple[int, str]]]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, list[Tuple[int, str]]]]: Iterator over items
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted_by_id(self) -> NodeStateReachability:
+ """
+ Sort results by node id
+
+ Returns:
+ NodeStateReachability: The sorted node state
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def values(self) -> Iterator[list[Tuple[int, str]]]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[list[Tuple[int, str]]]: Iterator over values
+ """
+
+class NodeStateListF64(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self Optional[list[float]]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[list[float]]): the default value. Defaults to None.
+
+ Returns:
+ Optional[list[float]]: the value for the node or the default value
+ """
+
+ def items(self) -> Iterator[Tuple[Node, list[float]]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, list[float]]]: Iterator over items
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted_by_id(self) -> NodeStateListF64:
+ """
+ Sort results by node id
+
+ Returns:
+ NodeStateListF64: The sorted node state
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def values(self) -> Iterator[list[float]]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[list[float]]: Iterator over values
+ """
+
+class NodeStateMotifs(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self NodeStateMotifs:
+ """
+ Compute the k smallest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateMotifs: The k smallest values as a node state
+ """
+
+ def get(
+ self, node: NodeInput, default: Optional[list[int]] = None
+ ) -> Optional[list[int]]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[list[int]]): the default value. Defaults to None.
+
+ Returns:
+ Optional[list[int]]: the value for the node or the default value
+ """
+
+ def items(self) -> Iterator[Tuple[Node, list[int]]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, list[int]]]: Iterator over items
+ """
+
+ def max(self) -> Optional[list[int]]:
+ """
+ Return the maximum value
+
+ Returns:
+ Optional[list[int]]: The maximum value or `None` if empty
+ """
+
+ def max_item(self) -> Optional[Tuple[Node, list[int]]]:
+ """
+ Return largest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, list[int]]]: The Node and maximum value or `None` if empty
+ """
+
+ def median(self) -> PropValue:
+ """
+ Return the median value
+
+ Returns:
+ PropValue:
+ Optional[list[int]]:
+ """
+
+ def median_item(self) -> Optional[Tuple[Node, list[int]]]:
+ """
+ Return median value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, list[int]]]: The median value or `None` if empty
+ """
+
+ def min(self) -> Optional[list[int]]:
+ """
+ Return the minimum value
+
+ Returns:
+ Optional[list[int]]: The minimum value or `None` if empty
+ """
+
+ def min_item(self) -> Optional[Tuple[Node, list[int]]]:
+ """
+ Return smallest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, list[int]]]: The Node and minimum value or `None` if empty
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted(self, reverse: bool = False) -> NodeStateMotifs:
+ """
+ Sort by value
+
+ Arguments:
+ reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
+
+ Returns:
+ NodeStateMotifs: Sorted node state
+ """
+
+ def sorted_by_id(self) -> NodeStateMotifs:
+ """
+ Sort results by node id
+
+ Returns:
+ NodeStateMotifs: The sorted node state
+ """
+
+ def to_df(self) -> DataFrame:
+ """
+ Convert results to pandas DataFrame
+
+ The DataFrame has two columns, "node" with the node ids and "value" with
+ the corresponding values.
+
+ Returns:
+ DataFrame: the pandas DataFrame
+ """
+
+ def top_k(self, k: int) -> NodeStateMotifs:
+ """
+ Compute the k largest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateMotifs: The k largest values as a node state
+ """
+
+ def values(self) -> Iterator[list[int]]:
+ """
+ Iterate over values
+
+ Returns:
+ Iterator[list[int]]: Iterator over values
+ """
+
+class NodeStateHits(object):
+ def __eq__(self, value):
+ """Return self==value."""
+
+ def __ge__(self, value):
+ """Return self>=value."""
+
+ def __getitem__(self, key):
+ """Return self[key]."""
+
+ def __gt__(self, value):
+ """Return self>value."""
+
+ def __iter__(self):
+ """Implement iter(self)."""
+
+ def __le__(self, value):
+ """Return self<=value."""
+
+ def __len__(self):
+ """Return len(self)."""
+
+ def __lt__(self, value):
+ """Return self NodeStateHits:
+ """
+ Compute the k smallest values
+
+ Arguments:
+ k (int): The number of values to return
+
+ Returns:
+ NodeStateHits: The k smallest values as a node state
+ """
+
+ def get(
+ self, node: NodeInput, default: Optional[Tuple[float, float]] = None
+ ) -> Optional[Tuple[float, float]]:
+ """
+ Get value for node
+
+ Arguments:
+ node (NodeInput): the node
+ default (Optional[Tuple[float, float]]): the default value. Defaults to None.
+
+ Returns:
+ Optional[Tuple[float, float]]: the value for the node or the default value
+ """
+
+ def items(self) -> Iterator[Tuple[Node, Tuple[float, float]]]:
+ """
+ Iterate over items
+
+ Returns:
+ Iterator[Tuple[Node, Tuple[float, float]]]: Iterator over items
+ """
+
+ def max(self) -> Optional[Tuple[float, float]]:
+ """
+ Return the maximum value
+
+ Returns:
+ Optional[Tuple[float, float]]: The maximum value or `None` if empty
+ """
+
+ def max_item(self) -> Optional[Tuple[Node, Tuple[float, float]]]:
+ """
+ Return largest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Tuple[float, float]]]: The Node and maximum value or `None` if empty
+ """
+
+ def median(self) -> PropValue:
+ """
+ Return the median value
+
+ Returns:
+ PropValue:
+ Optional[Tuple[float, float]]:
+ """
+
+ def median_item(self) -> Optional[Tuple[Node, Tuple[float, float]]]:
+ """
+ Return median value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Tuple[float, float]]]: The median value or `None` if empty
+ """
+
+ def min(self) -> Optional[Tuple[float, float]]:
+ """
+ Return the minimum value
+
+ Returns:
+ Optional[Tuple[float, float]]: The minimum value or `None` if empty
+ """
+
+ def min_item(self) -> Optional[Tuple[Node, Tuple[float, float]]]:
+ """
+ Return smallest value and corresponding node
+
+ Returns:
+ Optional[Tuple[Node, Tuple[float, float]]]: The Node and minimum value or `None` if empty
+ """
+
+ def nodes(self) -> Nodes:
+ """
+ Iterate over nodes
+
+ Returns:
+ Nodes: The nodes
+ """
+
+ def sorted(self, reverse: bool = False) -> NodeStateHits:
+ """
+ Sort by value
+
+ Arguments:
+ reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
+
+ Returns:
+ NodeStateHits: Sorted node state
"""
- def sum(self) -> PropValue:
+ def sorted_by_id(self) -> NodeStateHits:
"""
- sum of values over all nodes
+ Sort results by node id
Returns:
- PropValue:
- float: the sum
+ NodeStateHits: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -4271,7 +6670,7 @@ class NodeStateF64(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateF64:
+ def top_k(self, k: int) -> NodeStateHits:
"""
Compute the k largest values
@@ -4279,18 +6678,20 @@ class NodeStateF64(object):
k (int): The number of values to return
Returns:
- NodeStateF64: The k largest values as a node state
+ NodeStateHits: The k largest values as a node state
"""
- def values(self) -> Iterator[float]:
+ def values(self) -> Iterator[Tuple[float, float]]:
"""
Iterate over values
Returns:
- Iterator[float]: Iterator over values
+ Iterator[Tuple[float, float]]: Iterator over values
"""
-class NodeStateNodes(object):
+class NodeStateHistory(object):
+ """A NodeState of History objects for each node."""
+
def __eq__(self, value):
"""Return self==value."""
@@ -4321,40 +6722,110 @@ class NodeStateNodes(object):
def __repr__(self):
"""Return repr(self)."""
- def get(self, node: NodeInput, default: Optional[Nodes] = None) -> Optional[Nodes]:
+ def collect_time_entries(self) -> list[EventTime]:
"""
- Get value for node
+ Collect and return all the contained time entries as a sorted list.
+
+ Returns:
+ list[EventTime]: All time entries as a list.
+ """
+
+ @property
+ def dt(self) -> NodeStateHistoryDateTime:
+ """
+ Access history events as UTC datetimes.
+
+ Returns:
+ NodeStateHistoryDateTime: A NodeState with the computed HistoryDateTime object for each node.
+ """
+
+ def earliest_time(self) -> OptionalEventTime:
+ """
+ Get the earliest time entry of all nodes.
+
+ Returns:
+ OptionalEventTime: The earliest event present in any of the nodes' histories.
+ """
+
+ @property
+ def event_id(self) -> NodeStateHistoryEventId:
+ """
+ Access the unique event id of each time entry.
+
+ Returns:
+ NodeStateHistoryEventId: A NodeState with the computed HistoryEventId object for each node.
+ """
+
+ def flatten(self) -> History:
+ """
+ Flattens all history objects into a single history object with all time entries ordered.
+
+ Returns:
+ History: A history object containing all time entries.
+ """
+
+ def get(
+ self, node: NodeInput, default: Optional[History] = None
+ ) -> Optional[History]:
+ """
+ Get History object for the node.
Arguments:
node (NodeInput): the node
- default (Optional[Nodes]): the default value. Defaults to None.
+ default (Optional[History]): The default value. Defaults to None.
Returns:
- Optional[Nodes]: the value for the node or the default value
+ Optional[History]: The value for the node or the default value.
"""
- def items(self) -> Iterator[Tuple[Node, Nodes]]:
+ @property
+ def intervals(self) -> NodeStateIntervals:
+ """
+ Access the intervals between consecutive timestamps in milliseconds.
+
+ Returns:
+ NodeStateIntervals: A NodeState with the computed Intervals object for each node.
+ """
+
+ def items(self) -> Iterator[Tuple[Node, History]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, Nodes]]: Iterator over items
+ Iterator[Tuple[Node, History]]: Iterator over items.
+ """
+
+ def latest_time(self) -> OptionalEventTime:
+ """
+ Get the latest time entry.
+
+ Returns:
+ OptionalEventTime: The latest event present in any of the nodes' histories.
"""
def nodes(self) -> Nodes:
"""
- Iterate over nodes
+ Iterate over nodes.
Returns:
- Nodes: The nodes
+ Nodes: The nodes.
"""
- def sorted_by_id(self) -> NodeStateNodes:
+ def sorted_by_id(self) -> NodeStateHistory:
"""
Sort results by node id
Returns:
- NodeStateNodes: The sorted node state
+ NodeStateHistory: The sorted node state.
+ """
+
+ @property
+ def t(self) -> NodeStateHistoryTimestamp:
+ """
+ Access history events as timestamps (milliseconds since the Unix epoch).
+
+ Returns:
+ NodeStateHistoryTimestamp: A NodeState with the computed HistoryTimestamp object for each node.
"""
def to_df(self) -> DataFrame:
@@ -4365,18 +6836,18 @@ class NodeStateNodes(object):
the corresponding values.
Returns:
- DataFrame: the pandas DataFrame
+ DataFrame: A Pandas DataFrame.
"""
- def values(self) -> Iterator[Nodes]:
+ def values(self) -> Iterator[History]:
"""
- Iterate over values
+ Iterate over History objects.
Returns:
- Iterator[Nodes]: Iterator over values
+ Iterator[History]: Iterator over History objects.
"""
-class NodeStateReachability(object):
+class NodeStateHistoryTimestamp(object):
def __eq__(self, value):
"""Return self==value."""
@@ -4408,25 +6879,25 @@ class NodeStateReachability(object):
"""Return repr(self)."""
def get(
- self, node: NodeInput, default: Optional[list[Tuple[int, str]]] = None
- ) -> Optional[list[Tuple[int, str]]]:
+ self, node: NodeInput, default: Optional[HistoryTimestamp] = None
+ ) -> Optional[HistoryTimestamp]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[list[Tuple[int, str]]]): the default value. Defaults to None.
+ default (Optional[HistoryTimestamp]): the default value. Defaults to None.
Returns:
- Optional[list[Tuple[int, str]]]: the value for the node or the default value
+ Optional[HistoryTimestamp]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, list[Tuple[int, str]]]]:
+ def items(self) -> Iterator[Tuple[Node, HistoryTimestamp]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, list[Tuple[int, str]]]]: Iterator over items
+ Iterator[Tuple[Node, HistoryTimestamp]]: Iterator over items
"""
def nodes(self) -> Nodes:
@@ -4437,12 +6908,12 @@ class NodeStateReachability(object):
Nodes: The nodes
"""
- def sorted_by_id(self) -> NodeStateReachability:
+ def sorted_by_id(self) -> NodeStateHistoryTimestamp:
"""
Sort results by node id
Returns:
- NodeStateReachability: The sorted node state
+ NodeStateHistoryTimestamp: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -4456,15 +6927,15 @@ class NodeStateReachability(object):
DataFrame: the pandas DataFrame
"""
- def values(self) -> Iterator[list[Tuple[int, str]]]:
+ def values(self) -> Iterator[HistoryTimestamp]:
"""
Iterate over values
Returns:
- Iterator[list[Tuple[int, str]]]: Iterator over values
+ Iterator[HistoryTimestamp]: Iterator over values
"""
-class NodeStateListF64(object):
+class NodeStateHistoryDateTime(object):
def __eq__(self, value):
"""Return self==value."""
@@ -4496,25 +6967,25 @@ class NodeStateListF64(object):
"""Return repr(self)."""
def get(
- self, node: NodeInput, default: Optional[list[float]] = None
- ) -> Optional[list[float]]:
+ self, node: NodeInput, default: Optional[HistoryDateTime] = None
+ ) -> Optional[HistoryDateTime]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[list[float]]): the default value. Defaults to None.
+ default (Optional[HistoryDateTime]): the default value. Defaults to None.
Returns:
- Optional[list[float]]: the value for the node or the default value
+ Optional[HistoryDateTime]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, list[float]]]:
+ def items(self) -> Iterator[Tuple[Node, HistoryDateTime]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, list[float]]]: Iterator over items
+ Iterator[Tuple[Node, HistoryDateTime]]: Iterator over items
"""
def nodes(self) -> Nodes:
@@ -4525,12 +6996,12 @@ class NodeStateListF64(object):
Nodes: The nodes
"""
- def sorted_by_id(self) -> NodeStateListF64:
+ def sorted_by_id(self) -> NodeStateHistoryDateTime:
"""
Sort results by node id
Returns:
- NodeStateListF64: The sorted node state
+ NodeStateHistoryDateTime: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -4544,15 +7015,15 @@ class NodeStateListF64(object):
DataFrame: the pandas DataFrame
"""
- def values(self) -> Iterator[list[float]]:
+ def values(self) -> Iterator[HistoryDateTime]:
"""
Iterate over values
Returns:
- Iterator[list[float]]: Iterator over values
+ Iterator[HistoryDateTime]: Iterator over values
"""
-class NodeStateMotifs(object):
+class NodeStateHistoryEventId(object):
def __eq__(self, value):
"""Return self==value."""
@@ -4583,86 +7054,26 @@ class NodeStateMotifs(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateMotifs:
- """
- Compute the k smallest values
-
- Arguments:
- k (int): The number of values to return
-
- Returns:
- NodeStateMotifs: The k smallest values as a node state
- """
-
def get(
- self, node: NodeInput, default: Optional[list[int]] = None
- ) -> Optional[list[int]]:
+ self, node: NodeInput, default: Optional[HistoryEventId] = None
+ ) -> Optional[HistoryEventId]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[list[int]]): the default value. Defaults to None.
+ default (Optional[HistoryEventId]): the default value. Defaults to None.
Returns:
- Optional[list[int]]: the value for the node or the default value
+ Optional[HistoryEventId]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, list[int]]]:
+ def items(self) -> Iterator[Tuple[Node, HistoryEventId]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, list[int]]]: Iterator over items
- """
-
- def max(self) -> Optional[list[int]]:
- """
- Return the maximum value
-
- Returns:
- Optional[list[int]]: The maximum value or `None` if empty
- """
-
- def max_item(self) -> Optional[Tuple[Node, list[int]]]:
- """
- Return largest value and corresponding node
-
- Returns:
- Optional[Tuple[Node, list[int]]]: The Node and maximum value or `None` if empty
- """
-
- def median(self) -> PropValue:
- """
- Return the median value
-
- Returns:
- PropValue:
- Optional[list[int]]:
- """
-
- def median_item(self) -> Optional[Tuple[Node, list[int]]]:
- """
- Return median value and corresponding node
-
- Returns:
- Optional[Tuple[Node, list[int]]]: The median value or `None` if empty
- """
-
- def min(self) -> Optional[list[int]]:
- """
- Return the minimum value
-
- Returns:
- Optional[list[int]]: The minimum value or `None` if empty
- """
-
- def min_item(self) -> Optional[Tuple[Node, list[int]]]:
- """
- Return smallest value and corresponding node
-
- Returns:
- Optional[Tuple[Node, list[int]]]: The Node and minimum value or `None` if empty
+ Iterator[Tuple[Node, HistoryEventId]]: Iterator over items
"""
def nodes(self) -> Nodes:
@@ -4673,23 +7084,12 @@ class NodeStateMotifs(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateMotifs:
- """
- Sort by value
-
- Arguments:
- reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
-
- Returns:
- NodeStateMotifs: Sorted node state
- """
-
- def sorted_by_id(self) -> NodeStateMotifs:
+ def sorted_by_id(self) -> NodeStateHistoryEventId:
"""
Sort results by node id
Returns:
- NodeStateMotifs: The sorted node state
+ NodeStateHistoryEventId: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -4703,26 +7103,15 @@ class NodeStateMotifs(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateMotifs:
- """
- Compute the k largest values
-
- Arguments:
- k (int): The number of values to return
-
- Returns:
- NodeStateMotifs: The k largest values as a node state
- """
-
- def values(self) -> Iterator[list[int]]:
+ def values(self) -> Iterator[HistoryEventId]:
"""
Iterate over values
Returns:
- Iterator[list[int]]: Iterator over values
+ Iterator[HistoryEventId]: Iterator over values
"""
-class NodeStateHits(object):
+class NodeStateIntervals(object):
def __eq__(self, value):
"""Return self==value."""
@@ -4753,86 +7142,58 @@ class NodeStateHits(object):
def __repr__(self):
"""Return repr(self)."""
- def bottom_k(self, k: int) -> NodeStateHits:
- """
- Compute the k smallest values
-
- Arguments:
- k (int): The number of values to return
-
- Returns:
- NodeStateHits: The k smallest values as a node state
- """
-
def get(
- self, node: NodeInput, default: Optional[Tuple[float, float]] = None
- ) -> Optional[Tuple[float, float]]:
+ self, node: NodeInput, default: Optional[Intervals] = None
+ ) -> Optional[Intervals]:
"""
Get value for node
Arguments:
node (NodeInput): the node
- default (Optional[Tuple[float, float]]): the default value. Defaults to None.
+ default (Optional[Intervals]): the default value. Defaults to None.
Returns:
- Optional[Tuple[float, float]]: the value for the node or the default value
+ Optional[Intervals]: the value for the node or the default value
"""
- def items(self) -> Iterator[Tuple[Node, Tuple[float, float]]]:
+ def items(self) -> Iterator[Tuple[Node, Intervals]]:
"""
Iterate over items
Returns:
- Iterator[Tuple[Node, Tuple[float, float]]]: Iterator over items
- """
-
- def max(self) -> Optional[Tuple[float, float]]:
- """
- Return the maximum value
-
- Returns:
- Optional[Tuple[float, float]]: The maximum value or `None` if empty
- """
-
- def max_item(self) -> Optional[Tuple[Node, Tuple[float, float]]]:
- """
- Return largest value and corresponding node
-
- Returns:
- Optional[Tuple[Node, Tuple[float, float]]]: The Node and maximum value or `None` if empty
+ Iterator[Tuple[Node, Intervals]]: Iterator over items
"""
- def median(self) -> PropValue:
+ def max(self) -> NodeStateOptionI64:
"""
- Return the median value
+ Calculate the maximum interval in milliseconds for each node.
Returns:
- PropValue:
- Optional[Tuple[float, float]]:
+ NodeStateOptionI64: A NodeState with the computed maximum interval between consecutive timestamps for each node. The maximum is None if there is fewer than 1 interval.
"""
- def median_item(self) -> Optional[Tuple[Node, Tuple[float, float]]]:
+ def mean(self) -> NodeStateOptionF64:
"""
- Return median value and corresponding node
+ Calculate the mean interval in milliseconds for each node.
Returns:
- Optional[Tuple[Node, Tuple[float, float]]]: The median value or `None` if empty
+ NodeStateOptionF64: A NodeState with the computed mean interval between consecutive timestamps for each node. The mean is None if there is fewer than 1 interval.
"""
- def min(self) -> Optional[Tuple[float, float]]:
+ def median(self) -> NodeStateOptionI64:
"""
- Return the minimum value
+ Calculate the median interval in milliseconds for each node.
Returns:
- Optional[Tuple[float, float]]: The minimum value or `None` if empty
+ NodeStateOptionI64: A NodeState with the computed median interval between consecutive timestamps for each node. The median is None if there is fewer than 1 interval.
"""
- def min_item(self) -> Optional[Tuple[Node, Tuple[float, float]]]:
+ def min(self) -> NodeStateOptionI64:
"""
- Return smallest value and corresponding node
+ Calculate the minimum interval in milliseconds for each node.
Returns:
- Optional[Tuple[Node, Tuple[float, float]]]: The Node and minimum value or `None` if empty
+ NodeStateOptionI64: A NodeState with the computed minimum interval between consecutive timestamps for each node. The minimum is None if there is fewer than 1 interval.
"""
def nodes(self) -> Nodes:
@@ -4843,23 +7204,12 @@ class NodeStateHits(object):
Nodes: The nodes
"""
- def sorted(self, reverse: bool = False) -> NodeStateHits:
- """
- Sort by value
-
- Arguments:
- reverse (bool): If `True`, sort in descending order, otherwise ascending. Defaults to False.
-
- Returns:
- NodeStateHits: Sorted node state
- """
-
- def sorted_by_id(self) -> NodeStateHits:
+ def sorted_by_id(self) -> NodeStateIntervals:
"""
Sort results by node id
Returns:
- NodeStateHits: The sorted node state
+ NodeStateIntervals: The sorted node state
"""
def to_df(self) -> DataFrame:
@@ -4873,23 +7223,20 @@ class NodeStateHits(object):
DataFrame: the pandas DataFrame
"""
- def top_k(self, k: int) -> NodeStateHits:
+ def to_list(self) -> list[list[int]]:
"""
- Compute the k largest values
-
- Arguments:
- k (int): The number of values to return
+ Collect all intervals in milliseconds into a list for each node.
Returns:
- NodeStateHits: The k largest values as a node state
+ list[list[int]]: List of intervals in milliseconds for each node.
"""
- def values(self) -> Iterator[Tuple[float, float]]:
+ def values(self) -> Iterator[Intervals]:
"""
Iterate over values
Returns:
- Iterator[Tuple[float, float]]: Iterator over values
+ Iterator[Intervals]: Iterator over values
"""
class NodeStateSEIR(object):
diff --git a/python/python/raphtory/typing.py b/python/python/raphtory/typing.py
index ea32e40f6d..5da3eddb60 100644
--- a/python/python/raphtory/typing.py
+++ b/python/python/raphtory/typing.py
@@ -1,6 +1,6 @@
-from datetime import datetime
+from datetime import datetime, date
from typing import Union, Literal, Mapping
-
+import raphtory
PropValue = Union[
bool,
@@ -21,4 +21,6 @@
NodeInput = Union[int, str, "Node"]
-TimeInput = Union[int, str, float, datetime]
+TimeInput = Union[
+ int, str, float, datetime, date, raphtory.EventTime, raphtory.OptionalEventTime
+]
diff --git a/python/python/raphtory/vectors/__init__.pyi b/python/python/raphtory/vectors/__init__.pyi
index 75e6b0e6d7..c9011c1357 100644
--- a/python/python/raphtory/vectors/__init__.pyi
+++ b/python/python/raphtory/vectors/__init__.pyi
@@ -16,6 +16,8 @@ from raphtory.algorithms import *
from raphtory.node_state import *
from raphtory.graphql import *
from raphtory.typing import *
+import numpy as np
+from numpy.typing import NDArray
from datetime import datetime
from pandas import DataFrame
from os import PathLike
diff --git a/python/scripts/gen-stubs.py b/python/scripts/gen-stubs.py
index c896e29c92..ee83330014 100755
--- a/python/scripts/gen-stubs.py
+++ b/python/scripts/gen-stubs.py
@@ -11,6 +11,8 @@
"from raphtory.node_state import *",
"from raphtory.graphql import *",
"from raphtory.typing import *",
+ "import numpy as np",
+ "from numpy.typing import NDArray",
"from datetime import datetime",
"from pandas import DataFrame",
"from os import PathLike",
diff --git a/python/tests/expected/dataframe_output/node_df_no_datetime.json b/python/tests/expected/dataframe_output/node_df_no_datetime.json
index 84b16c5725..73b66d39e3 100644
--- a/python/tests/expected/dataframe_output/node_df_no_datetime.json
+++ b/python/tests/expected/dataframe_output/node_df_no_datetime.json
@@ -1 +1 @@
-{"name":{"0":"ServerA","1":"ServerB","2":"ServerC","3":"ServerD","4":"ServerE"},"type":{"0":"","1":"","2":"","3":"","4":""},"datasource":{"0":"data\/network_traffic_edges.csv","1":"data\/network_traffic_edges.csv","2":"data\/network_traffic_edges.csv","3":"data\/network_traffic_edges.csv","4":"data\/network_traffic_edges.csv"},"server_name":{"0":"Alpha","1":"Beta","2":"Charlie","3":"Delta","4":"Echo"},"hardware_type":{"0":"Blade Server","1":"Rack Server","2":"Blade Server","3":"Tower Server","4":"Rack Server"},"primary_function":{"0":[[1693555200000,"Database"],[1693555260000,"Database"],[1693555320000,"Database"]],"1":[[1693555500000,"Web Server"]],"2":[[1693555800000,"File Storage"]],"3":[[1693556100000,"Application Server"]],"4":[[1693556400000,"Backup"]]},"uptime_days":{"0":[[1693555200000,120],[1693555260000,121],[1693555320000,122]],"1":[[1693555500000,45]],"2":[[1693555800000,90]],"3":[[1693556100000,60]],"4":[[1693556400000,30]]},"OS_version":{"0":[[1693555200000,"Ubuntu 20.04"],[1693555260000,"Ubuntu 20.04"],[1693555320000,"Ubuntu 20.04"]],"1":[[1693555500000,"Red Hat 8.1"]],"2":[[1693555800000,"Windows Server 2022"]],"3":[[1693556100000,"Ubuntu 20.04"]],"4":[[1693556400000,"Red Hat 8.1"]]},"update_history":{"0":[1693555200000,1693555260000,1693555320000,1693555500000,1693556400000],"1":[1693555200000,1693555500000,1693555800000,1693556700000],"2":[1693555500000,1693555800000,1693556400000,1693557000000,1693557060000,1693557120000],"3":[1693555800000,1693556100000,1693557000000,1693557060000,1693557120000],"4":[1693556100000,1693556400000,1693556700000]}}
\ No newline at end of file
+{"name":{"0":"ServerA","1":"ServerB","2":"ServerC","3":"ServerD","4":"ServerE"},"type":{"0":"","1":"","2":"","3":"","4":""},"datasource":{"0":"data\/network_traffic_edges.csv","1":"data\/network_traffic_edges.csv","2":"data\/network_traffic_edges.csv","3":"data\/network_traffic_edges.csv","4":"data\/network_traffic_edges.csv"},"server_name":{"0":"Alpha","1":"Beta","2":"Charlie","3":"Delta","4":"Echo"},"hardware_type":{"0":"Blade Server","1":"Rack Server","2":"Blade Server","3":"Tower Server","4":"Rack Server"},"primary_function":{"0":[[1693555200000,"Database"],[1693555260000,"Database"],[1693555320000,"Database"]],"1":[[1693555500000,"Web Server"]],"2":[[1693555800000,"File Storage"]],"3":[[1693556100000,"Application Server"]],"4":[[1693556400000,"Backup"]]},"uptime_days":{"0":[[1693555200000,120],[1693555260000,121],[1693555320000,122]],"1":[[1693555500000,45]],"2":[[1693555800000,90]],"3":[[1693556100000,60]],"4":[[1693556400000,30]]},"OS_version":{"0":[[1693555200000,"Ubuntu 20.04"],[1693555260000,"Ubuntu 20.04"],[1693555320000,"Ubuntu 20.04"]],"1":[[1693555500000,"Red Hat 8.1"]],"2":[[1693555800000,"Windows Server 2022"]],"3":[[1693556100000,"Ubuntu 20.04"]],"4":[[1693556400000,"Red Hat 8.1"]]},"update_history":{"0":[1693555200000,1693555200000,1693555260000,1693555320000,1693555500000,1693556400000],"1":[1693555200000,1693555500000,1693555800000,1693556700000],"2":[1693555500000,1693555800000,1693556400000,1693557000000,1693557060000,1693557120000],"3":[1693555800000,1693556100000,1693556100000,1693557000000,1693557060000,1693557120000],"4":[1693556100000,1693556400000,1693556700000]}}
\ No newline at end of file
diff --git a/python/tests/expected/dataframe_output/node_df_no_explode.json b/python/tests/expected/dataframe_output/node_df_no_explode.json
index 22d3c89a49..cb25d5478f 100644
--- a/python/tests/expected/dataframe_output/node_df_no_explode.json
+++ b/python/tests/expected/dataframe_output/node_df_no_explode.json
@@ -1 +1 @@
-{"name":{"0":"ServerA","1":"ServerB","2":"ServerC","3":"ServerD","4":"ServerE"},"type":{"0":"","1":"","2":"","3":"","4":""},"server_name":{"0":"Alpha","1":"Beta","2":"Charlie","3":"Delta","4":"Echo"},"datasource":{"0":"data\/network_traffic_edges.csv","1":"data\/network_traffic_edges.csv","2":"data\/network_traffic_edges.csv","3":"data\/network_traffic_edges.csv","4":"data\/network_traffic_edges.csv"},"hardware_type":{"0":"Blade Server","1":"Rack Server","2":"Blade Server","3":"Tower Server","4":"Rack Server"},"uptime_days":{"0":[[1693555200000,120],[1693555260000,121],[1693555320000,122]],"1":[[1693555500000,45]],"2":[[1693555800000,90]],"3":[[1693556100000,60]],"4":[[1693556400000,30]]},"OS_version":{"0":[[1693555200000,"Ubuntu 20.04"],[1693555260000,"Ubuntu 20.04"],[1693555320000,"Ubuntu 20.04"]],"1":[[1693555500000,"Red Hat 8.1"]],"2":[[1693555800000,"Windows Server 2022"]],"3":[[1693556100000,"Ubuntu 20.04"]],"4":[[1693556400000,"Red Hat 8.1"]]},"primary_function":{"0":[[1693555200000,"Database"],[1693555260000,"Database"],[1693555320000,"Database"]],"1":[[1693555500000,"Web Server"]],"2":[[1693555800000,"File Storage"]],"3":[[1693556100000,"Application Server"]],"4":[[1693556400000,"Backup"]]},"update_history":{"0":[1693555200000,1693555260000,1693555320000,1693555500000,1693556400000],"1":[1693555200000,1693555500000,1693555800000,1693556700000],"2":[1693555500000,1693555800000,1693556400000,1693557000000,1693557060000,1693557120000],"3":[1693555800000,1693556100000,1693557000000,1693557060000,1693557120000],"4":[1693556100000,1693556400000,1693556700000]}}
\ No newline at end of file
+{"name":{"0":"ServerA","1":"ServerB","2":"ServerC","3":"ServerD","4":"ServerE"},"type":{"0":"","1":"","2":"","3":"","4":""},"server_name":{"0":"Alpha","1":"Beta","2":"Charlie","3":"Delta","4":"Echo"},"datasource":{"0":"data\/network_traffic_edges.csv","1":"data\/network_traffic_edges.csv","2":"data\/network_traffic_edges.csv","3":"data\/network_traffic_edges.csv","4":"data\/network_traffic_edges.csv"},"hardware_type":{"0":"Blade Server","1":"Rack Server","2":"Blade Server","3":"Tower Server","4":"Rack Server"},"uptime_days":{"0":[[1693555200000,120],[1693555260000,121],[1693555320000,122]],"1":[[1693555500000,45]],"2":[[1693555800000,90]],"3":[[1693556100000,60]],"4":[[1693556400000,30]]},"OS_version":{"0":[[1693555200000,"Ubuntu 20.04"],[1693555260000,"Ubuntu 20.04"],[1693555320000,"Ubuntu 20.04"]],"1":[[1693555500000,"Red Hat 8.1"]],"2":[[1693555800000,"Windows Server 2022"]],"3":[[1693556100000,"Ubuntu 20.04"]],"4":[[1693556400000,"Red Hat 8.1"]]},"primary_function":{"0":[[1693555200000,"Database"],[1693555260000,"Database"],[1693555320000,"Database"]],"1":[[1693555500000,"Web Server"]],"2":[[1693555800000,"File Storage"]],"3":[[1693556100000,"Application Server"]],"4":[[1693556400000,"Backup"]]},"update_history":{"0":[1693555200000,1693555200000,1693555260000,1693555320000,1693555500000,1693556400000],"1":[1693555200000,1693555500000,1693555800000,1693556700000],"2":[1693555500000,1693555800000,1693556400000,1693557000000,1693557060000,1693557120000],"3":[1693555800000,1693556100000,1693556100000,1693557000000,1693557060000,1693557120000],"4":[1693556100000,1693556400000,1693556700000]}}
\ No newline at end of file
diff --git a/python/tests/expected/dataframe_output/node_df_no_hist.json b/python/tests/expected/dataframe_output/node_df_no_hist.json
index 766fcacc20..a450012e5f 100644
--- a/python/tests/expected/dataframe_output/node_df_no_hist.json
+++ b/python/tests/expected/dataframe_output/node_df_no_hist.json
@@ -1 +1 @@
-{"name":{"0":"ServerA","1":"ServerB","2":"ServerC","3":"ServerD","4":"ServerE"},"type":{"0":"","1":"","2":"","3":"","4":""},"server_name":{"0":"Alpha","1":"Beta","2":"Charlie","3":"Delta","4":"Echo"},"hardware_type":{"0":"Blade Server","1":"Rack Server","2":"Blade Server","3":"Tower Server","4":"Rack Server"},"datasource":{"0":"data\/network_traffic_edges.csv","1":"data\/network_traffic_edges.csv","2":"data\/network_traffic_edges.csv","3":"data\/network_traffic_edges.csv","4":"data\/network_traffic_edges.csv"},"primary_function":{"0":"Database","1":"Web Server","2":"File Storage","3":"Application Server","4":"Backup"},"uptime_days":{"0":122,"1":45,"2":90,"3":60,"4":30},"OS_version":{"0":"Ubuntu 20.04","1":"Red Hat 8.1","2":"Windows Server 2022","3":"Ubuntu 20.04","4":"Red Hat 8.1"},"update_history":{"0":[1693555200000,1693555260000,1693555320000,1693555500000,1693556400000],"1":[1693555200000,1693555500000,1693555800000,1693556700000],"2":[1693555500000,1693555800000,1693556400000,1693557000000,1693557060000,1693557120000],"3":[1693555800000,1693556100000,1693557000000,1693557060000,1693557120000],"4":[1693556100000,1693556400000,1693556700000]}}
\ No newline at end of file
+{"name":{"0":"ServerA","1":"ServerB","2":"ServerC","3":"ServerD","4":"ServerE"},"type":{"0":"","1":"","2":"","3":"","4":""},"server_name":{"0":"Alpha","1":"Beta","2":"Charlie","3":"Delta","4":"Echo"},"hardware_type":{"0":"Blade Server","1":"Rack Server","2":"Blade Server","3":"Tower Server","4":"Rack Server"},"datasource":{"0":"data\/network_traffic_edges.csv","1":"data\/network_traffic_edges.csv","2":"data\/network_traffic_edges.csv","3":"data\/network_traffic_edges.csv","4":"data\/network_traffic_edges.csv"},"primary_function":{"0":"Database","1":"Web Server","2":"File Storage","3":"Application Server","4":"Backup"},"uptime_days":{"0":122,"1":45,"2":90,"3":60,"4":30},"OS_version":{"0":"Ubuntu 20.04","1":"Red Hat 8.1","2":"Windows Server 2022","3":"Ubuntu 20.04","4":"Red Hat 8.1"},"update_history":{"0":[1693555200000,1693555200000,1693555260000,1693555320000,1693555500000,1693556400000],"1":[1693555200000,1693555500000,1693555800000,1693556700000],"2":[1693555500000,1693555800000,1693556400000,1693557000000,1693557060000,1693557120000],"3":[1693555800000,1693556100000,1693556100000,1693557000000,1693557060000,1693557120000],"4":[1693556100000,1693556400000,1693556700000]}}
\ No newline at end of file
diff --git a/python/tests/notebook.ipynb b/python/tests/notebook.ipynb
index e24d76744c..3a9ce58e08 100644
--- a/python/tests/notebook.ipynb
+++ b/python/tests/notebook.ipynb
@@ -2,210 +2,16 @@
"cells": [
{
"cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "import pandas as pd\n",
- "import matplotlib.pyplot as plt\n",
- "import seaborn as sns\n",
- "import tempfile"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Basic functionality on a graph\n",
- "\n",
- "After importing a Raphtory graph we can create a blank one to work with:\n",
- "\n",
- "* Graphs in Raphtory are directed by default\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "pycharm": {
- "name": "#%%\n"
- }
- },
- "outputs": [
- {
- "data": {
- "text/plain": [
- "Graph(number_of_nodes=0, number_of_edges=0, number_of_temporal_edges=0, earliest_time=None, latest_time=None)"
- ]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "from raphtory import Graph\n",
- "\n",
- "g = Graph()\n",
- "g"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "NestedArcStringVecIterable([[[_default], [layer1], [layer2]], [[_default]], [[layer1]], [[layer2]]])"
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "g.add_edge(0, \"1\", \"2\")\n",
- "g.add_edge(0, \"1\", \"3\", layer=\"layer1\")\n",
- "g.add_edge(0, \"1\", \"4\", layer=\"layer2\")\n",
- "\n",
- "g.nodes.edges.layer_names"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Once we have a new graph we can add nodes and edges to it via `add_node()` and `add_edge()`. For these:\n",
- "* The ids of nodes and the source/destination of an edge can be either strings or integers\n",
- "* All additions into the graph must happen at a specific time - this means updates are also additions\n",
- "* If you add an edge between nodes which do no exist in the graph yet, these will be automatically created\n",
- "* Properties can be added onto nodes and edges - this is a dict of any value, but the keys must be strings\n",
- "* We have a special type of `static property` which exists outside of the timeline and is always accessible. \n",
- "* Additions can be completed out of order, making it very easy to merge datasets together\n",
- "\n",
- "\n",
- "We can then check the state of the graph:\n",
- "* To see if a node or edge exists you can use `has_node()` and `has_edge()`\n",
- "* To get the earliest and latest times at which updates have been applied to the graph you can use `earliest_time()` and `latest_time()` - if no updates have been applied these will return `None`\n",
- "* To get the total number of nodes and edges of a graph you can use `num_edges()` and `num_nodes()`."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "True True False\n",
- "True False\n",
- "702 142\n",
- "True True False\n",
- "True False\n",
- "703 144\n",
- "Node(name=Ben, earliest_time=5, latest_time=8)\n",
- "Edge(source=Haaroon, target=Hamza, earliest_time=7, latest_time=7, properties={property3: test, property1: 1, property2: 9.8, First-Met: {ArcStr(\"toad\"): Str(ArcStr(\"01/01/1990\"))}})\n",
- "Graph(number_of_nodes=146, number_of_edges=705, number_of_temporal_edges=2653, earliest_time=0, latest_time=32674)\n",
- "True\n"
- ]
- }
- ],
- "source": [
- "# Basic Addition of Nodes and Edges\n",
- "g.add_node(timestamp=1, id=\"10\")\n",
- "g.add_edge(timestamp=2, src=\"1\", dst=\"2\")\n",
- "\n",
- "# checking node 10, 1 and 5 exist\n",
- "print(g.has_node(\"10\"), g.has_node(\"1\"), g.has_node(\"5\"))\n",
- "# checking edge 1,2 exists and 2,1 doesn't as Raphtory is directed\n",
- "print(g.has_edge(\"1\", \"2\"), g.has_edge(\"2\", \"1\"))\n",
- "# Check the total number of edges and nodes\n",
- "print(g.count_edges(), g.count_nodes())\n",
- "\n",
- "# Adding nodes and edges with String IDs\n",
- "g.add_node(timestamp=5, id=\"Ben\")\n",
- "g.add_edge(timestamp=8, src=\"Hamza\", dst=\"Ben\", layer=\"toad\")\n",
- "\n",
- "# Performing the same checks as before, but with strings\n",
- "print(g.has_node(id=\"Ben\"), g.has_node(id=\"Hamza\"), g.has_node(id=\"Dave\"))\n",
- "print(g.has_edge(src=\"Hamza\", dst=\"Ben\"), g.has_edge(src=\"Ben\", dst=\"Hamza\"))\n",
- "print(g.count_edges(), g.count_nodes())\n",
- "\n",
- "g.add_edge(0, \"1\", \"3\", layer=\"toad\")\n",
- "# Add an edge with Temporal Properties which can change over time\n",
- "e = g.add_edge(\n",
- " timestamp=7,\n",
- " src=\"Haaroon\",\n",
- " dst=\"Hamza\",\n",
- " properties={\"property1\": 1, \"property2\": 9.8, \"property3\": \"test\"},\n",
- " layer=\"toad\",\n",
- ")\n",
- "# Add a static property which is immutable\n",
- "e.add_metadata(metadata={\"First-Met\": \"01/01/1990\"})\n",
- "\n",
- "# Add an node with Temporal Properties which can change over time\n",
- "v = g.add_node(\n",
- " timestamp=5,\n",
- " id=\"Hamza\",\n",
- " properties={\"property1\": 5, \"property2\": 12.5, \"property3\": \"test2\"},\n",
- ")\n",
- "# Add a static property which is immutable\n",
- "v.add_metadata(metadata={\"Date-of-Birth\": \"01/01/1990\"})\n",
- "print(g.node(\"Ben\").__repr__())\n",
- "print(g.edge(\"Haaroon\", \"Hamza\").__repr__())\n",
- "print(g.__repr__())\n",
- "g_path = tempfile.mkdtemp()\n",
- "g.save_to_file(g_path)\n",
- "loaded_graph = Graph.load_from_file(g_path)\n",
- "print(loaded_graph.has_node(\"Hamza\"))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "[['_default'], ['layer1'], ['layer2']]"
- ]
- },
- "execution_count": 5,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "list(g.edges.layer_names)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "g.nodes.edges.start"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
+ "execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- ""
+ ""
]
},
- "execution_count": 7,
+ "execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
@@ -221,209 +27,11 @@
"\n",
"g.to_networkx()"
]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "/Users/shivamkapoor/opt/miniconda3/envs/pyraphtory/lib/python3.10/site-packages/seaborn/_oldcore.py:1498: FutureWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, CategoricalDtype) instead\n",
- " if pd.api.types.is_categorical_dtype(vector):\n",
- "/Users/shivamkapoor/opt/miniconda3/envs/pyraphtory/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.\n",
- " with pd.option_context('mode.use_inf_as_na', True):\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- ""
- ]
- },
- "execution_count": 8,
- "metadata": {},
- "output_type": "execute_result"
- },
- {
- "data": {
- "image/png": "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/173339/TTZbid1u9wafCtdcc40kKTc313u7Kz8/X1deeaV3nfz8fLVr165W+3a56uYFYMnyThS3fvzj7InjlqwfA1H9vwADtd9gQf/0T//0bzKTjkGNbvBZllXpP7vdrqSkJE2fPl0PPfSQX4qbNGmShg0b5jO2detWSdLVV1+t9u3bq2nTpsrMzPQuLykp0bZt2y75rfYAAMAMNbryc/aHENalG2+8UWPGjNHixYv161//Wnv27NH06dN18803KzExUZI0ZMgQzZs3T7GxsWrVqpXmzp2rhIQE9evXr15qBAAADUut5vx8+umn+uKLL1RSUqLY2Fh169ZNvXr18ldt6tu3rxYuXKhly5Zp+fLlioqK0i233OJzZWn8+PFyuVyaOnWqTp06pdTUVK1YsUIhISF+qwMAADQeNuvsCSfVVF5erjFjxmjDhg1yOByKiYlRUVGRPB6PevTooaVLlzboT1d2uz0qLDzh9+06nXa9uj5H+w6VSJJ+3rGl8o6c1N5DP70z7aqW0Rp8Q1K93nd1Ou2KiYlUUdEJY+73no3+6Z/+6d/U/qXGcwxiYyOrPeG5RnN+Fi1apE2bNmnOnDn66quvtGHDBv3rX//SU089pS1btuj555+vyWYBAADqXI3Cz1/+8heNHTtWv/71r73fs+V0OnXrrbdq7Nixeu+99/xaJAAAgL/UKPwUFhaqQ4cOVS7r0KGD8vLyalUUAABAXalR+Lnyyiu1adOmKpdlZWVV+roJAACAYFGjd3v993//t2bPnq3w8HANGDBAzZo1U0FBgf7yl79o+fLlGjt2rL/rBAAA8IsahZ8777xT27Zt07x58/TMM894xy3L0sCBAzVy5Ei/FQgAAOBPNQo/5eXlmjlzpu6991598cUXOnr0qGw2m66//nrvhw8CAAAEo0ua87Nz504NGjRIL730kiQpMTFRd955p+666y49++yzevjhh7Vnz546KRQAAMAfqh1+Dh48qLvvvlsFBQVq06aNz7KQkBBNmjRJxcXFuuuuu3i3FwAACFrVDj/Lli3T5Zdfrrfeekv9+/f3WRYREaFhw4bpjTfeUFhYmJYuXer3QgEAAPyh2uFn48aNuu+++xQbG3vedZo3b657771Xn332mV+KAwAA8Ldqh5/8/Hy1bt36ouslJSUpNze3NjUBAADUmWqHn9jYWOXn5190vaKi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TbQ++6AOFRz3jl3XNk422QJYFQAAgUf4acRyC3xvcSXERQawGgAAggPhpwHilhYAADVH+GmAuKUFAEDNEX4aKG5pAQBQM9wrAQAARiH8AAAAoxB+AACAUQg/AADAKIQfAABgFMIPAAAwCuEHAAAYhfADAACMwocc4qKczsoZ2eXyBKASAABqj/CDSs4OOw6HXeuzDujQkRPesZZxkcpIaUUAAgA0SIQf+HA67fpk8/fesHNd2zjlHjnp81UaAAA0ZIQfVHLoyE/fG8Z3hgEAGhsmPAMAAKMQfgAAgFEIPwAAwCiEHwAAYBTCDwAAMArhBwAAGIW3uuOS2e02ORy+uZkPPAQANBSEH1yyFjFNtD77oA4VHJfEJz4DABoWwg9qJLfgBJ/6DABokJjzAwAAjEL4AQAARuG2l8HOnbh87iTmQDn7W+UlJlMDAPyL8GOwionLuQUn5HDa5XZ51KFtrGyyBaymc79VnsnUAAB/I/wYLrfghPbllsjpdMjlcis+ronftn3uFRypeldxzv5W+UvZdk32BQAwD+EHdeLcKziS/67inLvt69rGqbCkzPvWe3/uCwDQ+BB+UGtVfeihw2Gv1hWcmjp72wlxkco7cpK33gMAqoXwg1o790MPpTNXY6ozd+jc21fBMukaANB4EX7gF+d+6GFCXGSldap6d9n6rAM+t8aqG5oAAKgpwg/qzblXiK5rG6fcc25XVRWa/KWmE7Cruy1/bbs+OZ12byB1OOx1WrM/jz8A1EajCD8ej0eLFy/W66+/rmPHjik1NVWPP/64fvaznwW6NJzj7CtEdRl0znWhCdi13VZDnXBd0UfukZNyOO1qfnmEMrr+W53UXJcT4AHgUjWK8PPcc89pzZo1mj17thISEjR37lzdd999eu+99xQaGhro8hAAVd1i8+cE7ItNuK5qEngw/iN/6MgJ7c89JqfTIXcd13fu8W8oxwhA49Pgw095eblefPFFTZw4UX369JEkLViwQL169dKHH36om2++ObAFolbO906yi6nqFlt9ziU6d/+tmjVV3+5XyO2+9H/cqwoEjeFTsM89Rv68EhSMt9gawzkDGosGH3527NihEydOqGfPnt6x6OhodejQQVlZWYSfBq427yS72C226n69R03/kTp3/1X1cfbtsurePjv3FtL5glVd/eNa3WBx9nrnO7bnTpSvzv6qs69zJ9JXN1jVNDRdrMbanLNL7f98CFu+Ko5Zbee8BWPQDjbBeIxslmVZAa2glj788EONGzdO//rXvxQeHu4df/DBB3Xq1CktXbr0krdpWZY8Hv8fFptNKi1zy/XjL7zQEIc8Hsv7uKqx6qxT0+ed/dhmkyyr9tupyxrrYh2bJLdlySbJkuR02GV5LLl/PP8Ou02hIY4fl3rPpErLXPVSo9NhV0SY86L7t0nems9f97l+2o7NJjnsVe2r8nPKT7ursS/f9ZzOM/+wXKj/8/V69nZquq+qt/3T8+12mzweq5q9XfiYVPc1U71zVrP+z34NX7yPn/q/cJ+NyVnHzCbZJNlt1TnXF9jOj6r3mgk2dfkaON8xssvf6cNut8lmq94V/gZ/5ae0tFSSKs3tCQsL09GjR2u0TZvNJoejbm6RREbwOTYNk+/rITIipIHs/8Kv46q3c+HnhIed79eG7YLrhYU4Lrjd6m6ndvs6f292u63avZ2rOjXW9JzVpP/qbvtsdrvtgssbm5qe67raTjCoq9dA9V+f9afB/0tccbWnvLzcZ7ysrEwRERGBKAkAAASxBh9+WrZsKUnKz8/3Gc/Pz1d8fHwgSgIAAEGswYef9u3bq2nTpsrMzPSOlZSUaNu2bUpNTQ1gZQAAIBgF3424SxQaGqohQ4Zo3rx5io2NVatWrTR37lwlJCSoX79+gS4PAAAEmQYffiRp/Pjxcrlcmjp1qk6dOqXU1FStWLFCISH1PSkVAAAEuwb/VncAAIBL0eDn/AAAAFwKwg8AADAK4QcAABiF8AMAAIxC+AEAAEYh/AAAAKMQfgAAgFEIPwYpLi7W448/rvT0dKWkpOjOO+9Udna2d/nw4cPVrl07n/+GDh0awIr9Ky8vr1J/7dq109q1ayVJ27dv15AhQ9SlSxdlZGTolVdeCXDF/pWZmVll/+3atVPfvn0lSc8//3yVyxu6pUuXVnotX+x8ezwe/eEPf1CvXr3UpUsX3X///Tpw4EB9lu03VfX/ySefaNCgQeratasyMjL09NNP69SpU97lmzZtqvK1cPZXCTUUVfU/derUSr1lZGR4lzfm8z906NDz/i54++23JUlut1udOnWqtHzRokUB6sLPLBhj+PDh1s0332xlZWVZ3333nfXkk09anTp1sr799lvLsiyrZ8+e1po1a6z8/Hzvf0VFRYEt2o/+/ve/W8nJyVZeXp5Pj6WlpVZhYaH185//3Jo8ebK1e/du64033rCSk5OtN954I9Bl+01ZWZlP3/n5+daHH35otWvXztvngw8+aD366KOV1mvIVq9ebbVv394aMmSId6w653vRokXWz3/+c+tvf/ubtX37duvee++1+vXrZ5WVlQWijRqrqv+srCzr2muvtZ5//nlrz5491t///ncrPT3d+u1vf+td59VXX7Wuv/76Sq+FxtC/ZVnW7bffbs2fP9+ntyNHjniXN+bzX1RU5NN3Xl6eddddd1kDBgywjh8/blmWZe3evdtKSkqytm/f7rNuxfKGjvBjiL1791pJSUlWdna2d8zj8VjXX3+9tXDhQqugoMBKSkqyvvnmmwBWWbeWLVtm3XLLLVUuW7JkifXLX/7SOn36tHfsmWeesfr161df5dW7EydOWL/61a98/sG76aabrJdeeilwRflRbm6uNWrUKKtLly5W//79fX75X+x8l5WVWV27drVeffVV7/KjR49anTp1st577736a6IWLtT/I488Yg0bNsxn/bfeesu67rrrvP+4T5s2zfrNb35TrzX704X693g8VpcuXawPP/ywyuc29vN/rlWrVlkdO3b0/h9hy7Ks999/30pJSamPUgOC216GiImJ0bJly5ScnOwds9lsstlsKikp0c6dO2Wz2dSmTZsAVlm3du7cqcTExCqXZWdnKy0tTU7nT19316NHD+3du1cFBQX1VWK9WrJkiUpLS/XYY49JksrLy7V37161bds2wJX5xzfffKOQkBC9++676ty5s8+yi53vHTt26MSJE+rZs6d3eXR0tDp06KCsrKx666E2LtT/vffe6z3vFex2u06fPq3jx49LuvD/XhqCC/W/f/9+nTx58ryv9cZ+/s9WWFiohQsXavTo0T7Ho6Gf/4tpFF9siouLjo5W7969fcbWrVunffv2acqUKcrJyVFUVJSmT5+uzz77TE2aNFH//v01ZswYhYaGBqhq/8rJyVFMTIwGDx6sPXv26KqrrtLo0aOVnp6u3NxcJSUl+azfokULSdKhQ4fUrFmzQJRcZwoLC7Vy5Uo98sgjuvzyyyVJu3fvltvt1rp16zRz5kyVlZUpNTVVjz76qPdYNCQZGRk+czjOdrHznZubK0lq2bJlpXUqlgW7C/XfoUMHn8enT5/WypUr1bFjR8XGxkqSdu3apZiYGN12223Ky8tTUlKSJkyYoE6dOtV57f5wof5zcnIkSatWrdKnn34qu92u9PR0TZgwQVFRUY3+/J9t+fLlCg8P14gRI3zGc3Jy5HK5NGLECO3YsUPx8fG655579B//8R91VXK94sqPoTZv3qzJkyerX79+6tOnj3JyclRWVqZOnTrphRde0OjRo/X6669r6tSpgS7VL1wul7777jsdPXpU48aN07Jly9SlSxeNHDlSGzdu1KlTpyqFvLCwMElSWVlZIEquU2vWrFFUVJT+67/+yztW8Q9CRESEnn32Wc2cOVPfffed7r77bp+JsI3Bxc53aWmpJFW5TmN7PbhcLk2aNEm7du3StGnTJJ0JgMeOHdPJkyc1depUPffcc2rWrJmGDBmi3bt3B7ji2svJyZHdbleLFi20ZMkS/fa3v9WGDRs0ZswYeTweY87/8ePH9ec//1kjRozwvv4r7Nq1S8XFxRo6dKhWrFihG2+8UZMnT9Ybb7wRoGr9iys/Bvroo480ceJEpaSkaN68eZKk6dOn67HHHtNll10mSUpKSlJISIgmTJigSZMmNfgrH06nU5mZmXI4HAoPD5ckdezYUbt27dKKFSsUHh6u8vJyn+dU/JJr0qRJvddb195++23deuut3mMhSbfeeqvS09O9/89fkq655hqlp6frk08+0b//+78HotQ6cbHzXXFcysvLfY5RWVmZIiIi6q/QOnb8+HE99NBD+uKLL7R48WLvVZ2WLVsqKytLERERCgkJkSQlJydr27ZtWrVqlZ588slAll1ro0eP1l133aWYmBhJZ37fNW/eXHfccYe2bt1qzPn/6KOPVF5erkGDBlVa9pe//EVut1uRkZGSpPbt2+uHH37QihUrdPvtt9d3qX7HlR/DrF69WuPGjdOvfvUrLVmyxJv2nU6nN/hUuOaaaySpwVzmvZjIyEifX2TSmR7z8vKUkJCg/Px8n2UVj+Pj4+utxvqwY8cOHThwQLfcckulZWcHH+nMZf7LL7+80bwGKlzsfFfc7qhqncbyesjPz9fgwYO1ZcsWrVixotJt8ejoaG/wkc7MCUpMTFReXl59l+p3drvdG3wqnP37zoTzL50JP71791Z0dHSlZeHh4d7gUyEpKanR/C4g/BhkzZo1mjFjhgYPHqz58+f7XNIdOnSoJk+e7LP+1q1bFRISotatW9dzpf63a9cupaSkVPqMkq+//lpXX321UlNTtWnTJrndbu+yzz//XG3atFFcXFx9l1unsrOzFRcXp/bt2/uML1iwQDfeeKMsy/KOHTx4UEVFRbr66qvru8w6dbHz3b59ezVt2tTn9VJSUqJt27YpNTU1ECX71dGjR3XPPfeosLBQr776aqWePv30U3Xt2tXnc21cLpd27NjRKF4LkyZN0rBhw3zGtm7dKkm6+uqrG/35r5Cdne0zqbtCSUmJ0tLSvJ+BVmHr1q3ekNjQEX4MsWfPHs2aNUs33HCDRo0apYKCAh0+fFiHDx/WsWPHdOONN+qdd97Rn/70Jx04cEAffPCB5syZoxEjRqhp06aBLr/WEhMT1bZtW02fPl3Z2dn69ttv9dRTT2nLli0aPXq0Bg0apOPHj+t3v/uddu/erbVr12rlypUaNWpUoEv3u23btlX5wYU33HCDvv/+ez3xxBPas2ePsrKyNG7cOKWkpKhXr14BqLTuXOx8h4aGasiQIZo3b54+/vhj7dixQxMmTFBCQoL69esX4Opr76mnntKBAwc0d+5cxcbGen8XHD58WG63WykpKYqJidFjjz2mr7/+Wjt37tRjjz2m4uLiSqGhIbrxxhu1ceNGLV68WPv379c//vEPTZkyRTfffLMSExMb/fmXzszrKioqqvR/gqQzV/169OihBQsW6B//+If27t2rZcuW6d1339W4ceMCUK3/MefHEOvWrdPp06e1fv16rV+/3mfZwIEDNXv2bNlsNq1atUqzZs1S8+bNNWzYMI0cOTJAFfuX3W7XkiVL9Mwzz+ihhx5SSUmJOnTooJdeesn7rp8XXnhBM2fO1MCBA9W8eXNNmjRJAwcODHDl/nf48GHvO7zO1rFjRy1fvlzPPvusbrvtNoWGhqpv37567LHHZLPZ6r/QOhQXF3fR8z1+/Hi5XC5NnTpVp06dUmpqqlasWOFzK6ghcrvd+uCDD3T69Gndc889lZZ//PHHuuKKK7Ry5UrNmzdPI0aMUFlZmbp166bVq1c3+Pl/ktS3b18tXLhQy5Yt0/LlyxUVFaVbbrlFDz30kHedxnr+Kxw+fFiSqvxdIEmzZs3SokWLNG3aNB05ckSJiYneT7xuDGzW2de4AQAAGjluewEAAKMQfgAAgFEIPwAAwCiEHwAAYBTCDwAAMArhBwAAGIXwAwAAjEL4ARD01q5dq3bt2ungwYOBLgVAI0D4AQAARiH8AAAAoxB+AAQVj8ej5557Tn369FHnzp01ZswYHT161GednJwcjRo1SikpKUpJSdEDDzzg8w3kkvTtt9/q/vvvV0pKin7xi19owYIFmjx5soYOHepdp127dlq8eLFuu+02derUSYsXL5Yk/fDDD3r44YeVlpamzp0765577tG2bdt8tl9WVqY5c+aod+/e6tixo2655RZ98MEHdXRUAPgT3+0FIKg8/fTTeuWVVzR69Gh17txZ//u//6t3331Xp0+f1scff6zTp09r0KBBatu2rUaNGiWXy6Xnn39ehYWFeueddxQXF6fCwkINGDBAcXFxGjdunNxut5599ln98MMP6tKli1atWiXpTPgJCQnRI488ojZt2qhVq1aKi4vTrbfeqoiICI0dO1YRERF6+eWX9fXXX+uNN95QYmKiLMvS/fffr82bN2v8+PFKTEzU+vXr9dprr+npp5/WrbfeGtiDCODCLAAIEkePHrWuu+46a+7cuT7jI0aMsJKSkqwDBw5YDz/8sPWLX/zCOnbsmHd5UVGR1a1bN2v27NmWZVnWwoULreTkZCs3N9e7zsGDB63rrrvOGjJkiHcsKSnJuueee3z2NX/+fCs5Odk6ePCgd6ysrMzq27evNW7cOMuyLGvDhg1WUlKS9f777/s8d+LEidb/+3//zzp9+nTtDgSAOsVtLwBBY8uWLTp9+rR+9atf+YzfdNNN3p8///xzpaWlKTw8XC6XSy6XS02bNlX37t31f//3f951unbtqvj4eO/zWrVqpa5du1ba57XXXuvzeOPGjbr22msVHx/v3b7dbld6erp3+xs3bpTNZlPv3r2967hcLmVkZOjw4cPatWuX344JAP9zBroAAKhQMbcnJibGZ7x58+ben4uLi/XBBx9UOb8mNjZWklRYWKjrrruu0vJmzZqpoKDAZ6xJkyY+j4uLi7Vv374qny9JpaWlKi4ulmVZSklJqXKd/Pz8SqEKQPAg/AAIGhWh58iRI2rbtq13vLi42PtzVFSUfvGLX2j48OGVnu90nvmVlpCQUCnkVGz3YqKiopSWlqZJkyZVuTw0NFRRUVFq0qSJXnnllSrXueqqqy66HwCBw20vAEGja9euCg8P11//+lef8b/97W/en9PS0rR7925de+21Sk5OVnJysjp27KiVK1dq/fr1kqTU1FRt2bJFhw8f9j4vPz9fW7ZsuWgNaWlp2rNnj9q0aePdfnJyst555x298cYbcjgcSktL08mTJ2VZls86OTk5+uMf/yiXy+WfAwKgThB+AASNyMhIjRkzRmvWrNG8efO0YcMGzZw50yf8jBkzRvv379eoUaP00Ucf6Z///KfGjRun999/X+3bt5ck3X333YqMjNSIESO0bt06rVu3Tvfff79Onz4tm812wRqGDRsmj8ejYcOG6YMPPtDGjRv1+9//XqtWrVKbNm0kSb1791Zqaqq31szMTC1fvlxPPPGE7Ha79/YbgODEW90BBJ1Vq1bp5ZdfVl5enrp27aqbbrpJTzzxhD7++GNdccUV+uabb7RgwQJt3rxZlmUpKSlJI0eOVN++fb3b2LVrl2bOnKkvv/xSkZGRuuuuu7RhwwZdfvnlWrJkiaQzb3UfO3asxo0b57P//fv365lnntHGjRtVVlam1q1ba+jQobr99tu965w8eVLPPvus/vrXv+rIkSOKj4/XgAED9MADDygsLKx+DhSAGiH8AGh0/vWvf6m4uFi9e/f2jrlcLvXp00cDBgzQ5MmTA1gdgEBjwjOARueHH37QhAkT9MADDygtLU2lpaV67bXXdOzYMd1xxx2BLg9AgHHlB0Cj9Kc//Ulr1qzRgQMHFBISos6dO+vBBx9UcnJyoEsDEGCEHwAAYBTe7QUAAIxC+AEAAEYh/AAAAKMQfgAAgFEIPwAAwCiEHwAAYBTCDwAAMArhBwAAGIXwAwAAjPL/AXNlE7/xOS7+AAAAAElFTkSuQmCC",
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "from raphtory import graph_gen\n",
- "\n",
- "g = Graph()\n",
- "graph_gen.ba_preferential_attachment(g, nodes_to_add=1000, edges_per_step=10)\n",
- "view = g.window(0, 1000)\n",
- "\n",
- "ids = []\n",
- "degrees = []\n",
- "for v in view.nodes:\n",
- " ids.append(v.id)\n",
- " degrees.append(v.degree())\n",
- "\n",
- "df = pd.DataFrame.from_dict({\"id\": ids, \"degree\": degrees})\n",
- "\n",
- "sns.set()\n",
- "sns.histplot(df.degree)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {
- "pycharm": {
- "name": "#%%\n"
- }
- },
- "outputs": [],
- "source": [
- "from raphtory import Graph\n",
- "from raphtory import algorithms\n",
- "from raphtory import graph_loader\n",
- "\n",
- "g = graph_loader.lotr_graph()\n",
- "views_l1 = g.rolling(1000)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "/Users/shivamkapoor/opt/miniconda3/envs/pyraphtory/lib/python3.10/site-packages/seaborn/_oldcore.py:1498: FutureWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, CategoricalDtype) instead\n",
- " if pd.api.types.is_categorical_dtype(vector):\n",
- "/Users/shivamkapoor/opt/miniconda3/envs/pyraphtory/lib/python3.10/site-packages/seaborn/_oldcore.py:1498: FutureWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, CategoricalDtype) instead\n",
- " if pd.api.types.is_categorical_dtype(vector):\n",
- "/Users/shivamkapoor/opt/miniconda3/envs/pyraphtory/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.\n",
- " with pd.option_context('mode.use_inf_as_na', True):\n",
- "/Users/shivamkapoor/opt/miniconda3/envs/pyraphtory/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.\n",
- " with pd.option_context('mode.use_inf_as_na', True):\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- ""
- ]
- },
- "execution_count": 10,
- "metadata": {},
- "output_type": "execute_result"
- },
- {
- "data": {
- "image/png": "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",
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "views = g.expanding(100)\n",
- "\n",
- "timestamps = []\n",
- "node_count = []\n",
- "edge_count = []\n",
- "degree = []\n",
- "\n",
- "for view in views:\n",
- " timestamps.append(view.latest_time)\n",
- " # node_count.append(view.num_nodes())\n",
- " # edge_count.append(view.num_edges())\n",
- " degree.append(view.count_edges() / max(1, view.count_nodes()))\n",
- "\n",
- "sns.set_context()\n",
- "ax = plt.gca()\n",
- "plt.xticks(rotation=45)\n",
- "ax.set_xlabel(\"Time\")\n",
- "ax.set_ylabel(\"Average Interactions\")\n",
- "sns.lineplot(x=timestamps, y=degree, ax=ax)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "/Users/shivamkapoor/opt/miniconda3/envs/pyraphtory/lib/python3.10/site-packages/seaborn/_oldcore.py:1498: FutureWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, CategoricalDtype) instead\n",
- " if pd.api.types.is_categorical_dtype(vector):\n",
- "/Users/shivamkapoor/opt/miniconda3/envs/pyraphtory/lib/python3.10/site-packages/seaborn/_oldcore.py:1498: FutureWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, CategoricalDtype) instead\n",
- " if pd.api.types.is_categorical_dtype(vector):\n",
- "/Users/shivamkapoor/opt/miniconda3/envs/pyraphtory/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.\n",
- " with pd.option_context('mode.use_inf_as_na', True):\n",
- "/Users/shivamkapoor/opt/miniconda3/envs/pyraphtory/lib/python3.10/site-packages/seaborn/_oldcore.py:1119: FutureWarning: use_inf_as_na option is deprecated and will be removed in a future version. Convert inf values to NaN before operating instead.\n",
- " with pd.option_context('mode.use_inf_as_na', True):\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- ""
- ]
- },
- "execution_count": 11,
- "metadata": {},
- "output_type": "execute_result"
- },
- {
- "data": {
- "image/png": "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",
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "views = g.expanding(step=10)\n",
- "\n",
- "timestamps = []\n",
- "degree = []\n",
- "\n",
- "for view in views:\n",
- " timestamps.append(view.latest_time)\n",
- " gandalf = view.node(\"Gandalf\")\n",
- " if gandalf is not None:\n",
- " degree.append(gandalf.degree())\n",
- " else:\n",
- " degree.append(0)\n",
- "\n",
- "\n",
- "sns.set_context()\n",
- "ax = plt.gca()\n",
- "plt.xticks(rotation=45)\n",
- "ax.set_xlabel(\"Time\")\n",
- "ax.set_ylabel(\"Interactions\")\n",
- "sns.lineplot(x=timestamps, y=degree, ax=ax)"
- ]
}
],
"metadata": {
"kernelspec": {
- "display_name": "Python 3 (ipykernel)",
+ "display_name": "raphtory",
"language": "python",
"name": "python3"
},
@@ -437,12 +45,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.10.13"
- },
- "vscode": {
- "interpreter": {
- "hash": "cb87ca7661adc8f1194e8349af5289fc4d6184622935eb2bec01493e8d44e9d2"
- }
+ "version": "3.12.11"
}
},
"nbformat": 4,
diff --git a/python/tests/test_base_install/base_notebook.ipynb b/python/tests/test_base_install/base_notebook.ipynb
new file mode 100644
index 0000000000..bc469fab68
--- /dev/null
+++ b/python/tests/test_base_install/base_notebook.ipynb
@@ -0,0 +1,330 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pandas as pd\n",
+ "import tempfile\n",
+ "import datetime"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Basic functionality on a graph\n",
+ "\n",
+ "After importing a Raphtory graph we can create a blank one to work with:\n",
+ "\n",
+ "* Graphs in Raphtory are directed by default\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Graph(number_of_nodes=0, number_of_edges=0, number_of_temporal_edges=0, earliest_time=None, latest_time=None)"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "from raphtory import Graph\n",
+ "\n",
+ "g = Graph()\n",
+ "g"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "NestedArcStringVecIterable([[[_default], [layer1], [layer2]], [[_default]], [[layer1]], [[layer2]]])"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "g.add_edge(0, \"1\", \"2\")\n",
+ "g.add_edge(0, \"1\", \"3\", layer=\"layer1\")\n",
+ "g.add_edge(0, \"1\", \"4\", layer=\"layer2\")\n",
+ "\n",
+ "g.nodes.edges.layer_names"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Once we have a new graph we can add nodes and edges to it via `add_node()` and `add_edge()`. For these:\n",
+ "* The ids of nodes and the source/destination of an edge can be either strings or integers\n",
+ "* All additions into the graph must happen at a specific time - this means updates are also additions\n",
+ "* If you add an edge between nodes which do no exist in the graph yet, these will be automatically created\n",
+ "* Properties can be added onto nodes and edges - this is a dict of any value, but the keys must be strings\n",
+ "* We have a special type of `static property` which exists outside of the timeline and is always accessible. \n",
+ "* Additions can be completed out of order, making it very easy to merge datasets together\n",
+ "\n",
+ "\n",
+ "We can then check the state of the graph:\n",
+ "* To see if a node or edge exists you can use `has_node()` and `has_edge()`\n",
+ "* To get the earliest and latest times at which updates have been applied to the graph you can use `earliest_time()` and `latest_time()` - if no updates have been applied these will return `None`\n",
+ "* To get the total number of nodes and edges of a graph you can use `num_edges()` and `num_nodes()`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "True True False\n",
+ "True False\n",
+ "3 5\n",
+ "True True False\n",
+ "True False\n",
+ "4 7\n",
+ "Node(name=Ben, earliest_time=5, latest_time=8, properties=Properties({property1: None, property2: None, property3: None}))\n",
+ "Edge(source=Haaroon, target=Hamza, earliest_time=7, latest_time=7, properties={property3: test, property1: 1, property2: 9.8}, layer(s)=[toad])\n",
+ "Graph(number_of_nodes=8, number_of_edges=5, number_of_temporal_edges=7, earliest_time=0, latest_time=8)\n",
+ "True\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Basic Addition of Nodes and Edges\n",
+ "g.add_node(timestamp=1, id=\"10\")\n",
+ "g.add_edge(timestamp=2, src=\"1\", dst=\"2\")\n",
+ "\n",
+ "# checking node 10, 1 and 5 exist\n",
+ "print(g.has_node(\"10\"), g.has_node(\"1\"), g.has_node(\"5\"))\n",
+ "# checking edge 1,2 exists and 2,1 doesn't as Raphtory is directed\n",
+ "print(g.has_edge(\"1\", \"2\"), g.has_edge(\"2\", \"1\"))\n",
+ "# Check the total number of edges and nodes\n",
+ "print(g.count_edges(), g.count_nodes())\n",
+ "\n",
+ "# Adding nodes and edges with String IDs\n",
+ "g.add_node(timestamp=5, id=\"Ben\")\n",
+ "g.add_edge(timestamp=8, src=\"Hamza\", dst=\"Ben\", layer=\"toad\")\n",
+ "\n",
+ "# Performing the same checks as before, but with strings\n",
+ "print(g.has_node(id=\"Ben\"), g.has_node(id=\"Hamza\"), g.has_node(id=\"Dave\"))\n",
+ "print(g.has_edge(src=\"Hamza\", dst=\"Ben\"), g.has_edge(src=\"Ben\", dst=\"Hamza\"))\n",
+ "print(g.count_edges(), g.count_nodes())\n",
+ "\n",
+ "g.add_edge(0, \"1\", \"3\", layer=\"toad\")\n",
+ "# Add an edge with Temporal Properties which can change over time\n",
+ "e = g.add_edge(\n",
+ " timestamp=7,\n",
+ " src=\"Haaroon\",\n",
+ " dst=\"Hamza\",\n",
+ " properties={\"property1\": 1, \"property2\": 9.8, \"property3\": \"test\"},\n",
+ " layer=\"toad\",\n",
+ ")\n",
+ "# Add a static property which is immutable\n",
+ "e.add_metadata(metadata={\"First-Met\": \"01/01/1990\"})\n",
+ "\n",
+ "# Add an node with Temporal Properties which can change over time\n",
+ "v = g.add_node(\n",
+ " timestamp=5,\n",
+ " id=\"Hamza\",\n",
+ " properties={\"property1\": 5, \"property2\": 12.5, \"property3\": \"test2\"},\n",
+ ")\n",
+ "# Add a static property which is immutable\n",
+ "v.add_metadata(metadata={\"Date-of-Birth\": \"01/01/1990\"})\n",
+ "print(g.node(\"Ben\").__repr__())\n",
+ "print(g.edge(\"Haaroon\", \"Hamza\").__repr__())\n",
+ "print(g.__repr__())\n",
+ "g_path = tempfile.mkdtemp()\n",
+ "g.save_to_file(g_path)\n",
+ "loaded_graph = Graph.load_from_file(g_path)\n",
+ "print(loaded_graph.has_node(\"Hamza\"))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[['_default'], ['layer1', 'toad'], ['layer2'], ['toad'], ['toad']]"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "list(g.edges.layer_names)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "g.nodes.edges.start"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from raphtory import graph_gen\n",
+ "\n",
+ "g = Graph()\n",
+ "graph_gen.ba_preferential_attachment(g, nodes_to_add=1000, edges_per_step=10)\n",
+ "view = g.window(0, 1000)\n",
+ "\n",
+ "ids = []\n",
+ "degrees = []\n",
+ "for v in view.nodes:\n",
+ " ids.append(v.id)\n",
+ " degrees.append(v.degree())\n",
+ "\n",
+ "df = pd.DataFrame.from_dict({\"id\": ids, \"degree\": degrees})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from raphtory import Graph\n",
+ "from raphtory import algorithms\n",
+ "from raphtory import graph_loader\n",
+ "\n",
+ "g = graph_loader.lotr_graph()\n",
+ "views_l1 = g.rolling(1000)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "views = g.expanding(100)\n",
+ "\n",
+ "timestamps = []\n",
+ "node_count = []\n",
+ "edge_count = []\n",
+ "degree = []\n",
+ "\n",
+ "for view in views:\n",
+ " timestamps.append(view.latest_time)\n",
+ " # node_count.append(view.num_nodes())\n",
+ " # edge_count.append(view.num_edges())\n",
+ " degree.append(view.count_edges() / max(1, view.count_nodes()))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "views = g.expanding(step=10)\n",
+ "\n",
+ "timestamps = []\n",
+ "degree = []\n",
+ "\n",
+ "for view in views:\n",
+ " timestamps.append(view.latest_time)\n",
+ " gandalf = view.node(\"Gandalf\")\n",
+ " if gandalf is not None:\n",
+ " degree.append(gandalf.degree())\n",
+ " else:\n",
+ " degree.append(0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "d0f713d1727343d09df10592827d484e",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "HBox(children=(HTML(value=''), IntProgress(value=0, max=3), HTML(value='')))"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "df = pd.DataFrame(\n",
+ " {\n",
+ " \"time\": [0, 1, 2],\n",
+ " \"src\": [1, 2, 3],\n",
+ " \"dst\": [2, 3, 4],\n",
+ " }\n",
+ ")\n",
+ "g = Graph()\n",
+ "g.load_edges_from_df(df, \"time\", \"src\", \"dst\")\n",
+ "assert g.has_edge(1, 2)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "raphtory",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.12.11"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/python/tests/test_base_install/test_filters/semantics/test_edge_property_filter_semantics.py b/python/tests/test_base_install/test_filters/semantics/test_edge_property_filter_semantics.py
index bd829f82ee..f67c7a9866 100644
--- a/python/tests/test_base_install/test_filters/semantics/test_edge_property_filter_semantics.py
+++ b/python/tests/test_base_install/test_filters/semantics/test_edge_property_filter_semantics.py
@@ -9,7 +9,7 @@
import pytest
-def init_graph_for_secondary_indexes(graph):
+def init_graph_for_event_ids(graph):
edges = [
(1, "N16", "N15", {"p1": 2}),
(1, "N16", "N15", {"p1": 1}),
@@ -69,10 +69,10 @@ def check(graph):
@with_disk_variants(
- init_fn=combined([init_edges_graph, init_graph_for_secondary_indexes]),
+ init_fn=combined([init_edges_graph, init_graph_for_event_ids]),
variants=["graph", "event_disk_graph"],
)
-def test_temporal_any_semantics_for_secondary_indexes():
+def test_temporal_any_semantics_for_event_ids():
def check(graph):
filter_expr = filter.Edge.property("p1").temporal().any() == 1
result_ids = sorted(graph.filter(filter_expr).edges.id)
@@ -109,10 +109,10 @@ def check(graph):
@with_disk_variants(
- init_fn=combined([init_edges_graph, init_graph_for_secondary_indexes]),
+ init_fn=combined([init_edges_graph, init_graph_for_event_ids]),
variants=["graph", "event_disk_graph"],
)
-def test_temporal_last_semantics_for_secondary_indexes3():
+def test_temporal_latest_semantics_for_event_ids3():
def check(graph):
filter_expr = filter.Edge.property("p1").temporal().last() == 1
result_ids = sorted(graph.filter(filter_expr).edges.id)
@@ -165,10 +165,10 @@ def check(graph):
@with_disk_variants(
- init_fn=combined([init_edges_graph, init_graph_for_secondary_indexes]),
+ init_fn=combined([init_edges_graph, init_graph_for_event_ids]),
variants=["graph"],
)
-def test_property_semantics_for_secondary_indexes():
+def test_property_semantics_for_event_ids():
def check(graph):
filter_expr = filter.Edge.property("p1") == 1
result_ids = sorted(graph.filter(filter_expr).edges.id)
@@ -189,10 +189,10 @@ def check(graph):
# TODO: Const properties not supported for disk_graph.
@with_disk_variants(
- init_fn=combined([init_edges_graph, init_graph_for_secondary_indexes]),
+ init_fn=combined([init_edges_graph, init_graph_for_event_ids]),
variants=["event_disk_graph"],
)
-def test_property_semantics_for_secondary_indexes_dsg():
+def test_property_semantics_for_event_ids_dsg():
def check(graph):
filter_expr = filter.Edge.property("p1") == 1
result_ids = sorted(graph.filter(filter_expr).edges.id)
diff --git a/python/tests/test_base_install/test_filters/semantics/test_node_property_filter_semantics.py b/python/tests/test_base_install/test_filters/semantics/test_node_property_filter_semantics.py
index 2045973eff..df653e7a34 100644
--- a/python/tests/test_base_install/test_filters/semantics/test_node_property_filter_semantics.py
+++ b/python/tests/test_base_install/test_filters/semantics/test_node_property_filter_semantics.py
@@ -8,7 +8,7 @@
from utils import with_disk_variants
-def init_graph_for_secondary_indexes(graph):
+def init_graph_for_event_ids(graph):
graph.add_node(1, "N16", {"p1": 2})
graph.add_node(1, "N16", {"p1": 1})
@@ -41,9 +41,9 @@ def check(graph):
@with_disk_variants(
- init_fn=combined([init_nodes_graph, init_graph_for_secondary_indexes]),
+ init_fn=combined([init_nodes_graph, init_graph_for_event_ids]),
)
-def test_temporal_any_semantics_for_secondary_indexes():
+def test_temporal_any_semantics_for_event_ids():
def check(graph):
filter_expr = filter.Node.property("p1").temporal().any() == 1
result_ids = sorted(graph.filter(filter_expr).nodes.id)
@@ -67,9 +67,9 @@ def check(graph):
@with_disk_variants(
- init_fn=combined([init_nodes_graph, init_graph_for_secondary_indexes]),
+ init_fn=combined([init_nodes_graph, init_graph_for_event_ids]),
)
-def test_temporal_last_semantics_for_secondary_indexes():
+def test_temporal_latest_semantics_for_event_ids():
def check(graph):
filter_expr = filter.Node.property("p1").temporal().last() == 1
result_ids = sorted(graph.filter(filter_expr).nodes.id)
@@ -92,9 +92,9 @@ def check(graph):
@with_disk_variants(
- init_fn=combined([init_nodes_graph, init_graph_for_secondary_indexes]),
+ init_fn=combined([init_nodes_graph, init_graph_for_event_ids]),
)
-def test_property_semantics_for_secondary_indexes():
+def test_property_semantics_for_event_ids():
def check(graph):
filter_expr = filter.Node.property("p1") == 1
result_ids = sorted(graph.filter(filter_expr).nodes.id)
diff --git a/python/tests/test_base_install/test_filters/test_exploded_edge_filter.py b/python/tests/test_base_install/test_filters/test_exploded_edge_filter.py
index 085faf9208..977196b47b 100644
--- a/python/tests/test_base_install/test_filters/test_exploded_edge_filter.py
+++ b/python/tests/test_base_install/test_filters/test_exploded_edge_filter.py
@@ -1,4 +1,5 @@
from raphtory import Graph, PersistentGraph
+from raphtory import EventTime
from raphtory import filter
import pytest
from datetime import datetime
@@ -25,57 +26,65 @@ def test_graph(GraphClass):
e2 = f_g.edge(1, 3)
if type(g) == Graph:
- assert e1.deletions() == []
- assert e2.deletions() == []
+ assert e1.deletions == []
+ assert e2.deletions == []
else:
- assert e1.deletions() == [1, 2]
- assert e2.deletions() == [1, 2]
+ assert e1.deletions.t == [1, 2]
+ assert e2.deletions.t == [1, 2]
+ assert e1.deletions == [(1, 0), (2, 1)]
+ assert e2.deletions == [(1, 3), (2, 4)]
- assert list(e1.history()) == [3]
- assert list(e2.history()) == [3]
+ assert e1.history.t.collect() == [3]
+ assert e2.history.t.collect() == [3]
# assert e2.layer_names == ["red"] returning red blue for PersistentGraph which feels wrong?
- assert e1.properties.temporal.get("weight").items() == [(3, 3)]
- assert e2.properties.temporal.get("weight").items() == [(3, 3)]
+ assert e1.properties.temporal.get("weight").items() == [(EventTime(3, 2), 3)]
+ assert e2.properties.temporal.get("weight").items() == [(EventTime(3, 5), 3)]
f_g = g.filter(filter=weight_lt3 & name_bob)
e1 = f_g.edge(1, 2)
e2 = f_g.edge(1, 3)
if type(g) == Graph:
- assert e1.deletions() == []
- assert e2.deletions() == []
+ assert e1.deletions == []
+ assert e2.deletions == []
else:
- assert e1.deletions() == [2, 3]
- assert e2.deletions() == [2, 3]
+ assert e1.deletions.t == [2, 3]
+ assert e2.deletions.t == [2, 3]
- assert list(e1.history()) == [1]
- assert list(e2.history()) == [1]
+ assert e1.history.t.collect() == [1]
+ assert e2.history.t.collect() == [1]
# assert e2.layer_names == ["blue"] returning red blue for PersistentGraph which feels wrong?
- assert e1.properties.temporal.get("weight").items() == [(1, 1)]
- assert e2.properties.temporal.get("weight").items() == [(1, 1)]
+ assert e1.properties.temporal.get("weight").items() == [(EventTime(1, 0), 1)]
+ assert e2.properties.temporal.get("weight").items() == [(EventTime(1, 3), 1)]
f_g = g.filter(filter=weight_e3 | name_bob)
e1 = f_g.edge(1, 2)
e2 = f_g.edge(1, 3)
if type(g) == Graph:
- assert e1.deletions() == []
- assert e2.deletions() == []
+ assert e1.deletions == []
+ assert e2.deletions == []
else:
- assert e1.deletions() == [2]
- assert e2.deletions() == [2]
+ assert e1.deletions == [(2, 1)]
+ assert e2.deletions == [(2, 4)]
- assert list(e1.history()) == [1, 3]
- assert list(e2.history()) == [1, 3]
+ assert list(e1.history.t) == [1, 3]
+ assert list(e2.history.t) == [1, 3]
assert e2.layer_names == ["blue", "red"]
- assert e1.properties.temporal.get("weight").items() == [(1, 1), (3, 3)]
- assert e2.properties.temporal.get("weight").items() == [(1, 1), (3, 3)]
+ assert e1.properties.temporal.get("weight").items() == [
+ (EventTime(1, 0), 1),
+ (EventTime(3, 2), 3),
+ ]
+ assert e2.properties.temporal.get("weight").items() == [
+ (EventTime(1, 3), 1),
+ (EventTime(3, 5), 3),
+ ]
@pytest.mark.parametrize("GraphClass", [Graph, PersistentGraph])
@@ -98,38 +107,44 @@ def test_same_time_event(GraphClass):
e2 = f_g.edge(1, 3)
if type(g) == Graph:
- assert e1.deletions() == []
- assert e2.deletions() == []
+ assert e1.deletions == []
+ assert e2.deletions == []
else:
- assert e1.deletions() == [1, 1]
- assert e2.deletions() == [1, 1]
+ assert e1.deletions == [(1, 1), (1, 2)]
+ assert e2.deletions == [(1, 4), (1, 5)]
- assert list(e1.history()) == [1]
- assert list(e2.history()) == [1]
+ assert list(e1.history.t) == [1]
+ assert list(e2.history.t) == [1]
# assert e2.layer_names == ["blue"] returning red blue which seems wrong
- assert e1.properties.temporal.get("weight").items() == [(1, 1)]
- assert e2.properties.temporal.get("weight").items() == [(1, 1)]
+ assert e1.properties.temporal.get("weight").items() == [(EventTime(1, 0), 1)]
+ assert e2.properties.temporal.get("weight").items() == [(EventTime(1, 3), 1)]
f_g = g.filter(filter=weight_e3 | name_bob)
e1 = f_g.edge(1, 2)
e2 = f_g.edge(1, 3)
if type(g) == Graph:
- assert e1.deletions() == []
- assert e2.deletions() == []
+ assert e1.deletions == []
+ assert e2.deletions == []
else:
- assert e1.deletions() == [1]
- assert e2.deletions() == [1]
+ assert e1.deletions.t == [1]
+ assert e2.deletions.t == [1]
- assert list(e1.history()) == [1, 1]
- assert list(e2.history()) == [1, 1]
+ assert list(e1.history.t) == [1, 1]
+ assert list(e2.history.t) == [1, 1]
assert e2.layer_names == ["blue", "red"]
- assert e1.properties.temporal.get("weight").items() == [(1, 1), (1, 3)]
- assert e2.properties.temporal.get("weight").items() == [(1, 1), (1, 3)]
+ assert e1.properties.temporal.get("weight").items() == [
+ (EventTime(1, 0), 1),
+ (EventTime(1, 2), 3),
+ ]
+ assert e2.properties.temporal.get("weight").items() == [
+ (EventTime(1, 3), 1),
+ (EventTime(1, 5), 3),
+ ]
@pytest.mark.parametrize("GraphClass", [Graph, PersistentGraph])
diff --git a/python/tests/test_base_install/test_graphdb/test_edge_deletion.py b/python/tests/test_base_install/test_graphdb/test_edge_deletion.py
index abf0c5d16d..2335539d2e 100644
--- a/python/tests/test_base_install/test_graphdb/test_edge_deletion.py
+++ b/python/tests/test_base_install/test_graphdb/test_edge_deletion.py
@@ -7,5 +7,5 @@ def test_edge_deletions():
e = g.add_edge(1, 2, 3)
e.delete(5)
e.add_updates(10)
- check_arr(e.history(), [1, 10])
- check_arr(e.deletions(), [5])
+ check_arr(e.history.t.collect(), [1, 10])
+ check_arr(e.deletions, [(5, 1)])
diff --git a/python/tests/test_base_install/test_graphdb/test_event_time.py b/python/tests/test_base_install/test_graphdb/test_event_time.py
new file mode 100644
index 0000000000..fef6a73e5e
--- /dev/null
+++ b/python/tests/test_base_install/test_graphdb/test_event_time.py
@@ -0,0 +1,186 @@
+import pytest
+from raphtory import EventTime, Graph
+from datetime import datetime, timezone
+
+
+@pytest.fixture()
+def example_graph() -> Graph:
+ g: Graph = Graph()
+ g.add_node(datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc), 1) # 0
+ g.add_node(datetime(1970, 1, 2, 0, 0, 0, tzinfo=timezone.utc), 2) # 86400000
+ g.add_node(datetime(1970, 1, 2, 0, 30, 0, tzinfo=timezone.utc), 3) # 88200000
+ g.add_node(datetime(2000, 1, 1, 0, 0, 0, tzinfo=timezone.utc), 4) # 946684800000
+ return g
+
+
+def test_time_index():
+ t = EventTime(5, 1)
+ # check equality with int
+ assert t > 4
+ assert t == 5
+ assert t < 6
+ # check equality with tuple
+ assert t > (5, 0)
+ assert t == (5, 1)
+ assert t < (5, 2)
+ # check equality with datetime
+ assert t > datetime(1970, 1, 1, 0, 0, 0, 0)
+ assert t == datetime(1970, 1, 1, 0, 0, 0, 5000)
+ assert t < datetime(1970, 1, 1, 0, 0, 0, 10000)
+ # check equality with a tuple containing datetime
+ assert t > (datetime(1970, 1, 1, 0, 0, 0, 5000), 0)
+ assert t == (datetime(1970, 1, 1, 0, 0, 0, 5000), 1)
+ assert t < (datetime(1970, 1, 1, 0, 0, 0, 5000), 2)
+ # check equality with string
+ t2 = EventTime(1000, 1)
+ assert t2 > "1970-01-01 00:00:00"
+ assert t2 == "1970-01-01 00:00:01"
+ assert t2 < "1970-01-01 00:00:02"
+ # check equality with a tuple containing datetime and string
+ assert t > (datetime(1970, 1, 1, 0, 0, 0, 5000), "1970-01-01")
+ assert t == (datetime(1970, 1, 1, 0, 0, 0, 5000), "1970-01-01T00:00:00.001")
+ assert t < (datetime(1970, 1, 1, 0, 0, 0, 5000), "1970-01-01T00:00:00.010Z")
+
+
+def test_time_input_parsing(example_graph):
+ g: Graph = example_graph
+ start_variants = [
+ 0,
+ 0.0,
+ datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
+ # no timezone information, should assume UTC
+ datetime(1970, 1, 1, 0, 0, 0),
+ "1970-01-01T00:00:00Z", # RFC3339
+ "Thu, 01 Jan 1970 00:00:00 +0000", # RFC2822
+ "1970-01-01", # date-only
+ "1970-01-01T00:00:00.000", # naive ISO T with ms
+ "1970-01-01T00:00:00", # naive ISO T
+ "1970-01-01 00:00:00.000", # naive space with ms
+ "1970-01-01 00:00:00", # naive space
+ EventTime(0),
+ EventTime(0, 0),
+ # tuple/list indexed forms
+ (0, 0),
+ [0, 0],
+ ("1970-01-01T00:00:00Z", 0),
+ [datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc), 0],
+ (0.0, 0),
+ ]
+
+ end_variants = [
+ 10,
+ 0.01,
+ datetime(1970, 1, 1, 0, 0, 0, 10000, tzinfo=timezone.utc),
+ # no timezone information, should assume UTC
+ datetime(1970, 1, 1, 0, 0, 0, 10000),
+ "1970-01-01T00:00:00.010Z", # RFC3339 with ms
+ "Thu, 01 Jan 1970 00:00:01 +0000", # RFC2822 (1s)
+ "1970-01-01T00:00:00.010", # naive ISO T with ms
+ "1970-01-01 00:00:00.010", # naive space with ms
+ EventTime(10),
+ EventTime(10, 0),
+ # tuple/list indexed forms
+ (10, 0),
+ [10, 0],
+ ("1970-01-01T00:00:00.010Z", 0),
+ [datetime(1970, 1, 1, 0, 0, 0, 10000, tzinfo=timezone.utc), 0],
+ (0.01, 0),
+ ]
+
+ # Verify that every start form works when end is a simple int
+ for start in start_variants:
+ gw = g.window(start, 10)
+ assert gw.nodes == [1], f"Unexpected nodes for start={start!r}"
+
+ # Verify that every end form works when start is a simple int
+ for end in end_variants:
+ gw = g.window(0, end)
+ assert gw.nodes == [1], f"Unexpected nodes for end={end!r}"
+
+ assert g.window(86400000, 88200000).nodes == [2]
+ assert g.window(86400000, 88200001).nodes == [2, 3]
+ gw = g.window(88200000, "2000-01-01")
+ assert gw.nodes == [3]
+ gw = g.window(88200000, "2000-01-01 00:00:01")
+ assert gw.nodes == [3, 4]
+ gw = g.window(88200000, "2000-01-02")
+ assert gw.nodes == [3, 4]
+
+
+def test_optional_event_time_none_comparison():
+ g = Graph()
+ none_time = g.earliest_time
+
+ assert none_time.is_none()
+ assert not none_time
+
+ assert none_time == None
+
+ assert not (none_time > None)
+ assert not (none_time < None)
+ assert none_time >= None
+ assert none_time <= None
+
+ # None < Any EventTime/value
+ assert none_time < 0
+ assert none_time < 1000
+ assert none_time < "1970-01-01"
+ assert none_time < datetime(1970, 1, 1, tzinfo=timezone.utc)
+ assert none_time < EventTime(0)
+
+ # Reverse comparisons
+ assert 0 > none_time
+ assert "1970-01-01" > none_time
+ assert EventTime(0) > none_time
+
+
+def test_optional_event_time_some_comparison():
+ g = Graph()
+ g.add_node(1000, 1)
+ some_time = g.earliest_time
+
+ assert some_time.is_some()
+ assert some_time
+
+ assert some_time != None
+ assert some_time > None
+
+ assert some_time == 1000
+ assert some_time > 999
+ assert some_time < 1001
+
+ assert some_time == 1.0 # Floats are in seconds
+
+ assert some_time == "1970-01-01 00:00:01"
+ assert some_time > "1970-01-01 00:00:00"
+
+ assert some_time == EventTime(1000, 0)
+ assert some_time > EventTime(999, 0)
+
+ assert some_time == (1000, 0)
+
+
+def test_optional_vs_optional():
+ g1 = Graph()
+ none_time = g1.earliest_time
+
+ g2 = Graph()
+ g2.add_node(1000, 1)
+ t1000 = g2.earliest_time
+
+ g3 = Graph()
+ g3.add_node(2000, 1)
+ t2000 = g3.earliest_time
+
+ assert none_time < t1000
+ assert t1000 > none_time
+ assert none_time != t1000
+
+ assert t1000 < t2000
+ assert t2000 > t1000
+ assert t1000 != t2000
+
+ g4 = Graph()
+ g4.add_node(1000, 1)
+ t1000_2 = g4.earliest_time
+ assert t1000 == t1000_2
diff --git a/python/tests/test_base_install/test_graphdb/test_graphdb.py b/python/tests/test_base_install/test_graphdb/test_graphdb.py
index a15e816a05..c6c6209ed5 100644
--- a/python/tests/test_base_install/test_graphdb/test_graphdb.py
+++ b/python/tests/test_base_install/test_graphdb/test_graphdb.py
@@ -14,7 +14,7 @@
from raphtory import graph_loader
import tempfile
from math import isclose
-from datetime import datetime, timezone
+from datetime import date, datetime, timezone
import string
from pathlib import Path
from pytest import fixture
@@ -134,8 +134,8 @@ def test_nodes_time_iterable():
@with_disk_graph
def check(g):
- assert g.nodes.earliest_time.min() == -1
- assert g.nodes.latest_time.max() == 7
+ assert g.nodes.earliest_time.min().t == -1
+ assert g.nodes.latest_time.max().t == 7
check(g)
@@ -367,12 +367,22 @@ def test_entity_history_date_time():
g.add_edge(2, 1, 2)
e = g.add_edge(3, 1, 2)
- full_history_1 = [
+ full_edge_history_1 = [
datetime(1970, 1, 1, tzinfo=utc),
datetime(1970, 1, 1, 0, 0, 0, 1000, tzinfo=utc),
datetime(1970, 1, 1, 0, 0, 0, 2000, tzinfo=utc),
datetime(1970, 1, 1, 0, 0, 0, 3000, tzinfo=utc),
]
+ full_node_history_1 = [
+ datetime(1970, 1, 1, tzinfo=utc),
+ datetime(1970, 1, 1, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 1000, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 1000, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 2000, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 2000, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 3000, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 3000, tzinfo=utc),
+ ]
full_history_2 = [
datetime(1970, 1, 1, 0, 0, 0, 4000, tzinfo=utc),
@@ -381,40 +391,71 @@ def test_entity_history_date_time():
datetime(1970, 1, 1, 0, 0, 0, 7000, tzinfo=utc),
]
- windowed_history = [
+ windowed_edge_history = [
+ datetime(1970, 1, 1, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 1000, tzinfo=utc),
+ ]
+
+ windowed_node_history = [
+ datetime(1970, 1, 1, tzinfo=utc),
datetime(1970, 1, 1, tzinfo=utc),
datetime(1970, 1, 1, 0, 0, 0, 1000, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 1000, tzinfo=utc),
]
- assert v.history_date_time() == full_history_1
- assert v.window(0, 2).history_date_time() == windowed_history
- assert e.history_date_time() == full_history_1
- assert e.window(0, 2).history_date_time() == windowed_history
+ check_arr(v.history.dt.collect(), full_node_history_1)
+ assert v.window(0, 2).history.dt.collect() == windowed_node_history
+ assert e.history.dt.collect() == full_edge_history_1
+ assert e.window(0, 2).history.dt.collect() == windowed_edge_history
g.add_edge(4, 1, 3)
g.add_edge(5, 1, 3)
g.add_edge(6, 1, 3)
g.add_edge(7, 1, 3)
- assert g.edges.history_date_time() == [full_history_1, full_history_2]
- assert g.nodes.in_edges.history_date_time() == [
+ assert g.edges.history.dt.collect() == [full_edge_history_1, full_history_2]
+ assert g.nodes.in_edges.history.dt.collect() == [
[],
- [full_history_1],
+ [full_edge_history_1],
[full_history_2],
]
- assert g.nodes.earliest_date_time == [
+ expected_earliest_dt_1 = [
datetime(1970, 1, 1, tzinfo=utc),
datetime(1970, 1, 1, tzinfo=utc),
datetime(1970, 1, 1, 0, 0, 0, 4000, tzinfo=utc),
]
- assert g.nodes.latest_date_time == [
+
+ check_arr(g.nodes.earliest_time.collect(), expected_earliest_dt_1)
+ assert g.nodes.earliest_time.dt == [
+ datetime(1970, 1, 1, tzinfo=utc),
+ datetime(1970, 1, 1, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 4000, tzinfo=utc),
+ ]
+
+ expected_latest_dt_1 = [
datetime(1970, 1, 1, 0, 0, 0, 7000, tzinfo=utc),
datetime(1970, 1, 1, 0, 0, 0, 3000, tzinfo=utc),
datetime(1970, 1, 1, 0, 0, 0, 7000, tzinfo=utc),
]
+ check_arr(g.nodes.latest_time.collect(), expected_latest_dt_1)
+ assert g.nodes.latest_time.dt == [
+ datetime(1970, 1, 1, 0, 0, 0, 7000, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 3000, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 7000, tzinfo=utc),
+ ]
+
+ expected_latest_neighbours_dt_1 = [
+ [
+ datetime(1970, 1, 1, 0, 0, 0, 3000, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 7000, tzinfo=utc),
+ ],
+ [datetime(1970, 1, 1, 0, 0, 0, 7000, tzinfo=utc)],
+ [datetime(1970, 1, 1, 0, 0, 0, 7000, tzinfo=utc)],
+ ]
+ check_arr(g.nodes.neighbours.latest_time.collect(), expected_latest_neighbours_dt_1)
- assert g.nodes.neighbours.latest_date_time.collect() == [
+ assert g.nodes.neighbours.latest_time.dt.collect() == [
[
datetime(1970, 1, 1, 0, 0, 0, 3000, tzinfo=utc),
datetime(1970, 1, 1, 0, 0, 0, 7000, tzinfo=utc),
@@ -423,7 +464,19 @@ def test_entity_history_date_time():
[datetime(1970, 1, 1, 0, 0, 0, 7000, tzinfo=utc)],
]
- assert g.nodes.neighbours.earliest_date_time.collect() == [
+ expected_earliest_neighbours_dt_1 = [
+ [
+ datetime(1970, 1, 1, tzinfo=utc),
+ datetime(1970, 1, 1, 0, 0, 0, 4000, tzinfo=utc),
+ ],
+ [datetime(1970, 1, 1, tzinfo=utc)],
+ [datetime(1970, 1, 1, tzinfo=utc)],
+ ]
+ check_arr(
+ g.nodes.neighbours.earliest_time.collect(), expected_earliest_neighbours_dt_1
+ )
+
+ assert g.nodes.neighbours.earliest_time.dt.collect() == [
[
datetime(1970, 1, 1, tzinfo=utc),
datetime(1970, 1, 1, 0, 0, 0, 4000, tzinfo=utc),
@@ -580,10 +633,11 @@ def history_test(key, value):
[value]
]
- history_test("prop 1", [(1, 1), (2, 2)])
- history_test("prop 2", [(2, 0.6), (3, 0.9)])
- history_test("prop 3", [(1, "hi"), (3, "hello")])
- history_test("prop 4", [(1, True), (2, False), (3, True)])
+ # we're comparing EventTime values so we need to pass the event id, here as a tuple
+ history_test("prop 1", [((1, 1), 1), ((2, 2), 2)])
+ history_test("prop 2", [((2, 2), 0.6), ((3, 3), 0.9)])
+ history_test("prop 3", [((1, 1), "hi"), ((3, 3), "hello")])
+ history_test("prop 4", [((1, 1), True), ((2, 2), False), ((3, 3), True)])
history_test("undefined", None)
def time_history_test(time, key, value):
@@ -600,7 +654,7 @@ def time_history_test(time, key, value):
key
).items() == [[value]]
- time_history_test(1, "prop 4", [(1, True)])
+ time_history_test(1, "prop 4", [((1, 1), True)])
time_history_test(1, "static prop", None)
def time_static_property_test(time, key, value):
@@ -1134,7 +1188,7 @@ def test_exploded_edge_time():
@with_disk_graph
def check(g):
e = g.edge("Frodo", "Gandalf")
- his = e.history()
+ his = e.history.t.collect()
exploded_his = []
for ee in e.explode():
exploded_his.append(ee.time)
@@ -1186,8 +1240,8 @@ def test_graph_time_api():
@with_disk_graph
def check(g):
- earliest_time = g.earliest_time
- latest_time = g.latest_time
+ earliest_time = g.earliest_time.t
+ latest_time = g.latest_time.t
assert len(list(g.rolling(1))) == latest_time - earliest_time + 1
assert len(list(g.expanding(2))) == math.ceil(
(latest_time + 1 - earliest_time) / 2
@@ -1316,6 +1370,41 @@ def test_add_edge_num():
assert g.has_edge(1, 2)
+def test_window_date():
+ g = Graph()
+ g.add_node("1970-01-01", 1)
+ g.add_node("1970-01-02", 2)
+ g.add_node("1970-01-03", 3)
+ g.add_node("1970-01-04", 4)
+ start = date(1970, 1, 1) # start is inclusive
+ end = date(1970, 1, 3) # end is exclusive
+ windowed_g = g.window(start, end)
+ windowed_nodes = [n.id for n in windowed_g.nodes]
+ assert windowed_nodes == [1, 2]
+ assert 4 not in windowed_nodes
+
+
+def test_date_in_ingestion_functions():
+ dates = [
+ date(1970, 1, 1),
+ date(1970, 1, 2),
+ date(1970, 1, 3),
+ date(1970, 1, 4),
+ ]
+ g = Graph()
+ g.add_node(dates[0], 1)
+ g.add_edge(dates[1], 2, 3)
+ n1 = g.node(1)
+ n1.add_updates(dates[2])
+ e1 = g.edge(2, 3)
+ e1.add_updates(dates[3])
+ assert g.node(1).history == ["1970-01-01", "1970-01-03"]
+ assert g.edge(2, 3).history == ["1970-01-02", "1970-01-04"]
+ windowed_g = g.window(dates[0], dates[2]) # start is inclusive and end is exclusive
+ windowed_nodes = [n.id for n in windowed_g.nodes]
+ assert windowed_nodes == [1, 2, 3]
+
+
def test_all_neighbours_window():
g = Graph()
g.add_edge(1, 1, 2, {})
@@ -1575,9 +1664,9 @@ def test_node_history():
# @with_disk_graph FIXME: need special handling for nodes additions from Graph
def check(g):
- check_arr(g.node(1).history(), [1, 2, 3, 4, 8])
+ check_arr(g.node(1).history.t.collect(), [1, 2, 3, 4, 8])
view = g.window(1, 8)
- check_arr(view.node(1).history(), [1, 2, 3, 4])
+ check_arr(view.node(1).history.t.collect(), [1, 2, 3, 4])
check(g)
@@ -1590,9 +1679,9 @@ def check(g):
# @with_disk_graph FIXME: need special handling for nodes additions from Graph
def check(g):
- check_arr(g.node("Lord Farquaad").history(), [4, 6, 7, 8])
+ check_arr(g.node("Lord Farquaad").history.t.collect(), [4, 6, 7, 8])
view = g.window(1, 8)
- check_arr(view.node("Lord Farquaad").history(), [4, 6, 7])
+ check_arr(view.node("Lord Farquaad").history.t.collect(), [4, 6, 7])
check(g)
@@ -1610,18 +1699,18 @@ def check(g):
view = g.window(1, 5)
view2 = g.window(1, 4)
- check_arr(g.edge(1, 2).history(), [1, 3])
- check_arr(view.edge(1, 4).history(), [4])
- check_arr(list(g.edges.history()), [[1, 3], [2], [4]])
- check_arr(list(view2.edges.history()), [[1, 3], [2]])
+ check_arr(g.edge(1, 2).history.t.collect(), [1, 3])
+ check_arr(view.edge(1, 4).history.t.collect(), [4])
+ check_arr(g.edges.history.t.collect(), [[1, 3], [2], [4]])
+ check_arr(view2.edges.history.t.collect(), [[1, 3], [2]])
old_way = []
for e in g.edges:
- old_way.append(e.history())
- check_arr(list(g.edges.history()), old_way)
+ old_way.append(e.history.collect())
+ check_arr(g.edges.history.collect(), old_way)
check_arr(
- g.nodes.edges.history().collect(),
+ g.nodes.edges.history.t.collect(),
[
[[1, 3], [2], [4]],
[[1, 3]],
@@ -1633,8 +1722,8 @@ def check(g):
old_way2 = []
for edges in g.nodes.edges:
for edge in edges:
- old_way2.append(edge.history())
- new_way = g.nodes.edges.history().collect()
+ old_way2.append(edge.history.collect())
+ new_way = g.nodes.edges.history.collect()
check_arr([np.array(item) for sublist in new_way for item in sublist], old_way2)
check(g)
@@ -1646,7 +1735,7 @@ def test_lotr_edge_history():
@with_disk_graph
def check(g):
check_arr(
- g.edge("Frodo", "Gandalf").history(),
+ g.edge("Frodo", "Gandalf").history.t.collect(),
[
329,
555,
@@ -1713,13 +1802,19 @@ def check(g):
32656,
],
)
- check_arr(g.before(1000).edge("Frodo", "Gandalf").history(), [329, 555, 861])
- check_arr(g.edge("Frodo", "Gandalf").before(1000).history(), [329, 555, 861])
check_arr(
- g.window(100, 1000).edge("Frodo", "Gandalf").history(), [329, 555, 861]
+ g.before(1000).edge("Frodo", "Gandalf").history.t.collect(), [329, 555, 861]
+ )
+ check_arr(
+ g.edge("Frodo", "Gandalf").before(1000).history.t.collect(), [329, 555, 861]
)
check_arr(
- g.edge("Frodo", "Gandalf").window(100, 1000).history(), [329, 555, 861]
+ g.window(100, 1000).edge("Frodo", "Gandalf").history.t.collect(),
+ [329, 555, 861],
+ )
+ check_arr(
+ g.edge("Frodo", "Gandalf").window(100, 1000).history.t.collect(),
+ [329, 555, 861],
)
check(g)
@@ -1975,19 +2070,19 @@ def test_date_time():
@with_disk_graph
def check(g):
- assert g.earliest_date_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
- assert g.latest_date_time == datetime(2014, 2, 5, 0, 0, tzinfo=utc)
+ assert g.earliest_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
+ assert g.latest_time == datetime(2014, 2, 5, 0, 0, tzinfo=utc)
e = g.edge(1, 3)
exploded_edges = []
for edge in e.explode():
- exploded_edges.append(edge.date_time)
+ exploded_edges.append(edge.time.dt)
assert exploded_edges == [datetime(2014, 2, 3, tzinfo=utc)]
- assert g.edge(1, 2).earliest_date_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
- assert g.edge(1, 2).latest_date_time == datetime(2014, 2, 5, 0, 0, tzinfo=utc)
+ assert g.edge(1, 2).earliest_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
+ assert g.edge(1, 2).latest_time == datetime(2014, 2, 5, 0, 0, tzinfo=utc)
- assert g.node(1).earliest_date_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
- assert g.node(1).latest_date_time == datetime(2014, 2, 5, 0, 0, tzinfo=utc)
+ assert g.node(1).earliest_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
+ assert g.node(1).latest_time == datetime(2014, 2, 5, 0, 0, tzinfo=utc)
check(g)
@@ -2022,24 +2117,22 @@ def check(g):
view = g.window("2014-02-02", "2014-02-04")
view2 = g.window("2014-02-02", "2014-02-05")
- assert view.start_date_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
- assert view.end_date_time == datetime(2014, 2, 4, 0, 0, tzinfo=utc)
+ assert view.start == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
+ assert view.end == datetime(2014, 2, 4, 0, 0, tzinfo=utc)
- assert view.earliest_date_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
- assert view.latest_date_time == datetime(2014, 2, 3, 0, 0, tzinfo=utc)
+ assert view.earliest_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
+ assert view.latest_time == datetime(2014, 2, 3, 0, 0, tzinfo=utc)
- assert view2.edge(1, 2).start_date_time == datetime(
- 2014, 2, 2, 0, 0, tzinfo=utc
- )
- assert view2.edge(1, 2).end_date_time == datetime(2014, 2, 5, 0, 0, tzinfo=utc)
+ assert view2.edge(1, 2).start == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
+ assert view2.edge(1, 2).end == datetime(2014, 2, 5, 0, 0, tzinfo=utc)
- assert view.node(1).earliest_date_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
- assert view.node(1).latest_date_time == datetime(2014, 2, 3, 0, 0, tzinfo=utc)
+ assert view.node(1).earliest_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
+ assert view.node(1).latest_time == datetime(2014, 2, 3, 0, 0, tzinfo=utc)
e = view.edge(1, 2)
exploded_edges = []
for edge in e.explode():
- exploded_edges.append(edge.date_time)
+ exploded_edges.append(edge.time.dt)
assert exploded_edges == [datetime(2014, 2, 2, tzinfo=utc)]
check(g)
@@ -2058,17 +2151,17 @@ def check(g):
view = g.window("2014-02-02", "2014-02-04")
view2 = g.window("2014-02-02", "2014-02-05")
- assert view.start_date_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
- assert view.end_date_time == datetime(2014, 2, 4, 0, 0, tzinfo=utc)
+ assert view.start == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
+ assert view.end == datetime(2014, 2, 4, 0, 0, tzinfo=utc)
- assert view2.earliest_date_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
- assert view2.latest_date_time == datetime(2014, 2, 4, 0, 0, tzinfo=utc)
+ assert view2.earliest_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
+ assert view2.latest_time == datetime(2014, 2, 4, 0, 0, tzinfo=utc)
- assert view2.node(1).start_date_time == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
- assert view2.node(1).end_date_time == datetime(2014, 2, 5, 0, 0, tzinfo=utc)
+ assert view2.node(1).start == datetime(2014, 2, 2, 0, 0, tzinfo=utc)
+ assert view2.node(1).end == datetime(2014, 2, 5, 0, 0, tzinfo=utc)
- assert view.node(2).earliest_date_time == datetime(2014, 2, 3, 0, 0, tzinfo=utc)
- assert view.node(2).latest_date_time == datetime(2014, 2, 3, 0, 0, tzinfo=utc)
+ assert view.node(2).earliest_time == datetime(2014, 2, 3, 0, 0, tzinfo=utc)
+ assert view.node(2).latest_time == datetime(2014, 2, 3, 0, 0, tzinfo=utc)
check(g)
@@ -2104,7 +2197,7 @@ def test_datetime_with_timezone():
# @with_disk_graph FIXME: need special handling for nodes additions from Graph
def check(g):
- assert g.node(1).history_date_time() == results
+ assert g.node(1).history.dt.collect() == results
check(g)
@@ -2215,8 +2308,8 @@ def check_g_inner(mg):
assert mg.node(1).properties.get("type") == "wallet"
assert mg.node(4).metadata == {"abc": "xyz"}
assert mg.node(4).metadata.get("abc") == "xyz"
- check_arr(mg.node(1).history(), [-1, 0, 1, 2])
- check_arr(mg.node(4).history(), [6, 8])
+ check_arr(mg.node(1).history.t.collect(), [-1, 0, 0, 1, 1, 2])
+ check_arr(mg.node(4).history.t.collect(), [6, 8])
assert mg.nodes.id.collect() == [1, 2, 3, 4]
assert set(mg.edges.id) == {(1, 1), (1, 2), (1, 3), (2, 1), (3, 2), (2, 4)}
assert g.nodes.id.collect() == mg.nodes.id.collect()
@@ -2261,14 +2354,14 @@ def check(g):
# deleted at window start
assert deleted_edge.window(10, 20).is_deleted()
assert not deleted_edge.window(10, 20).is_valid()
- assert deleted_edge.window(10, 20).earliest_time is None
- assert deleted_edge.window(10, 20).latest_time is None
+ assert deleted_edge.window(10, 20).earliest_time.is_none()
+ assert deleted_edge.window(10, 20).latest_time.is_none()
# deleted before window start
assert deleted_edge.window(15, 20).is_deleted()
assert not deleted_edge.window(15, 20).is_valid()
- assert deleted_edge.window(15, 20).earliest_time is None
- assert deleted_edge.window(15, 20).latest_time is None
+ assert deleted_edge.window(15, 20).earliest_time.is_none()
+ assert deleted_edge.window(15, 20).latest_time.is_none()
# deleted in window
assert deleted_edge.window(5, 20).is_deleted()
@@ -2412,18 +2505,20 @@ def test_date_time_edges():
@with_disk_graph
def check(g):
old_date_way = []
- for edges in g.nodes.edges:
+ for edges in g.nodes.edges.explode():
for edge in edges:
- old_date_way.append(edge.date_time)
+ old_date_way.append(edge.time.dt)
assert old_date_way == [
- item for sublist in g.nodes.edges.date_time.collect() for item in sublist
+ item
+ for sublist in g.nodes.edges.explode().time.dt.collect()
+ for item in sublist
]
gw = g.window("2014-02-02", "2014-02-05")
- assert gw.edges.start_date_time == gw.start_date_time
- assert gw.edges.end_date_time == gw.end_date_time
- assert gw.nodes.edges.start_date_time == gw.start_date_time
- assert gw.nodes.edges.end_date_time == gw.end_date_time
+ assert gw.edges.start == gw.start
+ assert gw.edges.end == gw.end
+ assert gw.nodes.edges.start == gw.start
+ assert gw.nodes.edges.end == gw.end
check(g)
@@ -2735,8 +2830,8 @@ def check(g):
date_time = []
for e in g.edges.explode():
- date_time.append(e.date_time)
- assert list(g.edges.explode().date_time) == date_time
+ date_time.append(e.time.dt)
+ assert list(g.edges.explode().time.dt) == date_time
time_nested = []
for edges in g.nodes.edges.explode():
@@ -2751,10 +2846,10 @@ def check(g):
date_time_nested = []
for edges in g.nodes.edges.explode():
for edge in edges:
- date_time_nested.append(edge.date_time)
+ date_time_nested.append(edge.time.dt)
assert [
item
- for sublist in g.nodes.edges.explode().date_time.collect()
+ for sublist in g.nodes.edges.explode().time.dt.collect()
for item in sublist
] == date_time_nested
@@ -2781,8 +2876,8 @@ def check(g):
# @with_disk_graph # FIXME: need special handling for nodes additions from Graph
def check(g):
- check_arr(g.node(0).history(), [0, 1])
- check_arr(g.node("0").history(), [0, 1])
+ check_arr(g.node(0).history.t.collect(), [0, 1])
+ check_arr(g.node("0").history.t.collect(), [0, 1])
assert g.nodes.name.collect() == ["0"]
check(g)
@@ -3038,11 +3133,11 @@ def test_edge_layer_properties():
assert g.edge("A", "B").properties == {"greeting": "namaste"}
-def test_add_node_properties_ordered_by_secondary_index():
+def test_add_node_properties_ordered_by_event_id():
g = Graph()
- g.add_node(1, "A", properties={"prop": 1}, secondary_index=3)
- g.add_node(1, "A", properties={"prop": 2}, secondary_index=2)
- g.add_node(1, "A", properties={"prop": 3}, secondary_index=1)
+ g.add_node(1, "A", properties={"prop": 1}, event_id=3)
+ g.add_node(1, "A", properties={"prop": 2}, event_id=2)
+ g.add_node(1, "A", properties={"prop": 3}, event_id=1)
assert g.node("A").properties.temporal.get("prop").items() == [
(1, 3),
@@ -3051,27 +3146,27 @@ def test_add_node_properties_ordered_by_secondary_index():
]
-def test_add_node_properties_overwritten_for_same_secondary_index():
+def test_add_node_properties_overwritten_for_same_event_id():
g = Graph()
- g.add_node(1, "A", properties={"prop": 1}, secondary_index=1)
- g.add_node(1, "A", properties={"prop": 2}, secondary_index=1)
- g.add_node(1, "A", properties={"prop": 3}, secondary_index=1)
+ g.add_node(1, "A", properties={"prop": 1}, event_id=1)
+ g.add_node(1, "A", properties={"prop": 2}, event_id=1)
+ g.add_node(1, "A", properties={"prop": 3}, event_id=1)
assert g.node("A").properties.temporal.get("prop").items() == [(1, 3)]
g = Graph()
- g.add_node(1, "A", properties={"prop": 1}, secondary_index=1)
- g.add_node(1, "A", properties={"prop": 2}, secondary_index=2)
- g.add_node(1, "A", properties={"prop": 3}, secondary_index=2)
+ g.add_node(1, "A", properties={"prop": 1}, event_id=1)
+ g.add_node(1, "A", properties={"prop": 2}, event_id=2)
+ g.add_node(1, "A", properties={"prop": 3}, event_id=2)
assert g.node("A").properties.temporal.get("prop").items() == [(1, 1), (1, 3)]
-def test_create_node_properties_ordered_by_secondary_index():
+def test_create_node_properties_ordered_by_event_id():
g = Graph()
- g.create_node(1, "A", properties={"prop": 1}, secondary_index=3)
- g.add_node(1, "A", properties={"prop": 2}, secondary_index=2)
- g.add_node(1, "A", properties={"prop": 3}, secondary_index=1)
+ g.create_node(1, "A", properties={"prop": 1}, event_id=3)
+ g.add_node(1, "A", properties={"prop": 2}, event_id=2)
+ g.add_node(1, "A", properties={"prop": 3}, event_id=1)
assert g.node("A").properties.temporal.get("prop").items() == [
(1, 3),
@@ -3080,27 +3175,27 @@ def test_create_node_properties_ordered_by_secondary_index():
]
-def test_create_node_properties_overwritten_for_same_secondary_index():
+def test_create_node_properties_overwritten_for_same_event_id():
g = Graph()
- g.create_node(1, "A", properties={"prop": 1}, secondary_index=1)
- g.add_node(1, "A", properties={"prop": 2}, secondary_index=1)
- g.add_node(1, "A", properties={"prop": 3}, secondary_index=1)
+ g.create_node(1, "A", properties={"prop": 1}, event_id=1)
+ g.add_node(1, "A", properties={"prop": 2}, event_id=1)
+ g.add_node(1, "A", properties={"prop": 3}, event_id=1)
assert g.node("A").properties.temporal.get("prop").items() == [(1, 3)]
g = Graph()
- g.create_node(1, "A", properties={"prop": 1}, secondary_index=1)
- g.add_node(1, "A", properties={"prop": 2}, secondary_index=2)
- g.add_node(1, "A", properties={"prop": 3}, secondary_index=2)
+ g.create_node(1, "A", properties={"prop": 1}, event_id=1)
+ g.add_node(1, "A", properties={"prop": 2}, event_id=2)
+ g.add_node(1, "A", properties={"prop": 3}, event_id=2)
assert g.node("A").properties.temporal.get("prop").items() == [(1, 1), (1, 3)]
-def test_add_edge_properties_ordered_by_secondary_index():
+def test_add_edge_properties_ordered_by_event_id():
g = Graph()
- g.add_edge(1, "A", "B", properties={"prop": 1}, secondary_index=3)
- g.add_edge(1, "A", "B", properties={"prop": 2}, secondary_index=2)
- g.add_edge(1, "A", "B", properties={"prop": 3}, secondary_index=1)
+ g.add_edge(1, "A", "B", properties={"prop": 1}, event_id=3)
+ g.add_edge(1, "A", "B", properties={"prop": 2}, event_id=2)
+ g.add_edge(1, "A", "B", properties={"prop": 3}, event_id=1)
assert g.edge("A", "B").properties.temporal.get("prop").items() == [
(1, 3),
@@ -3109,53 +3204,53 @@ def test_add_edge_properties_ordered_by_secondary_index():
]
-def test_add_edge_properties_overwritten_for_same_secondary_index():
+def test_add_edge_properties_overwritten_for_same_event_id():
g = Graph()
- g.add_edge(1, "A", "B", properties={"prop": 1}, secondary_index=1)
- g.add_edge(1, "A", "B", properties={"prop": 2}, secondary_index=1)
- g.add_edge(1, "A", "B", properties={"prop": 3}, secondary_index=1)
+ g.add_edge(1, "A", "B", properties={"prop": 1}, event_id=1)
+ g.add_edge(1, "A", "B", properties={"prop": 2}, event_id=1)
+ g.add_edge(1, "A", "B", properties={"prop": 3}, event_id=1)
assert g.edge("A", "B").properties.temporal.get("prop").items() == [(1, 3)]
g = Graph()
- g.add_edge(1, "A", "B", properties={"prop": 1}, secondary_index=1)
- g.add_edge(1, "A", "B", properties={"prop": 2}, secondary_index=2)
- g.add_edge(1, "A", "B", properties={"prop": 3}, secondary_index=2)
+ g.add_edge(1, "A", "B", properties={"prop": 1}, event_id=1)
+ g.add_edge(1, "A", "B", properties={"prop": 2}, event_id=2)
+ g.add_edge(1, "A", "B", properties={"prop": 3}, event_id=2)
assert g.edge("A", "B").properties.temporal.get("prop").items() == [(1, 1), (1, 3)]
-def test_add_properties_properties_ordered_by_secondary_index():
+def test_add_properties_properties_ordered_by_event_id():
g = Graph()
- g.add_properties(1, properties={"prop": 1}, secondary_index=3)
- g.add_properties(1, properties={"prop": 2}, secondary_index=2)
- g.add_properties(1, properties={"prop": 3}, secondary_index=1)
+ g.add_properties(1, properties={"prop": 1}, event_id=3)
+ g.add_properties(1, properties={"prop": 2}, event_id=2)
+ g.add_properties(1, properties={"prop": 3}, event_id=1)
assert g.properties.temporal.get("prop").items() == [(1, 3), (1, 2), (1, 1)]
-def test_add_properties_properties_overwritten_for_same_secondary_index():
+def test_add_properties_properties_overwritten_for_same_event_id():
g = Graph()
- g.add_properties(1, properties={"prop": 1}, secondary_index=1)
- g.add_properties(1, properties={"prop": 2}, secondary_index=1)
- g.add_properties(1, properties={"prop": 3}, secondary_index=1)
+ g.add_properties(1, properties={"prop": 1}, event_id=1)
+ g.add_properties(1, properties={"prop": 2}, event_id=1)
+ g.add_properties(1, properties={"prop": 3}, event_id=1)
assert g.properties.temporal.get("prop").items() == [(1, 3)]
g = Graph()
- g.add_properties(1, properties={"prop": 1}, secondary_index=1)
- g.add_properties(1, properties={"prop": 2}, secondary_index=2)
- g.add_properties(1, properties={"prop": 3}, secondary_index=2)
+ g.add_properties(1, properties={"prop": 1}, event_id=1)
+ g.add_properties(1, properties={"prop": 2}, event_id=2)
+ g.add_properties(1, properties={"prop": 3}, event_id=2)
assert g.properties.temporal.get("prop").items() == [(1, 1), (1, 3)]
-def test_node_add_updates_properties_ordered_by_secondary_index():
+def test_node_add_updates_properties_ordered_by_event_id():
g = Graph()
g.add_node(1, "A")
- g.node("A").add_updates(1, properties={"prop": 1}, secondary_index=3)
- g.node("A").add_updates(1, properties={"prop": 2}, secondary_index=2)
- g.node("A").add_updates(1, properties={"prop": 3}, secondary_index=1)
+ g.node("A").add_updates(1, properties={"prop": 1}, event_id=3)
+ g.node("A").add_updates(1, properties={"prop": 2}, event_id=2)
+ g.node("A").add_updates(1, properties={"prop": 3}, event_id=1)
assert g.node("A").properties.temporal.get("prop").items() == [
(1, 3),
@@ -3164,30 +3259,30 @@ def test_node_add_updates_properties_ordered_by_secondary_index():
]
-def test_node_add_updates_properties_overwritten_for_same_secondary_index():
+def test_node_add_updates_properties_overwritten_for_same_event_id():
g = Graph()
g.add_node(1, "A")
- g.node("A").add_updates(1, properties={"prop": 1}, secondary_index=1)
- g.node("A").add_updates(1, properties={"prop": 2}, secondary_index=1)
- g.node("A").add_updates(1, properties={"prop": 3}, secondary_index=1)
+ g.node("A").add_updates(1, properties={"prop": 1}, event_id=1)
+ g.node("A").add_updates(1, properties={"prop": 2}, event_id=1)
+ g.node("A").add_updates(1, properties={"prop": 3}, event_id=1)
assert g.node("A").properties.temporal.get("prop").items() == [(1, 3)]
g = Graph()
g.add_node(1, "A")
- g.node("A").add_updates(1, properties={"prop": 1}, secondary_index=1)
- g.node("A").add_updates(1, properties={"prop": 2}, secondary_index=2)
- g.node("A").add_updates(1, properties={"prop": 3}, secondary_index=2)
+ g.node("A").add_updates(1, properties={"prop": 1}, event_id=1)
+ g.node("A").add_updates(1, properties={"prop": 2}, event_id=2)
+ g.node("A").add_updates(1, properties={"prop": 3}, event_id=2)
assert g.node("A").properties.temporal.get("prop").items() == [(1, 1), (1, 3)]
-def test_edge_add_updates_properties_ordered_by_secondary_index():
+def test_edge_add_updates_properties_ordered_by_event_id():
g = Graph()
g.add_edge(1, "A", "B")
- g.edge("A", "B").add_updates(1, properties={"prop": 1}, secondary_index=3)
- g.edge("A", "B").add_updates(1, properties={"prop": 2}, secondary_index=2)
- g.edge("A", "B").add_updates(1, properties={"prop": 3}, secondary_index=1)
+ g.edge("A", "B").add_updates(1, properties={"prop": 1}, event_id=3)
+ g.edge("A", "B").add_updates(1, properties={"prop": 2}, event_id=2)
+ g.edge("A", "B").add_updates(1, properties={"prop": 3}, event_id=1)
assert g.edge("A", "B").properties.temporal.get("prop").items() == [
(1, 3),
@@ -3196,20 +3291,20 @@ def test_edge_add_updates_properties_ordered_by_secondary_index():
]
-def test_edge_add_updates_properties_overwritten_for_same_secondary_index():
+def test_edge_add_updates_properties_overwritten_for_same_event_id():
g = Graph()
g.add_edge(1, "A", "B")
- g.edge("A", "B").add_updates(1, properties={"prop": 1}, secondary_index=1)
- g.edge("A", "B").add_updates(1, properties={"prop": 2}, secondary_index=1)
- g.edge("A", "B").add_updates(1, properties={"prop": 3}, secondary_index=1)
+ g.edge("A", "B").add_updates(1, properties={"prop": 1}, event_id=1)
+ g.edge("A", "B").add_updates(1, properties={"prop": 2}, event_id=1)
+ g.edge("A", "B").add_updates(1, properties={"prop": 3}, event_id=1)
assert g.edge("A", "B").properties.temporal.get("prop").items() == [(1, 3)]
g = Graph()
g.add_edge(1, "A", "B")
- g.edge("A", "B").add_updates(1, properties={"prop": 1}, secondary_index=1)
- g.edge("A", "B").add_updates(1, properties={"prop": 2}, secondary_index=2)
- g.edge("A", "B").add_updates(1, properties={"prop": 3}, secondary_index=2)
+ g.edge("A", "B").add_updates(1, properties={"prop": 1}, event_id=1)
+ g.edge("A", "B").add_updates(1, properties={"prop": 2}, event_id=2)
+ g.edge("A", "B").add_updates(1, properties={"prop": 3}, event_id=2)
assert g.edge("A", "B").properties.temporal.get("prop").items() == [(1, 1), (1, 3)]
diff --git a/python/tests/test_base_install/test_graphdb/test_graphdb_imports.py b/python/tests/test_base_install/test_graphdb/test_graphdb_imports.py
index a47f988cc3..c0aa8624b0 100644
--- a/python/tests/test_base_install/test_graphdb/test_graphdb_imports.py
+++ b/python/tests/test_base_install/test_graphdb/test_graphdb_imports.py
@@ -11,11 +11,11 @@ def test_import_into_graph():
gg = Graph()
res = gg.import_node(g_a)
assert res.name == g_a.name
- assert res.history() == g_a.history()
+ assert res.history == g_a.history
res = gg.import_node(g_b)
assert res.name == g_b.name
- assert res.history() == g_b.history()
+ assert res.history == g_b.history
assert res.properties.get("temp") == True
assert res.metadata.get("con") == 11
@@ -82,7 +82,7 @@ def test_import_node_as():
gg = Graph()
res = gg.import_node_as(a, "X")
assert res.name == "X"
- assert res.history().tolist() == [1]
+ assert res.history.collect() == [1]
gg.add_node(1, "Y")
@@ -94,7 +94,7 @@ def test_import_node_as():
assert gg.nodes.name == ["X", "Y"]
y = gg.node("Y")
assert y.name == "Y"
- assert y.history().tolist() == [1]
+ assert y.history.collect() == [1]
assert y.properties.get("temp") is None
assert y.metadata.get("con") is None
@@ -108,7 +108,7 @@ def test_import_node_as_merge():
gg = Graph()
res = gg.import_node_as(a, "X")
assert res.name == "X"
- assert res.history().tolist() == [1]
+ assert res.history.collect() == [1]
gg.add_node(1, "Y")
gg.import_node_as(b, "Y", True)
@@ -116,7 +116,7 @@ def test_import_node_as_merge():
assert gg.nodes.name == ["X", "Y"]
y = gg.node("Y")
assert y.name == "Y"
- assert y.history().tolist() == [1]
+ assert y.history.collect() == [1, 1]
assert y.properties.get("temp") == True
assert y.metadata.get("con") == 11
@@ -140,7 +140,7 @@ def test_import_nodes_as():
assert sorted(gg.nodes.name) == ["Y"]
y = gg.node("Y")
assert y.name == "Y"
- assert y.history().tolist() == [1]
+ assert y.history.collect() == [1]
assert y.properties.get("temp") is None
assert y.metadata.get("con") is None
@@ -158,11 +158,11 @@ def test_import_nodes_as_merge():
assert sorted(gg.nodes.name) == ["X", "Y"]
x = gg.node("X")
assert x.name == "X"
- assert x.history().tolist() == [1]
+ assert x.history.collect() == [1]
y = gg.node("Y")
assert y.name == "Y"
- assert y.history().tolist() == [1]
+ assert y.history.collect() == [1, 1]
assert y.properties.get("temp") == True
assert y.metadata.get("con") == 11
@@ -188,11 +188,11 @@ def test_import_edge_as():
assert sorted(gg.nodes.name) == ["X", "Y", "Z"]
x = gg.node("X")
assert x.name == "X"
- assert x.history().tolist() == [1]
+ assert x.history.collect() == [1]
y = gg.node("Y")
assert y.name == "Y"
- assert y.history().tolist() == [1, 2]
+ assert y.history.collect() == [1, 2]
assert y.properties.get("temp") is None
assert y.metadata.get("con") is None
@@ -215,12 +215,12 @@ def test_import_edge_as_merge():
assert sorted(gg.nodes.name) == ["X", "Y"]
x = gg.node("X")
assert x.name == "X"
- print(x.history())
- assert x.history().tolist() == [2, 3]
+ print(x.history)
+ assert x.history.collect() == [2, 3]
y = gg.node("Y")
assert y.name == "Y"
- assert y.history().tolist() == [2, 3]
+ assert y.history.collect() == [2, 3]
assert y.properties.get("temp") is None
assert y.metadata.get("con") is None
@@ -249,13 +249,13 @@ def test_import_edges_as():
y = gg.node("Y")
assert y.name == "Y"
- assert y.history().tolist() == [1]
+ assert y.history.collect() == [1]
assert y.properties.get("temp") is None
assert y.metadata.get("con") is None
z = gg.node("Z")
assert z.name == "Z"
- assert z.history().tolist() == [1]
+ assert z.history.collect() == [1]
def test_import_edges_as_merge():
@@ -276,17 +276,17 @@ def test_import_edges_as_merge():
x = gg.node("X")
assert x.name == "X"
- assert x.history().tolist() == [2]
+ assert x.history.collect() == [2]
y = gg.node("Y")
assert y.name == "Y"
- assert y.history().tolist() == [2, 3]
+ assert y.history.collect() == [2, 2, 3]
assert y.properties.get("temp") is None
assert y.metadata.get("con") is None
z = gg.node("Z")
assert z.name == "Z"
- assert z.history().tolist() == [2, 3]
+ assert z.history.collect() == [2, 3]
def test_import_edges():
diff --git a/python/tests/test_base_install/test_graphdb/test_history.py b/python/tests/test_base_install/test_graphdb/test_history.py
new file mode 100644
index 0000000000..b3a26719f2
--- /dev/null
+++ b/python/tests/test_base_install/test_graphdb/test_history.py
@@ -0,0 +1,283 @@
+import pytest
+from datetime import datetime, timezone
+from raphtory import Graph, History, PersistentGraph
+
+
+@pytest.fixture()
+def example_graph() -> Graph:
+ g: Graph = Graph()
+ g.add_node(100, "Dumbledore")
+ g.add_node(200, "Dumbledore", properties={"Age": 50})
+ g.add_node(300, "Dumbledore", properties={"Age": 51})
+
+ g.add_node(150, "Harry")
+ g.add_node(250, "Harry", properties={"Age": 20})
+ g.add_node(350, "Harry", properties={"Age": 21})
+
+ g.add_edge(150, "Dumbledore", "Harry", layer="communication")
+ g.add_edge(
+ 200, "Dumbledore", "Harry", properties={"weight": 0.5}, layer="friendship"
+ )
+ g.add_edge(
+ 300, "Dumbledore", "Harry", properties={"weight": 0.7}, layer="communication"
+ )
+ g.add_edge(
+ 350, "Dumbledore", "Harry", properties={"weight": 0.9}, layer="friendship"
+ )
+ return g
+
+
+def test_node_and_edge_history_timestamps(example_graph):
+ g: Graph = example_graph
+ assert g.node("Dumbledore").history.t == [100, 150, 200, 200, 300, 300, 350]
+ assert g.edge("Dumbledore", "Harry").history.t == [150, 200, 300, 350]
+
+ assert g.window(0, 150).node("Dumbledore").history.t == [100]
+ assert g.window(150, 300).node("Dumbledore").history.t == [150, 200, 200]
+ assert g.window(300, 450).node("Dumbledore").history.t == [300, 300, 350]
+
+ assert g.window(0, 150).edge("Dumbledore", "Harry") is None
+ assert g.window(150, 300).edge("Dumbledore", "Harry").history.t == [150, 200]
+ assert g.window(300, 450).edge("Dumbledore", "Harry").history.t == [300, 350]
+
+
+def test_history_equality(example_graph):
+ g: Graph = example_graph
+ history = g.edge("Dumbledore", "Harry").history
+ assert history.t == [150, 200, 300, 350]
+ # compare with list of ints
+ assert history == [150, 200, 300, 350]
+ # compare with tuples
+ assert history == [(150, 6), (200, 7), (300, 8), (350, 9)]
+ # compare with floats
+ assert history == [0.150, 0.2, 0.3, 0.35]
+ # compare with datetime
+ assert history == [
+ datetime(1970, 1, 1, 0, 0, 0, 150_000),
+ datetime(1970, 1, 1, 0, 0, 0, 200_000),
+ datetime(1970, 1, 1, 0, 0, 0, 300_000),
+ datetime(1970, 1, 1, 0, 0, 0, 350_000),
+ ]
+ # compare with tuples of datetime
+ assert history == [
+ (datetime(1970, 1, 1, 0, 0, 0, 150_000), 6),
+ (datetime(1970, 1, 1, 0, 0, 0, 200_000), 7),
+ (datetime(1970, 1, 1, 0, 0, 0, 300_000), 8),
+ (datetime(1970, 1, 1, 0, 0, 0, 350_000), 9),
+ ]
+ # compare with datetime strings
+ assert history == [
+ "1970-01-01 00:00:00.150",
+ "1970-01-01 00:00:00.200",
+ "1970-01-01 00:00:00.300",
+ "1970-01-01 00:00:00.350",
+ ]
+ # compare with tuples where event id is string
+ assert history == [
+ (datetime(1970, 1, 1, 0, 0, 0, 150_000), "1970-01-01 00:00:00.006"),
+ (datetime(1970, 1, 1, 0, 0, 0, 200_000), "1970-01-01 00:00:00.007"),
+ (datetime(1970, 1, 1, 0, 0, 0, 300_000), "1970-01-01 00:00:00.008"),
+ (datetime(1970, 1, 1, 0, 0, 0, 350_000), "1970-01-01 00:00:00.009"),
+ ]
+ # compare mismatched input types
+ assert history == [
+ 150,
+ datetime(1970, 1, 1, 0, 0, 0, 200_000),
+ "1970-01-01 00:00:00.300",
+ "1970-01-01T00:00:00.350Z",
+ ]
+ # compare mismatched tuples
+ assert history == [
+ (150, 6),
+ (datetime(1970, 1, 1, 0, 0, 0, 200_000), "1970-01-01 00:00:00.007"),
+ ("1970-01-01 00:00:00.300", 0.008),
+ ("1970-01-01T00:00:00.350Z", "1970-01-01T00:00:00.009"),
+ ]
+
+
+def test_layered_history_timestamps(example_graph):
+ g: Graph = example_graph
+ assert g.layer("friendship").node("Dumbledore").history.t == [
+ 100,
+ 200,
+ 200,
+ 300,
+ 350,
+ ]
+ assert g.layer("communication").edge("Dumbledore", "Harry").history.t == [150, 300]
+
+
+def test_history_reverse_merge_and_compose(example_graph):
+ g: Graph = example_graph
+ node_history: History = g.node("Dumbledore").history
+ edge_history: History = g.edge("Dumbledore", "Harry").history
+
+ forward = node_history.t.collect().tolist()
+ reverse = node_history.reverse().t.collect().tolist()
+ assert reverse == list(reversed(forward))
+
+ merged = node_history.merge(edge_history)
+ assert merged.t.collect().tolist() == sorted(
+ forward + edge_history.t.collect().tolist()
+ )
+ assert merged.t == [100, 150, 150, 200, 200, 200, 300, 300, 300, 350, 350]
+
+ composed = History.compose_histories([node_history, edge_history])
+ assert composed.t.collect().tolist() == merged.t.collect().tolist()
+
+
+def test_intervals_basic_and_same_timestamp():
+ g: Graph = Graph()
+ g.add_node(1, "N")
+ g.add_node(4, "N")
+ g.add_node(10, "N")
+ g.add_node(30, "N")
+ assert g.node("N").history.intervals == [3, 6, 20]
+
+ # Same timestamp intervals include 0
+ g2 = Graph()
+ g2.add_node(1, "X")
+ g2.add_node(1, "X")
+ assert g2.node("X").history.intervals == [0]
+ g2.add_node(2, "X")
+ assert g2.node("X").history.intervals == [0, 1]
+
+
+def test_intervals_stats():
+ g: Graph = Graph()
+ g.add_node(1, "N")
+ g.add_node(4, "N")
+ g.add_node(10, "N")
+ g.add_node(30, "N")
+ interval_values = g.node("N").history.intervals
+ assert interval_values.mean() == 29.0 / 3.0
+ assert interval_values.median() == 6
+ assert interval_values.max() == 20
+ assert interval_values.min() == 3
+
+ # No intervals if only one time entry
+ g2: Graph = Graph()
+ g2.add_node(1, "M")
+ inter2 = g2.node("M").history.intervals
+ assert inter2.mean() is None
+ assert inter2.median() is None
+ assert inter2.max() is None
+ assert inter2.min() is None
+
+
+def test_event_id_ordering_with_same_timestamp():
+ g: Graph = Graph()
+
+ g.add_node(1, "A", event_id=3)
+ g.add_node(1, "A", event_id=2)
+ g.add_node(1, "A", event_id=1)
+
+ assert g.node("A").history.t == [1, 1, 1]
+
+ # Event id should be ascending
+ assert g.node("A").history.event_id == [1, 2, 3]
+ assert g.node("A").history == [(1, 1), (1, 2), (1, 3)]
+
+
+def test_composed_neighbours_history():
+ g: Graph = Graph()
+ g.add_node(1, "node")
+ g.add_node(2, "node2")
+ g.add_node(3, "node3")
+ g.add_edge(4, "node", "node2")
+ g.add_edge(5, "node", "node3")
+ g.add_node(6, "node4")
+ g.add_edge(7, "node2", "node4")
+
+ # neighbours are node2 and node3
+ neighbours = list(g.node("node").neighbours)
+ neighbour_histories = [nb.history for nb in neighbours]
+ combined = History.compose_histories(neighbour_histories)
+
+ assert combined.earliest_time().t == 2
+ assert combined.latest_time().t == 7
+ # Expected: node2(2,4,7) + node3(3,5) in order
+ assert combined == [(2, 1), (3, 2), (4, 3), (5, 4), (7, 6)]
+
+
+def test_history_datetime_view():
+ g: Graph = Graph()
+ # Use datetimes and ensure dt view matches UTC conversions
+ dt = datetime(2024, 1, 5, 12, 0, 0, tzinfo=timezone.utc)
+ g.add_node(dt, "Z")
+ g.add_node(dt.replace(hour=13), "Z")
+ lst = g.node("Z").history.dt.collect()
+ assert [d.tzinfo for d in lst] == [timezone.utc, timezone.utc]
+ assert [d.hour for d in lst] == [12, 13]
+
+
+def test_nodes_history_iterable(example_graph):
+ g: Graph = example_graph
+ names = g.nodes.name.collect()
+ histories = [arr.collect().tolist() for arr in g.nodes.history.t.collect()]
+ expected_by_node = {
+ "Dumbledore": [100, 150, 200, 200, 300, 300, 350],
+ "Harry": [150, 150, 200, 250, 300, 350, 350],
+ }
+ expected = [expected_by_node[n] for n in names]
+ assert histories == expected
+
+
+def test_node_states_intervals(example_graph):
+ g: Graph = example_graph
+ intervals = g.nodes.history.intervals
+ assert intervals.collect() == [[50, 50, 0, 100, 0, 50], [0, 50, 50, 50, 50, 0]]
+ assert intervals.mean() == [(50 + 50 + 100 + 50) / 6, (50 + 50 + 50 + 50) / 6]
+ assert intervals.mean().min() == (50 + 50 + 50 + 50) / 6
+ assert intervals.median() == [50, 50]
+ assert intervals.max() == [100, 50]
+ assert intervals.min() == [0, 0]
+
+ # the same functions should work on computed NodeStates
+ intervals = intervals.compute()
+ assert intervals.to_list() == [[50, 50, 0, 100, 0, 50], [0, 50, 50, 50, 50, 0]]
+ assert intervals.mean() == [(50 + 50 + 100 + 50) / 6, (50 + 50 + 50 + 50) / 6]
+ assert intervals.mean().min() == (50 + 50 + 50 + 50) / 6
+ assert intervals.median() == [50, 50]
+ assert intervals.max() == [100, 50]
+ assert intervals.min() == [0, 0]
+
+
+def test_edges_history_iterable(example_graph):
+ g: Graph = example_graph
+ edges_histories = [arr.tolist() for arr in g.edges.history.t.collect()]
+ assert edges_histories == [[150, 200, 300, 350]]
+
+
+def test_nodes_neighbours_history_iterable(example_graph):
+ g: Graph = example_graph
+
+ neighbour_names = g.nodes.neighbours.name.collect() # nested: List[List[str]]
+ expected_by_node = {
+ "Dumbledore": [100, 150, 200, 200, 300, 300, 350],
+ "Harry": [150, 150, 200, 250, 300, 350, 350],
+ }
+ expected_nested = [
+ [expected_by_node[n] for n in inner] for inner in neighbour_names
+ ]
+ nested_histories = [
+ [arr.tolist() for arr in inner]
+ for inner in g.nodes.neighbours.history.t.collect()
+ ]
+ assert nested_histories == expected_nested
+
+
+def test_edge_deletions_history():
+ g: PersistentGraph = PersistentGraph()
+ g.add_edge(1, "A", "B")
+ g.add_edge(2, "A", "C")
+ g.delete_edge(3, "A", "B")
+ g.delete_edge(4, "A", "C")
+
+ # Per-edge deletions
+ assert g.edge("A", "B").deletions.t == [3]
+ assert g.edge("A", "C").deletions.t == [4]
+
+ # Iterable of deletions across edges
+ deletions_by_edge = [arr.tolist() for arr in g.edges.deletions.t.collect()]
+ assert deletions_by_edge == [[3], [4]]
diff --git a/python/tests/test_base_install/test_graphdb/test_latest_graph.py b/python/tests/test_base_install/test_graphdb/test_latest_graph.py
index 5e6708cac2..a3e63c3ec3 100644
--- a/python/tests/test_base_install/test_graphdb/test_latest_graph.py
+++ b/python/tests/test_base_install/test_graphdb/test_latest_graph.py
@@ -59,7 +59,7 @@ def test_node_latest():
assert not g.node(2).latest().is_active()
wg = g.window(5, 12)
- assert wg.node(1).latest().history() == [10]
+ assert wg.node(1).latest().history.collect() == [10]
assert g.nodes.latest().id.collect() == [1, 2, 3]
assert wg.nodes.latest().id.collect() == [1, 2]
@@ -170,7 +170,7 @@ def test_persistent_node_latest():
wg = g.window(5, 12)
assert wg.latest_time == 10
assert wg.earliest_time == 5
- check_arr(wg.node(1).latest().history(), [10])
+ check_arr(wg.node(1).latest().history.t.collect(), [10])
check_arr(g.nodes.latest().id.collect(), [1, 2, 3])
check_arr(wg.nodes.latest().id.collect(), [1, 2])
diff --git a/python/tests/test_base_install/test_graphdb/test_persistent_graph.py b/python/tests/test_base_install/test_graphdb/test_persistent_graph.py
index c80a54c631..67e55cf5ea 100644
--- a/python/tests/test_base_install/test_graphdb/test_persistent_graph.py
+++ b/python/tests/test_base_install/test_graphdb/test_persistent_graph.py
@@ -17,7 +17,7 @@ def test_hanging_edges():
assert G.at(6).count_edges() == 0
assert G.latest_time == 5
assert G.at(G.latest_time).count_edges() == 0
- assert G.at(G.latest_time - 1).count_edges() == 0
+ assert G.at(G.latest_time.t - 1).count_edges() == 0
def test_overlapping_times():
diff --git a/python/tests/test_base_install/test_graphdb/test_rolling_expanding_alignment.py b/python/tests/test_base_install/test_graphdb/test_rolling_expanding_alignment.py
index 9527fea572..1a65fb0193 100644
--- a/python/tests/test_base_install/test_graphdb/test_rolling_expanding_alignment.py
+++ b/python/tests/test_base_install/test_graphdb/test_rolling_expanding_alignment.py
@@ -52,18 +52,14 @@ def test_rolling_month_alignment_default_true(example_graph):
[(exp0_start, exp0_end), (exp1_start, exp1_end), (exp2_start, exp2_end)]
):
w = windows[i]
- start = w.start_date_time
- end = w.end_date_time
+ start = w.start.dt
+ end = w.end.dt
assert start == exp_start, f"window[{i}] start: {start} != {exp_start}"
assert end == exp_end, f"window[{i}] end: {end} != {exp_end}"
# check last window for errors at boundary. Latest time is 2025-11-22 21:45:30
- assert windows[-1].start_date_time == datetime(
- 2025, 11, 1, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[-1].end_date_time == datetime(
- 2025, 12, 1, 0, 0, 0, tzinfo=timezone.utc
- )
+ assert windows[-1].start.dt == datetime(2025, 11, 1, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[-1].end.dt == datetime(2025, 12, 1, 0, 0, 0, tzinfo=timezone.utc)
def test_rolling_day_alignment_default_true(example_graph):
@@ -77,20 +73,16 @@ def test_rolling_day_alignment_default_true(example_graph):
exp2_start = datetime(2025, 3, 17, 0, 0, 0, 0, tzinfo=timezone.utc)
exp2_end = datetime(2025, 3, 18, 0, 0, 0, 0, tzinfo=timezone.utc)
- s0, e0 = windows[0].start_date_time, windows[0].end_date_time
- s1, e1 = windows[1].start_date_time, windows[1].end_date_time
- s2, e2 = windows[2].start_date_time, windows[2].end_date_time
+ s0, e0 = windows[0].start.dt, windows[0].end.dt
+ s1, e1 = windows[1].start.dt, windows[1].end.dt
+ s2, e2 = windows[2].start.dt, windows[2].end.dt
assert s0 == exp0_start and e0 == exp0_end
assert s1 == exp1_start and e1 == exp1_end
assert s2 == exp2_start and e2 == exp2_end
# Latest time is 2025-11-22 21:45:30
- assert windows[-1].start_date_time == datetime(
- 2025, 11, 22, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[-1].end_date_time == datetime(
- 2025, 11, 23, 0, 0, 0, tzinfo=timezone.utc
- )
+ assert windows[-1].start.dt == datetime(2025, 11, 22, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[-1].end.dt == datetime(2025, 11, 23, 0, 0, 0, tzinfo=timezone.utc)
def test_rolling_month_and_day_alignment_default_true(example_graph):
@@ -104,51 +96,31 @@ def test_rolling_month_and_day_alignment_default_true(example_graph):
exp2_start = datetime(2025, 5, 17, 0, 0, 0, 0, tzinfo=timezone.utc)
exp2_end = datetime(2025, 6, 18, 0, 0, 0, 0, tzinfo=timezone.utc)
- s0, e0 = windows[0].start_date_time, windows[0].end_date_time
- s1, e1 = windows[1].start_date_time, windows[1].end_date_time
- s2, e2 = windows[2].start_date_time, windows[2].end_date_time
+ s0, e0 = windows[0].start.dt, windows[0].end.dt
+ s1, e1 = windows[1].start.dt, windows[1].end.dt
+ s2, e2 = windows[2].start.dt, windows[2].end.dt
assert s0 == exp0_start and e0 == exp0_end
assert s1 == exp1_start and e1 == exp1_end
assert s2 == exp2_start and e2 == exp2_end
# Latest time is 2025-11-22 21:45:30
- assert windows[-1].start_date_time == datetime(
- 2025, 10, 22, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[-1].end_date_time == datetime(
- 2025, 11, 23, 0, 0, 0, tzinfo=timezone.utc
- )
+ assert windows[-1].start.dt == datetime(2025, 10, 22, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[-1].end.dt == datetime(2025, 11, 23, 0, 0, 0, tzinfo=timezone.utc)
def test_rolling_alignment_smallest_of_window_and_step(example_graph):
g: Graph = example_graph
windows = list(g.rolling("1 month", step="1 day"))
# Earliest event is 2025-03-15 14:37:52; day alignment (not month): 2025-03-15 00:00:00
- assert windows[0].start_date_time == datetime(
- 2025, 2, 16, 0, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[0].end_date_time == datetime(
- 2025, 3, 16, 0, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[1].start_date_time == datetime(
- 2025, 2, 17, 0, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[1].end_date_time == datetime(
- 2025, 3, 17, 0, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[2].start_date_time == datetime(
- 2025, 2, 18, 0, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[2].end_date_time == datetime(
- 2025, 3, 18, 0, 0, 0, 0, tzinfo=timezone.utc
- )
+ assert windows[0].start.dt == datetime(2025, 2, 16, 0, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[0].end.dt == datetime(2025, 3, 16, 0, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[1].start.dt == datetime(2025, 2, 17, 0, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[1].end.dt == datetime(2025, 3, 17, 0, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[2].start.dt == datetime(2025, 2, 18, 0, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[2].end.dt == datetime(2025, 3, 18, 0, 0, 0, 0, tzinfo=timezone.utc)
# Latest time is 2025-11-22 21:45:30
- assert windows[-1].start_date_time == datetime(
- 2025, 10, 23, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[-1].end_date_time == datetime(
- 2025, 11, 23, 0, 0, 0, tzinfo=timezone.utc
- )
+ assert windows[-1].start.dt == datetime(2025, 10, 23, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[-1].end.dt == datetime(2025, 11, 23, 0, 0, 0, tzinfo=timezone.utc)
def test_rolling_no_alignment_for_discrete_ms(example_graph):
@@ -156,95 +128,59 @@ def test_rolling_no_alignment_for_discrete_ms(example_graph):
# Discrete window (1 day in milliseconds), align_start = true but alignment_unit = None so no alignment
windows = list(g.rolling(86400000))
# expect no alignment
- assert windows[0].start_date_time == datetime(
- 2025, 3, 15, 14, 37, 52, tzinfo=timezone.utc
- )
- assert windows[0].end_date_time == datetime(
- 2025, 3, 16, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert windows[0].start.dt == datetime(2025, 3, 15, 14, 37, 52, tzinfo=timezone.utc)
+ assert windows[0].end.dt == datetime(2025, 3, 16, 14, 37, 52, tzinfo=timezone.utc)
# Latest time is 2025-11-22 21:45:30
- assert windows[-1].start_date_time == datetime(
+ assert windows[-1].start.dt == datetime(
2025, 11, 22, 14, 37, 52, tzinfo=timezone.utc
)
- assert windows[-1].end_date_time == datetime(
- 2025, 11, 23, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert windows[-1].end.dt == datetime(2025, 11, 23, 14, 37, 52, tzinfo=timezone.utc)
def test_rolling_align_start_false(example_graph):
g: Graph = example_graph
windows = list(g.rolling("1 month", alignment_unit="unaligned"))
# no month alignment
- assert windows[0].start_date_time == datetime(
- 2025, 3, 15, 14, 37, 52, tzinfo=timezone.utc
- )
- assert windows[0].end_date_time == datetime(
- 2025, 4, 15, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert windows[0].start.dt == datetime(2025, 3, 15, 14, 37, 52, tzinfo=timezone.utc)
+ assert windows[0].end.dt == datetime(2025, 4, 15, 14, 37, 52, tzinfo=timezone.utc)
# Latest time is 2025-11-22 21:45:30
- assert windows[-1].start_date_time == datetime(
+ assert windows[-1].start.dt == datetime(
2025, 11, 15, 14, 37, 52, tzinfo=timezone.utc
)
- assert windows[-1].end_date_time == datetime(
- 2025, 12, 15, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert windows[-1].end.dt == datetime(2025, 12, 15, 14, 37, 52, tzinfo=timezone.utc)
def test_rolling_custom_align_day(example_graph):
g: Graph = example_graph
windows = list(g.rolling("1 month", alignment_unit="day"))
# no month alignment
- assert windows[0].start_date_time == datetime(
- 2025, 3, 15, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[0].end_date_time == datetime(
- 2025, 4, 15, 0, 0, 0, tzinfo=timezone.utc
- )
+ assert windows[0].start.dt == datetime(2025, 3, 15, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[0].end.dt == datetime(2025, 4, 15, 0, 0, 0, tzinfo=timezone.utc)
# Latest time is 2025-11-22 21:45:30
- assert windows[-1].start_date_time == datetime(
- 2025, 11, 15, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[-1].end_date_time == datetime(
- 2025, 12, 15, 0, 0, 0, tzinfo=timezone.utc
- )
+ assert windows[-1].start.dt == datetime(2025, 11, 15, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[-1].end.dt == datetime(2025, 12, 15, 0, 0, 0, tzinfo=timezone.utc)
def test_rolling_custom_align_hour(example_graph):
g: Graph = example_graph
windows = list(g.rolling("1 month", alignment_unit="hours"))
# no month alignment
- assert windows[0].start_date_time == datetime(
- 2025, 3, 15, 14, 0, 0, tzinfo=timezone.utc
- )
- assert windows[0].end_date_time == datetime(
- 2025, 4, 15, 14, 0, 0, tzinfo=timezone.utc
- )
+ assert windows[0].start.dt == datetime(2025, 3, 15, 14, 0, 0, tzinfo=timezone.utc)
+ assert windows[0].end.dt == datetime(2025, 4, 15, 14, 0, 0, tzinfo=timezone.utc)
# Latest time is 2025-11-22 21:45:30
- assert windows[-1].start_date_time == datetime(
- 2025, 11, 15, 14, 0, 0, tzinfo=timezone.utc
- )
- assert windows[-1].end_date_time == datetime(
- 2025, 12, 15, 14, 0, 0, tzinfo=timezone.utc
- )
+ assert windows[-1].start.dt == datetime(2025, 11, 15, 14, 0, 0, tzinfo=timezone.utc)
+ assert windows[-1].end.dt == datetime(2025, 12, 15, 14, 0, 0, tzinfo=timezone.utc)
def test_rolling_custom_align_day_with_step(example_graph):
g: Graph = example_graph
windows = list(g.rolling("1 month", step="1 week", alignment_unit="day"))
# no month alignment
- assert windows[0].start_date_time == datetime(
- 2025, 2, 22, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[0].end_date_time == datetime(
- 2025, 3, 22, 0, 0, 0, tzinfo=timezone.utc
- )
+ assert windows[0].start.dt == datetime(2025, 2, 22, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[0].end.dt == datetime(2025, 3, 22, 0, 0, 0, tzinfo=timezone.utc)
# Latest time is 2025-11-22 21:45:30
- assert windows[-1].start_date_time == datetime(
- 2025, 10, 29, 0, 0, 0, tzinfo=timezone.utc
- )
- assert windows[-1].end_date_time == datetime(
- 2025, 11, 29, 0, 0, 0, tzinfo=timezone.utc
- )
+ assert windows[-1].start.dt == datetime(2025, 10, 29, 0, 0, 0, tzinfo=timezone.utc)
+ assert windows[-1].end.dt == datetime(2025, 11, 29, 0, 0, 0, tzinfo=timezone.utc)
def test_expanding_day_alignment_default_true(example_graph):
@@ -253,12 +189,12 @@ def test_expanding_day_alignment_default_true(example_graph):
# With expanding, start is unbounded (None) on an unwindowed graph, so validate ends align to day boundaries
exp_end0 = datetime(2025, 3, 16, 0, 0, 0, tzinfo=timezone.utc)
exp_end1 = datetime(2025, 3, 17, 0, 0, 0, tzinfo=timezone.utc)
- assert ws[0].end_date_time == exp_end0
- assert ws[1].end_date_time == exp_end1
+ assert ws[0].end.dt == exp_end0
+ assert ws[1].end.dt == exp_end1
# Latest time is 2025-11-22 21:45:30
exp_end_last = datetime(2025, 11, 23, 0, 0, 0, tzinfo=timezone.utc)
- assert ws[-1].end_date_time == exp_end_last
+ assert ws[-1].end.dt == exp_end_last
def test_expanding_align_start_false(example_graph):
@@ -266,8 +202,8 @@ def test_expanding_align_start_false(example_graph):
ws = list(g.expanding("1 day", alignment_unit="unaligned"))
exp_end0 = datetime(2025, 3, 16, 14, 37, 52, tzinfo=timezone.utc)
exp_end_last = datetime(2025, 11, 23, 14, 37, 52, tzinfo=timezone.utc)
- assert ws[0].end_date_time == exp_end0
- assert ws[-1].end_date_time == exp_end_last
+ assert ws[0].end.dt == exp_end0
+ assert ws[-1].end.dt == exp_end_last
def test_expanding_custom_align_month(example_graph):
@@ -275,8 +211,8 @@ def test_expanding_custom_align_month(example_graph):
ws = list(g.expanding("1 day", alignment_unit="month"))
exp_end0 = datetime(2025, 3, 2, 0, 0, 0, tzinfo=timezone.utc)
exp_end_last = datetime(2025, 11, 23, 0, 0, 0, tzinfo=timezone.utc)
- assert ws[0].end_date_time == exp_end0
- assert ws[-1].end_date_time == exp_end_last
+ assert ws[0].end.dt == exp_end0
+ assert ws[-1].end.dt == exp_end_last
def test_expanding_custom_align_week(example_graph):
@@ -284,8 +220,8 @@ def test_expanding_custom_align_week(example_graph):
ws = list(g.expanding("1 day", alignment_unit="weeks"))
exp_end0 = datetime(2025, 3, 14, 0, 0, 0, tzinfo=timezone.utc)
exp_end_last = datetime(2025, 11, 23, 0, 0, 0, tzinfo=timezone.utc)
- assert ws[0].end_date_time == exp_end0
- assert ws[-1].end_date_time == exp_end_last
+ assert ws[0].end.dt == exp_end0
+ assert ws[-1].end.dt == exp_end_last
def test_week_alignment_epoch_buckets():
@@ -296,10 +232,10 @@ def test_week_alignment_epoch_buckets():
exp_start0 = datetime(1970, 1, 8, 0, 0, 0, tzinfo=timezone.utc)
exp_end0 = datetime(1970, 1, 15, 0, 0, 0, tzinfo=timezone.utc)
# only one window
- assert ws[0].start_date_time == exp_start0
- assert ws[0].end_date_time == exp_end0
- assert ws[-1].start_date_time == exp_start0
- assert ws[-1].end_date_time == exp_end0
+ assert ws[0].start.dt == exp_start0
+ assert ws[0].end.dt == exp_end0
+ assert ws[-1].start.dt == exp_start0
+ assert ws[-1].end.dt == exp_end0
def test_rolling_month_alignment_with_layers():
@@ -324,12 +260,12 @@ def test_rolling_month_alignment_with_layers():
exp3_end = datetime(2025, 6, 1, 0, 0, 0, tzinfo=timezone.utc)
w0, w1, w2, w3, w_last = windows[0], windows[1], windows[2], windows[3], windows[-1]
- assert w0.start_date_time == exp0_start and w0.end_date_time == exp0_end
- assert w1.start_date_time == exp1_start and w1.end_date_time == exp1_end
- assert w2.start_date_time == exp2_start and w2.end_date_time == exp2_end
- assert w3.start_date_time == exp3_start and w3.end_date_time == exp3_end
+ assert w0.start.dt == exp0_start and w0.end.dt == exp0_end
+ assert w1.start.dt == exp1_start and w1.end.dt == exp1_end
+ assert w2.start.dt == exp2_start and w2.end.dt == exp2_end
+ assert w3.start.dt == exp3_start and w3.end.dt == exp3_end
# only 3 windows
- assert w_last.start_date_time == exp3_start and w_last.end_date_time == exp3_end
+ assert w_last.start.dt == exp3_start and w_last.end.dt == exp3_end
def test_node_alignment(example_graph_with_edges):
@@ -344,42 +280,33 @@ def test_node_alignment(example_graph_with_edges):
exp_start_last = datetime(2025, 11, 22, 0, 0, tzinfo=timezone.utc)
exp_end_last = datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
- assert (
- windows[0].start_date_time == exp_start0
- and windows[0].end_date_time == exp_end0
- )
- assert (
- windows[1].start_date_time == exp_start1
- and windows[1].end_date_time == exp_end1
- )
+ assert windows[0].start.dt == exp_start0 and windows[0].end.dt == exp_end0
+ assert windows[1].start.dt == exp_start1 and windows[1].end.dt == exp_end1
# Latest time is 2025-11-22 21:45:30
- assert (
- windows[-1].start_date_time == exp_start_last
- and windows[-1].end_date_time == exp_end_last
- )
+ assert windows[-1].start.dt == exp_start_last and windows[-1].end.dt == exp_end_last
expand_windows = list(n1.expanding("1 day"))
- assert expand_windows[0].end_date_time == exp_end0
- assert expand_windows[1].end_date_time == exp_end1
- assert expand_windows[-1].end_date_time == exp_end_last
+ assert expand_windows[0].end.dt == exp_end0
+ assert expand_windows[1].end.dt == exp_end1
+ assert expand_windows[-1].end.dt == exp_end_last
def test_nodes_alignment(example_graph_with_edges):
g: Graph = example_graph_with_edges
# there is only one node "1"
ws = list(g.nodes.rolling("1 day"))
- assert ws[0].start_date_time == datetime(2025, 3, 15, 0, 0, tzinfo=timezone.utc)
- assert ws[0].end_date_time == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
- assert ws[1].start_date_time == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
- assert ws[1].end_date_time == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
+ assert ws[0].start.dt == datetime(2025, 3, 15, 0, 0, tzinfo=timezone.utc)
+ assert ws[0].end.dt == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
+ assert ws[1].start.dt == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
+ assert ws[1].end.dt == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
# Latest time is 2025-11-22 21:45:30
- assert ws[-1].start_date_time == datetime(2025, 11, 22, 0, 0, tzinfo=timezone.utc)
- assert ws[-1].end_date_time == datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
+ assert ws[-1].start.dt == datetime(2025, 11, 22, 0, 0, tzinfo=timezone.utc)
+ assert ws[-1].end.dt == datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
exp_ws = list(g.nodes.expanding("1 day"))
- assert exp_ws[0].end_date_time == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
- assert exp_ws[1].end_date_time == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
- assert exp_ws[-1].end_date_time == datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
+ assert exp_ws[0].end.dt == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
+ assert exp_ws[1].end.dt == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
+ assert exp_ws[-1].end.dt == datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
def test_edge_alignment(example_graph_with_edges):
@@ -387,37 +314,37 @@ def test_edge_alignment(example_graph_with_edges):
e = g.edge(1, 2)
assert e is not None
ws = list(e.rolling("1 day"))
- assert ws[0].start_date_time == datetime(2025, 3, 15, 0, 0, tzinfo=timezone.utc)
- assert ws[0].end_date_time == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
- assert ws[1].start_date_time == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
- assert ws[1].end_date_time == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
+ assert ws[0].start.dt == datetime(2025, 3, 15, 0, 0, tzinfo=timezone.utc)
+ assert ws[0].end.dt == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
+ assert ws[1].start.dt == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
+ assert ws[1].end.dt == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
# Latest time is 2025-11-22 21:45:30
# even if we call it on an edge, we still get all windows where the underlying graph is valid
- assert ws[-1].start_date_time == datetime(2025, 11, 22, 0, 0, tzinfo=timezone.utc)
- assert ws[-1].end_date_time == datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
+ assert ws[-1].start.dt == datetime(2025, 11, 22, 0, 0, tzinfo=timezone.utc)
+ assert ws[-1].end.dt == datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
exp_ws = list(e.expanding("1 day"))
- assert exp_ws[0].end_date_time == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
- assert exp_ws[1].end_date_time == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
- assert exp_ws[-1].end_date_time == datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
+ assert exp_ws[0].end.dt == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
+ assert exp_ws[1].end.dt == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
+ assert exp_ws[-1].end.dt == datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
def test_edges_alignment(example_graph_with_edges):
g: Graph = example_graph_with_edges
ws = list(g.edges.rolling("1 day"))
- assert ws[0].start_date_time == datetime(2025, 3, 15, 0, 0, tzinfo=timezone.utc)
- assert ws[0].end_date_time == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
- assert ws[1].start_date_time == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
- assert ws[1].end_date_time == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
+ assert ws[0].start.dt == datetime(2025, 3, 15, 0, 0, tzinfo=timezone.utc)
+ assert ws[0].end.dt == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
+ assert ws[1].start.dt == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
+ assert ws[1].end.dt == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
# Latest time is 2025-11-22 21:45:30
# even if we call it on an edge, we still get all windows where the underlying graph is valid
- assert ws[-1].start_date_time == datetime(2025, 11, 22, 0, 0, tzinfo=timezone.utc)
- assert ws[-1].end_date_time == datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
+ assert ws[-1].start.dt == datetime(2025, 11, 22, 0, 0, tzinfo=timezone.utc)
+ assert ws[-1].end.dt == datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
exp_ws = list(g.edges.expanding("1 day"))
- assert exp_ws[0].end_date_time == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
- assert exp_ws[1].end_date_time == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
- assert exp_ws[-1].end_date_time == datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
+ assert exp_ws[0].end.dt == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
+ assert exp_ws[1].end.dt == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
+ assert exp_ws[-1].end.dt == datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
def test_path_from_node_neighbours_alignment(example_graph_with_edges):
@@ -425,21 +352,19 @@ def test_path_from_node_neighbours_alignment(example_graph_with_edges):
n1 = g.node(1)
assert n1 is not None
ws = list(n1.neighbours.rolling("1 hour"))
- assert ws[0].start_date_time == datetime(2025, 3, 15, 14, 0, tzinfo=timezone.utc)
- assert ws[0].end_date_time == datetime(2025, 3, 15, 15, 0, tzinfo=timezone.utc)
- assert ws[1].start_date_time == datetime(2025, 3, 15, 15, 0, tzinfo=timezone.utc)
- assert ws[1].end_date_time == datetime(2025, 3, 15, 16, 0, tzinfo=timezone.utc)
+ assert ws[0].start.dt == datetime(2025, 3, 15, 14, 0, tzinfo=timezone.utc)
+ assert ws[0].end.dt == datetime(2025, 3, 15, 15, 0, tzinfo=timezone.utc)
+ assert ws[1].start.dt == datetime(2025, 3, 15, 15, 0, tzinfo=timezone.utc)
+ assert ws[1].end.dt == datetime(2025, 3, 15, 16, 0, tzinfo=timezone.utc)
# Latest time is 2025-11-22 21:45:30
# even if we call it on an edge, we still get all windows where the underlying graph is valid
- assert ws[-1].start_date_time == datetime(2025, 11, 22, 21, 0, tzinfo=timezone.utc)
- assert ws[-1].end_date_time == datetime(2025, 11, 22, 22, 0, tzinfo=timezone.utc)
+ assert ws[-1].start.dt == datetime(2025, 11, 22, 21, 0, tzinfo=timezone.utc)
+ assert ws[-1].end.dt == datetime(2025, 11, 22, 22, 0, tzinfo=timezone.utc)
exp_ws = list(n1.neighbours.expanding("1 hour"))
- assert exp_ws[0].end_date_time == datetime(2025, 3, 15, 15, 0, tzinfo=timezone.utc)
- assert exp_ws[1].end_date_time == datetime(2025, 3, 15, 16, 0, tzinfo=timezone.utc)
- assert exp_ws[-1].end_date_time == datetime(
- 2025, 11, 22, 22, 0, tzinfo=timezone.utc
- )
+ assert exp_ws[0].end.dt == datetime(2025, 3, 15, 15, 0, tzinfo=timezone.utc)
+ assert exp_ws[1].end.dt == datetime(2025, 3, 15, 16, 0, tzinfo=timezone.utc)
+ assert exp_ws[-1].end.dt == datetime(2025, 11, 22, 22, 0, tzinfo=timezone.utc)
def test_path_from_graph_neighbours_alignment(example_graph_with_edges):
@@ -447,20 +372,18 @@ def test_path_from_graph_neighbours_alignment(example_graph_with_edges):
# there is only one node
neigh = g.nodes.neighbours
ws = list(neigh.rolling("2 hours"))
- assert ws[0].start_date_time == datetime(2025, 3, 15, 14, 0, tzinfo=timezone.utc)
- assert ws[0].end_date_time == datetime(2025, 3, 15, 16, 0, tzinfo=timezone.utc)
- assert ws[1].start_date_time == datetime(2025, 3, 15, 16, 0, tzinfo=timezone.utc)
- assert ws[1].end_date_time == datetime(2025, 3, 15, 18, 0, tzinfo=timezone.utc)
+ assert ws[0].start.dt == datetime(2025, 3, 15, 14, 0, tzinfo=timezone.utc)
+ assert ws[0].end.dt == datetime(2025, 3, 15, 16, 0, tzinfo=timezone.utc)
+ assert ws[1].start.dt == datetime(2025, 3, 15, 16, 0, tzinfo=timezone.utc)
+ assert ws[1].end.dt == datetime(2025, 3, 15, 18, 0, tzinfo=timezone.utc)
# Latest time is 2025-11-22 21:45:30
- assert ws[-1].start_date_time == datetime(2025, 11, 22, 20, 0, tzinfo=timezone.utc)
- assert ws[-1].end_date_time == datetime(2025, 11, 22, 22, 0, tzinfo=timezone.utc)
+ assert ws[-1].start.dt == datetime(2025, 11, 22, 20, 0, tzinfo=timezone.utc)
+ assert ws[-1].end.dt == datetime(2025, 11, 22, 22, 0, tzinfo=timezone.utc)
exp_ws = list(neigh.expanding("2 hours"))
- assert exp_ws[0].end_date_time == datetime(2025, 3, 15, 16, 0, tzinfo=timezone.utc)
- assert exp_ws[1].end_date_time == datetime(2025, 3, 15, 18, 0, tzinfo=timezone.utc)
- assert exp_ws[-1].end_date_time == datetime(
- 2025, 11, 22, 22, 0, tzinfo=timezone.utc
- )
+ assert exp_ws[0].end.dt == datetime(2025, 3, 15, 16, 0, tzinfo=timezone.utc)
+ assert exp_ws[1].end.dt == datetime(2025, 3, 15, 18, 0, tzinfo=timezone.utc)
+ assert exp_ws[-1].end.dt == datetime(2025, 11, 22, 22, 0, tzinfo=timezone.utc)
def test_mismatched_window_step_basic(example_graph_with_edges):
@@ -475,12 +398,9 @@ def test_mismatched_window_step_basic(example_graph_with_edges):
# Latest time is 2025-11-22 21:45:30
exp_start_last = datetime(2025, 11, 21, 22, 37, 52, tzinfo=timezone.utc)
exp_end_last = datetime(2025, 11, 22, 22, 37, 52, tzinfo=timezone.utc)
- assert ws[0].start_date_time == exp_start0 and ws[0].end_date_time == exp_end0
- assert ws[1].start_date_time == exp_start1 and ws[1].end_date_time == exp_end1
- assert (
- ws[-1].start_date_time == exp_start_last
- and ws[-1].end_date_time == exp_end_last
- )
+ assert ws[0].start.dt == exp_start0 and ws[0].end.dt == exp_end0
+ assert ws[1].start.dt == exp_start1 and ws[1].end.dt == exp_end1
+ assert ws[-1].start.dt == exp_start_last and ws[-1].end.dt == exp_end_last
def test_mismatched_window_step_basic2(example_graph_with_edges):
@@ -489,14 +409,14 @@ def test_mismatched_window_step_basic2(example_graph_with_edges):
ws = list(g.rolling(3600000, step="1 day"))
# Earliest time: 2025-03-15 14:37:52
# The first window is past the first item because we are aligning when the step is larger than the window.
- assert ws[0].start_date_time == datetime(2025, 3, 15, 23, 0, tzinfo=timezone.utc)
- assert ws[0].end_date_time == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
- assert ws[1].start_date_time == datetime(2025, 3, 16, 23, 0, tzinfo=timezone.utc)
- assert ws[1].end_date_time == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
+ assert ws[0].start.dt == datetime(2025, 3, 15, 23, 0, tzinfo=timezone.utc)
+ assert ws[0].end.dt == datetime(2025, 3, 16, 0, 0, tzinfo=timezone.utc)
+ assert ws[1].start.dt == datetime(2025, 3, 16, 23, 0, tzinfo=timezone.utc)
+ assert ws[1].end.dt == datetime(2025, 3, 17, 0, 0, tzinfo=timezone.utc)
# Latest time is 2025-11-22 21:45:30
# The last window is before the last item because we are aligning when the step is larger than the window.
- assert ws[-1].start_date_time == datetime(2025, 11, 21, 23, 0, tzinfo=timezone.utc)
- assert ws[-1].end_date_time == datetime(2025, 11, 22, 0, 0, tzinfo=timezone.utc)
+ assert ws[-1].start.dt == datetime(2025, 11, 21, 23, 0, tzinfo=timezone.utc)
+ assert ws[-1].end.dt == datetime(2025, 11, 22, 0, 0, tzinfo=timezone.utc)
def test_window_different_intervals_1(example_graph_with_edges):
@@ -512,22 +432,10 @@ def test_window_different_intervals_1(example_graph_with_edges):
# Latest time is 2025-11-22 21:45:30
exp_start_last = datetime(2025, 11, 3, 0, 0, tzinfo=timezone.utc)
exp_end_last = datetime(2025, 11, 24, 0, 0, tzinfo=timezone.utc)
- assert (
- windows[0].start_date_time == exp_start0
- and windows[0].end_date_time == exp_end0
- )
- assert (
- windows[1].start_date_time == exp_start1
- and windows[1].end_date_time == exp_end1
- )
- assert (
- windows[2].start_date_time == exp_start2
- and windows[2].end_date_time == exp_end2
- )
- assert (
- windows[-1].start_date_time == exp_start_last
- and windows[-1].end_date_time == exp_end_last
- )
+ assert windows[0].start.dt == exp_start0 and windows[0].end.dt == exp_end0
+ assert windows[1].start.dt == exp_start1 and windows[1].end.dt == exp_end1
+ assert windows[2].start.dt == exp_start2 and windows[2].end.dt == exp_end2
+ assert windows[-1].start.dt == exp_start_last and windows[-1].end.dt == exp_end_last
def test_window_different_intervals_2(example_graph_with_edges):
@@ -543,22 +451,10 @@ def test_window_different_intervals_2(example_graph_with_edges):
# Latest time is 2025-11-22 21:45:30
exp_start_last = datetime(2025, 10, 31, 0, 0, tzinfo=timezone.utc)
exp_end_last = datetime(2025, 11, 23, 0, 0, tzinfo=timezone.utc)
- assert (
- windows[0].start_date_time == exp_start0
- and windows[0].end_date_time == exp_end0
- )
- assert (
- windows[1].start_date_time == exp_start1
- and windows[1].end_date_time == exp_end1
- )
- assert (
- windows[2].start_date_time == exp_start2
- and windows[2].end_date_time == exp_end2
- )
- assert (
- windows[-1].start_date_time == exp_start_last
- and windows[-1].end_date_time == exp_end_last
- )
+ assert windows[0].start.dt == exp_start0 and windows[0].end.dt == exp_end0
+ assert windows[1].start.dt == exp_start1 and windows[1].end.dt == exp_end1
+ assert windows[2].start.dt == exp_start2 and windows[2].end.dt == exp_end2
+ assert windows[-1].start.dt == exp_start_last and windows[-1].end.dt == exp_end_last
def test_window_different_intervals_3(example_graph_with_edges):
@@ -574,22 +470,10 @@ def test_window_different_intervals_3(example_graph_with_edges):
# Latest time is 2025-11-22 21:45:30
exp_start_last = datetime(2025, 11, 2, 0, 0, tzinfo=timezone.utc)
exp_end_last = datetime(2025, 11, 25, 0, 0, tzinfo=timezone.utc)
- assert (
- windows[0].start_date_time == exp_start0
- and windows[0].end_date_time == exp_end0
- )
- assert (
- windows[1].start_date_time == exp_start1
- and windows[1].end_date_time == exp_end1
- )
- assert (
- windows[2].start_date_time == exp_start2
- and windows[2].end_date_time == exp_end2
- )
- assert (
- windows[-1].start_date_time == exp_start_last
- and windows[-1].end_date_time == exp_end_last
- )
+ assert windows[0].start.dt == exp_start0 and windows[0].end.dt == exp_end0
+ assert windows[1].start.dt == exp_start1 and windows[1].end.dt == exp_end1
+ assert windows[2].start.dt == exp_start2 and windows[2].end.dt == exp_end2
+ assert windows[-1].start.dt == exp_start_last and windows[-1].end.dt == exp_end_last
def test_window_different_intervals_4(example_graph_with_edges):
@@ -607,19 +491,7 @@ def test_window_different_intervals_4(example_graph_with_edges):
# The last window is before the last item because we are aligning when the step is larger than the window.
exp_start_last = datetime(2025, 11, 18, 0, 0, tzinfo=timezone.utc)
exp_end_last = datetime(2025, 11, 20, 0, 0, tzinfo=timezone.utc)
- assert (
- windows[0].start_date_time == exp_start0
- and windows[0].end_date_time == exp_end0
- )
- assert (
- windows[1].start_date_time == exp_start1
- and windows[1].end_date_time == exp_end1
- )
- assert (
- windows[2].start_date_time == exp_start2
- and windows[2].end_date_time == exp_end2
- )
- assert (
- windows[-1].start_date_time == exp_start_last
- and windows[-1].end_date_time == exp_end_last
- )
+ assert windows[0].start.dt == exp_start0 and windows[0].end.dt == exp_end0
+ assert windows[1].start.dt == exp_start1 and windows[1].end.dt == exp_end1
+ assert windows[2].start.dt == exp_start2 and windows[2].end.dt == exp_end2
+ assert windows[-1].start.dt == exp_start_last and windows[-1].end.dt == exp_end_last
diff --git a/python/tests/test_base_install/test_graphdb/test_rolling_weird_alignment.py b/python/tests/test_base_install/test_graphdb/test_rolling_weird_alignment.py
index 4e4809cead..c36fa23ffd 100644
--- a/python/tests/test_base_install/test_graphdb/test_rolling_weird_alignment.py
+++ b/python/tests/test_base_install/test_graphdb/test_rolling_weird_alignment.py
@@ -22,34 +22,28 @@ def test_31st_lines_up_on_30th():
)[:3]
# April only has 30 days, so ends are on 30th. The window is 1 month so the start is 1 month ago.
- assert window[0].start_date_time == datetime(2025, 3, 30, 0, 0, tzinfo=timezone.utc)
- assert window[0].end_date_time == datetime(2025, 4, 30, 0, 0, tzinfo=timezone.utc)
- assert window_ms[0].start_date_time == datetime(
+ assert window[0].start.dt == datetime(2025, 3, 30, 0, 0, tzinfo=timezone.utc)
+ assert window[0].end.dt == datetime(2025, 4, 30, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[0].start.dt == datetime(
2025, 3, 30, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[0].end_date_time == datetime(
- 2025, 4, 30, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[0].end.dt == datetime(2025, 4, 30, 14, 37, 52, tzinfo=timezone.utc)
# importantly, the ends here are on the 31st instead if 30th
- assert window[1].start_date_time == datetime(2025, 4, 30, 0, 0, tzinfo=timezone.utc)
- assert window[1].end_date_time == datetime(2025, 5, 31, 0, 0, tzinfo=timezone.utc)
- assert window_ms[1].start_date_time == datetime(
+ assert window[1].start.dt == datetime(2025, 4, 30, 0, 0, tzinfo=timezone.utc)
+ assert window[1].end.dt == datetime(2025, 5, 31, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[1].start.dt == datetime(
2025, 4, 30, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[1].end_date_time == datetime(
- 2025, 5, 31, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[1].end.dt == datetime(2025, 5, 31, 14, 37, 52, tzinfo=timezone.utc)
# ends go back to the 30th
- assert window[2].start_date_time == datetime(2025, 5, 30, 0, 0, tzinfo=timezone.utc)
- assert window[2].end_date_time == datetime(2025, 6, 30, 0, 0, tzinfo=timezone.utc)
- assert window_ms[2].start_date_time == datetime(
+ assert window[2].start.dt == datetime(2025, 5, 30, 0, 0, tzinfo=timezone.utc)
+ assert window[2].end.dt == datetime(2025, 6, 30, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[2].start.dt == datetime(
2025, 5, 30, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[2].end_date_time == datetime(
- 2025, 6, 30, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[2].end.dt == datetime(2025, 6, 30, 14, 37, 52, tzinfo=timezone.utc)
def test_30th_never_lines_up_on_31st():
@@ -66,32 +60,26 @@ def test_30th_never_lines_up_on_31st():
)[:3]
# never line up on 31st if the first event is on the 30th
- assert window[0].start_date_time == datetime(2025, 4, 30, 0, 0, tzinfo=timezone.utc)
- assert window[0].end_date_time == datetime(2025, 5, 30, 0, 0, tzinfo=timezone.utc)
- assert window_ms[0].start_date_time == datetime(
+ assert window[0].start.dt == datetime(2025, 4, 30, 0, 0, tzinfo=timezone.utc)
+ assert window[0].end.dt == datetime(2025, 5, 30, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[0].start.dt == datetime(
2025, 4, 30, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[0].end_date_time == datetime(
- 2025, 5, 30, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[0].end.dt == datetime(2025, 5, 30, 14, 37, 52, tzinfo=timezone.utc)
- assert window[1].start_date_time == datetime(2025, 5, 30, 0, 0, tzinfo=timezone.utc)
- assert window[1].end_date_time == datetime(2025, 6, 30, 0, 0, tzinfo=timezone.utc)
- assert window_ms[1].start_date_time == datetime(
+ assert window[1].start.dt == datetime(2025, 5, 30, 0, 0, tzinfo=timezone.utc)
+ assert window[1].end.dt == datetime(2025, 6, 30, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[1].start.dt == datetime(
2025, 5, 30, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[1].end_date_time == datetime(
- 2025, 6, 30, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[1].end.dt == datetime(2025, 6, 30, 14, 37, 52, tzinfo=timezone.utc)
- assert window[2].start_date_time == datetime(2025, 6, 30, 0, 0, tzinfo=timezone.utc)
- assert window[2].end_date_time == datetime(2025, 7, 30, 0, 0, tzinfo=timezone.utc)
- assert window_ms[2].start_date_time == datetime(
+ assert window[2].start.dt == datetime(2025, 6, 30, 0, 0, tzinfo=timezone.utc)
+ assert window[2].end.dt == datetime(2025, 7, 30, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[2].start.dt == datetime(
2025, 6, 30, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[2].end_date_time == datetime(
- 2025, 7, 30, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[2].end.dt == datetime(2025, 7, 30, 14, 37, 52, tzinfo=timezone.utc)
def test_31st_lines_up_on_31st_july():
@@ -109,44 +97,28 @@ def test_31st_lines_up_on_31st_july():
)[:3]
# starts and ends both 31st because july and august both have 31 days
- assert window[0].start_date_time == datetime(
- 2025, 7, 31, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[0].end_date_time == datetime(
- 2025, 8, 31, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window_ms[0].start_date_time == datetime(
+ assert window[0].start.dt == datetime(2025, 7, 31, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[0].end.dt == datetime(2025, 8, 31, 0, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[0].start.dt == datetime(
2025, 7, 31, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[0].end_date_time == datetime(
- 2025, 8, 31, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[0].end.dt == datetime(2025, 8, 31, 14, 37, 52, tzinfo=timezone.utc)
# ends are now 30th so starts as well
- assert window[1].start_date_time == datetime(
- 2025, 8, 30, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[1].end_date_time == datetime(
- 2025, 9, 30, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window_ms[1].start_date_time == datetime(
+ assert window[1].start.dt == datetime(2025, 8, 30, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[1].end.dt == datetime(2025, 9, 30, 0, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[1].start.dt == datetime(
2025, 8, 30, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[1].end_date_time == datetime(
- 2025, 9, 30, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[1].end.dt == datetime(2025, 9, 30, 14, 37, 52, tzinfo=timezone.utc)
# ends go back to 31st, but start months only have 30 days (October 31st - "1 month" = September 30th) september only has 30 days
- assert window[2].start_date_time == datetime(
- 2025, 9, 30, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[2].end_date_time == datetime(
- 2025, 10, 31, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window_ms[2].start_date_time == datetime(
+ assert window[2].start.dt == datetime(2025, 9, 30, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[2].end.dt == datetime(2025, 10, 31, 0, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[2].start.dt == datetime(
2025, 9, 30, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[2].end_date_time == datetime(
+ assert window_ms[2].end.dt == datetime(
2025, 10, 31, 14, 37, 52, tzinfo=timezone.utc
)
@@ -166,40 +138,28 @@ def test_31st_lines_up_on_28th_february():
)[:3]
# december and january both have 31 days so start and end are both on the 31st
- assert window[0].start_date_time == datetime(
- 2025, 12, 31, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[0].end_date_time == datetime(2026, 1, 31, 0, 0, tzinfo=timezone.utc)
- assert window_ms[0].start_date_time == datetime(
+ assert window[0].start.dt == datetime(2025, 12, 31, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[0].end.dt == datetime(2026, 1, 31, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[0].start.dt == datetime(
2025, 12, 31, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[0].end_date_time == datetime(
- 2026, 1, 31, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[0].end.dt == datetime(2026, 1, 31, 14, 37, 52, tzinfo=timezone.utc)
# february has 28 days so the ends line up on the 28th, and the starts follow
- assert window[1].start_date_time == datetime(
- 2026, 1, 28, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[1].end_date_time == datetime(2026, 2, 28, 0, 0, tzinfo=timezone.utc)
- assert window_ms[1].start_date_time == datetime(
+ assert window[1].start.dt == datetime(2026, 1, 28, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[1].end.dt == datetime(2026, 2, 28, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[1].start.dt == datetime(
2026, 1, 28, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[1].end_date_time == datetime(
- 2026, 2, 28, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[1].end.dt == datetime(2026, 2, 28, 14, 37, 52, tzinfo=timezone.utc)
# march has 31 days, but March 31st - "1 month" = February 28th (feb 31st doesn't exist)
- assert window[2].start_date_time == datetime(
- 2026, 2, 28, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[2].end_date_time == datetime(2026, 3, 31, 0, 0, tzinfo=timezone.utc)
- assert window_ms[2].start_date_time == datetime(
+ assert window[2].start.dt == datetime(2026, 2, 28, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[2].end.dt == datetime(2026, 3, 31, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[2].start.dt == datetime(
2026, 2, 28, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[2].end_date_time == datetime(
- 2026, 3, 31, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[2].end.dt == datetime(2026, 3, 31, 14, 37, 52, tzinfo=timezone.utc)
# starting on January 31st
g = Graph()
@@ -215,40 +175,28 @@ def test_31st_lines_up_on_28th_february():
)[:3]
# february has 28 days so the ends line up on the 28th, and the starts follow
- assert window[0].start_date_time == datetime(
- 2025, 1, 28, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[0].end_date_time == datetime(2025, 2, 28, 0, 0, tzinfo=timezone.utc)
- assert window_ms[0].start_date_time == datetime(
+ assert window[0].start.dt == datetime(2025, 1, 28, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[0].end.dt == datetime(2025, 2, 28, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[0].start.dt == datetime(
2025, 1, 28, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[0].end_date_time == datetime(
- 2025, 2, 28, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[0].end.dt == datetime(2025, 2, 28, 14, 37, 52, tzinfo=timezone.utc)
# march has 31 days, but March 31st - "1 month" = February 28th (feb 31st doesn't exist)
- assert window[1].start_date_time == datetime(
- 2025, 2, 28, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[1].end_date_time == datetime(2025, 3, 31, 0, 0, tzinfo=timezone.utc)
- assert window_ms[1].start_date_time == datetime(
+ assert window[1].start.dt == datetime(2025, 2, 28, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[1].end.dt == datetime(2025, 3, 31, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[1].start.dt == datetime(
2025, 2, 28, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[1].end_date_time == datetime(
- 2025, 3, 31, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[1].end.dt == datetime(2025, 3, 31, 14, 37, 52, tzinfo=timezone.utc)
# ends are now lined on 30th (April has 30 days)
- assert window[2].start_date_time == datetime(
- 2025, 3, 30, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[2].end_date_time == datetime(2025, 4, 30, 0, 0, tzinfo=timezone.utc)
- assert window_ms[2].start_date_time == datetime(
+ assert window[2].start.dt == datetime(2025, 3, 30, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[2].end.dt == datetime(2025, 4, 30, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[2].start.dt == datetime(
2025, 3, 30, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[2].end_date_time == datetime(
- 2025, 4, 30, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[2].end.dt == datetime(2025, 4, 30, 14, 37, 52, tzinfo=timezone.utc)
def test_31st_lines_up_on_29th_february():
@@ -266,40 +214,28 @@ def test_31st_lines_up_on_29th_february():
)[:3]
# december and january both have 31 days so start and end are both on the 31st
- assert window[0].start_date_time == datetime(
- 2023, 12, 31, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[0].end_date_time == datetime(2024, 1, 31, 0, 0, tzinfo=timezone.utc)
- assert window_ms[0].start_date_time == datetime(
+ assert window[0].start.dt == datetime(2023, 12, 31, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[0].end.dt == datetime(2024, 1, 31, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[0].start.dt == datetime(
2023, 12, 31, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[0].end_date_time == datetime(
- 2024, 1, 31, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[0].end.dt == datetime(2024, 1, 31, 14, 37, 52, tzinfo=timezone.utc)
# leap year february has 29 days so the ends line up on the 29th, and the starts follow
- assert window[1].start_date_time == datetime(
- 2024, 1, 29, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[1].end_date_time == datetime(2024, 2, 29, 0, 0, tzinfo=timezone.utc)
- assert window_ms[1].start_date_time == datetime(
+ assert window[1].start.dt == datetime(2024, 1, 29, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[1].end.dt == datetime(2024, 2, 29, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[1].start.dt == datetime(
2024, 1, 29, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[1].end_date_time == datetime(
- 2024, 2, 29, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[1].end.dt == datetime(2024, 2, 29, 14, 37, 52, tzinfo=timezone.utc)
# march has 31 days, but March 31st - "1 month" = February 29th (feb 31st doesn't exist)
- assert window[2].start_date_time == datetime(
- 2024, 2, 29, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[2].end_date_time == datetime(2024, 3, 31, 0, 0, tzinfo=timezone.utc)
- assert window_ms[2].start_date_time == datetime(
+ assert window[2].start.dt == datetime(2024, 2, 29, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[2].end.dt == datetime(2024, 3, 31, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[2].start.dt == datetime(
2024, 2, 29, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[2].end_date_time == datetime(
- 2024, 3, 31, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[2].end.dt == datetime(2024, 3, 31, 14, 37, 52, tzinfo=timezone.utc)
# starting on January 31st
g = Graph()
@@ -315,40 +251,28 @@ def test_31st_lines_up_on_29th_february():
)[:3]
# leap year february has 29 days so the ends line up on the 29th, and the starts follow
- assert window[0].start_date_time == datetime(
- 2024, 1, 29, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[0].end_date_time == datetime(2024, 2, 29, 0, 0, tzinfo=timezone.utc)
- assert window_ms[0].start_date_time == datetime(
+ assert window[0].start.dt == datetime(2024, 1, 29, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[0].end.dt == datetime(2024, 2, 29, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[0].start.dt == datetime(
2024, 1, 29, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[0].end_date_time == datetime(
- 2024, 2, 29, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[0].end.dt == datetime(2024, 2, 29, 14, 37, 52, tzinfo=timezone.utc)
# march has 31 days, but March 31st - "1 month" = February 29th (feb 31st doesn't exist)
- assert window[1].start_date_time == datetime(
- 2024, 2, 29, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[1].end_date_time == datetime(2024, 3, 31, 0, 0, tzinfo=timezone.utc)
- assert window_ms[1].start_date_time == datetime(
+ assert window[1].start.dt == datetime(2024, 2, 29, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[1].end.dt == datetime(2024, 3, 31, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[1].start.dt == datetime(
2024, 2, 29, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[1].end_date_time == datetime(
- 2024, 3, 31, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[1].end.dt == datetime(2024, 3, 31, 14, 37, 52, tzinfo=timezone.utc)
# ends are now lined on 30th (April has 30 days)
- assert window[2].start_date_time == datetime(
- 2024, 3, 30, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[2].end_date_time == datetime(2024, 4, 30, 0, 0, tzinfo=timezone.utc)
- assert window_ms[2].start_date_time == datetime(
+ assert window[2].start.dt == datetime(2024, 3, 30, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[2].end.dt == datetime(2024, 4, 30, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[2].start.dt == datetime(
2024, 3, 30, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[2].end_date_time == datetime(
- 2024, 4, 30, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[2].end.dt == datetime(2024, 4, 30, 14, 37, 52, tzinfo=timezone.utc)
def test_feb_28th_start_alignment_with_step():
@@ -375,55 +299,55 @@ def test_feb_28th_start_alignment_with_step():
)[:2]
# start aligns to february 28th because March 15th + "2 weeks" = March 29th - "1 month" = february 28th
- assert window_1_week[0].start_date_time == datetime(
+ assert window_1_week[0].start.dt == datetime(
2025, 2, 22, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_1_week[0].end_date_time == datetime(
+ assert window_1_week[0].end.dt == datetime(
2025, 3, 22, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_1_week_ms[0].start_date_time == datetime(
+ assert window_1_week_ms[0].start.dt == datetime(
2025, 2, 22, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_1_week_ms[0].end_date_time == datetime(
+ assert window_1_week_ms[0].end.dt == datetime(
2025, 3, 22, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_2_weeks[0].start_date_time == datetime(
+ assert window_2_weeks[0].start.dt == datetime(
2025, 2, 28, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_2_weeks[0].end_date_time == datetime(
+ assert window_2_weeks[0].end.dt == datetime(
2025, 3, 29, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_2_weeks_ms[0].start_date_time == datetime(
+ assert window_2_weeks_ms[0].start.dt == datetime(
2025, 2, 28, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_2_weeks_ms[0].end_date_time == datetime(
+ assert window_2_weeks_ms[0].end.dt == datetime(
2025, 3, 29, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_1_week[1].start_date_time == datetime(
+ assert window_1_week[1].start.dt == datetime(
2025, 2, 28, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_1_week[1].end_date_time == datetime(
+ assert window_1_week[1].end.dt == datetime(
2025, 3, 29, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_1_week_ms[1].start_date_time == datetime(
+ assert window_1_week_ms[1].start.dt == datetime(
2025, 2, 28, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_1_week_ms[1].end_date_time == datetime(
+ assert window_1_week_ms[1].end.dt == datetime(
2025, 3, 29, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_2_weeks[1].start_date_time == datetime(
+ assert window_2_weeks[1].start.dt == datetime(
2025, 3, 12, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_2_weeks[1].end_date_time == datetime(
+ assert window_2_weeks[1].end.dt == datetime(
2025, 4, 12, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_2_weeks_ms[1].start_date_time == datetime(
+ assert window_2_weeks_ms[1].start.dt == datetime(
2025, 3, 12, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_2_weeks_ms[1].end_date_time == datetime(
+ assert window_2_weeks_ms[1].end.dt == datetime(
2025, 4, 12, 14, 37, 52, tzinfo=timezone.utc
)
@@ -451,55 +375,55 @@ def test_feb_29th_start_alignment_with_step():
g.rolling("1 month", step="2 weeks", alignment_unit="millisecond")
)[:2]
- assert window_1_week[0].start_date_time == datetime(
+ assert window_1_week[0].start.dt == datetime(
2024, 2, 24, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_1_week[0].end_date_time == datetime(
+ assert window_1_week[0].end.dt == datetime(
2024, 3, 24, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_1_week_ms[0].start_date_time == datetime(
+ assert window_1_week_ms[0].start.dt == datetime(
2024, 2, 24, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_1_week_ms[0].end_date_time == datetime(
+ assert window_1_week_ms[0].end.dt == datetime(
2024, 3, 24, 14, 37, 52, tzinfo=timezone.utc
)
# march 31st - "1 month" = February 29th (leap year)
- assert window_2_weeks[0].start_date_time == datetime(
+ assert window_2_weeks[0].start.dt == datetime(
2024, 2, 29, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_2_weeks[0].end_date_time == datetime(
+ assert window_2_weeks[0].end.dt == datetime(
2024, 3, 31, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_2_weeks_ms[0].start_date_time == datetime(
+ assert window_2_weeks_ms[0].start.dt == datetime(
2024, 2, 29, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_2_weeks_ms[0].end_date_time == datetime(
+ assert window_2_weeks_ms[0].end.dt == datetime(
2024, 3, 31, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_1_week[1].start_date_time == datetime(
+ assert window_1_week[1].start.dt == datetime(
2024, 2, 29, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_1_week[1].end_date_time == datetime(
+ assert window_1_week[1].end.dt == datetime(
2024, 3, 31, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_1_week_ms[1].start_date_time == datetime(
+ assert window_1_week_ms[1].start.dt == datetime(
2024, 2, 29, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_1_week_ms[1].end_date_time == datetime(
+ assert window_1_week_ms[1].end.dt == datetime(
2024, 3, 31, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_2_weeks[1].start_date_time == datetime(
+ assert window_2_weeks[1].start.dt == datetime(
2024, 3, 14, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_2_weeks[1].end_date_time == datetime(
+ assert window_2_weeks[1].end.dt == datetime(
2024, 4, 14, 0, 0, 0, tzinfo=timezone.utc
)
- assert window_2_weeks_ms[1].start_date_time == datetime(
+ assert window_2_weeks_ms[1].start.dt == datetime(
2024, 3, 14, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_2_weeks_ms[1].end_date_time == datetime(
+ assert window_2_weeks_ms[1].end.dt == datetime(
2024, 4, 14, 14, 37, 52, tzinfo=timezone.utc
)
@@ -517,32 +441,20 @@ def test_feb_28th_no_weirdness():
g.rolling("1 month", alignment_unit="millisecond")
)[:3]
- assert window[0].start_date_time == datetime(
- 2025, 2, 28, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[0].end_date_time == datetime(
- 2025, 3, 28, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window_ms[0].start_date_time == datetime(
+ assert window[0].start.dt == datetime(2025, 2, 28, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[0].end.dt == datetime(2025, 3, 28, 0, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[0].start.dt == datetime(
2025, 2, 28, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[0].end_date_time == datetime(
- 2025, 3, 28, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[0].end.dt == datetime(2025, 3, 28, 14, 37, 52, tzinfo=timezone.utc)
# no weirdness
- assert window[1].start_date_time == datetime(
- 2025, 3, 28, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[1].end_date_time == datetime(
- 2025, 4, 28, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window_ms[1].start_date_time == datetime(
+ assert window[1].start.dt == datetime(2025, 3, 28, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[1].end.dt == datetime(2025, 4, 28, 0, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[1].start.dt == datetime(
2025, 3, 28, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[1].end_date_time == datetime(
- 2025, 4, 28, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[1].end.dt == datetime(2025, 4, 28, 14, 37, 52, tzinfo=timezone.utc)
def test_feb_29th_no_weirdness():
@@ -558,29 +470,17 @@ def test_feb_29th_no_weirdness():
g.rolling("1 month", alignment_unit="millisecond")
)[:3]
- assert window[0].start_date_time == datetime(
- 2024, 2, 29, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[0].end_date_time == datetime(
- 2024, 3, 29, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window_ms[0].start_date_time == datetime(
+ assert window[0].start.dt == datetime(2024, 2, 29, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[0].end.dt == datetime(2024, 3, 29, 0, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[0].start.dt == datetime(
2024, 2, 29, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[0].end_date_time == datetime(
- 2024, 3, 29, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[0].end.dt == datetime(2024, 3, 29, 14, 37, 52, tzinfo=timezone.utc)
# no weirdness
- assert window[1].start_date_time == datetime(
- 2024, 3, 29, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window[1].end_date_time == datetime(
- 2024, 4, 29, 0, 0, 0, tzinfo=timezone.utc
- )
- assert window_ms[1].start_date_time == datetime(
+ assert window[1].start.dt == datetime(2024, 3, 29, 0, 0, 0, tzinfo=timezone.utc)
+ assert window[1].end.dt == datetime(2024, 4, 29, 0, 0, 0, tzinfo=timezone.utc)
+ assert window_ms[1].start.dt == datetime(
2024, 3, 29, 14, 37, 52, tzinfo=timezone.utc
)
- assert window_ms[1].end_date_time == datetime(
- 2024, 4, 29, 14, 37, 52, tzinfo=timezone.utc
- )
+ assert window_ms[1].end.dt == datetime(2024, 4, 29, 14, 37, 52, tzinfo=timezone.utc)
diff --git a/python/tests/test_base_install/test_graphql/edit_graph/test_graphql.py b/python/tests/test_base_install/test_graphql/edit_graph/test_graphql.py
index 29bcb5dfa4..3f21bdca32 100644
--- a/python/tests/test_base_install/test_graphql/edit_graph/test_graphql.py
+++ b/python/tests/test_base_install/test_graphql/edit_graph/test_graphql.py
@@ -222,11 +222,23 @@ def test_graph_windows_and_layers_query():
window(start: 200, end: 800) {
node(name: "Frodo") {
after(time: 500) {
- history
+ history {
+ list {
+ timestamp
+ eventId
+ }
+ }
neighbours {
list {
- name
- before(time: 700) { history }
+ name
+ before(time: 300) {
+ history {
+ list {
+ timestamp
+ eventId
+ }
+ }
+ }
}
}
}
@@ -237,23 +249,63 @@ def test_graph_windows_and_layers_query():
"""
ra = """
{
- "graph": {
- "window": {
- "node": {
- "after": {
- "history": [555, 562],
- "neighbours": {
- "list": [
- {"name": "Gandalf", "before": {"history": [543, 555]}},
- {"name": "Bilbo", "before": {"history": [543, 555, 562]}}
- ]
- }
+ "graph": {
+ "window": {
+ "node": {
+ "after": {
+ "history": {
+ "list": [
+ {
+ "timestamp": 555,
+ "eventId": 93
+ },
+ {
+ "timestamp": 555,
+ "eventId": 95
+ },
+ {
+ "timestamp": 555,
+ "eventId": 96
+ },
+ {
+ "timestamp": 555,
+ "eventId": 98
+ },
+ {
+ "timestamp": 562,
+ "eventId": 102
+ },
+ {
+ "timestamp": 562,
+ "eventId": 104
+ }
+ ]
+ },
+ "neighbours": {
+ "list": [
+ {
+ "name": "Gandalf",
+ "before": {
+ "history": {
+ "list": []
+ }
+ }
+ },
+ {
+ "name": "Bilbo",
+ "before": {
+ "history": {
+ "list": []
+ }
+ }
+ }
+ ]
}
}
}
}
- }
- """
+ }
+ }"""
a = json.dumps(client.query(q))
json_a = json.loads(a)
json_ra = json.loads(ra)
@@ -268,7 +320,7 @@ def test_graph_windows_and_layers_query():
neighbours {
list {
name
- layer(name: "layer1") { neighbours { list { name } } }
+ layer(name: "layer2") { neighbours { list { name } } }
}
}
}
@@ -285,7 +337,7 @@ def test_graph_windows_and_layers_query():
"neighbours": {
"list": [{
"name": "2",
- "layer": {"neighbours": {"list": [{ "name": "1" }]}}
+ "layer": {"neighbours": {"list": []}}
}]
}
}
@@ -325,7 +377,12 @@ def test_graph_properties_query():
temporal {
values(keys:["prop2"]) {
key
- history
+ history {
+ list {
+ timestamp
+ eventId
+ }
+ }
}
}
}
@@ -348,7 +405,27 @@ def test_graph_properties_query():
"properties": {
"values": [{"key": "prop1", "asString": "val3"}],
"temporal": {
- "values": [{"key": "prop2", "history": [1, 2, 3]}]
+ "values": [
+ {
+ "key": "prop2",
+ "history": {
+ "list": [
+ {
+ "timestamp": 1,
+ "eventId": 0,
+ },
+ {
+ "timestamp": 2,
+ "eventId": 1,
+ },
+ {
+ "timestamp": 3,
+ "eventId": 2,
+ },
+ ]
+ },
+ }
+ ]
},
},
"metadata": {"values": [{"key": "prop5", "value": "val4"}]},
diff --git a/python/tests/test_base_install/test_graphql/misc/test_components.py b/python/tests/test_base_install/test_graphql/misc/test_components.py
index 64448d2fc7..9197d17069 100644
--- a/python/tests/test_base_install/test_graphql/misc/test_components.py
+++ b/python/tests/test_base_install/test_graphql/misc/test_components.py
@@ -40,7 +40,7 @@ def test_in_out_components():
}
}
}
- window(start:1,end:6){
+ window(start: 1, end: 6){
node(name:"3"){
inComponent{
list {
@@ -49,7 +49,7 @@ def test_in_out_components():
}
}
}
- at(time:4){
+ at(time: 4){
node(name:"4"){
outComponent{
list {
diff --git a/python/tests/test_base_install/test_graphql/misc/test_latest.py b/python/tests/test_base_install/test_graphql/misc/test_latest.py
index 2f1dc41b36..967618667f 100644
--- a/python/tests/test_base_install/test_graphql/misc/test_latest.py
+++ b/python/tests/test_base_install/test_graphql/misc/test_latest.py
@@ -13,19 +13,34 @@ def test_latest_and_active():
name
isActive
latest {
- history
+ history {
+ list {
+ timestamp
+ eventId
+ }
+ }
}
}
e12: edge(src: "1", dst: "2") {
isActive
latest {
- history
+ history {
+ list {
+ timestamp
+ eventId
+ }
+ }
}
}
e13: edge(src: "1", dst: "3") {
latest {
isActive
- history
+ history {
+ list {
+ timestamp
+ eventId
+ }
+ }
}
}
nodes {
@@ -34,7 +49,12 @@ def test_latest_and_active():
edges {
latest {
list {
- history
+ history {
+ list {
+ timestamp
+ eventId
+ }
+ }
}
}
}
@@ -42,14 +62,24 @@ def test_latest_and_active():
latest {
list {
name
- history
+ history {
+ list {
+ timestamp
+ eventId
+ }
+ }
}
}
}
edges {
latest {
list {
- history
+ history {
+ list {
+ timestamp
+ eventId
+ }
+ }
}
}
}
@@ -59,9 +89,23 @@ def test_latest_and_active():
result = {
"graph": {
- "node": {"name": "1", "isActive": True, "latest": {"history": [3]}},
- "e12": {"isActive": True, "latest": {"history": [3]}},
- "e13": {"latest": {"isActive": False, "history": []}},
+ "node": {
+ "name": "1",
+ "isActive": True,
+ "latest": {
+ "history": {
+ "list": [
+ {"timestamp": 3, "eventId": 2},
+ {"timestamp": 3, "eventId": 5},
+ ]
+ }
+ },
+ },
+ "e12": {
+ "isActive": True,
+ "latest": {"history": {"list": [{"timestamp": 3, "eventId": 2}]}},
+ },
+ "e13": {"latest": {"isActive": False, "history": {"list": []}}},
"nodes": {
"list": [
{
@@ -69,29 +113,87 @@ def test_latest_and_active():
"edges": {
"latest": {
"list": [
- {"history": [3]},
- {"history": []},
- {"history": [3]},
+ {
+ "history": {
+ "list": [{"timestamp": 3, "eventId": 2}]
+ }
+ },
+ {"history": {"list": []}},
+ {
+ "history": {
+ "list": [{"timestamp": 3, "eventId": 5}]
+ }
+ },
+ ]
+ }
+ },
+ },
+ {
+ "name": "2",
+ "edges": {
+ "latest": {
+ "list": [
+ {
+ "history": {
+ "list": [{"timestamp": 3, "eventId": 2}]
+ }
+ }
+ ]
+ }
+ },
+ },
+ {
+ "name": "3",
+ "edges": {"latest": {"list": [{"history": {"list": []}}]}},
+ },
+ {
+ "name": "4",
+ "edges": {
+ "latest": {
+ "list": [
+ {
+ "history": {
+ "list": [{"timestamp": 3, "eventId": 5}]
+ }
+ }
]
}
},
},
- {"name": "2", "edges": {"latest": {"list": [{"history": [3]}]}}},
- {"name": "3", "edges": {"latest": {"list": [{"history": []}]}}},
- {"name": "4", "edges": {"latest": {"list": [{"history": [3]}]}}},
],
"latest": {
"list": [
- {"name": "1", "history": [3]},
- {"name": "2", "history": [3]},
- {"name": "3", "history": []},
- {"name": "4", "history": [3]},
+ {
+ "name": "1",
+ "history": {
+ "list": [
+ {"timestamp": 3, "eventId": 2},
+ {"timestamp": 3, "eventId": 5},
+ ]
+ },
+ },
+ {
+ "name": "2",
+ "history": {"list": [{"timestamp": 3, "eventId": 2}]},
+ },
+ {
+ "name": "3",
+ "history": {"list": []},
+ },
+ {
+ "name": "4",
+ "history": {"list": [{"timestamp": 3, "eventId": 5}]},
+ },
]
},
},
"edges": {
"latest": {
- "list": [{"history": [3]}, {"history": []}, {"history": [3]}]
+ "list": [
+ {"history": {"list": [{"timestamp": 3, "eventId": 2}]}},
+ {"history": {"list": []}},
+ {"history": {"list": [{"timestamp": 3, "eventId": 5}]}},
+ ]
}
},
}
diff --git a/python/tests/test_base_install/test_graphql/test_apply_views.py b/python/tests/test_base_install/test_graphql/test_apply_views.py
index 221bed64ce..f417f6b2a6 100644
--- a/python/tests/test_base_install/test_graphql/test_apply_views.py
+++ b/python/tests/test_base_install/test_graphql/test_apply_views.py
@@ -51,28 +51,46 @@ def test_apply_view_snapshot_latest():
{
graph(path: "g") {
applyViews(views: [{snapshotLatest: true}]) {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{snapshotLatest: true}]) {
page(limit: 1, offset: 0) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
node(name: "1") {
applyViews(views: [{snapshotLatest: true}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{snapshotLatest: true}]) {
page(limit: 1, offset: 0) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
dst {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -80,7 +98,11 @@ def test_apply_view_snapshot_latest():
edge(src: "1", dst: "2") {
applyViews(views: [{snapshotLatest: true}]) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -90,29 +112,43 @@ def test_apply_view_snapshot_latest():
correct = {
"graph": {
- "applyViews": {"earliestTime": 1735689600000},
+ "applyViews": {"earliestTime": {"timestamp": 1735689600000}},
"nodes": {
"applyViews": {
"page": [
{
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ]
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ }
}
]
}
},
"node": {
"applyViews": {
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ]
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ }
}
},
"edges": {
@@ -120,19 +156,30 @@ def test_apply_view_snapshot_latest():
"page": [
{
"src": {
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ]
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ }
},
"dst": {
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- ]
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735776000000,
+ 1735862400000,
+ ]
+ }
+ }
},
}
]
@@ -141,12 +188,19 @@ def test_apply_view_snapshot_latest():
"edge": {
"applyViews": {
"src": {
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ]
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ }
}
}
},
@@ -163,28 +217,46 @@ def test_apply_view_default_layer():
{
graph(path: "g") {
applyViews(views: [{defaultLayer: true}]) {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{defaultLayer: true}]) {
page(limit: 1, offset: 0) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
node(name: "2") {
applyViews(views: [{defaultLayer: true}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{defaultLayer: true}]) {
page(limit: 1, offset: 0) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
dst {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -192,7 +264,11 @@ def test_apply_view_default_layer():
edge(src: "6", dst: "7") {
applyViews(views: [{defaultLayer: true}]) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -200,40 +276,60 @@ def test_apply_view_default_layer():
}"""
correct = {
"graph": {
- "applyViews": {"earliestTime": 1735689600000},
- "edge": {"applyViews": {"src": {"history": []}}},
- "edges": {
+ "applyViews": {"earliestTime": {"timestamp": 1735689600000}},
+ "nodes": {
"applyViews": {
"page": [
{
- "dst": {"history": [1735862400000]},
- "src": {
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ]
- },
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ }
}
]
}
},
- "node": {"applyViews": {"history": [1735862400000]}},
- "nodes": {
+ "node": {
+ "applyViews": {"history": {"timestamps": {"list": [1735862400000]}}}
+ },
+ "edges": {
"applyViews": {
"page": [
{
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ]
+ "src": {
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ }
+ },
+ "dst": {
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735862400000,
+ ]
+ }
+ }
+ },
}
]
}
},
+ "edge": {"applyViews": {"src": {"history": {"timestamps": {"list": []}}}}},
}
}
run_graphql_test(query, correct, graph)
@@ -246,28 +342,46 @@ def test_apply_view_latest():
{
graph(path: "g") {
applyViews(views: [{latest: true}]) {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{latest: true}]) {
page(limit: 1, offset: 0) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
node(name: "2") {
applyViews(views: [{latest: true}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{latest: true}]) {
page(limit: 1, offset: 0) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
dst {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -275,7 +389,11 @@ def test_apply_view_latest():
edge(src: "6", dst: "7") {
applyViews(views: [{latest: true}]) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -284,15 +402,26 @@ def test_apply_view_latest():
"""
correct = {
"graph": {
- "applyViews": {"earliestTime": 1736035200000},
- "nodes": {"applyViews": {"page": [{"history": []}]}},
- "node": {"applyViews": {"history": []}},
+ "applyViews": {"earliestTime": {"timestamp": 1736035200000}},
+ "nodes": {
+ "applyViews": {"page": [{"history": {"timestamps": {"list": []}}}]}
+ },
+ "node": {"applyViews": {"history": {"timestamps": {"list": []}}}},
"edges": {
"applyViews": {
- "page": [{"src": {"history": []}, "dst": {"history": []}}]
+ "page": [
+ {
+ "src": {"history": {"timestamps": {"list": []}}},
+ "dst": {"history": {"timestamps": {"list": []}}},
+ }
+ ]
+ }
+ },
+ "edge": {
+ "applyViews": {
+ "src": {"history": {"timestamps": {"list": [1736035200000]}}}
}
},
- "edge": {"applyViews": {"src": {"history": [1736035200000]}}},
}
}
run_graphql_test(query, correct, graph)
@@ -305,28 +434,46 @@ def test_apply_view_at():
{
graph(path: "g") {
applyViews(views: [{at: 1735689600000}]) {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{at: 1735689600000}]) {
page(limit: 1, offset: 0) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
node(name: "2") {
applyViews(views: [{at: 1735689600000}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{at: 1735689600000}]) {
page(limit: 1, offset: 0) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
dst {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -334,7 +481,11 @@ def test_apply_view_at():
edge(src: "6", dst: "7") {
applyViews(views: [{at: 1735689600000}]) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -343,20 +494,43 @@ def test_apply_view_at():
"""
correct = {
"graph": {
- "applyViews": {"earliestTime": 1735689600000},
- "nodes": {"applyViews": {"page": [{"history": [1735689600000]}]}},
- "node": {"applyViews": {"history": [1735689600000]}},
+ "applyViews": {"earliestTime": {"timestamp": 1735689600000}},
+ "nodes": {
+ "applyViews": {
+ "page": [
+ {
+ "history": {
+ "timestamps": {"list": [1735689600000, 1735689600000]}
+ }
+ }
+ ]
+ }
+ },
+ "node": {
+ "applyViews": {"history": {"timestamps": {"list": [1735689600000]}}}
+ },
"edges": {
"applyViews": {
"page": [
{
- "src": {"history": [1735689600000]},
- "dst": {"history": [1735689600000]},
+ "src": {
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ ]
+ }
+ }
+ },
+ "dst": {
+ "history": {"timestamps": {"list": [1735689600000]}}
+ },
}
]
}
},
- "edge": {"applyViews": {"src": {"history": []}}},
+ "edge": {"applyViews": {"src": {"history": {"timestamps": {"list": []}}}}},
}
}
run_graphql_test(query, correct, graph)
@@ -369,28 +543,46 @@ def test_apply_view_snapshot_at():
{
graph(path: "g") {
applyViews(views: [{snapshotAt: 1740873600000}]) {
- latestTime
+ latestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{snapshotAt: 1735901379000}]) {
page(limit: 1, offset: 0) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
node(name: "2") {
applyViews(views: [{snapshotAt: 1735901379000}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{snapshotAt: 1735901379000}]) {
page(limit: 1, offset: 0) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
dst {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -398,7 +590,11 @@ def test_apply_view_snapshot_at():
edge(src: "6", dst: "7") {
applyViews(views: [{snapshotAt: 1735901379000}]) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -407,30 +603,70 @@ def test_apply_view_snapshot_at():
"""
correct = {
"graph": {
- "applyViews": {"latestTime": 1736035200000},
+ "applyViews": {"latestTime": {"timestamp": 1736035200000}},
"nodes": {
"applyViews": {
- "page": [{"history": [1735689600000, 1735776000000, 1735862400000]}]
+ "page": [
+ {
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ ]
+ }
+ }
+ }
+ ]
}
},
"node": {
- "applyViews": {"history": [1735689600000, 1735776000000, 1735862400000]}
+ "applyViews": {
+ "history": {
+ "timestamps": {
+ "list": [1735689600000, 1735776000000, 1735862400000]
+ }
+ }
+ }
},
"edges": {
"applyViews": {
"page": [
{
"src": {
- "history": [1735689600000, 1735776000000, 1735862400000]
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ ]
+ }
+ }
},
"dst": {
- "history": [1735689600000, 1735776000000, 1735862400000]
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735776000000,
+ 1735862400000,
+ ]
+ }
+ }
},
}
]
}
},
- "edge": {"applyViews": {"src": {"history": []}}},
+ "edge": {"applyViews": {"src": {"history": {"timestamps": {"list": []}}}}},
}
}
run_graphql_test(query, correct, graph)
@@ -446,7 +682,9 @@ def test_apply_view_window():
start: 1735689600000
end: 1735862400000
}}]) {
- latestTime
+ latestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{window: {
@@ -454,7 +692,11 @@ def test_apply_view_window():
end: 1735862400000
}}]) {
page( limit: 1,offset: 0) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -463,7 +705,11 @@ def test_apply_view_window():
start: 1735689600000
end: 1735862400000
}}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
@@ -473,21 +719,33 @@ def test_apply_view_window():
}}]) {
page(limit: 1, offset: 0) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
dst {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
}
edge(src: "1", dst: "2") {
applyViews(views: [{window: {
- start: 1735689600000
- end: 1735862400000
- }}]) {
+ start: 1735689600000
+ end: 1735862400000
+ }}]) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -496,23 +754,75 @@ def test_apply_view_window():
"""
correct = {
"graph": {
- "applyViews": {"latestTime": 1735776000000},
+ "applyViews": {"latestTime": {"timestamp": 1735776000000}},
"nodes": {
- "applyViews": {"page": [{"history": [1735689600000, 1735776000000]}]}
+ "applyViews": {
+ "page": [
+ {
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ "node": {
+ "applyViews": {
+ "history": {"timestamps": {"list": [1735689600000, 1735776000000]}}
+ }
},
- "node": {"applyViews": {"history": [1735689600000, 1735776000000]}},
"edges": {
"applyViews": {
"page": [
{
- "src": {"history": [1735689600000, 1735776000000]},
- "dst": {"history": [1735689600000, 1735776000000]},
+ "src": {
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ ]
+ }
+ }
+ },
+ "dst": {
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735776000000,
+ ]
+ }
+ }
+ },
}
]
}
},
"edge": {
- "applyViews": {"src": {"history": [1735689600000, 1735776000000]}}
+ "applyViews": {
+ "src": {
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ ]
+ }
+ }
+ }
+ }
},
}
}
@@ -527,28 +837,46 @@ def test_apply_view_before():
{
graph(path: "g") {
applyViews(views: [{before: 1735862400000}]) {
- latestTime
+ latestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{before: 1735862400000}]) {
page(limit: 1, offset: 0) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
node(name: "2") {
applyViews(views: [{before: 1735862400000}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{before: 1735862400000}]) {
page(limit: 1, offset: 0) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
dst {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -556,7 +884,11 @@ def test_apply_view_before():
edge(src: "1", dst: "2") {
applyViews(views: [{before: 1735862400000}]) {
src {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -565,23 +897,75 @@ def test_apply_view_before():
correct = {
"graph": {
- "applyViews": {"latestTime": 1735776000000},
+ "applyViews": {"latestTime": {"timestamp": 1735776000000}},
"nodes": {
- "applyViews": {"page": [{"history": [1735689600000, 1735776000000]}]}
+ "applyViews": {
+ "page": [
+ {
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ ]
+ }
+ }
+ }
+ ]
+ }
+ },
+ "node": {
+ "applyViews": {
+ "history": {"timestamps": {"list": [1735689600000, 1735776000000]}}
+ }
},
- "node": {"applyViews": {"history": [1735689600000, 1735776000000]}},
"edges": {
"applyViews": {
"page": [
{
- "src": {"history": [1735689600000, 1735776000000]},
- "dst": {"history": [1735689600000, 1735776000000]},
+ "src": {
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ ]
+ }
+ }
+ },
+ "dst": {
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735776000000,
+ ]
+ }
+ }
+ },
}
]
}
},
"edge": {
- "applyViews": {"src": {"history": [1735689600000, 1735776000000]}}
+ "applyViews": {
+ "src": {
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ ]
+ }
+ }
+ }
+ }
},
}
}
@@ -596,24 +980,38 @@ def test_apply_view_after():
{
graph(path: "g") {
applyViews(views: [{after: 6}]) {
- latestTime
+ latestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{after: 6}]) {
list {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
node(name: "2") {
applyViews(views: [{after: 3}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{after: 6}]) {
list {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -625,7 +1023,11 @@ def test_apply_view_after():
}
edge(src: "1", dst: "2") {
applyViews(views: [{after: 3}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -636,29 +1038,46 @@ def test_apply_view_after():
"""
correct = {
"graph": {
- "applyViews": {"latestTime": None},
+ "applyViews": {"latestTime": {"timestamp": None}},
"nodes": {
"applyViews": {
"list": [
- {"history": []},
- {"history": []},
- {"history": []},
- {"history": []},
- {"history": []},
+ {"history": {"timestamps": {"list": []}}},
+ {"history": {"timestamps": {"list": []}}},
+ {"history": {"timestamps": {"list": []}}},
+ {"history": {"timestamps": {"list": []}}},
+ {"history": {"timestamps": {"list": []}}},
]
}
},
- "node": {"applyViews": {"history": []}},
+ "node": {"applyViews": {"history": {"timestamps": {"list": []}}}},
"edges": {
"applyViews": {
"list": [
- {"history": [], "src": {"name": "1"}, "dst": {"name": "2"}},
- {"history": [], "src": {"name": "1"}, "dst": {"name": "3"}},
- {"history": [], "src": {"name": "6"}, "dst": {"name": "7"}},
+ {
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "1"},
+ "dst": {"name": "2"},
+ },
+ {
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "1"},
+ "dst": {"name": "3"},
+ },
+ {
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "6"},
+ "dst": {"name": "7"},
+ },
]
}
},
- "edge": {"applyViews": {"history": [], "src": {"name": "1"}}},
+ "edge": {
+ "applyViews": {
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "1"},
+ }
+ },
}
}
run_graphql_test(query, correct, graph)
@@ -671,7 +1090,9 @@ def test_apply_view_shrink_window():
{
graph(path: "g") {
applyViews(views: [{shrinkWindow: {start: 1736035200000, end: 1736121600000}}]) {
- latestTime
+ latestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{shrinkWindow: {start: 1736035200000, end: 1736121600000}}]) {
@@ -682,13 +1103,21 @@ def test_apply_view_shrink_window():
}
node(name: "2") {
applyViews(views: [{shrinkWindow: {start: 1736035200000, end: 1736121600000}}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{shrinkWindow: {start: 1736035200000, end: 1736121600000}}]) {
list {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -700,7 +1129,11 @@ def test_apply_view_shrink_window():
}
edge(src: "1", dst: "2") {
applyViews(views: [{shrinkWindow: {start: 1736035200000, end: 1736121600000}}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -714,7 +1147,7 @@ def test_apply_view_shrink_window():
correct = {
"graph": {
- "applyViews": {"latestTime": 1736035200000},
+ "applyViews": {"latestTime": {"timestamp": 1736035200000}},
"nodes": {
"applyViews": {
"list": [
@@ -726,14 +1159,22 @@ def test_apply_view_shrink_window():
]
}
},
- "node": {"applyViews": {"history": []}},
+ "node": {"applyViews": {"history": {"timestamps": {"list": []}}}},
"edges": {
"applyViews": {
"list": [
- {"history": [], "src": {"name": "1"}, "dst": {"name": "2"}},
- {"history": [], "src": {"name": "1"}, "dst": {"name": "3"}},
{
- "history": [1736035200000],
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "1"},
+ "dst": {"name": "2"},
+ },
+ {
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "1"},
+ "dst": {"name": "3"},
+ },
+ {
+ "history": {"timestamps": {"list": [1736035200000]}},
"src": {"name": "6"},
"dst": {"name": "7"},
},
@@ -742,7 +1183,7 @@ def test_apply_view_shrink_window():
},
"edge": {
"applyViews": {
- "history": [],
+ "history": {"timestamps": {"list": []}},
"src": {"name": "1"},
"dst": {"name": "2"},
}
@@ -759,7 +1200,9 @@ def test_apply_view_shrink_start():
{
graph(path: "g") {
applyViews(views: [{shrinkStart: 1736035200000}]) {
- latestTime
+ latestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{shrinkStart: 1736035200000}]) {
@@ -770,13 +1213,21 @@ def test_apply_view_shrink_start():
}
node(name: "2") {
applyViews(views: [{shrinkStart: 1736035200000}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{shrinkStart: 1736035200000}]) {
list {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -788,7 +1239,11 @@ def test_apply_view_shrink_start():
}
edge(src: "1", dst: "2") {
applyViews(views: [{shrinkStart: 1736035200000}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -802,7 +1257,7 @@ def test_apply_view_shrink_start():
"""
correct = {
"graph": {
- "applyViews": {"latestTime": 1736035200000},
+ "applyViews": {"latestTime": {"timestamp": 1736035200000}},
"nodes": {
"applyViews": {
"list": [
@@ -814,14 +1269,22 @@ def test_apply_view_shrink_start():
]
}
},
- "node": {"applyViews": {"history": []}},
+ "node": {"applyViews": {"history": {"timestamps": {"list": []}}}},
"edges": {
"applyViews": {
"list": [
- {"history": [], "src": {"name": "1"}, "dst": {"name": "2"}},
- {"history": [], "src": {"name": "1"}, "dst": {"name": "3"}},
{
- "history": [1736035200000],
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "1"},
+ "dst": {"name": "2"},
+ },
+ {
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "1"},
+ "dst": {"name": "3"},
+ },
+ {
+ "history": {"timestamps": {"list": [1736035200000]}},
"src": {"name": "6"},
"dst": {"name": "7"},
},
@@ -830,7 +1293,7 @@ def test_apply_view_shrink_start():
},
"edge": {
"applyViews": {
- "history": [],
+ "history": {"timestamps": {"list": []}},
"src": {"name": "1"},
"dst": {"name": "2"},
}
@@ -847,7 +1310,9 @@ def test_apply_view_shrink_end():
{
graph(path: "g") {
applyViews(views: [{shrinkEnd: 1735776000000}]) {
- latestTime
+ latestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{shrinkEnd: 1735776000000}]) {
@@ -858,13 +1323,21 @@ def test_apply_view_shrink_end():
}
node(name: "2") {
applyViews(views: [{shrinkEnd: 1735776000000}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{shrinkEnd: 1735776000000}]) {
list {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -876,7 +1349,11 @@ def test_apply_view_shrink_end():
}
edge(src: "1", dst: "2") {
applyViews(views: [{shrinkEnd: 1735776000000}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -890,7 +1367,7 @@ def test_apply_view_shrink_end():
"""
correct = {
"graph": {
- "applyViews": {"latestTime": 1735689600000},
+ "applyViews": {"latestTime": {"timestamp": 1735689600000}},
"nodes": {
"applyViews": {
"list": [
@@ -902,23 +1379,33 @@ def test_apply_view_shrink_end():
]
}
},
- "node": {"applyViews": {"history": [1735689600000]}},
+ "node": {
+ "applyViews": {"history": {"timestamps": {"list": [1735689600000]}}}
+ },
"edges": {
"applyViews": {
"list": [
{
- "history": [1735689600000],
+ "history": {"timestamps": {"list": [1735689600000]}},
"src": {"name": "1"},
"dst": {"name": "2"},
},
- {"history": [], "src": {"name": "1"}, "dst": {"name": "3"}},
- {"history": [], "src": {"name": "6"}, "dst": {"name": "7"}},
+ {
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "1"},
+ "dst": {"name": "3"},
+ },
+ {
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "6"},
+ "dst": {"name": "7"},
+ },
]
}
},
"edge": {
"applyViews": {
- "history": [1735689600000],
+ "history": {"timestamps": {"list": [1735689600000]}},
"src": {"name": "1"},
"dst": {"name": "2"},
}
@@ -935,25 +1422,39 @@ def test_apply_view_layers():
{
graph(path: "g") {
applyViews(views: [{layers: ["finds", "Person"]}]) {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{layers: ["finds", "Person"]}]) {
list {
name
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
node(name: "1") {
applyViews(views: [{layers: ["finds"]}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{layers: ["finds", "Person"]}]) {
list {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -965,7 +1466,11 @@ def test_apply_view_layers():
}
edge(src: "1", dst: "2") {
applyViews(views: [{layers: ["finds", "met"]}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -979,26 +1484,51 @@ def test_apply_view_layers():
"""
correct = {
"graph": {
- "applyViews": {"earliestTime": 1735689600000},
+ "applyViews": {"earliestTime": {"timestamp": 1735689600000}},
"nodes": {
"applyViews": {
"list": [
- {"history": [1735689600000], "name": "1"},
- {"history": [], "name": "2"},
- {"history": [], "name": "3"},
- {"history": [1736035200000], "name": "6"},
- {"history": [1736035200000], "name": "7"},
+ {
+ "name": "1",
+ "history": {"timestamps": {"list": [1735689600000]}},
+ },
+ {
+ "name": "2",
+ "history": {"timestamps": {"list": []}},
+ },
+ {
+ "name": "3",
+ "history": {"timestamps": {"list": []}},
+ },
+ {
+ "name": "6",
+ "history": {"timestamps": {"list": [1736035200000]}},
+ },
+ {
+ "name": "7",
+ "history": {"timestamps": {"list": [1736035200000]}},
+ },
]
}
},
- "node": {"applyViews": {"history": [1735689600000]}},
+ "node": {
+ "applyViews": {"history": {"timestamps": {"list": [1735689600000]}}}
+ },
"edges": {
"applyViews": {
"list": [
- {"history": [], "src": {"name": "1"}, "dst": {"name": "2"}},
- {"history": [], "src": {"name": "1"}, "dst": {"name": "3"}},
{
- "history": [1736035200000],
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "1"},
+ "dst": {"name": "2"},
+ },
+ {
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "1"},
+ "dst": {"name": "3"},
+ },
+ {
+ "history": {"timestamps": {"list": [1736035200000]}},
"src": {"name": "6"},
"dst": {"name": "7"},
},
@@ -1007,7 +1537,7 @@ def test_apply_view_layers():
},
"edge": {
"applyViews": {
- "history": [1735689600000],
+ "history": {"timestamps": {"list": [1735689600000]}},
"src": {"name": "1"},
"dst": {"name": "2"},
}
@@ -1024,25 +1554,39 @@ def test_apply_view_layer():
{
graph(path: "g") {
applyViews(views: [{layers: ["Person"]}]) {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{typeFilter: ["Person"]}]) {
list {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
name
}
}
}
node(name: "1") {
applyViews(views: [{layers: ["finds"]}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{layers: ["finds"]}]) {
list {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -1054,7 +1598,11 @@ def test_apply_view_layer():
}
edge(src: "1", dst: "2") {
applyViews(views: [{layers: ["met"]}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -1067,30 +1615,47 @@ def test_apply_view_layer():
}"""
correct = {
"graph": {
- "applyViews": {"earliestTime": 1735689600000},
+ "applyViews": {"earliestTime": {"timestamp": 1735689600000}},
"nodes": {
"applyViews": {
"list": [
{
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ],
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ },
"name": "1",
}
]
}
},
- "node": {"applyViews": {"history": [1735689600000]}},
+ "node": {
+ "applyViews": {"history": {"timestamps": {"list": [1735689600000]}}}
+ },
"edges": {
"applyViews": {
"list": [
- {"history": [], "src": {"name": "1"}, "dst": {"name": "2"}},
- {"history": [], "src": {"name": "1"}, "dst": {"name": "3"}},
{
- "history": [1736035200000],
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "1"},
+ "dst": {"name": "2"},
+ },
+ {
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "1"},
+ "dst": {"name": "3"},
+ },
+ {
+ "history": {"timestamps": {"list": [1736035200000]}},
"src": {"name": "6"},
"dst": {"name": "7"},
},
@@ -1099,7 +1664,7 @@ def test_apply_view_layer():
},
"edge": {
"applyViews": {
- "history": [1735689600000],
+ "history": {"timestamps": {"list": [1735689600000]}},
"src": {"name": "1"},
"dst": {"name": "2"},
}
@@ -1116,25 +1681,39 @@ def test_apply_view_exclude_layer():
{
graph(path: "g") {
applyViews(views: [{excludeLayer: "Person"}]) {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{excludeLayer: "Person"}]) {
list {
name
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
node(name: "1") {
applyViews(views: [{excludeLayer: "Person"}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{excludeLayer: "finds"}]) {
list {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -1146,7 +1725,11 @@ def test_apply_view_exclude_layer():
}
edge(src: "6", dst: "7") {
applyViews(views: [{excludeLayer: "finds"}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -1160,57 +1743,109 @@ def test_apply_view_exclude_layer():
"""
correct = {
"graph": {
- "applyViews": {"earliestTime": 1735689600000},
+ "applyViews": {"earliestTime": {"timestamp": 1735689600000}},
"nodes": {
"applyViews": {
"list": [
{
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ],
"name": "1",
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ },
},
{
- "history": [1735689600000, 1735776000000, 1735862400000],
"name": "2",
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735776000000,
+ 1735862400000,
+ ]
+ }
+ },
},
{
- "history": [1735776000000, 1735862400000, 1735948800000],
"name": "3",
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735776000000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ },
+ },
+ {
+ "name": "6",
+ "history": {"timestamps": {"list": [1736035200000]}},
+ },
+ {
+ "name": "7",
+ "history": {"timestamps": {"list": [1736035200000]}},
},
- {"history": [1736035200000], "name": "6"},
- {"history": [1736035200000], "name": "7"},
]
}
},
"node": {
"applyViews": {
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ]
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ }
}
},
"edges": {
"applyViews": {
"list": [
{
- "history": [1735689600000, 1735776000000, 1735862400000],
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735776000000,
+ 1735862400000,
+ ]
+ }
+ },
"src": {"name": "1"},
"dst": {"name": "2"},
},
{
- "history": [1735776000000, 1735862400000, 1735948800000],
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735776000000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ },
"src": {"name": "1"},
"dst": {"name": "3"},
},
{
- "history": [],
+ "history": {"timestamps": {"list": []}},
"src": {"name": "6"},
"dst": {"name": "7"},
},
@@ -1219,7 +1854,7 @@ def test_apply_view_exclude_layer():
},
"edge": {
"applyViews": {
- "history": [],
+ "history": {"timestamps": {"list": []}},
"src": {"name": "6"},
"dst": {"name": "7"},
}
@@ -1236,25 +1871,39 @@ def test_apply_view_exclude_layers():
{
graph(path: "g") {
applyViews(views: [{excludeLayers: ["Person", "finds"]}]) {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
nodes {
applyViews(views: [{excludeLayers: ["Person"]}]) {
list {
name
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
node(name: "1") {
applyViews(views: [{excludeLayers: ["Person"]}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
edges {
applyViews(views: [{excludeLayers: ["finds", "met"]}]) {
list {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -1266,7 +1915,11 @@ def test_apply_view_exclude_layers():
}
edge(src: "6", dst: "7") {
applyViews(views: [{excludeLayers: ["finds"]}]) {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
src {
name
}
@@ -1280,62 +1933,112 @@ def test_apply_view_exclude_layers():
"""
correct = {
"graph": {
- "applyViews": {"earliestTime": 1735689600000},
+ "applyViews": {"earliestTime": {"timestamp": 1735689600000}},
"nodes": {
"applyViews": {
"list": [
{
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ],
"name": "1",
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ },
},
{
- "history": [1735689600000, 1735776000000, 1735862400000],
"name": "2",
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735776000000,
+ 1735862400000,
+ ]
+ }
+ },
},
{
- "history": [1735776000000, 1735862400000, 1735948800000],
"name": "3",
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735776000000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ },
+ },
+ {
+ "name": "6",
+ "history": {"timestamps": {"list": [1736035200000]}},
+ },
+ {
+ "name": "7",
+ "history": {"timestamps": {"list": [1736035200000]}},
},
- {"history": [1736035200000], "name": "6"},
- {"history": [1736035200000], "name": "7"},
]
}
},
"node": {
"applyViews": {
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ]
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ 1735862400000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ }
}
},
"edges": {
"applyViews": {
"list": [
{
- "history": [1735776000000, 1735862400000],
+ "history": {
+ "timestamps": {"list": [1735776000000, 1735862400000]}
+ },
"src": {"name": "1"},
"dst": {"name": "2"},
},
{
- "history": [1735776000000, 1735862400000, 1735948800000],
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735776000000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ },
"src": {"name": "1"},
"dst": {"name": "3"},
},
- {"history": [], "src": {"name": "6"}, "dst": {"name": "7"}},
+ {
+ "history": {"timestamps": {"list": []}},
+ "src": {"name": "6"},
+ "dst": {"name": "7"},
+ },
]
}
},
"edge": {
"applyViews": {
- "history": [],
+ "history": {"timestamps": {"list": []}},
"src": {"name": "6"},
"dst": {"name": "7"},
}
@@ -1372,13 +2075,15 @@ def test_apply_view_exclude_nodes():
{
graph(path: "g") {
applyViews(views: [{excludeNodes: ["6", "7"]}]) {
- latestTime
+ latestTime {
+ timestamp
+ }
}
}
}"""
correct = {
"graph": {
- "applyViews": {"latestTime": 1735948800000},
+ "applyViews": {"latestTime": {"timestamp": 1735948800000}},
}
}
run_graphql_test(query, correct, graph)
@@ -1411,11 +2116,17 @@ def test_apply_view_nested():
{
graph(path: "g") {
applyViews(views: [{layers: ["finds"]}]) {
- earliestTime
+ earliestTime {
+ timestamp
+ }
edges {
applyViews(views: [{layers: ["finds"]}]) {
list {
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -1425,8 +2136,12 @@ def test_apply_view_nested():
correct = {
"graph": {
"applyViews": {
- "earliestTime": 1735689600000,
- "edges": {"applyViews": {"list": [{"history": [1736035200000]}]}},
+ "earliestTime": {"timestamp": 1735689600000},
+ "edges": {
+ "applyViews": {
+ "list": [{"history": {"timestamps": {"list": [1736035200000]}}}]
+ }
+ },
}
}
}
@@ -1443,7 +2158,9 @@ def test_apply_view_invalid_argument():
{
graph(path: "g") {
applyViews(views: [{layers: "finds"}]) {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1499,15 +2216,24 @@ def test_apply_view_edge_filter():
]) {
edges {
list {
- history
+ history{
+ timestamps {
+ list
}
+ }}
}
}
}
}
"""
correct = {
- "graph": {"applyViews": {"edges": {"list": [{"history": [1736035200000]}]}}}
+ "graph": {
+ "applyViews": {
+ "edges": {
+ "list": [{"history": {"timestamps": {"list": [1736035200000]}}}]
+ }
+ }
+ }
}
run_graphql_test(query, correct, graph)
@@ -1562,7 +2288,11 @@ def test_apply_view_nodes_multiple_views():
nodes {
list {
name
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -1574,7 +2304,7 @@ def test_apply_view_nodes_multiple_views():
"nodes": {
"list": [
{
- "history": [1735689600000],
+ "history": {"timestamps": {"list": [1735689600000]}},
"name": "1",
},
]
@@ -1600,7 +2330,11 @@ def test_apply_view_edges_multiple_views():
dst {
name
}
- history
+ history {
+ timestamps {
+ list
+ }
+ }
}
}
}
@@ -1613,7 +2347,7 @@ def test_apply_view_edges_multiple_views():
"list": [
{
"dst": {"name": "2"},
- "history": [1735689600000],
+ "history": {"timestamps": {"list": [1735689600000]}},
"src": {"name": "1"},
},
]
@@ -1638,8 +2372,11 @@ def test_apply_view_a_lot_of_views():
]) {
list {
name
- history
+ history{
+ timestamps {
+ list
}
+ }}
}
}
}
@@ -1650,11 +2387,14 @@ def test_apply_view_a_lot_of_views():
"nodes": {
"applyViews": {
"list": [
- {"history": [1735689600000], "name": "1"},
- {"history": [], "name": "2"},
- {"history": [], "name": "3"},
- {"history": [], "name": "6"},
- {"history": [], "name": "7"},
+ {
+ "name": "1",
+ "history": {"timestamps": {"list": [1735689600000]}},
+ },
+ {"name": "2", "history": {"timestamps": {"list": []}}},
+ {"name": "3", "history": {"timestamps": {"list": []}}},
+ {"name": "6", "history": {"timestamps": {"list": []}}},
+ {"name": "7", "history": {"timestamps": {"list": []}}},
]
}
}
@@ -1669,17 +2409,22 @@ def test_apply_view_neighbours():
query = """
{
graph(path: "g") {
- nodes {
- list {
- neighbours {
- applyViews(views: [{latest: true}]) {
- list {
+ nodes {
+ list {
+ neighbours {
+ applyViews(views: [{latest: true}]) {
+ list {
name
- history}
+ history {
+ timestamps {
+ list
+ }
}
}
}
}
+ }
+ }
}
}"""
@@ -1691,33 +2436,67 @@ def test_apply_view_neighbours():
"neighbours": {
"applyViews": {
"list": [
- {"history": [], "name": "2"},
- {"history": [], "name": "3"},
+ {
+ "history": {"timestamps": {"list": []}},
+ "name": "2",
+ },
+ {
+ "history": {"timestamps": {"list": []}},
+ "name": "3",
+ },
]
}
}
},
{
"neighbours": {
- "applyViews": {"list": [{"history": [], "name": "1"}]}
+ "applyViews": {
+ "list": [
+ {
+ "history": {"timestamps": {"list": []}},
+ "name": "1",
+ }
+ ]
+ }
}
},
{
"neighbours": {
- "applyViews": {"list": [{"history": [], "name": "1"}]}
+ "applyViews": {
+ "list": [
+ {
+ "history": {"timestamps": {"list": []}},
+ "name": "1",
+ }
+ ]
+ }
}
},
{
"neighbours": {
"applyViews": {
- "list": [{"history": [1736035200000], "name": "7"}]
+ "list": [
+ {
+ "history": {
+ "timestamps": {"list": [1736035200000]}
+ },
+ "name": "7",
+ }
+ ]
}
}
},
{
"neighbours": {
"applyViews": {
- "list": [{"history": [1736035200000], "name": "6"}]
+ "list": [
+ {
+ "history": {
+ "timestamps": {"list": [1736035200000]}
+ },
+ "name": "6",
+ }
+ ]
}
}
},
@@ -1735,18 +2514,21 @@ def test_apply_view_neighbours_latest():
query = """
{
graph(path: "g") {
- node(name: "1") {
+ node(name: "1") {
neighbours {
- applyViews(views: [{latest: true}]) {
- list {
- name
- history
- }
+ applyViews(views: [{latest: true}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
}
-
}
}
}
+ }
+ }
+ }
}"""
correct = {
"graph": {
@@ -1754,8 +2536,8 @@ def test_apply_view_neighbours_latest():
"neighbours": {
"applyViews": {
"list": [
- {"history": [], "name": "2"},
- {"history": [], "name": "3"},
+ {"history": {"timestamps": {"list": []}}, "name": "2"},
+ {"history": {"timestamps": {"list": []}}, "name": "3"},
]
}
}
@@ -1772,16 +2554,20 @@ def test_apply_view_neighbours_layer():
query = """
{
graph(path: "g") {
- node(name: "6") {
- neighbours {
- applyViews(views: [{layers: ["finds"]}]) {
- list {
- name
- history}
+ node(name: "6") {
+ neighbours {
+ applyViews(views: [{layers: ["finds"]}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
}
}
-
+ }
}
+ }
+ }
}
}"""
@@ -1789,7 +2575,14 @@ def test_apply_view_neighbours_layer():
"graph": {
"node": {
"neighbours": {
- "applyViews": {"list": [{"history": [1736035200000], "name": "7"}]}
+ "applyViews": {
+ "list": [
+ {
+ "history": {"timestamps": {"list": [1736035200000]}},
+ "name": "7",
+ }
+ ]
+ }
}
}
}
@@ -1804,23 +2597,31 @@ def test_apply_view_neighbours_exclude_layer():
query = """
{
graph(path: "g") {
- node(name: "6") {
- neighbours {
- applyViews(views: [{excludeLayer: "finds"}]) {
- list {
- name
- history}
+ node(name: "6") {
+ neighbours {
+ applyViews(views: [{excludeLayer: "finds"}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
}
}
-
+ }
}
+ }
+ }
}
}"""
correct = {
"graph": {
"node": {
- "neighbours": {"applyViews": {"list": [{"history": [], "name": "7"}]}}
+ "neighbours": {
+ "applyViews": {
+ "list": [{"history": {"timestamps": {"list": []}}, "name": "7"}]
+ }
+ }
}
}
}
@@ -1834,18 +2635,22 @@ def test_apply_view_neighbours_layers():
query = """
{
graph(path: "g") {
- node(name: "1") {
- neighbours {
- applyViews(views: [{layers: ["met", "Person"]}]) {
- list {
- name
- history
- }
- }
- }
- }
+ node(name: "1") {
+ neighbours {
+ applyViews(views: [{layers: ["met", "Person"]}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
}
- }"""
+ }
+ }
+ }
+ }
+ }
+ }
+}"""
correct = {
"graph": {
@@ -1853,8 +2658,11 @@ def test_apply_view_neighbours_layers():
"neighbours": {
"applyViews": {
"list": [
- {"history": [1735689600000], "name": "2"},
- {"history": [], "name": "3"},
+ {
+ "history": {"timestamps": {"list": [1735689600000]}},
+ "name": "2",
+ },
+ {"history": {"timestamps": {"list": []}}, "name": "3"},
]
}
}
@@ -1870,18 +2678,22 @@ def test_apply_view_neighbours_exclude_layers():
query = """
{
graph(path: "g") {
- node(name: "1") {
- neighbours {
- applyViews(views: [{excludeLayers: ["met", "Person"]}]) {
- list {
- name
- history
- }
- }
- }
- }
+ node(name: "1") {
+ neighbours {
+ applyViews(views: [{excludeLayers: ["met", "Person"]}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
}
- }"""
+ }
+ }
+ }
+ }
+ }
+ }
+}"""
correct = {
"graph": {
@@ -1889,14 +2701,25 @@ def test_apply_view_neighbours_exclude_layers():
"neighbours": {
"applyViews": {
"list": [
- {"history": [1735776000000, 1735862400000], "name": "2"},
{
- "history": [
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ],
+ "name": "2",
+ "history": {
+ "timestamps": {
+ "list": [1735776000000, 1735862400000]
+ }
+ },
+ },
+ {
"name": "3",
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735776000000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ },
},
]
}
@@ -1913,26 +2736,33 @@ def test_apply_view_neighbours_after():
query = """
{
graph(path: "g") {
- node(name: "1") {
- neighbours {
- applyViews(views: [{after: 1735862400000}]) {
- list {
- name
- history
- }
+ node(name: "1") {
+ neighbours {
+ applyViews(views: [{after: 1735862400000}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
}
}
- }
- }
- }"""
+ }
+ }
+ }
+ }
+ }
+}"""
correct = {
"graph": {
"node": {
"neighbours": {
"applyViews": {
"list": [
- {"history": [], "name": "2"},
- {"history": [1735948800000], "name": "3"},
+ {"history": {"timestamps": {"list": []}}, "name": "2"},
+ {
+ "history": {"timestamps": {"list": [1735948800000]}},
+ "name": "3",
+ },
]
}
}
@@ -1948,26 +2778,40 @@ def test_apply_view_neighbours_before():
query = """
{
graph(path: "g") {
- node(name: "1") {
- neighbours {
- applyViews(views: [{before: 1735862400000}]) {
- list {
- name
- history
- }
+ node(name: "1") {
+ neighbours {
+ applyViews(views: [{before: 1735862400000}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
}
}
+ }
}
- }
- }"""
+ }
+ }
+ }
+}"""
correct = {
"graph": {
"node": {
"neighbours": {
"applyViews": {
"list": [
- {"history": [1735689600000, 1735776000000], "name": "2"},
- {"history": [1735776000000], "name": "3"},
+ {
+ "history": {
+ "timestamps": {
+ "list": [1735689600000, 1735776000000]
+ }
+ },
+ "name": "2",
+ },
+ {
+ "history": {"timestamps": {"list": [1735776000000]}},
+ "name": "3",
+ },
]
}
}
@@ -1983,18 +2827,22 @@ def test_apply_view_in_neighbours_window():
query = """
{
graph(path: "g") {
- node(name: "1") {
- inNeighbours {
- applyViews(views: [{window: {start: 1735689600000, end: 1735862400000}}]) {
- list {
- name
- history
- }
+ node(name: "1") {
+ inNeighbours {
+ applyViews(views: [{window: {start: 1735689600000, end: 1735862400000}}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
+ }
}
}
}
- }
- }"""
+ }
+ }
+ }
+}"""
correct = {"graph": {"node": {"inNeighbours": {"applyViews": {"list": []}}}}}
run_graphql_test(query, correct, graph)
@@ -2006,26 +2854,40 @@ def test_apply_view_out_neighbours_window():
query = """
{
graph(path: "g") {
- node(name: "1") {
- outNeighbours {
- applyViews(views: [{window: {start: 1735689600000, end: 1735862400000}}]) {
- list {
- name
- history
- }
+ node(name: "1") {
+ outNeighbours {
+ applyViews(views: [{window: {start: 1735689600000, end: 1735862400000}}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
+ }
}
}
}
- }
- }"""
+ }
+ }
+ }
+}"""
correct = {
"graph": {
"node": {
"outNeighbours": {
"applyViews": {
"list": [
- {"history": [1735689600000, 1735776000000], "name": "2"},
- {"history": [1735776000000], "name": "3"},
+ {
+ "history": {
+ "timestamps": {
+ "list": [1735689600000, 1735776000000]
+ }
+ },
+ "name": "2",
+ },
+ {
+ "history": {"timestamps": {"list": [1735776000000]}},
+ "name": "3",
+ },
]
}
}
@@ -2042,25 +2904,29 @@ def test_apply_view_out_neighbours_shrink_window():
query = """
{
graph(path: "g") {
- node(name: "6") {
- outNeighbours {
- applyViews(views: [{shrinkWindow: {start: 1735948800000, end: 1736035200000}}]) {
- list {
- name
- history
- }
+ node(name: "6") {
+ outNeighbours {
+ applyViews(views: [{shrinkWindow: {start: 1735948800000, end: 1736035200000}}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
+ }
}
}
}
- }
- }"""
+ }
+ }
+ }
+}"""
correct = {
"graph": {
"node": {
"outNeighbours": {
"applyViews": {
"list": [
- {"history": [], "name": "7"},
+ {"history": {"timestamps": {"list": []}}, "name": "7"},
]
}
}
@@ -2077,25 +2943,32 @@ def test_apply_view_in_neighbours_shrink_start():
query = """
{
graph(path: "g") {
- node(name: "7") {
- inNeighbours {
- applyViews(views: [{shrinkStart: 1735948800000}]) {
- list {
- name
- history
- }
+ node(name: "7") {
+ inNeighbours {
+ applyViews(views: [{shrinkStart: 1735948800000}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
+ }
}
}
}
- }
- }"""
+ }
+ }
+ }
+}"""
correct = {
"graph": {
"node": {
"inNeighbours": {
"applyViews": {
"list": [
- {"history": [1736035200000], "name": "6"},
+ {
+ "history": {"timestamps": {"list": [1736035200000]}},
+ "name": "6",
+ },
]
}
}
@@ -2111,25 +2984,41 @@ def test_apply_view_in_neighbours_shrink_end():
query = """
{
graph(path: "g") {
- node(name: "2") {
- inNeighbours {
- applyViews(views: [{shrinkEnd: 1735862400000}]) {
- list {
- name
- history
- }
+ node(name: "2") {
+ inNeighbours {
+ applyViews(views: [{shrinkEnd: 1735862400000}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
+ }
}
}
}
- }
- }"""
+ }
+ }
+ }
+}"""
correct = {
"graph": {
"node": {
"inNeighbours": {
"applyViews": {
"list": [
- {"history": [1735689600000, 1735776000000], "name": "1"},
+ {
+ "name": "1",
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735689600000,
+ 1735776000000,
+ 1735776000000,
+ ]
+ }
+ },
+ }
]
}
}
@@ -2145,23 +3034,38 @@ def test_apply_view_in_neighbours_at():
query = """
{
graph(path: "g") {
- node(name: "2") {
- inNeighbours {
- applyViews(views: [{at: 1735862400000}]) {
- list {
- name
- history
- }
+ node(name: "2") {
+ inNeighbours {
+ applyViews(views: [{at: 1735862400000}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
+ }
}
}
}
- }
- }"""
+ }
+ }
+ }
+}"""
correct = {
"graph": {
"node": {
"inNeighbours": {
- "applyViews": {"list": [{"history": [1735862400000], "name": "1"}]}
+ "applyViews": {
+ "list": [
+ {
+ "history": {
+ "timestamps": {
+ "list": [1735862400000, 1735862400000]
+ }
+ },
+ "name": "1",
+ }
+ ]
+ }
}
}
}
@@ -2175,18 +3079,22 @@ def test_apply_view_out_neighbours_snapshot_latest():
query = """
{
graph(path: "g") {
- node(name: "1") {
- outNeighbours {
- applyViews(views: [{snapshotLatest: true}]) {
- list {
- name
- history
- }
+ node(name: "1") {
+ outNeighbours {
+ applyViews(views: [{snapshotLatest: true}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
+ }
}
}
}
- }
- }"""
+ }
+ }
+ }
+}"""
correct = {
"graph": {
"node": {
@@ -2194,20 +3102,28 @@ def test_apply_view_out_neighbours_snapshot_latest():
"applyViews": {
"list": [
{
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- ],
"name": "2",
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735776000000,
+ 1735862400000,
+ ]
+ }
+ },
},
{
- "history": [
- 1735776000000,
- 1735862400000,
- 1735948800000,
- ],
"name": "3",
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735776000000,
+ 1735862400000,
+ 1735948800000,
+ ]
+ }
+ },
},
]
}
@@ -2225,18 +3141,22 @@ def test_apply_view_out_neighbours_snapshot_at():
query = """
{
graph(path: "g") {
- node(name: "1") {
- outNeighbours {
- applyViews(views: [{snapshotAt: 1735862400000}]) {
- list {
- name
- history
- }
+ node(name: "1") {
+ outNeighbours {
+ applyViews(views: [{snapshotAt: 1735862400000}]) {
+ list {
+ name
+ history {
+ timestamps {
+ list
+ }
}
}
}
- }
- }"""
+ }
+ }
+ }
+}"""
correct = {
"graph": {
"node": {
@@ -2244,14 +3164,25 @@ def test_apply_view_out_neighbours_snapshot_at():
"applyViews": {
"list": [
{
- "history": [
- 1735689600000,
- 1735776000000,
- 1735862400000,
- ],
"name": "2",
+ "history": {
+ "timestamps": {
+ "list": [
+ 1735689600000,
+ 1735776000000,
+ 1735862400000,
+ ]
+ }
+ },
+ },
+ {
+ "name": "3",
+ "history": {
+ "timestamps": {
+ "list": [1735776000000, 1735862400000]
+ }
+ },
},
- {"history": [1735776000000, 1735862400000], "name": "3"},
]
}
}
@@ -2272,7 +3203,9 @@ def test_valid_graph():
edges{
list{
id
- latestTime
+ latestTime {
+ timestamp
+ }
}
}
}
@@ -2280,7 +3213,9 @@ def test_valid_graph():
}"""
correct = {
"graph": {
- "applyViews": {"edges": {"list": [{"id": ["6", "7"], "latestTime": 5}]}}
+ "applyViews": {
+ "edges": {"list": [{"id": ["6", "7"], "latestTime": {"timestamp": 5}}]}
+ }
}
}
run_graphql_test(query, correct, graph)
diff --git a/python/tests/test_base_install/test_graphql/test_edge_sorting.py b/python/tests/test_base_install/test_graphql/test_edge_sorting.py
index 799b511c4f..a068a49069 100644
--- a/python/tests/test_base_install/test_graphql/test_edge_sorting.py
+++ b/python/tests/test_base_install/test_graphql/test_edge_sorting.py
@@ -231,7 +231,10 @@ def test_graph_edge_sort_by_earliest_time(graph):
dst {
id
}
- earliestTime
+ earliestTime {
+ timestamp
+ datetime
+ }
}
}
}
@@ -243,11 +246,46 @@ def test_graph_edge_sort_by_earliest_time(graph):
"edges": {
"sorted": {
"list": [
- {"src": {"id": "c"}, "dst": {"id": "d"}, "earliestTime": 1},
- {"src": {"id": "a"}, "dst": {"id": "b"}, "earliestTime": 1},
- {"src": {"id": "b"}, "dst": {"id": "d"}, "earliestTime": 2},
- {"src": {"id": "a"}, "dst": {"id": "d"}, "earliestTime": 3},
- {"src": {"id": "b"}, "dst": {"id": "c"}, "earliestTime": 4},
+ {
+ "src": {"id": "c"},
+ "dst": {"id": "d"},
+ "earliestTime": {
+ "timestamp": 1,
+ "datetime": "1970-01-01T00:00:00.001+00:00",
+ },
+ },
+ {
+ "src": {"id": "a"},
+ "dst": {"id": "b"},
+ "earliestTime": {
+ "timestamp": 1,
+ "datetime": "1970-01-01T00:00:00.001+00:00",
+ },
+ },
+ {
+ "src": {"id": "b"},
+ "dst": {"id": "d"},
+ "earliestTime": {
+ "timestamp": 2,
+ "datetime": "1970-01-01T00:00:00.002+00:00",
+ },
+ },
+ {
+ "src": {"id": "a"},
+ "dst": {"id": "d"},
+ "earliestTime": {
+ "timestamp": 3,
+ "datetime": "1970-01-01T00:00:00.003+00:00",
+ },
+ },
+ {
+ "src": {"id": "b"},
+ "dst": {"id": "c"},
+ "earliestTime": {
+ "timestamp": 4,
+ "datetime": "1970-01-01T00:00:00.004+00:00",
+ },
+ },
]
}
}
@@ -285,8 +323,8 @@ def test_graph_edge_sort_by_earliest_time_reversed(graph):
{"src": {"id": "a"}, "dst": {"id": "d"}},
{"src": {"id": "b"}, "dst": {"id": "d"}},
# C->D and A->B have the same time so will maintain their relative order
- {"src": {"id": "c"}, "dst": {"id": "d"}},
{"src": {"id": "a"}, "dst": {"id": "b"}},
+ {"src": {"id": "c"}, "dst": {"id": "d"}},
]
}
}
diff --git a/python/tests/test_base_install/test_graphql/test_gql_history.py b/python/tests/test_base_install/test_graphql/test_gql_history.py
new file mode 100644
index 0000000000..859eba299f
--- /dev/null
+++ b/python/tests/test_base_install/test_graphql/test_gql_history.py
@@ -0,0 +1,760 @@
+from datetime import datetime, timezone
+
+from utils import run_group_graphql_test
+from raphtory import Graph
+
+
+def create_graph() -> Graph:
+ graph = Graph()
+
+ # Add nodes with timestamps and properties
+ graph.add_node(100, "Dumbledore")
+ graph.add_node(200, "Dumbledore", properties={"Age": 50})
+ graph.add_node(300, "Dumbledore", properties={"Age": 51})
+
+ graph.add_node(150, "Harry")
+ graph.add_node(250, "Harry", properties={"Age": 20})
+ graph.add_node(350, "Harry", properties={"Age": 21})
+
+ # Add edges with timestamps and layers
+ graph.add_edge(150, "Dumbledore", "Harry", layer="communication")
+ graph.add_edge(
+ 200, "Dumbledore", "Harry", properties={"weight": 0.5}, layer="friendship"
+ )
+ graph.add_edge(
+ 300, "Dumbledore", "Harry", properties={"weight": 0.7}, layer="communication"
+ )
+ graph.add_edge(
+ 350, "Dumbledore", "Harry", properties={"weight": 0.9}, layer="friendship"
+ )
+ return graph
+
+
+def test_history():
+ graph = create_graph()
+ queries_and_expected_outputs = []
+
+ # test node
+ query = """
+ {
+ graph(path: "g") {
+ node(name: "Dumbledore") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }"""
+ expected_output = {
+ "graph": {
+ "node": {
+ "history": {"timestamps": {"list": [100, 150, 200, 200, 300, 300, 350]}}
+ }
+ }
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ # test edge
+ query = """
+ {
+ graph(path: "g") {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }"""
+ expected_output = {
+ "graph": {"edge": {"history": {"timestamps": {"list": [150, 200, 300, 350]}}}}
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ # test windowed node
+ query = """
+ {
+ graph(path: "g") {
+ window(start: 0, end: 150) {
+ node(name: "Dumbledore") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }"""
+ expected_output_1 = {
+ "graph": {"window": {"node": {"history": {"timestamps": {"list": [100]}}}}}
+ }
+ queries_and_expected_outputs.append((query, expected_output_1))
+ query = """
+ {
+ graph(path: "g") {
+ window(start: 150, end: 300) {
+ node(name: "Dumbledore") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }"""
+ expected_output_2 = {
+ "graph": {
+ "window": {"node": {"history": {"timestamps": {"list": [150, 200, 200]}}}}
+ }
+ }
+ queries_and_expected_outputs.append((query, expected_output_2))
+
+ query = """
+ {
+ graph(path: "g") {
+ window(start: 300, end: 450) {
+ node(name: "Dumbledore") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }"""
+ expected_output_3 = {
+ "graph": {
+ "window": {"node": {"history": {"timestamps": {"list": [300, 300, 350]}}}}
+ }
+ }
+ queries_and_expected_outputs.append((query, expected_output_3))
+
+ # test windowed edge
+ query = """
+ {
+ graph(path: "g") {
+ window(start: 0, end: 150) {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output_1 = {"graph": {"window": {"edge": None}}}
+ queries_and_expected_outputs.append((query, expected_output_1))
+
+ query = """
+ {
+ graph(path: "g") {
+ window(start: 150, end: 300) {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output_2 = {
+ "graph": {"window": {"edge": {"history": {"timestamps": {"list": [150, 200]}}}}}
+ }
+ queries_and_expected_outputs.append((query, expected_output_2))
+
+ query = """
+ {
+ graph(path: "g") {
+ window(start: 300, end: 450) {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output_3 = {
+ "graph": {"window": {"edge": {"history": {"timestamps": {"list": [300, 350]}}}}}
+ }
+ queries_and_expected_outputs.append((query, expected_output_3))
+
+ # test layered node
+ query = """
+ {
+ graph(path: "g") {
+ layer(name: "friendship") {
+ node(name: "Dumbledore") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }"""
+ expected_output = {
+ "graph": {
+ "layer": {
+ "node": {"history": {"timestamps": {"list": [100, 200, 200, 300, 350]}}}
+ }
+ }
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ # test layered edge
+ query = """
+ {
+ graph(path: "g") {
+ layer(name: "communication") {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output = {
+ "graph": {"layer": {"edge": {"history": {"timestamps": {"list": [150, 300]}}}}}
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ # test edge filtered by property, when the same property for the same edge is updated at different times
+ query_1 = """
+ {
+ graph(path: "g") {
+ filterEdges(
+ expr: {
+ property: {
+ name: "weight"
+ where: {
+ eq: {
+ f64: 0.9
+ }
+ }
+ }
+ }
+ ) {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output_1 = {
+ "graph": {
+ "filterEdges": {
+ "edge": {"history": {"timestamps": {"list": [150, 200, 300, 350]}}}
+ }
+ }
+ }
+ queries_and_expected_outputs.append((query_1, expected_output_1))
+
+ query_2 = """
+ {
+ graph(path: "g") {
+ filterEdges(
+ expr: {
+ property: {
+ name: "weight"
+ where: {
+ eq: {
+ f64: 0.7
+ }
+ }
+ }
+ }
+ ) {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }"""
+ expected_output_2 = {"graph": {"filterEdges": {"edge": None}}}
+ queries_and_expected_outputs.append((query_2, expected_output_2))
+
+ # test node filtered by property, when the same property is updated at different times
+ query_1 = """
+ {
+ graph(path: "g") {
+ filterNodes(
+ expr: {
+ property: {
+ name: "Age"
+ where: {
+ lt: {
+ i64: 51
+ }
+ }
+ }
+ }
+ ) {
+ node(name: "Dumbledore") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }"""
+ expected_output_1 = {"graph": {"filterNodes": {"node": None}}}
+ queries_and_expected_outputs.append((query_1, expected_output_1))
+
+ query_2 = """
+ {
+ graph(path: "g") {
+ filterNodes(
+ expr: {
+ property: {
+ name: "Age"
+ where: {
+ ge: {
+ i64: 51
+ }
+ }
+ }
+ }
+ ) {
+ node(name: "Dumbledore") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }"""
+ expected_output_2 = {
+ "graph": {
+ "filterNodes": {
+ "node": {"history": {"timestamps": {"list": [100, 200, 300]}}}
+ }
+ }
+ }
+ queries_and_expected_outputs.append((query_2, expected_output_2))
+
+ query_3 = """
+ {
+ graph(path: "g") {
+ filterNodes(
+ expr: {
+ property: {
+ name: "Age"
+ where: {
+ lt: {
+ i64: 21
+ }
+ }
+ }
+ }
+ ) {
+ node(name: "Harry") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }"""
+ expected_output_3 = {"graph": {"filterNodes": {"node": None}}}
+ queries_and_expected_outputs.append((query_3, expected_output_3))
+
+ query_4 = """
+ {
+ graph(path: "g") {
+ filterNodes(
+ expr: {
+ property: {
+ name: "Age"
+ where: {
+ ge: {
+ i64: 21
+ }
+ }
+ }
+ }
+ ) {
+ node(name: "Harry") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }"""
+ expected_output_4 = {
+ "graph": {
+ "filterNodes": {
+ "node": {
+ "history": {
+ "timestamps": {"list": [150, 150, 200, 250, 300, 350, 350]}
+ }
+ }
+ }
+ }
+ }
+ queries_and_expected_outputs.append((query_4, expected_output_4))
+
+ run_group_graphql_test(queries_and_expected_outputs, graph)
+
+
+def test_gql_event_time():
+ graph = create_graph()
+ graph.add_edge(
+ datetime(2025, 1, 20, 0, 0, tzinfo=timezone.utc), "Dumbledore", "Harry"
+ )
+ queries_and_expected_outputs = []
+ queries_and_expected_errors = []
+
+ query = """
+ {
+ graph(path: "g") {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ datetimes {
+ list
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output = {
+ "graph": {
+ "edge": {
+ "history": {
+ "datetimes": {
+ "list": [
+ "1970-01-01T00:00:00.150+00:00",
+ "1970-01-01T00:00:00.200+00:00",
+ "1970-01-01T00:00:00.300+00:00",
+ "1970-01-01T00:00:00.350+00:00",
+ "2025-01-20T00:00:00+00:00",
+ ]
+ }
+ }
+ }
+ }
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ query = """
+ {
+ graph(path: "g") {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ datetimes(formatString: "%Y-%m-%d") {
+ list
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output = {
+ "graph": {
+ "edge": {
+ "history": {
+ "datetimes": {
+ "list": [
+ "1970-01-01",
+ "1970-01-01",
+ "1970-01-01",
+ "1970-01-01",
+ "2025-01-20",
+ ]
+ }
+ }
+ }
+ }
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ query = """
+ {
+ graph(path: "g") {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ datetimes(formatString: "%Y-%m-%d %H:%M:%S %3fms") {
+ list
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output = {
+ "graph": {
+ "edge": {
+ "history": {
+ "datetimes": {
+ "list": [
+ "1970-01-01 00:00:00 150ms",
+ "1970-01-01 00:00:00 200ms",
+ "1970-01-01 00:00:00 300ms",
+ "1970-01-01 00:00:00 350ms",
+ "2025-01-20 00:00:00 000ms",
+ ]
+ }
+ }
+ }
+ }
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ # call datetime on individual EventTimes
+ query = """
+ {
+ graph(path: "g") {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ list {
+ datetime(formatString: "%Y-%m-%d %H:%M:%S %3fms")
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output = {
+ "graph": {
+ "edge": {
+ "history": {
+ "list": [
+ {"datetime": "1970-01-01 00:00:00 150ms"},
+ {"datetime": "1970-01-01 00:00:00 200ms"},
+ {"datetime": "1970-01-01 00:00:00 300ms"},
+ {"datetime": "1970-01-01 00:00:00 350ms"},
+ {"datetime": "2025-01-20 00:00:00 000ms"},
+ ]
+ }
+ }
+ }
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ # invalid format string should return error but not crash server
+ query = """
+ {
+ graph(path: "g") {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ datetimes(formatString: "%Y-%m-%d %H:%M:%S %4fms") {
+ list
+ }
+ }
+ }
+ }
+ }
+ """
+ queries_and_expected_errors.append(
+ (query, "Invalid datetime format string: '%Y-%m-%d %H:%M:%S %4fms'")
+ )
+
+ # error when we call datetime on individual EventTimes
+ query = """
+ {
+ graph(path: "g") {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ list {
+ datetime(formatString: "%Y-%m-%d %H:%M:%S %4fms")
+ }
+ }
+ }
+ }
+ }
+ """
+ queries_and_expected_errors.append(
+ (query, "Invalid datetime format string: '%Y-%m-%d %H:%M:%S %4fms'")
+ )
+
+ query = """
+ {
+ graph(path: "g") {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ eventId {
+ list
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output = {
+ "graph": {"edge": {"history": {"eventId": {"list": [6, 7, 8, 9, 10]}}}}
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ # test parsing of time inputs
+ query = """
+ {
+ graph(path: "g") {
+ window(start: "1970-01-01T00:00:00", end: "1970-01-01T00:00:00.150") {
+ node(name: "Dumbledore") {
+ history {
+ timestamps {
+ list
+ }
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output = {
+ "graph": {"window": {"node": {"history": {"timestamps": {"list": [100]}}}}}
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ # test eventId time parsing and windowing behaviour
+ query = """
+ {
+ graph(path: "g") {
+ window(
+ start: {timestamp: "1970-01-01T00:00:00.150+00:00", id: 0}
+ end: {time: "1970-01-01T00:00:00.350+00:00", eventId: 10}
+ ) {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ list {
+ timestamp
+ eventId
+ }
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output = {
+ "graph": {
+ "window": {
+ "edge": {
+ "history": {
+ "list": [
+ {"timestamp": 150, "eventId": 6},
+ {"timestamp": 200, "eventId": 7},
+ {"timestamp": 300, "eventId": 8},
+ {"timestamp": 350, "eventId": 9},
+ ]
+ }
+ }
+ }
+ }
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ # end of the window is exclusive, timestamp: 350 should be missing
+ query = """
+ {
+ graph(path: "g") {
+ window(
+ start: {timestamp: 150, eventId: 6}
+ end: {time: "1970-01-01 00:00:00.350", id: 9}
+ ) {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ list {
+ timestamp
+ eventId
+ }
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output = {
+ "graph": {
+ "window": {
+ "edge": {
+ "history": {
+ "list": [
+ {"timestamp": 150, "eventId": 6},
+ {"timestamp": 200, "eventId": 7},
+ {"timestamp": 300, "eventId": 8},
+ ]
+ }
+ }
+ }
+ }
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ # start of window is inclusive, we need to go past it
+ query = """
+ {
+ graph(path: "g") {
+ window(
+ start: {timestamp: 150, id: 7}
+ end: {time: "1970-01-01T00:00:00.351", eventId: 8}
+ ) {
+ edge(src: "Dumbledore", dst: "Harry") {
+ history {
+ list {
+ timestamp
+ eventId
+ }
+ }
+ }
+ }
+ }
+ }
+ """
+ expected_output = {
+ "graph": {
+ "window": {
+ "edge": {
+ "history": {
+ "list": [
+ {"timestamp": 200, "eventId": 7},
+ {"timestamp": 300, "eventId": 8},
+ {"timestamp": 350, "eventId": 9},
+ ]
+ }
+ }
+ }
+ }
+ }
+ queries_and_expected_outputs.append((query, expected_output))
+
+ run_group_graphql_test(queries_and_expected_outputs, graph)
diff --git a/python/tests/test_base_install/test_graphql/test_node_sorting.py b/python/tests/test_base_install/test_graphql/test_node_sorting.py
index 26410e0116..9a091dd17c 100644
--- a/python/tests/test_base_install/test_graphql/test_node_sorting.py
+++ b/python/tests/test_base_install/test_graphql/test_node_sorting.py
@@ -134,7 +134,7 @@ def test_graph_nodes_sort_by_earliest_time(graph):
expected_output = {
"graph": {
"nodes": {
- "sorted": {"list": [{"id": "b"}, {"id": "d"}, {"id": "a"}, {"id": "c"}]}
+ "sorted": {"list": [{"id": "d"}, {"id": "b"}, {"id": "a"}, {"id": "c"}]}
}
}
}
diff --git a/python/tests/test_base_install/test_graphql/test_rolling_expanding.py b/python/tests/test_base_install/test_graphql/test_rolling_expanding.py
index 26d1309ab4..62a9b82a2c 100644
--- a/python/tests/test_base_install/test_graphql/test_rolling_expanding.py
+++ b/python/tests/test_base_install/test_graphql/test_rolling_expanding.py
@@ -49,8 +49,12 @@ def test_graph_date():
}
count
list{
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -67,15 +71,42 @@ def test_graph_date():
],
"count": 9,
"list": [
- {"start": 1735646400000, "end": 1735732800000},
- {"start": 1735689600000, "end": 1735776000000},
- {"start": 1735732800000, "end": 1735819200000},
- {"start": 1735776000000, "end": 1735862400000},
- {"start": 1735819200000, "end": 1735905600000},
- {"start": 1735862400000, "end": 1735948800000},
- {"start": 1735905600000, "end": 1735992000000},
- {"start": 1735948800000, "end": 1736035200000},
- {"start": 1735992000000, "end": 1736078400000},
+ {
+ "start": {"timestamp": 1735646400000},
+ "end": {"timestamp": 1735732800000},
+ },
+ {
+ "start": {"timestamp": 1735689600000},
+ "end": {"timestamp": 1735776000000},
+ },
+ {
+ "start": {"timestamp": 1735732800000},
+ "end": {"timestamp": 1735819200000},
+ },
+ {
+ "start": {"timestamp": 1735776000000},
+ "end": {"timestamp": 1735862400000},
+ },
+ {
+ "start": {"timestamp": 1735819200000},
+ "end": {"timestamp": 1735905600000},
+ },
+ {
+ "start": {"timestamp": 1735862400000},
+ "end": {"timestamp": 1735948800000},
+ },
+ {
+ "start": {"timestamp": 1735905600000},
+ "end": {"timestamp": 1735992000000},
+ },
+ {
+ "start": {"timestamp": 1735948800000},
+ "end": {"timestamp": 1736035200000},
+ },
+ {
+ "start": {"timestamp": 1735992000000},
+ "end": {"timestamp": 1736078400000},
+ },
],
}
}
@@ -87,8 +118,12 @@ def test_graph_date():
graph(path: "g") {
expanding(step: {duration: "3 days"}) {
list{
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -98,8 +133,8 @@ def test_graph_date():
"graph": {
"expanding": {
"list": [
- {"start": None, "end": 1735948800000},
- {"start": None, "end": 1736208000000},
+ {"start": {"timestamp": None}, "end": {"timestamp": 1735948800000}},
+ {"start": {"timestamp": None}, "end": {"timestamp": 1736208000000}},
]
}
}
@@ -115,8 +150,12 @@ def test_graph_epoch():
graph(path: "g") {
rolling(window: {epoch: 1}) {
list {
- earliestTime
- latestTime
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
}
}
@@ -126,11 +165,11 @@ def test_graph_epoch():
"graph": {
"rolling": {
"list": [
- {"earliestTime": 1, "latestTime": 1},
- {"earliestTime": 2, "latestTime": 2},
- {"earliestTime": 3, "latestTime": 3},
- {"earliestTime": 4, "latestTime": 4},
- {"earliestTime": 5, "latestTime": 5},
+ {"earliestTime": {"timestamp": 1}, "latestTime": {"timestamp": 1}},
+ {"earliestTime": {"timestamp": 2}, "latestTime": {"timestamp": 2}},
+ {"earliestTime": {"timestamp": 3}, "latestTime": {"timestamp": 3}},
+ {"earliestTime": {"timestamp": 4}, "latestTime": {"timestamp": 4}},
+ {"earliestTime": {"timestamp": 5}, "latestTime": {"timestamp": 5}},
]
}
}
@@ -140,10 +179,14 @@ def test_graph_epoch():
query = """
{
graph(path: "g") {
- rolling(window: {epoch: 1},step:{epoch:2}) {
+ rolling(window: {epoch: 1}, step:{epoch: 2}) {
list {
- earliestTime
- latestTime
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
}
}
@@ -153,8 +196,8 @@ def test_graph_epoch():
"graph": {
"rolling": {
"list": [
- {"earliestTime": 2, "latestTime": 2},
- {"earliestTime": 4, "latestTime": 4},
+ {"earliestTime": {"timestamp": 2}, "latestTime": {"timestamp": 2}},
+ {"earliestTime": {"timestamp": 4}, "latestTime": {"timestamp": 4}},
]
}
}
@@ -167,8 +210,12 @@ def test_graph_epoch():
window(start: 2, end: 5) {
rolling(window: {epoch: 2}, step: {epoch: 1}) {
list {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -180,9 +227,9 @@ def test_graph_epoch():
"window": {
"rolling": {
"list": [
- {"start": 2, "end": 3},
- {"start": 2, "end": 4},
- {"start": 3, "end": 5},
+ {"start": {"timestamp": 2}, "end": {"timestamp": 3}},
+ {"start": {"timestamp": 2}, "end": {"timestamp": 4}},
+ {"start": {"timestamp": 3}, "end": {"timestamp": 5}},
]
}
}
@@ -196,7 +243,9 @@ def test_graph_epoch():
window(start: 2, end: 7) {
expanding(step: {epoch: 3}) {
list {
- end
+ end {
+ timestamp
+ }
nodes {
list {
name
@@ -214,13 +263,13 @@ def test_graph_epoch():
"expanding": {
"list": [
{
- "end": 5,
+ "end": {"timestamp": 5},
"nodes": {
"list": [{"name": "1"}, {"name": "2"}, {"name": "3"}]
},
},
{
- "end": 7,
+ "end": {"timestamp": 7},
"nodes": {
"list": [
{"name": "1"},
@@ -244,8 +293,12 @@ def test_graph_epoch():
window(start: 2, end: 5) {
expanding(step: {epoch: 1}) {
list {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -257,9 +310,9 @@ def test_graph_epoch():
"window": {
"expanding": {
"list": [
- {"start": 2, "end": 3},
- {"start": 2, "end": 4},
- {"start": 2, "end": 5},
+ {"start": {"timestamp": 2}, "end": {"timestamp": 3}},
+ {"start": {"timestamp": 2}, "end": {"timestamp": 4}},
+ {"start": {"timestamp": 2}, "end": {"timestamp": 5}},
]
}
}
@@ -272,10 +325,18 @@ def test_graph_epoch():
graph(path: "g") {
rolling(window: {epoch: 3}, step: {epoch: 4}) {
list {
- start
- end
- earliestTime
- latestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
}
}
@@ -284,7 +345,14 @@ def test_graph_epoch():
correct = {
"graph": {
"rolling": {
- "list": [{"start": 2, "end": 5, "earliestTime": 2, "latestTime": 4}]
+ "list": [
+ {
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 4},
+ }
+ ]
}
}
}
@@ -294,12 +362,20 @@ def test_graph_epoch():
query = """
{
graph(path: "g") {
- rolling(window: {epoch: 3}, step: {epoch:1000}) {
+ rolling(window: {epoch: 3}, step: {epoch: 1000}) {
list {
- start
- end
- earliestTime
- latestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
}
}
@@ -311,7 +387,7 @@ def test_graph_epoch():
query = """
{
graph(path: "g") {
- rolling(window: {epoch: 1}, step: {epoch:1}) {
+ rolling(window: {epoch: 1}, step: {epoch: 1}) {
count
}
}
@@ -325,14 +401,26 @@ def test_graph_epoch():
graph(path: "g") {
rolling(window: {epoch: 1}, step: {epoch: 1}) {
page(limit: 2, pageIndex: 1, offset: 2) {
- earliestTime
- latestTime
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
}
}
}
"""
- correct = {"graph": {"rolling": {"page": [{"earliestTime": 5, "latestTime": 5}]}}}
+ correct = {
+ "graph": {
+ "rolling": {
+ "page": [
+ {"earliestTime": {"timestamp": 5}, "latestTime": {"timestamp": 5}}
+ ]
+ }
+ }
+ }
run_graphql_test(query, correct, graph)
@@ -343,28 +431,40 @@ def test_node():
{
graph(path: "g") {
node(name:"1"){
- rolling(window:{epoch:1},step:{epoch:1}){
+ rolling(window:{epoch: 1}, step:{epoch: 1}){
list{
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
degree
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
count
page(limit:3,offset:3){
- start
+ start {
+ timestamp
+ }
degree
}
}
- before(time:4){
- expanding(step:{epoch:1}){
+ before(time: 4){
+ expanding(step:{epoch: 1}){
list{
- end
+ end {
+ timestamp
+ }
degree
}
count
page(limit:1,offset:2){
- end
+ end {
+ timestamp
+ }
degree
}
}
@@ -379,24 +479,52 @@ def test_node():
"node": {
"rolling": {
"list": [
- {"start": 1, "end": 2, "degree": 1, "earliestTime": 1},
- {"start": 2, "end": 3, "degree": 2, "earliestTime": 2},
- {"start": 3, "end": 4, "degree": 2, "earliestTime": 3},
- {"start": 4, "end": 5, "degree": 1, "earliestTime": 4},
- {"start": 5, "end": 6, "degree": 0, "earliestTime": None},
+ {
+ "start": {"timestamp": 1},
+ "end": {"timestamp": 2},
+ "degree": 1,
+ "earliestTime": {"timestamp": 1},
+ },
+ {
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 3},
+ "degree": 2,
+ "earliestTime": {"timestamp": 2},
+ },
+ {
+ "start": {"timestamp": 3},
+ "end": {"timestamp": 4},
+ "degree": 2,
+ "earliestTime": {"timestamp": 3},
+ },
+ {
+ "start": {"timestamp": 4},
+ "end": {"timestamp": 5},
+ "degree": 1,
+ "earliestTime": {"timestamp": 4},
+ },
+ {
+ "start": {"timestamp": 5},
+ "end": {"timestamp": 6},
+ "degree": 0,
+ "earliestTime": {"timestamp": None},
+ },
],
"count": 5,
- "page": [{"start": 4, "degree": 1}, {"start": 5, "degree": 0}],
+ "page": [
+ {"start": {"timestamp": 4}, "degree": 1},
+ {"start": {"timestamp": 5}, "degree": 0},
+ ],
},
"before": {
"expanding": {
"list": [
- {"end": 2, "degree": 1},
- {"end": 3, "degree": 2},
- {"end": 4, "degree": 2},
+ {"end": {"timestamp": 2}, "degree": 1},
+ {"end": {"timestamp": 3}, "degree": 2},
+ {"end": {"timestamp": 4}, "degree": 2},
],
"count": 3,
- "page": [{"end": 4, "degree": 2}],
+ "page": [{"end": {"timestamp": 4}, "degree": 2}],
}
},
}
@@ -418,9 +546,15 @@ def test_nodes():
page(limit: 1, offset: 0) {
id
degree
- start
- end
- earliestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
}
}
count
@@ -428,9 +562,15 @@ def test_nodes():
page(limit: 1, offset: 0) {
id
degree
- start
- end
- earliestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -440,10 +580,18 @@ def test_nodes():
page(limit: 1, offset: 0) {
id
degree
- start
- end
- earliestTime
- latestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
}
count
@@ -451,10 +599,18 @@ def test_nodes():
page(limit: 1, offset: 0) {
id
degree
- start
- end
- earliestTime
- latestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
}
}
@@ -466,171 +622,171 @@ def test_nodes():
correct = {
"graph": {
"nodes": {
- "after": {
- "expanding": {
- "count": 4,
- "list": [
- {
- "page": [
- {
- "degree": 2,
- "earliestTime": 2,
- "end": 3,
- "id": "1",
- "latestTime": 2,
- "start": 2,
- }
- ]
- },
- {
- "page": [
- {
- "degree": 2,
- "earliestTime": 2,
- "end": 4,
- "id": "1",
- "latestTime": 3,
- "start": 2,
- }
- ]
- },
- {
- "page": [
- {
- "degree": 2,
- "earliestTime": 2,
- "end": 5,
- "id": "1",
- "latestTime": 4,
- "start": 2,
- }
- ]
- },
- {
- "page": [
- {
- "degree": 2,
- "earliestTime": 2,
- "end": 6,
- "id": "1",
- "latestTime": 4,
- "start": 2,
- }
- ]
- },
- ],
- "page": [
- {
- "page": [
- {
- "degree": 2,
- "earliestTime": 2,
- "end": 5,
- "id": "1",
- "latestTime": 4,
- "start": 2,
- }
- ]
- },
- {
- "page": [
- {
- "degree": 2,
- "earliestTime": 2,
- "end": 6,
- "id": "1",
- "latestTime": 4,
- "start": 2,
- }
- ]
- },
- ],
- }
- },
"rolling": {
- "count": 5,
"list": [
{
"page": [
{
- "degree": 1,
- "earliestTime": 1,
- "end": 2,
"id": "1",
- "start": 1,
+ "degree": 1,
+ "start": {"timestamp": 1},
+ "end": {"timestamp": 2},
+ "earliestTime": {"timestamp": 1},
}
]
},
{
"page": [
{
- "degree": 2,
- "earliestTime": 2,
- "end": 3,
"id": "1",
- "start": 2,
+ "degree": 2,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 3},
+ "earliestTime": {"timestamp": 2},
}
]
},
{
"page": [
{
- "degree": 2,
- "earliestTime": 3,
- "end": 4,
"id": "1",
- "start": 3,
+ "degree": 2,
+ "start": {"timestamp": 3},
+ "end": {"timestamp": 4},
+ "earliestTime": {"timestamp": 3},
}
]
},
{
"page": [
{
- "degree": 1,
- "earliestTime": 4,
- "end": 5,
"id": "1",
- "start": 4,
+ "degree": 1,
+ "start": {"timestamp": 4},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": 4},
}
]
},
{
"page": [
{
- "degree": 0,
- "earliestTime": None,
- "end": 6,
"id": "1",
- "start": 5,
+ "degree": 0,
+ "start": {"timestamp": 5},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": None},
}
]
},
],
+ "count": 5,
"page": [
{
"page": [
{
- "degree": 1,
- "earliestTime": 4,
- "end": 5,
"id": "1",
- "start": 4,
+ "degree": 1,
+ "start": {"timestamp": 4},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": 4},
}
]
},
{
"page": [
{
- "degree": 0,
- "earliestTime": None,
- "end": 6,
"id": "1",
- "start": 5,
+ "degree": 0,
+ "start": {"timestamp": 5},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": None},
}
]
},
],
},
+ "after": {
+ "expanding": {
+ "list": [
+ {
+ "page": [
+ {
+ "id": "1",
+ "degree": 2,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 3},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 2},
+ }
+ ]
+ },
+ {
+ "page": [
+ {
+ "id": "1",
+ "degree": 2,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 4},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
+ }
+ ]
+ },
+ {
+ "page": [
+ {
+ "id": "1",
+ "degree": 2,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 4},
+ }
+ ]
+ },
+ {
+ "page": [
+ {
+ "id": "1",
+ "degree": 2,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 4},
+ }
+ ]
+ },
+ ],
+ "count": 4,
+ "page": [
+ {
+ "page": [
+ {
+ "id": "1",
+ "degree": 2,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 4},
+ }
+ ]
+ },
+ {
+ "page": [
+ {
+ "id": "1",
+ "degree": 2,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 4},
+ }
+ ]
+ },
+ ],
+ }
+ },
}
}
}
@@ -651,9 +807,15 @@ def test_path():
page(limit: 1, offset: 0) {
id
degree
- start
- end
- earliestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
}
}
count
@@ -661,9 +823,15 @@ def test_path():
page(limit: 1, offset: 0) {
id
degree
- start
- end
- earliestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -673,10 +841,18 @@ def test_path():
page(limit: 1, offset: 0) {
id
degree
- start
- end
- earliestTime
- latestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
}
count
@@ -684,10 +860,18 @@ def test_path():
page(limit: 1, offset: 0) {
id
degree
- start
- end
- earliestTime
- latestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
}
}
@@ -708,9 +892,9 @@ def test_path():
{
"id": "2",
"degree": 1,
- "start": 1,
- "end": 2,
- "earliestTime": 1,
+ "start": {"timestamp": 1},
+ "end": {"timestamp": 2},
+ "earliestTime": {"timestamp": 1},
}
]
},
@@ -719,9 +903,9 @@ def test_path():
{
"id": "2",
"degree": 1,
- "start": 2,
- "end": 3,
- "earliestTime": 2,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 3},
+ "earliestTime": {"timestamp": 2},
}
]
},
@@ -730,9 +914,9 @@ def test_path():
{
"id": "2",
"degree": 1,
- "start": 3,
- "end": 4,
- "earliestTime": 3,
+ "start": {"timestamp": 3},
+ "end": {"timestamp": 4},
+ "earliestTime": {"timestamp": 3},
}
]
},
@@ -741,9 +925,9 @@ def test_path():
{
"id": "2",
"degree": 0,
- "start": 4,
- "end": 5,
- "earliestTime": None,
+ "start": {"timestamp": 4},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": None},
}
]
},
@@ -752,9 +936,9 @@ def test_path():
{
"id": "2",
"degree": 0,
- "start": 5,
- "end": 6,
- "earliestTime": None,
+ "start": {"timestamp": 5},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": None},
}
]
},
@@ -766,9 +950,9 @@ def test_path():
{
"id": "2",
"degree": 0,
- "start": 4,
- "end": 5,
- "earliestTime": None,
+ "start": {"timestamp": 4},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": None},
}
]
},
@@ -777,9 +961,9 @@ def test_path():
{
"id": "2",
"degree": 0,
- "start": 5,
- "end": 6,
- "earliestTime": None,
+ "start": {"timestamp": 5},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": None},
}
]
},
@@ -793,10 +977,10 @@ def test_path():
{
"id": "2",
"degree": 1,
- "start": 2,
- "end": 3,
- "earliestTime": 2,
- "latestTime": 2,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 3},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 2},
}
]
},
@@ -805,10 +989,10 @@ def test_path():
{
"id": "2",
"degree": 1,
- "start": 2,
- "end": 4,
- "earliestTime": 2,
- "latestTime": 3,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 4},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
}
]
},
@@ -817,10 +1001,10 @@ def test_path():
{
"id": "2",
"degree": 1,
- "start": 2,
- "end": 5,
- "earliestTime": 2,
- "latestTime": 3,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
}
]
},
@@ -829,10 +1013,10 @@ def test_path():
{
"id": "2",
"degree": 1,
- "start": 2,
- "end": 6,
- "earliestTime": 2,
- "latestTime": 3,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
}
]
},
@@ -844,10 +1028,10 @@ def test_path():
{
"id": "2",
"degree": 1,
- "start": 2,
- "end": 5,
- "earliestTime": 2,
- "latestTime": 3,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
}
]
},
@@ -856,10 +1040,10 @@ def test_path():
{
"id": "2",
"degree": 1,
- "start": 2,
- "end": 6,
- "earliestTime": 2,
- "latestTime": 3,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
}
]
},
@@ -881,33 +1065,61 @@ def test_edge():
{
graph(path: "g") {
edge(src:"1",dst:"2"){
- rolling(window:{epoch:1},step:{epoch:1}){
+ rolling(window:{epoch: 1},step:{epoch: 1}){
list{
- start
- end
- earliestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
}
count
page(limit:3,offset:3){
- start
- end
- earliestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
}
}
- after(time:1){
- expanding(step:{epoch:1}){
+ after(time: 1){
+ expanding(step:{epoch: 1}){
list{
- start
- end
- earliestTime
- latestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
count
page(limit:2,offset:2){
- start
- end
- earliestTime
- latestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
}
}
@@ -919,35 +1131,119 @@ def test_edge():
correct = {
"graph": {
"edge": {
- "after": {
- "expanding": {
- "count": 4,
- "list": [
- {"earliestTime": 2, "end": 3, "latestTime": 2, "start": 2},
- {"earliestTime": 2, "end": 4, "latestTime": 3, "start": 2},
- {"earliestTime": 2, "end": 5, "latestTime": 3, "start": 2},
- {"earliestTime": 2, "end": 6, "latestTime": 3, "start": 2},
- ],
- "page": [
- {"earliestTime": 2, "end": 5, "latestTime": 3, "start": 2},
- {"earliestTime": 2, "end": 6, "latestTime": 3, "start": 2},
- ],
- }
- },
"rolling": {
- "count": 5,
"list": [
- {"earliestTime": 1, "end": 2, "start": 1},
- {"earliestTime": 2, "end": 3, "start": 2},
- {"earliestTime": 3, "end": 4, "start": 3},
- {"earliestTime": None, "end": 5, "start": 4},
- {"earliestTime": None, "end": 6, "start": 5},
- ],
- "page": [
- {"earliestTime": None, "end": 5, "start": 4},
- {"earliestTime": None, "end": 6, "start": 5},
+ {
+ "start": {"timestamp": 1},
+ "end": {"timestamp": 2},
+ "earliestTime": {"timestamp": 1},
+ },
+ {
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 3},
+ "earliestTime": {"timestamp": 2},
+ },
+ {
+ "start": {"timestamp": 3},
+ "end": {"timestamp": 4},
+ "earliestTime": {"timestamp": 3},
+ },
+ {
+ "start": {"timestamp": 4},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": None},
+ },
+ {
+ "start": {"timestamp": 5},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": None},
+ },
+ ],
+ "count": 5,
+ "page": [
+ {
+ "start": {"timestamp": 4},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": None},
+ },
+ {
+ "start": {"timestamp": 5},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": None},
+ },
],
},
+ "after": {
+ "expanding": {
+ "list": [
+ {
+ "start": {"timestamp": None},
+ "end": {"timestamp": 3},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 2},
+ },
+ {
+ "start": {"timestamp": None},
+ "end": {"timestamp": 4},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
+ },
+ {
+ "start": {"timestamp": None},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
+ },
+ {
+ "start": {"timestamp": None},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
+ },
+ ],
+ "count": 4,
+ "list": [
+ {
+ "earliestTime": {"timestamp": 2},
+ "end": {"timestamp": 3},
+ "latestTime": {"timestamp": 2},
+ "start": {"timestamp": 2},
+ },
+ {
+ "earliestTime": {"timestamp": 2},
+ "end": {"timestamp": 4},
+ "latestTime": {"timestamp": 3},
+ "start": {"timestamp": 2},
+ },
+ {
+ "earliestTime": {"timestamp": 2},
+ "end": {"timestamp": 5},
+ "latestTime": {"timestamp": 3},
+ "start": {"timestamp": 2},
+ },
+ {
+ "earliestTime": {"timestamp": 2},
+ "end": {"timestamp": 6},
+ "latestTime": {"timestamp": 3},
+ "start": {"timestamp": 2},
+ },
+ ],
+ "page": [
+ {
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
+ },
+ {
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
+ },
+ ],
+ }
+ },
}
}
}
@@ -966,18 +1262,30 @@ def test_edges():
list {
page(limit: 1, offset: 0) {
id
- start
- end
- earliestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
}
}
count
page(limit: 3, pageIndex: 1) {
page(limit: 1, offset: 0) {
id
- start
- end
- earliestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -986,20 +1294,36 @@ def test_edges():
list {
page(limit: 1, offset: 0) {
id
- start
- end
- earliestTime
- latestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
}
count
page(limit: 2, pageIndex: 1) {
page(limit: 1, offset: 0) {
id
- start
- end
- earliestTime
- latestTime
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
+ earliestTime {
+ timestamp
+ }
+ latestTime {
+ timestamp
+ }
}
}
}
@@ -1011,158 +1335,158 @@ def test_edges():
correct = {
"graph": {
"edges": {
- "after": {
- "expanding": {
- "count": 4,
- "list": [
- {
- "page": [
- {
- "earliestTime": 2,
- "end": 3,
- "id": ["1", "2"],
- "latestTime": 2,
- "start": 2,
- }
- ]
- },
- {
- "page": [
- {
- "earliestTime": 2,
- "end": 4,
- "id": ["1", "2"],
- "latestTime": 3,
- "start": 2,
- }
- ]
- },
- {
- "page": [
- {
- "earliestTime": 2,
- "end": 5,
- "id": ["1", "2"],
- "latestTime": 3,
- "start": 2,
- }
- ]
- },
- {
- "page": [
- {
- "earliestTime": 2,
- "end": 6,
- "id": ["1", "2"],
- "latestTime": 3,
- "start": 2,
- }
- ]
- },
- ],
- "page": [
- {
- "page": [
- {
- "earliestTime": 2,
- "end": 5,
- "id": ["1", "2"],
- "latestTime": 3,
- "start": 2,
- }
- ]
- },
- {
- "page": [
- {
- "earliestTime": 2,
- "end": 6,
- "id": ["1", "2"],
- "latestTime": 3,
- "start": 2,
- }
- ]
- },
- ],
- }
- },
"rolling": {
- "count": 5,
"list": [
{
"page": [
{
- "earliestTime": 1,
- "end": 2,
"id": ["1", "2"],
- "start": 1,
+ "start": {"timestamp": 1},
+ "end": {"timestamp": 2},
+ "earliestTime": {"timestamp": 1},
}
]
},
{
"page": [
{
- "earliestTime": 2,
- "end": 3,
"id": ["1", "2"],
- "start": 2,
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 3},
+ "earliestTime": {"timestamp": 2},
}
]
},
{
"page": [
{
- "earliestTime": 3,
- "end": 4,
"id": ["1", "2"],
- "start": 3,
+ "start": {"timestamp": 3},
+ "end": {"timestamp": 4},
+ "earliestTime": {"timestamp": 3},
}
]
},
{
"page": [
{
- "earliestTime": None,
- "end": 5,
"id": ["1", "2"],
- "start": 4,
+ "start": {"timestamp": 4},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": None},
}
]
},
{
"page": [
{
- "earliestTime": None,
- "end": 6,
"id": ["1", "2"],
- "start": 5,
+ "start": {"timestamp": 5},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": None},
}
]
},
],
+ "count": 5,
"page": [
{
"page": [
{
- "earliestTime": None,
- "end": 5,
"id": ["1", "2"],
- "start": 4,
+ "start": {"timestamp": 4},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": None},
}
]
},
{
"page": [
{
- "earliestTime": None,
- "end": 6,
"id": ["1", "2"],
- "start": 5,
+ "start": {"timestamp": 5},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": None},
}
]
},
],
},
+ "after": {
+ "expanding": {
+ "list": [
+ {
+ "page": [
+ {
+ "id": ["1", "2"],
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 3},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 2},
+ }
+ ]
+ },
+ {
+ "page": [
+ {
+ "id": ["1", "2"],
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 4},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
+ }
+ ]
+ },
+ {
+ "page": [
+ {
+ "id": ["1", "2"],
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
+ }
+ ]
+ },
+ {
+ "page": [
+ {
+ "id": ["1", "2"],
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
+ }
+ ]
+ },
+ ],
+ "count": 4,
+ "page": [
+ {
+ "page": [
+ {
+ "id": ["1", "2"],
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 5},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
+ }
+ ]
+ },
+ {
+ "page": [
+ {
+ "id": ["1", "2"],
+ "start": {"timestamp": 2},
+ "end": {"timestamp": 6},
+ "earliestTime": {"timestamp": 2},
+ "latestTime": {"timestamp": 3},
+ }
+ ]
+ },
+ ],
+ }
+ },
}
}
}
@@ -1173,14 +1497,16 @@ def test_zero_step():
graph = Graph()
create_graph_epoch(graph)
queries_and_exceptions = []
- zero_exception = "Failed to parse time string: 0 size step is not supported"
+ zero_exception = "Failed to parse time string: 0 size step is not supported."
# graph fail test
query = """
{
graph(path: "g") {
rolling(window:{duration:"1 day"},step:{duration:"0 day"}){
list{
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1191,9 +1517,11 @@ def test_zero_step():
query = """
{
graph(path: "g") {
- rolling(window:{epoch:100},step:{epoch:0}){
+ rolling(window:{epoch: 100},step:{epoch: 0}){
list{
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1206,7 +1534,9 @@ def test_zero_step():
graph(path: "g") {
rolling(window:{duration:"0 day"}){
list{
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1217,9 +1547,11 @@ def test_zero_step():
query = """
{
graph(path: "g") {
- rolling(window:{epoch:0}){
+ rolling(window:{epoch: 0}){
list{
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1232,7 +1564,9 @@ def test_zero_step():
graph(path: "g") {
expanding(step:{duration:"0 day"}){
list{
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1243,9 +1577,11 @@ def test_zero_step():
query = """
{
graph(path: "g") {
- expanding(step:{epoch:0}){
+ expanding(step:{epoch: 0}){
list{
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1260,7 +1596,9 @@ def test_zero_step():
node(name: "1") {
rolling(window:{duration:"1 day"},step:{duration:"0 year"}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1273,9 +1611,11 @@ def test_zero_step():
{
graph(path: "g") {
node(name: "1") {
- rolling(window:{epoch:100},step:{epoch:0}){
+ rolling(window:{epoch: 100},step:{epoch: 0}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1290,7 +1630,9 @@ def test_zero_step():
node(name: "1") {
rolling(window:{duration:"0 day"}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1303,9 +1645,11 @@ def test_zero_step():
{
graph(path: "g") {
node(name: "1") {
- rolling(window:{epoch:0}){
+ rolling(window:{epoch: 0}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1320,7 +1664,9 @@ def test_zero_step():
node(name: "1") {
expanding(step:{duration:"0 day"}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1333,9 +1679,11 @@ def test_zero_step():
{
graph(path: "g") {
node(name: "1") {
- expanding(step:{epoch:0}){
+ expanding(step:{epoch: 0}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1352,7 +1700,9 @@ def test_zero_step():
list {
rolling(window:{duration:"1 day"},step:{duration:"0 year"}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1367,9 +1717,11 @@ def test_zero_step():
graph(path: "g") {
nodes {
list {
- rolling(window:{epoch:100},step:{epoch:0}){
+ rolling(window:{epoch: 100},step:{epoch: 0}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1386,7 +1738,9 @@ def test_zero_step():
list {
rolling(window:{duration:"0 day"}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1401,9 +1755,11 @@ def test_zero_step():
graph(path: "g") {
nodes {
list {
- rolling(window:{epoch:0}){
+ rolling(window:{epoch: 0}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1420,7 +1776,9 @@ def test_zero_step():
list {
expanding(step:{duration:"0 day"}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1435,9 +1793,11 @@ def test_zero_step():
graph(path: "g") {
nodes {
list {
- expanding(step:{epoch:0}){
+ expanding(step:{epoch: 0}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1456,7 +1816,9 @@ def test_zero_step():
rolling(window:{duration:"1 day"},step:{duration:"0 year"}){
list {
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1472,10 +1834,12 @@ def test_zero_step():
graph(path: "g") {
node(name: "1") {
neighbours {
- rolling(window:{epoch:100},step:{epoch:0}){
+ rolling(window:{epoch: 100},step:{epoch: 0}){
list {
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1494,7 +1858,9 @@ def test_zero_step():
rolling(window:{duration:"0 year"}){
list {
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1510,10 +1876,12 @@ def test_zero_step():
graph(path: "g") {
node(name: "1") {
neighbours {
- rolling(window:{epoch:0}){
+ rolling(window:{epoch: 0}){
list {
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1532,7 +1900,9 @@ def test_zero_step():
expanding(step:{duration:"0 year"}){
list {
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1548,10 +1918,12 @@ def test_zero_step():
graph(path: "g") {
node(name: "1") {
neighbours {
- expanding(step:{epoch:0}){
+ expanding(step:{epoch: 0}){
list {
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1569,7 +1941,9 @@ def test_zero_step():
edge(src: "1", dst: "2") {
rolling(window:{duration:"1 day"},step:{duration:"0 year"}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1582,9 +1956,11 @@ def test_zero_step():
{
graph(path: "g") {
edge(src: "1", dst: "2") {
- rolling(window:{epoch:100},step:{epoch:0}){
+ rolling(window:{epoch: 100},step:{epoch: 0}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1599,7 +1975,9 @@ def test_zero_step():
edge(src: "1", dst: "2") {
rolling(window:{duration:"0 year"}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1612,9 +1990,11 @@ def test_zero_step():
{
graph(path: "g") {
edge(src: "1", dst: "2") {
- rolling(window:{epoch:0}){
+ rolling(window:{epoch: 0}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1629,7 +2009,9 @@ def test_zero_step():
edge(src: "1", dst: "2") {
expanding(step:{duration:"0 year"}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1642,9 +2024,11 @@ def test_zero_step():
{
graph(path: "g") {
edge(src: "1", dst: "2") {
- expanding(step:{epoch:0}){
+ expanding(step:{epoch: 0}){
list {
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1661,9 +2045,10 @@ def test_zero_step():
rolling(window:{duration:"1 day"},step:{duration:"0 year"}){
list {
list{
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
-
}
}
}
@@ -1676,12 +2061,13 @@ def test_zero_step():
{
graph(path: "g") {
edges {
- rolling(window:{epoch:100},step:{epoch:0}){
+ rolling(window:{epoch: 100},step:{epoch: 0}){
list {
list{
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
-
}
}
}
@@ -1697,9 +2083,10 @@ def test_zero_step():
rolling(window:{duration:"0 year"}){
list {
list{
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
-
}
}
}
@@ -1712,12 +2099,13 @@ def test_zero_step():
{
graph(path: "g") {
edges {
- rolling(window:{epoch:0}){
+ rolling(window:{epoch: 0}){
list {
list{
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
-
}
}
}
@@ -1733,9 +2121,10 @@ def test_zero_step():
expanding(step:{duration:"0 year"}){
list {
list{
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
-
}
}
}
@@ -1748,12 +2137,13 @@ def test_zero_step():
{
graph(path: "g") {
edges {
- expanding(step:{epoch:0}){
+ expanding(step:{epoch: 0}){
list {
list{
- earliestTime
+ earliestTime {
+ timestamp
+ }
}
-
}
}
}
@@ -1769,7 +2159,7 @@ def test_mismatched_window_step_and_errors():
create_graph_date(graph)
queries_and_expected_outputs = []
queries_and_exceptions = []
- parse_exception = "Failed to parse time string: one of the tokens in the interval string supposed to be a number couldn't be parsed"
+ parse_exception = "Failed to parse time string: One of the tokens in the interval string supposed to be a number couldn't be parsed."
parse_exception2 = "Failed to parse time string: 'monthdas' is not a valid unit. Valid units are year(s), month(s), week(s), day(s), hour(s), minute(s), second(s) and millisecond(s)."
too_many_exception = "Invalid value for argument \\"
# go forward 1 hour (end of window), then go back 1 day (start of window) from the earliest event in the graph (2025-01-01 00:00:00)
@@ -1778,8 +2168,12 @@ def test_mismatched_window_step_and_errors():
graph(path: "g") {
rolling(window: {duration: "1 day"}, step: {epoch: 3600000}) {
page(limit: 3) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -1790,40 +2184,58 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2024, 12, 31, 1, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(2025, 1, 1, 1, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024, 12, 31, 1, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 1, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
},
{
- "start": int(
- datetime(
- 2024, 12, 31, 2, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(2025, 1, 1, 2, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024, 12, 31, 2, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 2, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
},
{
- "start": int(
- datetime(
- 2024, 12, 31, 3, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(2025, 1, 1, 3, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024, 12, 31, 3, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 3, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
},
]
}
@@ -1838,8 +2250,12 @@ def test_mismatched_window_step_and_errors():
graph(path: "g") {
rolling(window: {duration: "1 day"}, step: {epoch: 3600000}) {
page(limit: 3, offset: 95) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -1850,28 +2266,44 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"page": [
{
- "start": int(
- datetime(2025, 1, 4, 0, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
- "end": int(
- datetime(2025, 1, 5, 0, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 5, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
},
{
- "start": int(
- datetime(2025, 1, 4, 1, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
- "end": int(
- datetime(2025, 1, 5, 1, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
- },
- ]
- }
- }
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 1, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 5, 1, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
+ },
+ ]
+ }
+ }
}
queries_and_expected_outputs.append((query, expected_output))
@@ -1881,8 +2313,12 @@ def test_mismatched_window_step_and_errors():
graph(path: "g") {
rolling(window: {epoch: 3600000}, step: {duration: "1 day"}) {
list {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -1893,44 +2329,76 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"list": [
{
- "start": int(
- datetime(2025, 1, 1, 23, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
- "end": int(
- datetime(2025, 1, 2, 0, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
},
{
- "start": int(
- datetime(2025, 1, 2, 23, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
- "end": int(
- datetime(2025, 1, 3, 0, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
},
{
- "start": int(
- datetime(2025, 1, 3, 23, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
- "end": int(
- datetime(2025, 1, 4, 0, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
},
{
- "start": int(
- datetime(2025, 1, 4, 23, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
- "end": int(
- datetime(2025, 1, 5, 0, 0, tzinfo=timezone.utc).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 5, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ )
+ * 1000
+ },
},
]
}
@@ -1941,9 +2409,11 @@ def test_mismatched_window_step_and_errors():
query = """
{
graph(path: "g") {
- rolling(window:{duration:"1dasdas day"}){
- list{
- earliestTime
+ rolling(window: {duration: "1dasdas day"}) {
+ list {
+ earliestTime {
+ timestamp
+ }
}
}
}
@@ -1953,27 +2423,31 @@ def test_mismatched_window_step_and_errors():
query = """
{
- graph(path: "g") {
- rolling(window:{duration:"1 day"},step:{duration:"1 monthdas"}){
- list{
- earliestTime
+ graph(path: "g") {
+ rolling(window: {duration: "1 day"}, step: {duration: "1 monthdas"}) {
+ list {
+ earliestTime {
+ timestamp
+ }
+ }
+ }
}
}
- }
-}
"""
queries_and_exceptions.append((query, parse_exception2))
query = """
{
- graph(path: "g") {
- rolling(window:{duration:"1 day",epoch:11}){
- list{
- earliestTime
+ graph(path: "g") {
+ rolling(window: {duration: "1 day", epoch: 11}) {
+ list {
+ earliestTime {
+ timestamp
+ }
+ }
+ }
}
}
- }
-}
"""
queries_and_exceptions.append((query, too_many_exception))
@@ -1984,8 +2458,12 @@ def test_mismatched_window_step_and_errors():
node(name: "1") {
rolling(window: {duration: "1 day"}, step: {epoch: 11}) {
page(limit: 3) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -1998,46 +2476,79 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 0, 0, 11_000, tzinfo=timezone.utc
- ).timestamp()
- * 1000
- ),
- "end": int(
- datetime(
- 2025, 1, 1, 0, 0, 0, 11_000, tzinfo=timezone.utc
- ).timestamp()
- * 1000
- ),
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024,
+ 12,
+ 31,
+ 0,
+ 0,
+ 0,
+ 11_000,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 0, 0, 0, 11_000, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 0, 0, 22_000, tzinfo=timezone.utc
- ).timestamp()
- * 1000
- ),
- "end": int(
- datetime(
- 2025, 1, 1, 0, 0, 0, 22_000, tzinfo=timezone.utc
- ).timestamp()
- * 1000
- ),
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024,
+ 12,
+ 31,
+ 0,
+ 0,
+ 0,
+ 22_000,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 0, 0, 0, 22_000, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 0, 0, 33_000, tzinfo=timezone.utc
- ).timestamp()
- * 1000
- ),
- "end": int(
- datetime(
- 2025, 1, 1, 0, 0, 0, 33_000, tzinfo=timezone.utc
- ).timestamp()
- * 1000
- ),
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024,
+ 12,
+ 31,
+ 0,
+ 0,
+ 0,
+ 33_000,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 0, 0, 0, 33_000, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -2053,8 +2564,12 @@ def test_mismatched_window_step_and_errors():
node(name: "1") {
rolling(window: {duration: "1 day"}, step: {epoch: 11}) {
page(limit: 3, offset: 31418180) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -2067,32 +2582,54 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 1, 3, 23, 59, 59, 991_000, tzinfo=timezone.utc
- ).timestamp()
- * 1000
- ),
- "end": int(
- datetime(
- 2025, 1, 4, 23, 59, 59, 991_000, tzinfo=timezone.utc
- ).timestamp()
- * 1000
- ),
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 3,
+ 23,
+ 59,
+ 59,
+ 991_000,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 4,
+ 23,
+ 59,
+ 59,
+ 991_000,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 4, 0, 0, 0, 2_000, tzinfo=timezone.utc
- ).timestamp()
- * 1000
- ),
- "end": int(
- datetime(
- 2025, 1, 5, 0, 0, 0, 2_000, tzinfo=timezone.utc
- ).timestamp()
- * 1000
- ),
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 0, 0, 0, 2_000, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 5, 0, 0, 0, 2_000, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -2107,8 +2644,12 @@ def test_mismatched_window_step_and_errors():
node(name: "1") {
rolling(window: {epoch: 3600000}, step: {duration: "1 day"}) {
list {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -2121,60 +2662,76 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"list": [
{
- "start": int(
- datetime(
- 2025, 1, 1, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 2, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 2, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 3, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 3, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 4, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 4, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 5, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 5, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -2190,8 +2747,12 @@ def test_mismatched_window_step_and_errors():
page(limit: 2) {
rolling(window: {duration: "1 day"}, step: {epoch: 60000}) {
page(limit: 2) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -2207,56 +2768,64 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2024,
- 12,
- 31,
- 0,
- 1,
- 0,
- tzinfo=timezone.utc,
- ).timestamp()
- * 1000
- ),
- "end": int(
- datetime(
- 2025,
- 1,
- 1,
- 0,
- 1,
- 0,
- tzinfo=timezone.utc,
- ).timestamp()
- * 1000
- ),
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024,
+ 12,
+ 31,
+ 0,
+ 1,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2024,
- 12,
- 31,
- 0,
- 2,
- 0,
- tzinfo=timezone.utc,
- ).timestamp()
- * 1000
- ),
- "end": int(
- datetime(
- 2025,
- 1,
- 1,
- 0,
- 2,
- 0,
- tzinfo=timezone.utc,
- ).timestamp()
- * 1000
- ),
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024,
+ 12,
+ 31,
+ 0,
+ 2,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 1,
+ 0,
+ 2,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -2265,56 +2834,64 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2024,
- 12,
- 31,
- 0,
- 1,
- 0,
- tzinfo=timezone.utc,
- ).timestamp()
- * 1000
- ),
- "end": int(
- datetime(
- 2025,
- 1,
- 1,
- 0,
- 1,
- 0,
- tzinfo=timezone.utc,
- ).timestamp()
- * 1000
- ),
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024,
+ 12,
+ 31,
+ 0,
+ 1,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 1,
+ 0,
+ 1,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2024,
- 12,
- 31,
- 0,
- 2,
- 0,
- tzinfo=timezone.utc,
- ).timestamp()
- * 1000
- ),
- "end": int(
- datetime(
- 2025,
- 1,
- 1,
- 0,
- 2,
- 0,
- tzinfo=timezone.utc,
- ).timestamp()
- * 1000
- ),
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024,
+ 12,
+ 31,
+ 0,
+ 2,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 1,
+ 0,
+ 2,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -2333,8 +2910,12 @@ def test_mismatched_window_step_and_errors():
page(limit: 2) {
rolling(window: {duration: "1 day"}, step: {epoch: 60000}) {
page(limit: 2, offset: 5760) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -2350,38 +2931,46 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 1, 4, 0, 1, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 5, 0, 1, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- }
- ]
- }
- },
- {
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 0, 1, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 5, 0, 1, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ }
+ ]
+ }
+ },
+ {
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 1, 4, 0, 1, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 5, 0, 1, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 0, 1, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 5, 0, 1, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
}
]
}
@@ -2399,8 +2988,12 @@ def test_mismatched_window_step_and_errors():
page(limit: 2) {
rolling(window: {epoch: 3600000}, step: {duration: "1 day"}) {
list {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -2416,60 +3009,76 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"list": [
{
- "start": int(
- datetime(
- 2025, 1, 1, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 2, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 2, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 3, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 3, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 4, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 4, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 5, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 5, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -2478,60 +3087,76 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"list": [
{
- "start": int(
- datetime(
- 2025, 1, 1, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 2, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 2, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 3, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 3, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 4, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 4, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 5, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 5, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -2550,8 +3175,12 @@ def test_mismatched_window_step_and_errors():
rolling(window: {duration: "1 day"}, step: {epoch: 60000}) {
page(limit: 2) {
list {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -2569,64 +3198,120 @@ def test_mismatched_window_step_and_errors():
{
"list": [
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024,
+ 12,
+ 31,
+ 0,
+ 1,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 1,
+ 0,
+ 1,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024,
+ 12,
+ 31,
+ 0,
+ 1,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 1,
+ 0,
+ 1,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
},
{
"list": [
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 2, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 2, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024,
+ 12,
+ 31,
+ 0,
+ 2,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 1,
+ 0,
+ 2,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 2, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 2, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024,
+ 12,
+ 31,
+ 0,
+ 2,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 1,
+ 0,
+ 2,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
},
@@ -2646,8 +3331,12 @@ def test_mismatched_window_step_and_errors():
rolling(window: {epoch: 3600000}, step: {duration: "1 day"}) {
page(limit: 2) {
list {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -2665,64 +3354,120 @@ def test_mismatched_window_step_and_errors():
{
"list": [
{
- "start": int(
- datetime(
- 2025, 1, 1, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 2, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 1,
+ 23,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 2,
+ 0,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 1, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 2, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 1,
+ 23,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 2,
+ 0,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
},
{
"list": [
{
- "start": int(
- datetime(
- 2025, 1, 2, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 3, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 2,
+ 23,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 3,
+ 0,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 2, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 3, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 2,
+ 23,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 1,
+ 3,
+ 0,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
},
@@ -2740,8 +3485,12 @@ def test_mismatched_window_step_and_errors():
edge(src: "1", dst: "2") {
rolling(window: {duration: "1 day"}, step: {epoch: 60000}) {
page(limit: 3) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -2754,46 +3503,58 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024, 12, 31, 0, 1, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 0, 1, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 2, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 2, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024, 12, 31, 0, 2, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 0, 2, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 3, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 3, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024, 12, 31, 0, 3, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 0, 3, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -2809,8 +3570,12 @@ def test_mismatched_window_step_and_errors():
edge(src: "1", dst: "2") {
rolling(window: {duration: "1 day"}, step: {epoch: 60000}) {
page(limit: 2, offset: 5760) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -2823,18 +3588,22 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 1, 4, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 5, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 0, 1, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 5, 0, 1, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
}
]
}
@@ -2849,8 +3618,12 @@ def test_mismatched_window_step_and_errors():
edge(src: "1", dst: "2") {
rolling(window: {epoch: 3600000}, step: {duration: "1 day"}) {
list {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -2863,60 +3636,76 @@ def test_mismatched_window_step_and_errors():
"rolling": {
"list": [
{
- "start": int(
- datetime(
- 2025, 1, 1, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 2, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 2, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 3, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 3, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 4, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 4, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 5, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 4, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 5, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -2932,8 +3721,12 @@ def test_mismatched_window_step_and_errors():
rolling(window: {duration: "1 day"}, step: {epoch: 60000}) {
page(limit: 2) {
list {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -2949,92 +3742,116 @@ def test_mismatched_window_step_and_errors():
{
"list": [
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- },
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024, 12, 31, 0, 1, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 0, 1, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ },
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024, 12, 31, 0, 1, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 0, 1, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 1, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024, 12, 31, 0, 1, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 0, 1, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
},
{
"list": [
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 2, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 2, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024, 12, 31, 0, 2, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 0, 2, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 2, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 2, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024, 12, 31, 0, 2, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 0, 2, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2024, 12, 31, 0, 2, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 1, 0, 2, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2024, 12, 31, 0, 2, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 0, 2, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
},
@@ -3052,8 +3869,12 @@ def test_mismatched_window_step_and_errors():
rolling(window: {epoch: 3600000}, step: {duration: "1 day"}) {
page(limit: 2) {
list {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -3069,92 +3890,116 @@ def test_mismatched_window_step_and_errors():
{
"list": [
{
- "start": int(
- datetime(
- 2025, 1, 1, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 2, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 1, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 2, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 1, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 2, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 1, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
},
{
"list": [
{
- "start": int(
- datetime(
- 2025, 1, 2, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 3, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 2, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 3, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 1, 2, 23, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 1, 3, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 2, 23, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 1, 3, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
},
@@ -3186,8 +4031,12 @@ def test_alignment():
graph(path: "g") {
rolling(window: {duration: "1 month"}) {
page(limit: 3) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -3198,46 +4047,58 @@ def test_alignment():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 3, 1, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 4, 1, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 1, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 1, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 4, 1, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 5, 1, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 1, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 1, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 5, 1, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 6, 1, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 1, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 6, 1, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -3250,8 +4111,12 @@ def test_alignment():
graph(path: "g") {
rolling(window: {duration: "1 month"}) {
page(limit: 3) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -3265,8 +4130,12 @@ def test_alignment():
graph(path: "g") {
rolling(window: {duration: "1 month"}, alignmentUnit: UNALIGNED) {
page(limit: 3) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -3277,46 +4146,58 @@ def test_alignment():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 3, 15, 14, 37, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 4, 15, 14, 37, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 15, 14, 37, 52, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 15, 14, 37, 52, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 4, 15, 14, 37, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 5, 15, 14, 37, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 15, 14, 37, 52, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 15, 14, 37, 52, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 5, 15, 14, 37, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 6, 15, 14, 37, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 15, 14, 37, 52, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 6, 15, 14, 37, 52, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -3329,8 +4210,12 @@ def test_alignment():
graph(path: "g") {
rolling(window: {duration: "1 month"}, alignmentUnit: DAY) {
page(limit: 3) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -3341,46 +4226,58 @@ def test_alignment():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 3, 15, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 4, 15, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 15, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 15, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 4, 15, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 5, 15, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 15, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 15, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 5, 15, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 6, 15, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 15, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 6, 15, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -3393,8 +4290,12 @@ def test_alignment():
graph(path: "g") {
rolling(window: {duration: "1 month"}, alignmentUnit: HOUR) {
page(limit: 3) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -3405,46 +4306,58 @@ def test_alignment():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 3, 15, 14, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 4, 15, 14, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 15, 14, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 15, 14, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 4, 15, 14, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 5, 15, 14, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 15, 14, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 15, 14, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 5, 15, 14, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 6, 15, 14, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 15, 14, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 6, 15, 14, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -3457,8 +4370,12 @@ def test_alignment():
graph(path: "g") {
rolling(window: {duration: "1 month"}, step: {duration: "2 weeks"}, alignmentUnit: DAY) {
page(limit: 3) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -3469,46 +4386,58 @@ def test_alignment():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 2, 28, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 3, 29, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 2, 28, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 29, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 3, 12, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 4, 12, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 12, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 12, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 3, 26, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 4, 26, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 26, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 26, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -3521,8 +4450,12 @@ def test_alignment():
graph(path: "g") {
rolling(window: {duration: "1 day"}) {
page(limit: 3) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -3533,46 +4466,58 @@ def test_alignment():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 3, 15, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 3, 16, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 15, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 16, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 3, 16, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 3, 17, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 16, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 17, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 3, 17, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 3, 18, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 17, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 18, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -3585,8 +4530,12 @@ def test_alignment():
graph(path: "g") {
rolling(window: {duration: "1 month and 1 day"}) {
page(limit: 3) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -3597,46 +4546,58 @@ def test_alignment():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 3, 15, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 4, 16, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 15, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 16, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 4, 16, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 5, 17, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 16, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 17, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 5, 17, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 6, 18, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 17, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 6, 18, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -3649,8 +4610,12 @@ def test_alignment():
graph(path: "g") {
rolling(window: {duration: "1 month"}, step: {duration: "1 day"}) {
page(limit: 3) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -3661,46 +4626,58 @@ def test_alignment():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 2, 16, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 3, 16, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 2, 16, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 16, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 2, 17, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 3, 17, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 2, 17, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 17, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
{
- "start": int(
- datetime(
- 2025, 2, 18, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 3, 18, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 2, 18, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 18, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
},
]
}
@@ -3714,8 +4691,12 @@ def test_alignment():
graph(path: "g") {
rolling(window: {epoch: 1000}) {
page(limit: 1) {
- start
- end
+ start {
+ timestamp
+ }
+ end {
+ timestamp
+ }
}
}
}
@@ -3727,18 +4708,22 @@ def test_alignment():
"rolling": {
"page": [
{
- "start": int(
- datetime(
- 2025, 3, 15, 14, 37, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
- "end": int(
- datetime(
- 2025, 3, 15, 14, 37, 53, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000,
+ "start": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 15, 14, 37, 52, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 15, 14, 37, 53, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ },
}
]
}
@@ -3752,7 +4737,9 @@ def test_alignment():
graph(path: "g") {
expanding(step: {duration: "1 day"}) {
page(limit: 2) {
- end
+ end {
+ timestamp
+ }
}
}
}
@@ -3763,20 +4750,24 @@ def test_alignment():
"expanding": {
"page": [
{
- "end": int(
- datetime(
- 2025, 3, 16, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 16, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
{
- "end": int(
- datetime(
- 2025, 3, 17, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 17, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
]
}
@@ -3789,7 +4780,9 @@ def test_alignment():
graph(path: "g") {
expanding(step: {duration: "1 month"}, alignmentUnit: UNALIGNED) {
page(limit: 2) {
- end
+ end {
+ timestamp
+ }
}
}
}
@@ -3800,20 +4793,24 @@ def test_alignment():
"expanding": {
"page": [
{
- "end": int(
- datetime(
- 2025, 4, 15, 14, 37, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 15, 14, 37, 52, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
{
- "end": int(
- datetime(
- 2025, 5, 15, 14, 37, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 15, 14, 37, 52, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
]
}
@@ -3826,7 +4823,9 @@ def test_alignment():
graph(path: "g") {
expanding(step: {duration: "1 month"}, alignmentUnit: DAY) {
page(limit: 2) {
- end
+ end {
+ timestamp
+ }
}
}
}
@@ -3837,20 +4836,24 @@ def test_alignment():
"expanding": {
"page": [
{
- "end": int(
- datetime(
- 2025, 4, 15, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 15, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
{
- "end": int(
- datetime(
- 2025, 5, 15, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 15, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
]
}
@@ -3863,7 +4866,9 @@ def test_alignment():
graph(path: "g") {
expanding(step: {duration: "1 month"}, alignmentUnit: WEEK) {
page(limit: 2) {
- end
+ end {
+ timestamp
+ }
}
}
}
@@ -3874,20 +4879,24 @@ def test_alignment():
"expanding": {
"page": [
{
- "end": int(
- datetime(
- 2025, 4, 13, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 4, 13, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
{
- "end": int(
- datetime(
- 2025, 5, 13, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 5, 13, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
]
}
@@ -3901,7 +4910,9 @@ def test_alignment():
node(name: "1") {
expanding(step: {duration: "2 days"}) {
page(limit: 2) {
- end
+ end {
+ timestamp
+ }
}
}
}
@@ -3914,20 +4925,24 @@ def test_alignment():
"expanding": {
"page": [
{
- "end": int(
- datetime(
- 2025, 3, 17, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 17, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
{
- "end": int(
- datetime(
- 2025, 3, 19, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 3, 19, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
]
}
@@ -3943,7 +4958,9 @@ def test_alignment():
node(name: "1") {
expanding(step: {duration: "2 days"}) {
page(limit: 2, offset: 126) {
- end
+ end {
+ timestamp
+ }
}
}
}
@@ -3956,12 +4973,14 @@ def test_alignment():
"expanding": {
"page": [
{
- "end": int(
- datetime(
- 2025, 11, 24, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 11, 24, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
}
]
}
@@ -3978,7 +4997,9 @@ def test_alignment():
page(limit: 2) {
expanding(step: {epoch: 60000}) {
page(limit: 2) {
- end
+ end {
+ timestamp
+ }
}
}
}
@@ -3994,20 +5015,36 @@ def test_alignment():
"expanding": {
"page": [
{
- "end": int(
- datetime(
- 2025, 3, 15, 14, 38, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 3,
+ 15,
+ 14,
+ 38,
+ 52,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ }
},
{
- "end": int(
- datetime(
- 2025, 3, 15, 14, 39, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 3,
+ 15,
+ 14,
+ 39,
+ 52,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ }
},
]
}
@@ -4016,20 +5053,36 @@ def test_alignment():
"expanding": {
"page": [
{
- "end": int(
- datetime(
- 2025, 3, 15, 14, 38, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 3,
+ 15,
+ 14,
+ 38,
+ 52,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ }
},
{
- "end": int(
- datetime(
- 2025, 3, 15, 14, 39, 52, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 3,
+ 15,
+ 14,
+ 39,
+ 52,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ }
},
]
}
@@ -4048,7 +5101,9 @@ def test_alignment():
page(limit: 2) {
expanding(step: {epoch: 60000}) {
page(limit: 2, offset: 363307) {
- end
+ end {
+ timestamp
+ }
}
}
}
@@ -4064,18 +5119,20 @@ def test_alignment():
"expanding": {
"page": [
{
- "end": int(
- datetime(
- 2025,
- 11,
- 22,
- 21,
- 45,
- 52,
- tzinfo=timezone.utc,
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 11,
+ 22,
+ 21,
+ 45,
+ 52,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ }
}
]
}
@@ -4084,18 +5141,20 @@ def test_alignment():
"expanding": {
"page": [
{
- "end": int(
- datetime(
- 2025,
- 11,
- 22,
- 21,
- 45,
- 52,
- tzinfo=timezone.utc,
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 11,
+ 22,
+ 21,
+ 45,
+ 52,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ }
}
]
}
@@ -4112,7 +5171,9 @@ def test_alignment():
edge(src: "1", dst: "2") {
expanding(step: {duration: "3 months"}) {
list {
- end
+ end {
+ timestamp
+ }
}
}
}
@@ -4125,28 +5186,34 @@ def test_alignment():
"expanding": {
"list": [
{
- "end": int(
- datetime(
- 2025, 6, 1, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 6, 1, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
{
- "end": int(
- datetime(
- 2025, 9, 1, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 9, 1, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
{
- "end": int(
- datetime(
- 2025, 12, 1, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025, 12, 1, 0, 0, 0, tzinfo=timezone.utc
+ ).timestamp()
+ * 1000
+ )
+ }
},
]
}
@@ -4162,7 +5229,9 @@ def test_alignment():
expanding(step: {duration: "10 weeks"}) {
list {
list {
- end
+ end {
+ timestamp
+ }
}
}
}
@@ -4178,48 +5247,80 @@ def test_alignment():
{
"list": [
{
- "end": int(
- datetime(
- 2025, 5, 22, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 5,
+ 22,
+ 0,
+ 0,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ }
}
]
},
{
"list": [
{
- "end": int(
- datetime(
- 2025, 7, 31, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 7,
+ 31,
+ 0,
+ 0,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ }
}
]
},
{
"list": [
{
- "end": int(
- datetime(
- 2025, 10, 9, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 10,
+ 9,
+ 0,
+ 0,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ }
}
]
},
{
"list": [
{
- "end": int(
- datetime(
- 2025, 12, 18, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 12,
+ 18,
+ 0,
+ 0,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ }
}
]
},
@@ -4238,7 +5339,9 @@ def test_alignment():
expanding(step: {duration: "70 days"}) {
page(limit: 2) {
list {
- end
+ end {
+ timestamp
+ }
}
}
}
@@ -4256,30 +5359,40 @@ def test_alignment():
{
"list": [
{
- "end": int(
- datetime(
- 2025,
- 5,
- 24,
- 0,
- 0,
- 0,
- tzinfo=timezone.utc,
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 5,
+ 24,
+ 0,
+ 0,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ }
}
]
},
{
"list": [
{
- "end": int(
- datetime(
- 2025, 8, 2, 0, 0, 0, tzinfo=timezone.utc
- ).timestamp()
- )
- * 1000
+ "end": {
+ "timestamp": int(
+ datetime(
+ 2025,
+ 8,
+ 2,
+ 0,
+ 0,
+ 0,
+ tzinfo=timezone.utc,
+ ).timestamp()
+ * 1000
+ )
+ }
}
]
},
diff --git a/python/tests/test_base_install/test_graphql/update_graph/test_batch_updates.py b/python/tests/test_base_install/test_graphql/update_graph/test_batch_updates.py
index f7bf61cb4d..bc7e9b5ec4 100644
--- a/python/tests/test_base_install/test_graphql/update_graph/test_batch_updates.py
+++ b/python/tests/test_base_install/test_graphql/update_graph/test_batch_updates.py
@@ -115,9 +115,9 @@ def test_add_nodes():
3.0,
],
)
- check_arr(ben.history(), [1, 2, 3, 4, 5, 6])
+ check_arr(ben.history.t.collect(), [1, 2, 3, 4, 5, 6])
assert ben.node_type == "person"
- check_arr(hamza.history(), [1, 2])
+ check_arr(hamza.history.t.collect(), [1, 2])
assert hamza.node_type is None
assert_has_metadata(lucas, lucas_props)
assert lucas.node_type == "person"
@@ -176,8 +176,8 @@ def test_add_edges():
3.0,
],
)
- check_arr(ben_hammza.history(), [1, 2, 3, 4, 5, 6])
+ check_arr(ben_hammza.history.t.collect(), [1, 2, 3, 4, 5, 6])
assert_set_eq(ben_hammza.layer_names, {"_default", "test"})
- check_arr(hamza_lucas.history(), [1, 2])
+ check_arr(hamza_lucas.history.t.collect(), [1, 2])
assert hamza_lucas.layer_names == ["_default"]
assert_has_metadata(lucas_hamza.layer("_default"), lucas_props)
diff --git a/python/tests/test_base_install/test_graphql/update_graph/test_edge_updates.py b/python/tests/test_base_install/test_graphql/update_graph/test_edge_updates.py
index 6df3eaff16..731e2a6c60 100644
--- a/python/tests/test_base_install/test_graphql/update_graph/test_edge_updates.py
+++ b/python/tests/test_base_install/test_graphql/update_graph/test_edge_updates.py
@@ -68,9 +68,12 @@ def test_add_updates():
g = client.receive_graph("path/to/event_graph")
e = g.edge("ben", "hamza")
assert_has_properties(e, props)
- check_arr(e.properties.temporal.get("prop_float").history(), [2, 3])
- check_arr(e.layer("test").properties.temporal.get("prop_float").history(), [2])
- check_arr(e.history(), [1, 2, 3, 4, 5, 6])
+ check_arr(e.properties.temporal.get("prop_float").history.t.collect(), [2, 3])
+ check_arr(
+ e.layer("test").properties.temporal.get("prop_float").history.t.collect(),
+ [2],
+ )
+ check_arr(e.history.t.collect(), [1, 2, 3, 4, 5, 6])
def test_add_metadata():
@@ -127,7 +130,7 @@ def test_delete():
edge = rg.add_edge(1, "ben", "hamza")
edge.delete(2)
g = client.receive_graph("path/to/event_graph")
- assert g.edge("ben", "hamza").deletions() == [2]
+ assert g.edge("ben", "hamza").deletions.t == [2]
client.new_graph("path/to/persistent_graph", "PERSISTENT")
rg = client.remote_graph("path/to/persistent_graph")
@@ -136,5 +139,5 @@ def test_delete():
edge = rg.add_edge(1, "ben", "lucas", layer="colleagues")
edge.delete(2, layer="colleagues")
g = client.receive_graph("path/to/persistent_graph")
- assert g.edge("ben", "hamza").deletions() == [2]
- assert g.edge("ben", "lucas").deletions() == [2]
+ assert g.edge("ben", "hamza").deletions == [(2, 1)]
+ assert g.edge("ben", "lucas").deletions == [(2, 3)]
diff --git a/python/tests/test_base_install/test_graphql/update_graph/test_graph_updates.py b/python/tests/test_base_install/test_graphql/update_graph/test_graph_updates.py
index c086ce8546..36f7bdd5e9 100644
--- a/python/tests/test_base_install/test_graphql/update_graph/test_graph_updates.py
+++ b/python/tests/test_base_install/test_graphql/update_graph/test_graph_updates.py
@@ -90,7 +90,7 @@ def test_add_properties():
]
)
- check_arr(g.properties.temporal.get("prop_map").history(), timestamps)
+ check_arr(g.properties.temporal.get("prop_map").history.t.collect(), timestamps)
def test_add_node():
@@ -141,8 +141,8 @@ def test_delete_edge():
rg.add_edge(1, "ben", "hamza")
rg.delete_edge(2, "ben", "hamza")
g = client.receive_graph("path/to/event_graph")
- assert g.edge("ben", "hamza").history() == [1]
- assert g.edge("ben", "hamza").deletions() == [2]
+ assert g.edge("ben", "hamza").history.t.collect() == [1]
+ assert g.edge("ben", "hamza").deletions == [(2, 1)]
client.new_graph("path/to/persistent_graph", "PERSISTENT")
rg = client.remote_graph("path/to/persistent_graph")
@@ -151,5 +151,5 @@ def test_delete_edge():
rg.add_edge(1, "ben", "lucas", layer="colleagues")
rg.delete_edge(2, "ben", "lucas", layer="colleagues")
g = client.receive_graph("path/to/persistent_graph")
- assert g.edge("ben", "hamza").deletions() == [2]
- assert g.edge("ben", "lucas").deletions() == [2]
+ assert g.edge("ben", "hamza").deletions == [(2, 1)]
+ assert g.edge("ben", "lucas").deletions == [(2, 3)]
diff --git a/python/tests/test_base_install/test_graphql/update_graph/test_node_updates.py b/python/tests/test_base_install/test_graphql/update_graph/test_node_updates.py
index 117c005fdd..37ca6c0150 100644
--- a/python/tests/test_base_install/test_graphql/update_graph/test_node_updates.py
+++ b/python/tests/test_base_install/test_graphql/update_graph/test_node_updates.py
@@ -62,7 +62,7 @@ def test_add_updates():
rg.node("ben").add_updates(4)
g = client.receive_graph("path/to/event_graph")
assert_has_properties(g.node("ben"), props)
- check_arr(g.node("ben").history(), [1, 2, 3, 4])
+ check_arr(g.node("ben").history.t.collect(), [1, 2, 3, 4])
def test_add_metadata():
diff --git a/python/tests/test_base_install/test_loaders/test_load_from_pandas.py b/python/tests/test_base_install/test_loaders/test_load_from_pandas.py
index d4594428ab..14c77332ee 100644
--- a/python/tests/test_base_install/test_loaders/test_load_from_pandas.py
+++ b/python/tests/test_base_install/test_loaders/test_load_from_pandas.py
@@ -1504,7 +1504,7 @@ def test_load_date32_from_pandas():
)
# check equality
- actual_node_datetimes = {v.id: v.history_date_time()[0] for v in g.nodes}
+ actual_node_datetimes = {v.id: v.history.dt[0] for v in g.nodes}
for id, dt in expected_node_datetimes.items():
assert actual_node_datetimes[id] == dt
@@ -1512,6 +1512,39 @@ def test_load_date32_from_pandas():
assert actual_edge_times == expected_edge_times
+def test_load_date_dataframe():
+ dates = [
+ datetime.date(1970, 1, 1),
+ datetime.date(1970, 1, 2),
+ datetime.date(1970, 1, 3),
+ datetime.date(1970, 1, 4),
+ ]
+ df = pd.DataFrame(
+ {
+ "time": dates,
+ "src": [1, 2, 3, 4],
+ "dst": [5, 6, 7, 8],
+ }
+ )
+ g = Graph()
+ g.load_edges_from_pandas(df=df, time="time", src="src", dst="dst")
+ assert sorted(g.edges.history.flatten()) == [
+ "1970-01-01 00:00:00",
+ "1970-01-02 00:00:00",
+ "1970-01-03 00:00:00",
+ "1970-01-04 00:00:00",
+ ]
+
+ g = Graph()
+ g.load_nodes_from_pandas(df=df, time="time", id="src")
+ assert sorted(g.nodes.history.flatten()) == [
+ "1970-01-01 00:00:00",
+ "1970-01-02 00:00:00",
+ "1970-01-03 00:00:00",
+ "1970-01-04 00:00:00",
+ ]
+
+
def test_load_edges_from_pandas_csv_c_engine_time_utf8(tmp_path):
# test c engine
csv_1 = """src,dst,time,value
@@ -1684,7 +1717,7 @@ def test_load_edges_from_pandas_csv_c_engine_time_utf8(tmp_path):
expected_node_time_props[60_000] = "2022-01-06T00:00:00"
# check edges
- assert sorted(e.history_date_time()[0] for e in g.edges) == expected_edge_times
+ assert sorted(e.history.dt[0] for e in g.edges) == expected_edge_times
assert (
sorted(e.properties["time"] for e in g.edges if "time" in e.properties)
== expected_edge_time_props
@@ -1692,7 +1725,7 @@ def test_load_edges_from_pandas_csv_c_engine_time_utf8(tmp_path):
assert sorted(e.properties["value"] for e in g.edges) == expected_edge_values
# check nodes
- actual_node_times = {v.id: v.history_date_time()[0] for v in g.nodes}
+ actual_node_times = {v.id: v.history.dt[0] for v in g.nodes}
for id, dt in expected_node_times.items():
assert actual_node_times[id] == dt
assert {
diff --git a/python/tests/test_base_install/test_loaders/test_load_from_parquet.py b/python/tests/test_base_install/test_loaders/test_load_from_parquet.py
index 2289d476ee..2bbc981189 100644
--- a/python/tests/test_base_install/test_loaders/test_load_from_parquet.py
+++ b/python/tests/test_base_install/test_loaders/test_load_from_parquet.py
@@ -906,7 +906,7 @@ def test_load_nodes_from_parquet_date32(tmp_path):
2: _utc_midnight(dates[1]),
3: _utc_midnight(dates[2]),
}
- actual = {v.id: v.history_date_time()[0] for v in g.nodes}
+ actual = {v.id: v.history.dt[0] for v in g.nodes}
assert actual == expected
@@ -953,7 +953,7 @@ def test_load_edges_from_parquet_timestamp_ms_utc(tmp_path):
g = Graph()
g.load_edges_from_parquet(parquet_path=str(path), time="time", src="src", dst="dst")
- actual = sorted(e.history_date_time()[0] for e in g.edges)
+ actual = sorted(e.history.dt[0] for e in g.edges)
assert actual == times
@@ -973,7 +973,7 @@ def test_load_nodes_from_parquet_timestamp_ms_utc(tmp_path):
g = Graph()
g.load_nodes_from_parquet(parquet_path=str(path), time="time", id="id")
- actual = sorted(v.history_date_time()[0] for v in g.nodes)
+ actual = sorted(v.history.dt[0] for v in g.nodes)
assert actual == times
@@ -1005,7 +1005,7 @@ def test_load_edges_from_parquet_timestamp_ns_no_tz(tmp_path):
datetime.datetime(2020, 1, 1, 0, 0, 0, 123000, tzinfo=datetime.timezone.utc),
datetime.datetime(2020, 1, 2, 0, 0, 0, 999000, tzinfo=datetime.timezone.utc),
]
- actual = sorted(e.history_date_time()[0] for e in g.edges)
+ actual = sorted(e.history.dt[0] for e in g.edges)
assert actual == expected
@@ -1026,5 +1026,5 @@ def test_load_nodes_from_parquet_timestamp_ns_no_tz(tmp_path):
datetime.datetime(2020, 1, 1, 0, 0, 0, 123000, tzinfo=datetime.timezone.utc),
datetime.datetime(2020, 1, 2, 0, 0, 0, 999000, tzinfo=datetime.timezone.utc),
]
- actual = sorted(v.history_date_time()[0] for v in g.nodes)
+ actual = sorted(v.history.dt[0] for v in g.nodes)
assert actual == expected
diff --git a/python/tests/test_base_install/test_repr.py b/python/tests/test_base_install/test_repr.py
index a5c7a01464..62e975e7f3 100644
--- a/python/tests/test_base_install/test_repr.py
+++ b/python/tests/test_base_install/test_repr.py
@@ -11,58 +11,43 @@ def test_no_layers(self):
G = Graph()
G.add_edge(1, "A", "B")
G.add_edge(1, "A", "B")
- expected_out = "Edge(source=A, target=B, earliest_time=1, latest_time=1, layer(s)=[_default])\n"
-
- with patch("sys.stdout", new=StringIO()) as fake_out:
- print(G.edge("A", "B"))
- self.assertEqual(fake_out.getvalue(), expected_out)
+ expected_out = "Edge(source=A, target=B, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=1), layer(s)=[_default])"
+ self.assertEqual(repr(G.edge("A", "B")), expected_out)
# event graph with two layers
def test_layers_no_props(self):
G = Graph()
G.add_edge(1, "A", "B", layer="layer 1")
G.add_edge(1, "A", "B", layer="layer 2")
- expected_out = "Edge(source=A, target=B, earliest_time=1, latest_time=1, layer(s)=[layer 1, layer 2])\n"
-
- with patch("sys.stdout", new=StringIO()) as fake_out:
- print(G.edge("A", "B"))
- self.assertEqual(fake_out.getvalue(), expected_out)
+ expected_out = "Edge(source=A, target=B, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=1), layer(s)=[layer 1, layer 2])"
+ self.assertEqual(repr(G.edge("A", "B")), expected_out)
# edge with more than 11 layers
def test_many_layers(self):
G = Graph()
for i in range(20):
G.add_edge(i, "A", "B", layer=f"layer {i}")
- expected_out = "Edge(source=A, target=B, earliest_time=0, latest_time=19, layer(s)=[layer 0,layer 1,layer 2,layer 3,layer 4,layer 5,layer 6,layer 7,layer 8,layer 9, ...])\n"
-
- with patch("sys.stdout", new=StringIO()) as fake_out:
- print(G.edge("A", "B"))
- self.assertEqual(fake_out.getvalue(), expected_out)
+ expected_out = "Edge(source=A, target=B, earliest_time=EventTime(timestamp=0, event_id=0), latest_time=EventTime(timestamp=19, event_id=19), layer(s)=[layer 0,layer 1,layer 2,layer 3,layer 4,layer 5,layer 6,layer 7,layer 8,layer 9, ...])"
+ self.assertEqual(repr(G.edge("A", "B")), expected_out)
# event graph with two layers and properties
def test_layers_and_props(self):
G = Graph()
G.add_edge(1, "A", "B", layer="layer 1", properties={"greeting": "howdy"})
G.add_edge(2, "A", "B", layer="layer 2", properties={"greeting": "yo"})
- expected_out = "Edge(source=A, target=B, earliest_time=1, latest_time=2, properties={greeting: yo}, layer(s)=[layer 1, layer 2])\n"
- with patch("sys.stdout", new=StringIO()) as fake_out:
- print(G.edge("A", "B"))
- self.assertEqual(fake_out.getvalue(), expected_out)
+ expected_out = "Edge(source=A, target=B, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=2, event_id=1), properties={greeting: yo}, layer(s)=[layer 1, layer 2])"
+ self.assertEqual(repr(G.edge("A", "B")), expected_out)
- expected_out = "Edges(Edge(source=A, target=B, earliest_time=1, latest_time=1, properties={greeting: howdy}, layer(s)=[layer 1]), Edge(source=A, target=B, earliest_time=2, latest_time=2, properties={greeting: yo}, layer(s)=[layer 2]))\n"
- with patch("sys.stdout", new=StringIO()) as fake_out:
- print(G.edge("A", "B").explode())
- self.assertEqual(fake_out.getvalue(), expected_out)
+ expected_out = "Edges(Edge(source=A, target=B, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=1, event_id=0), properties={greeting: howdy}, layer(s)=[layer 1]), Edge(source=A, target=B, earliest_time=EventTime(timestamp=2, event_id=1), latest_time=EventTime(timestamp=2, event_id=1), properties={greeting: yo}, layer(s)=[layer 2]))"
+ self.assertEqual(repr(G.edge("A", "B").explode()), expected_out)
# event graph with one layer and one non-layer
- def layers_and_non_layers(self):
+ def test_layers_and_non_layers(self):
G = Graph()
G.add_edge(1, "A", "B", layer="layer 1", properties={"greeting": "howdy"})
G.add_edge(2, "A", "B", properties={"greeting": "yo"})
- expected_out = "Edge(source=A, target=B, earliest_time=1, latest_time=2, properties={greeting: yo}, layer(s)=[_default, layer 1])\n"
- with patch("sys.stdout", new=StringIO()) as fake_out:
- print(G.edge("A", "B"))
- self.assertEqual(fake_out.getvalue(), expected_out)
+ expected_out = "Edge(source=A, target=B, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=2, event_id=1), properties={greeting: yo}, layer(s)=[layer 1, _default])"
+ self.assertEqual(repr(G.edge("A", "B")), expected_out)
# persistent graph with layers
def test_persistent_graph(self):
@@ -71,10 +56,8 @@ def test_persistent_graph(self):
G.delete_edge(5, "A", "B", layer="layer 1")
G.add_edge(2, "A", "B", layer="layer 2", properties={"greeting": "yo"})
G.delete_edge(6, "A", "B", layer="layer 2")
- expected_out = "Edge(source=A, target=B, earliest_time=1, latest_time=6, properties={greeting: yo}, layer(s)=[layer 1, layer 2])\n"
- with patch("sys.stdout", new=StringIO()) as fake_out:
- print(G.edge("A", "B"))
- self.assertEqual(fake_out.getvalue(), expected_out)
+ expected_out = "Edge(source=A, target=B, earliest_time=EventTime(timestamp=1, event_id=0), latest_time=EventTime(timestamp=6, event_id=3), properties={greeting: yo}, layer(s)=[layer 1, layer 2])"
+ self.assertEqual(repr(G.edge("A", "B")), expected_out)
if __name__ == "__main__":
diff --git a/python/tests/test_export.py b/python/tests/test_export.py
index 2eb177e9b8..4359cc54cd 100644
--- a/python/tests/test_export.py
+++ b/python/tests/test_export.py
@@ -244,22 +244,23 @@ def test_networkx_full_history():
"server_name": "Alpha",
},
"temporal": [
- ("OS_version", (1693555200000, "Ubuntu 20.04")),
- ("OS_version", (1693555260000, "Ubuntu 20.04")),
- ("OS_version", (1693555320000, "Ubuntu 20.04")),
- ("primary_function", (1693555200000, "Database")),
- ("primary_function", (1693555260000, "Database")),
- ("primary_function", (1693555320000, "Database")),
- ("uptime_days", (1693555200000, 120)),
- ("uptime_days", (1693555260000, 121)),
- ("uptime_days", (1693555320000, 122)),
+ ("OS_version", ((1693555200000, 9), "Ubuntu 20.04")),
+ ("OS_version", ((1693555260000, 10), "Ubuntu 20.04")),
+ ("OS_version", ((1693555320000, 11), "Ubuntu 20.04")),
+ ("primary_function", ((1693555200000, 9), "Database")),
+ ("primary_function", ((1693555260000, 10), "Database")),
+ ("primary_function", ((1693555320000, 11), "Database")),
+ ("uptime_days", ((1693555200000, 9), 120)),
+ ("uptime_days", ((1693555260000, 10), 121)),
+ ("uptime_days", ((1693555320000, 11), 122)),
],
"update_history": [
- 1693555200000,
- 1693555260000,
- 1693555320000,
- 1693555500000,
- 1693556400000,
+ (1693555200000, 0),
+ (1693555200000, 9),
+ (1693555260000, 10),
+ (1693555320000, 11),
+ (1693555500000, 1),
+ (1693556400000, 4),
],
},
),
@@ -272,15 +273,15 @@ def test_networkx_full_history():
"server_name": "Beta",
},
"temporal": [
- ("OS_version", (1693555500000, "Red Hat 8.1")),
- ("primary_function", (1693555500000, "Web Server")),
- ("uptime_days", (1693555500000, 45)),
+ ("OS_version", ((1693555500000, 12), "Red Hat 8.1")),
+ ("primary_function", ((1693555500000, 12), "Web Server")),
+ ("uptime_days", ((1693555500000, 12), 45)),
],
"update_history": [
- 1693555200000,
- 1693555500000,
- 1693555800000,
- 1693556700000,
+ (1693555200000, 0),
+ (1693555500000, 12),
+ (1693555800000, 2),
+ (1693556700000, 5),
],
},
),
@@ -293,17 +294,17 @@ def test_networkx_full_history():
"server_name": "Charlie",
},
"temporal": [
- ("OS_version", (1693555800000, "Windows Server 2022")),
- ("primary_function", (1693555800000, "File Storage")),
- ("uptime_days", (1693555800000, 90)),
+ ("OS_version", ((1693555800000, 13), "Windows Server 2022")),
+ ("primary_function", ((1693555800000, 13), "File Storage")),
+ ("uptime_days", ((1693555800000, 13), 90)),
],
"update_history": [
- 1693555500000,
- 1693555800000,
- 1693556400000,
- 1693557000000,
- 1693557060000,
- 1693557120000,
+ (1693555500000, 1),
+ (1693555800000, 13),
+ (1693556400000, 4),
+ (1693557000000, 6),
+ (1693557060000, 7),
+ (1693557120000, 8),
],
},
),
@@ -316,16 +317,17 @@ def test_networkx_full_history():
"server_name": "Delta",
},
"temporal": [
- ("OS_version", (1693556100000, "Ubuntu 20.04")),
- ("primary_function", (1693556100000, "Application Server")),
- ("uptime_days", (1693556100000, 60)),
+ ("OS_version", ((1693556100000, 14), "Ubuntu 20.04")),
+ ("primary_function", ((1693556100000, 14), "Application Server")),
+ ("uptime_days", ((1693556100000, 14), 60)),
],
"update_history": [
- 1693555800000,
- 1693556100000,
- 1693557000000,
- 1693557060000,
- 1693557120000,
+ (1693555800000, 2),
+ (1693556100000, 3),
+ (1693556100000, 14),
+ (1693557000000, 6),
+ (1693557060000, 7),
+ (1693557120000, 8),
],
},
),
@@ -338,11 +340,15 @@ def test_networkx_full_history():
"datasource": "data/network_traffic_edges.csv",
},
"temporal": [
- ("OS_version", (1693556400000, "Red Hat 8.1")),
- ("primary_function", (1693556400000, "Backup")),
- ("uptime_days", (1693556400000, 30)),
+ ("OS_version", ((1693556400000, 15), "Red Hat 8.1")),
+ ("primary_function", ((1693556400000, 15), "Backup")),
+ ("uptime_days", ((1693556400000, 15), 30)),
+ ],
+ "update_history": [
+ (1693556100000, 3),
+ (1693556400000, 15),
+ (1693556700000, 5),
],
- "update_history": [1693556100000, 1693556400000, 1693556700000],
},
),
]
@@ -358,9 +364,9 @@ def test_networkx_full_history():
"is_encrypted": True,
"datasource": "data/network_traffic_edges.csv",
},
- "temporal": [("data_size_MB", [(1693555200000, 5.6)])],
+ "temporal": [("data_size_MB", [((1693555200000, 0), 5.6)])],
"layer": "Critical System Request",
- "update_history": [1693555200000],
+ "update_history": [(1693555200000, 0)],
},
),
(
@@ -371,9 +377,9 @@ def test_networkx_full_history():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": False,
},
- "temporal": [("data_size_MB", [(1693555500000, 7.1)])],
+ "temporal": [("data_size_MB", [((1693555500000, 1), 7.1)])],
"layer": "File Transfer",
- "update_history": [1693555500000],
+ "update_history": [(1693555500000, 1)],
},
),
(
@@ -384,9 +390,9 @@ def test_networkx_full_history():
"is_encrypted": True,
"datasource": "data/network_traffic_edges.csv",
},
- "temporal": [("data_size_MB", [(1693555800000, 3.2)])],
+ "temporal": [("data_size_MB", [((1693555800000, 2), 3.2)])],
"layer": "Standard Service Request",
- "update_history": [1693555800000],
+ "update_history": [(1693555800000, 2)],
},
),
(
@@ -397,9 +403,9 @@ def test_networkx_full_history():
"is_encrypted": True,
"datasource": "data/network_traffic_edges.csv",
},
- "temporal": [("data_size_MB", [(1693556400000, 4.5)])],
+ "temporal": [("data_size_MB", [((1693556400000, 4), 4.5)])],
"layer": "Critical System Request",
- "update_history": [1693556400000],
+ "update_history": [(1693556400000, 4)],
},
),
(
@@ -410,9 +416,9 @@ def test_networkx_full_history():
"is_encrypted": False,
"datasource": "data/network_traffic_edges.csv",
},
- "temporal": [("data_size_MB", [(1693556100000, 8.9)])],
+ "temporal": [("data_size_MB", [((1693556100000, 3), 8.9)])],
"layer": "Administrative Command",
- "update_history": [1693556100000],
+ "update_history": [(1693556100000, 3)],
},
),
(
@@ -427,14 +433,18 @@ def test_networkx_full_history():
(
"data_size_MB",
[
- (1693557000000, 5.0),
- (1693557060000, 10.0),
- (1693557120000, 15.0),
+ ((1693557000000, 6), 5.0),
+ ((1693557060000, 7), 10.0),
+ ((1693557120000, 8), 15.0),
],
)
],
"layer": "Standard Service Request",
- "update_history": [1693557000000, 1693557060000, 1693557120000],
+ "update_history": [
+ (1693557000000, 6),
+ (1693557060000, 7),
+ (1693557120000, 8),
+ ],
},
),
(
@@ -445,9 +455,9 @@ def test_networkx_full_history():
"is_encrypted": False,
"datasource": "data/network_traffic_edges.csv",
},
- "temporal": [("data_size_MB", [(1693556700000, 6.2)])],
+ "temporal": [("data_size_MB", [((1693556700000, 5), 6.2)])],
"layer": "File Transfer",
- "update_history": [1693556700000],
+ "update_history": [(1693556700000, 5)],
},
),
]
@@ -471,9 +481,9 @@ def test_networkx_exploded():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": True,
},
- "temporal": [("data_size_MB", [(1693555200000, 5.6)])],
+ "temporal": [("data_size_MB", [((1693555200000, 0), 5.6)])],
"layer": "Critical System Request",
- "update_history": 1693555200000,
+ "update_history": (1693555200000, 0),
},
),
(
@@ -484,9 +494,9 @@ def test_networkx_exploded():
"is_encrypted": False,
"datasource": "data/network_traffic_edges.csv",
},
- "temporal": [("data_size_MB", [(1693555500000, 7.1)])],
+ "temporal": [("data_size_MB", [((1693555500000, 1), 7.1)])],
"layer": "File Transfer",
- "update_history": 1693555500000,
+ "update_history": (1693555500000, 1),
},
),
(
@@ -497,9 +507,9 @@ def test_networkx_exploded():
"is_encrypted": True,
"datasource": "data/network_traffic_edges.csv",
},
- "temporal": [("data_size_MB", [(1693555800000, 3.2)])],
+ "temporal": [("data_size_MB", [((1693555800000, 2), 3.2)])],
"layer": "Standard Service Request",
- "update_history": 1693555800000,
+ "update_history": (1693555800000, 2),
},
),
(
@@ -510,9 +520,9 @@ def test_networkx_exploded():
"is_encrypted": True,
"datasource": "data/network_traffic_edges.csv",
},
- "temporal": [("data_size_MB", [(1693556400000, 4.5)])],
+ "temporal": [("data_size_MB", [((1693556400000, 4), 4.5)])],
"layer": "Critical System Request",
- "update_history": 1693556400000,
+ "update_history": (1693556400000, 4),
},
),
(
@@ -523,9 +533,9 @@ def test_networkx_exploded():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": False,
},
- "temporal": [("data_size_MB", [(1693556100000, 8.9)])],
+ "temporal": [("data_size_MB", [((1693556100000, 3), 8.9)])],
"layer": "Administrative Command",
- "update_history": 1693556100000,
+ "update_history": (1693556100000, 3),
},
),
(
@@ -536,9 +546,9 @@ def test_networkx_exploded():
"is_encrypted": True,
"datasource": "data/network_traffic_edges.csv",
},
- "temporal": [("data_size_MB", [(1693557000000, 5.0)])],
+ "temporal": [("data_size_MB", [((1693557000000, 6), 5.0)])],
"layer": "Standard Service Request",
- "update_history": 1693557000000,
+ "update_history": (1693557000000, 6),
},
),
(
@@ -549,9 +559,9 @@ def test_networkx_exploded():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": True,
},
- "temporal": [("data_size_MB", [(1693557060000, 10.0)])],
+ "temporal": [("data_size_MB", [((1693557060000, 7), 10.0)])],
"layer": "Standard Service Request",
- "update_history": 1693557060000,
+ "update_history": (1693557060000, 7),
},
),
(
@@ -562,9 +572,9 @@ def test_networkx_exploded():
"is_encrypted": True,
"datasource": "data/network_traffic_edges.csv",
},
- "temporal": [("data_size_MB", [(1693557120000, 15.0)])],
+ "temporal": [("data_size_MB", [((1693557120000, 8), 15.0)])],
"layer": "Standard Service Request",
- "update_history": 1693557120000,
+ "update_history": (1693557120000, 8),
},
),
(
@@ -575,9 +585,9 @@ def test_networkx_exploded():
"is_encrypted": False,
"datasource": "data/network_traffic_edges.csv",
},
- "temporal": [("data_size_MB", [(1693556700000, 6.2)])],
+ "temporal": [("data_size_MB", [((1693556700000, 5), 6.2)])],
"layer": "File Transfer",
- "update_history": 1693556700000,
+ "update_history": (1693556700000, 5),
},
),
]
@@ -597,11 +607,12 @@ def test_networkx_no_props():
"ServerA",
{
"update_history": [
- 1693555200000,
- 1693555260000,
- 1693555320000,
- 1693555500000,
- 1693556400000,
+ (1693555200000, 0),
+ (1693555200000, 9),
+ (1693555260000, 10),
+ (1693555320000, 11),
+ (1693555500000, 1),
+ (1693556400000, 4),
]
},
),
@@ -609,10 +620,10 @@ def test_networkx_no_props():
"ServerB",
{
"update_history": [
- 1693555200000,
- 1693555500000,
- 1693555800000,
- 1693556700000,
+ (1693555200000, 0),
+ (1693555500000, 12),
+ (1693555800000, 2),
+ (1693556700000, 5),
]
},
),
@@ -620,12 +631,12 @@ def test_networkx_no_props():
"ServerC",
{
"update_history": [
- 1693555500000,
- 1693555800000,
- 1693556400000,
- 1693557000000,
- 1693557060000,
- 1693557120000,
+ (1693555500000, 1),
+ (1693555800000, 13),
+ (1693556400000, 4),
+ (1693557000000, 6),
+ (1693557060000, 7),
+ (1693557120000, 8),
]
},
),
@@ -633,15 +644,25 @@ def test_networkx_no_props():
"ServerD",
{
"update_history": [
- 1693555800000,
- 1693556100000,
- 1693557000000,
- 1693557060000,
- 1693557120000,
+ (1693555800000, 2),
+ (1693556100000, 3),
+ (1693556100000, 14),
+ (1693557000000, 6),
+ (1693557060000, 7),
+ (1693557120000, 8),
+ ]
+ },
+ ),
+ (
+ "ServerE",
+ {
+ "update_history": [
+ (1693556100000, 3),
+ (1693556400000, 15),
+ (1693556700000, 5),
]
},
),
- ("ServerE", {"update_history": [1693556100000, 1693556400000, 1693556700000]}),
]
compare_list_of_dicts(nodeList, resultList)
@@ -650,40 +671,53 @@ def test_networkx_no_props():
(
"ServerA",
"ServerB",
- {"layer": "Critical System Request", "update_history": [1693555200000]},
+ {
+ "layer": "Critical System Request",
+ "update_history": [(1693555200000, 0)],
+ },
),
(
"ServerA",
"ServerC",
- {"layer": "File Transfer", "update_history": [1693555500000]},
+ {"layer": "File Transfer", "update_history": [(1693555500000, 1)]},
),
(
"ServerB",
"ServerD",
- {"layer": "Standard Service Request", "update_history": [1693555800000]},
+ {
+ "layer": "Standard Service Request",
+ "update_history": [(1693555800000, 2)],
+ },
),
(
"ServerC",
"ServerA",
- {"layer": "Critical System Request", "update_history": [1693556400000]},
+ {
+ "layer": "Critical System Request",
+ "update_history": [(1693556400000, 4)],
+ },
),
(
"ServerD",
"ServerC",
{
"layer": "Standard Service Request",
- "update_history": [1693557000000, 1693557060000, 1693557120000],
+ "update_history": [
+ (1693557000000, 6),
+ (1693557060000, 7),
+ (1693557120000, 8),
+ ],
},
),
(
"ServerD",
"ServerE",
- {"layer": "Administrative Command", "update_history": [1693556100000]},
+ {"layer": "Administrative Command", "update_history": [(1693556100000, 3)]},
),
(
"ServerE",
"ServerB",
- {"layer": "File Transfer", "update_history": [1693556700000]},
+ {"layer": "File Transfer", "update_history": [(1693556700000, 5)]},
),
]
compare_list_of_dicts(edgeList, resultList)
@@ -722,47 +756,47 @@ def test_networkx_no_props():
(
"ServerA",
"ServerB",
- {"layer": "Critical System Request", "update_history": 1693555200000},
+ {"layer": "Critical System Request", "update_history": (1693555200000, 0)},
),
(
"ServerA",
"ServerC",
- {"layer": "File Transfer", "update_history": 1693555500000},
+ {"layer": "File Transfer", "update_history": (1693555500000, 1)},
),
(
"ServerB",
"ServerD",
- {"layer": "Standard Service Request", "update_history": 1693555800000},
+ {"layer": "Standard Service Request", "update_history": (1693555800000, 2)},
),
(
"ServerC",
"ServerA",
- {"layer": "Critical System Request", "update_history": 1693556400000},
+ {"layer": "Critical System Request", "update_history": (1693556400000, 4)},
),
(
"ServerD",
"ServerC",
- {"layer": "Standard Service Request", "update_history": 1693557000000},
+ {"layer": "Standard Service Request", "update_history": (1693557000000, 6)},
),
(
"ServerD",
"ServerC",
- {"layer": "Standard Service Request", "update_history": 1693557060000},
+ {"layer": "Standard Service Request", "update_history": (1693557060000, 7)},
),
(
"ServerD",
"ServerC",
- {"layer": "Standard Service Request", "update_history": 1693557120000},
+ {"layer": "Standard Service Request", "update_history": (1693557120000, 8)},
),
(
"ServerD",
"ServerE",
- {"layer": "Administrative Command", "update_history": 1693556100000},
+ {"layer": "Administrative Command", "update_history": (1693556100000, 3)},
),
(
"ServerE",
"ServerB",
- {"layer": "File Transfer", "update_history": 1693556700000},
+ {"layer": "File Transfer", "update_history": (1693556700000, 5)},
),
]
compare_list_of_dicts(edgeList, resultList)
@@ -921,7 +955,7 @@ def test_networkx_no_history():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": True,
"layer": "Critical System Request",
- "update_history": 1693555200000,
+ "update_history": (1693555200000, 0),
},
),
(
@@ -932,7 +966,7 @@ def test_networkx_no_history():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": False,
"layer": "File Transfer",
- "update_history": 1693555500000,
+ "update_history": (1693555500000, 1),
},
),
(
@@ -943,7 +977,7 @@ def test_networkx_no_history():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": True,
"layer": "Standard Service Request",
- "update_history": 1693555800000,
+ "update_history": (1693555800000, 2),
},
),
(
@@ -954,7 +988,7 @@ def test_networkx_no_history():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": True,
"layer": "Critical System Request",
- "update_history": 1693556400000,
+ "update_history": (1693556400000, 4),
},
),
(
@@ -965,7 +999,7 @@ def test_networkx_no_history():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": True,
"layer": "Standard Service Request",
- "update_history": 1693557000000,
+ "update_history": (1693557000000, 6),
},
),
(
@@ -976,7 +1010,7 @@ def test_networkx_no_history():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": True,
"layer": "Standard Service Request",
- "update_history": 1693557060000,
+ "update_history": (1693557060000, 7),
},
),
(
@@ -987,7 +1021,7 @@ def test_networkx_no_history():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": True,
"layer": "Standard Service Request",
- "update_history": 1693557120000,
+ "update_history": (1693557120000, 8),
},
),
(
@@ -998,7 +1032,7 @@ def test_networkx_no_history():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": False,
"layer": "Administrative Command",
- "update_history": 1693556100000,
+ "update_history": (1693556100000, 3),
},
),
(
@@ -1009,7 +1043,7 @@ def test_networkx_no_history():
"datasource": "data/network_traffic_edges.csv",
"is_encrypted": False,
"layer": "File Transfer",
- "update_history": 1693556700000,
+ "update_history": (1693556700000, 5),
},
),
]
diff --git a/python/tests/test_ingestion_equivalence_df.py b/python/tests/test_ingestion_equivalence_df.py
new file mode 100644
index 0000000000..82267aa5e5
--- /dev/null
+++ b/python/tests/test_ingestion_equivalence_df.py
@@ -0,0 +1,371 @@
+import os.path
+import pytest
+from pathlib import Path
+import pandas as pd
+import polars as pl
+import pyarrow as pa
+import duckdb
+
+try:
+ import fireducks.pandas as fpd
+except ModuleNotFoundError:
+ fpd = None
+from raphtory import Graph, PersistentGraph
+
+base_dir = Path(__file__).parent
+EDGES_FILE = os.path.join(base_dir, "data/network_traffic_edges.csv")
+NODES_FILE = os.path.join(base_dir, "data/network_traffic_nodes.csv")
+
+
+def duck_query(con, sql: str):
+ return con.execute(sql).arrow()
+
+
+@pytest.fixture(scope="module")
+def dataframes():
+ # Load Data using Pandas
+ df_edges_pd = pd.read_csv(EDGES_FILE)
+ df_nodes_pd = pd.read_csv(NODES_FILE)
+
+ con = duckdb.connect(database=":memory:")
+ con.register("edges_df", df_edges_pd)
+ con.register("nodes_df", df_nodes_pd)
+
+ data = {
+ "pandas": {"edges": df_edges_pd, "nodes": df_nodes_pd},
+ "polars": {
+ "edges": pl.from_pandas(df_edges_pd),
+ "nodes": pl.from_pandas(df_nodes_pd),
+ },
+ "arrow": {
+ "edges": pa.Table.from_pandas(df_edges_pd),
+ "nodes": pa.Table.from_pandas(df_nodes_pd),
+ },
+ "duckdb": {"con": con},
+ }
+ if fpd:
+ data["fireducks"] = {
+ "edges": fpd.read_csv(EDGES_FILE),
+ "nodes": fpd.read_csv(NODES_FILE),
+ }
+
+ return data
+
+
+@pytest.mark.parametrize("graph_type", [Graph, PersistentGraph])
+def test_edge_ingestion_equivalence(dataframes, graph_type):
+ # reference graph
+ g_pd = graph_type()
+ g_pd.load_edges_from_pandas(
+ df=dataframes["pandas"]["edges"],
+ time="timestamp",
+ src="source",
+ dst="destination",
+ properties=["data_size_MB", "transaction_type"],
+ metadata=["is_encrypted"],
+ )
+
+ # Pandas streaming
+ g_pd_stream = graph_type()
+ g_pd_stream.load_edges_from_df(
+ data=dataframes["pandas"]["edges"],
+ time="timestamp",
+ src="source",
+ dst="destination",
+ properties=["data_size_MB", "transaction_type"],
+ metadata=["is_encrypted"],
+ )
+ assert (
+ g_pd == g_pd_stream
+ ), "Pandas streaming edge ingestion failed equivalence check"
+
+ # Polars
+ g_pl = graph_type()
+ g_pl.load_edges_from_df(
+ data=dataframes["polars"]["edges"],
+ time="timestamp",
+ src="source",
+ dst="destination",
+ properties=["data_size_MB", "transaction_type"],
+ metadata=["is_encrypted"],
+ )
+ assert g_pd == g_pl, "Polars edge ingestion failed equivalence check"
+
+ # Arrow
+ g_arrow = graph_type()
+ g_arrow.load_edges_from_df(
+ data=dataframes["arrow"]["edges"],
+ time="timestamp",
+ src="source",
+ dst="destination",
+ properties=["data_size_MB", "transaction_type"],
+ metadata=["is_encrypted"],
+ )
+ assert g_pd == g_arrow, "Arrow edge ingestion failed equivalence check"
+
+ # DuckDB
+ g_duckdb = graph_type()
+ con = dataframes["duckdb"]["con"]
+ g_duckdb.load_edges_from_df(
+ data=duck_query(con, "SELECT * FROM edges_df"),
+ time="timestamp",
+ src="source",
+ dst="destination",
+ properties=["data_size_MB", "transaction_type"],
+ metadata=["is_encrypted"],
+ )
+ assert g_pd == g_duckdb, "DuckDB edge ingestion failed equivalence check"
+
+ if fpd:
+ # FireDucks
+ g_fd = graph_type()
+ g_fd.load_edges_from_df(
+ data=dataframes["fireducks"]["edges"],
+ time="timestamp",
+ src="source",
+ dst="destination",
+ properties=["data_size_MB", "transaction_type"],
+ metadata=["is_encrypted"],
+ )
+ assert g_pd == g_fd, "FireDucks edge ingestion failed equivalence check"
+
+
+@pytest.mark.parametrize("graph_type", [Graph, PersistentGraph])
+def test_node_ingestion_equivalence(dataframes, graph_type):
+ # reference graph
+ g_pd = graph_type()
+ g_pd.load_nodes_from_pandas(
+ df=dataframes["pandas"]["nodes"],
+ time="timestamp",
+ id="server_id",
+ properties=["OS_version", "uptime_days"],
+ metadata=["primary_function", "server_name", "hardware_type"],
+ )
+
+ # Pandas streaming
+ g_pd_stream = graph_type()
+ g_pd_stream.load_nodes_from_df(
+ data=dataframes["pandas"]["nodes"],
+ time="timestamp",
+ id="server_id",
+ properties=["OS_version", "uptime_days"],
+ metadata=["primary_function", "server_name", "hardware_type"],
+ )
+ assert (
+ g_pd == g_pd_stream
+ ), "Pandas streaming node ingestion failed equivalence check"
+
+ # Polars
+ g_pl = graph_type()
+ g_pl.load_nodes_from_df(
+ data=dataframes["polars"]["nodes"],
+ time="timestamp",
+ id="server_id",
+ properties=["OS_version", "uptime_days"],
+ metadata=["primary_function", "server_name", "hardware_type"],
+ )
+ assert g_pd == g_pl, "Polars node ingestion failed equivalence check"
+
+ # Arrow
+ g_arrow = graph_type()
+ g_arrow.load_nodes_from_df(
+ data=dataframes["arrow"]["nodes"],
+ time="timestamp",
+ id="server_id",
+ properties=["OS_version", "uptime_days"],
+ metadata=["primary_function", "server_name", "hardware_type"],
+ )
+ assert g_pd == g_arrow, "Arrow node ingestion failed equivalence check"
+
+ # DuckDB
+ g_duckdb = graph_type()
+ con = dataframes["duckdb"]["con"]
+ g_duckdb.load_nodes_from_df(
+ data=duck_query(con, "SELECT * FROM nodes_df"),
+ time="timestamp",
+ id="server_id",
+ properties=["OS_version", "uptime_days"],
+ metadata=["primary_function", "server_name", "hardware_type"],
+ )
+ assert g_pd == g_duckdb, "DuckDB node ingestion failed equivalence check"
+
+ if fpd:
+ # FireDucks
+ print("Testing fireducks...")
+ g_fd = graph_type()
+ g_fd.load_nodes_from_df(
+ data=dataframes["fireducks"]["nodes"],
+ time="timestamp",
+ id="server_id",
+ properties=["OS_version", "uptime_days"],
+ metadata=["primary_function", "server_name", "hardware_type"],
+ )
+ assert g_pd == g_fd, "FireDucks node ingestion failed equivalence check"
+
+
+@pytest.mark.parametrize("graph_type", [Graph, PersistentGraph])
+def test_metadata_update_equivalence(dataframes, graph_type):
+ # reference graph
+ g_pd = graph_type()
+ g_pd.load_edges_from_pandas(
+ df=dataframes["pandas"]["edges"],
+ time="timestamp",
+ src="source",
+ dst="destination",
+ )
+ g_pd.load_nodes_from_pandas(
+ df=dataframes["pandas"]["nodes"],
+ time="timestamp",
+ id="server_id",
+ )
+ # update metadata
+ g_pd.load_node_props_from_pandas(
+ df=dataframes["pandas"]["nodes"],
+ id="server_id",
+ metadata=["primary_function", "server_name", "hardware_type"],
+ )
+ g_pd.load_edge_props_from_pandas(
+ df=dataframes["pandas"]["edges"],
+ src="source",
+ dst="destination",
+ metadata=["is_encrypted"],
+ )
+
+ # Pandas streaming
+ g_pd_stream = graph_type()
+ g_pd_stream.load_edges_from_df(
+ data=dataframes["pandas"]["edges"],
+ time="timestamp",
+ src="source",
+ dst="destination",
+ )
+ g_pd_stream.load_nodes_from_df(
+ data=dataframes["pandas"]["nodes"],
+ time="timestamp",
+ id="server_id",
+ )
+ # update metadata
+ g_pd_stream.load_node_metadata_from_df(
+ data=dataframes["pandas"]["nodes"],
+ id="server_id",
+ metadata=["primary_function", "server_name", "hardware_type"],
+ )
+ g_pd_stream.load_edge_metadata_from_df(
+ data=dataframes["pandas"]["edges"],
+ src="source",
+ dst="destination",
+ metadata=["is_encrypted"],
+ )
+ assert (
+ g_pd == g_pd_stream
+ ), "Pandas streaming metadata ingestion failed equivalence check"
+
+ # Polars
+ g_pl = graph_type()
+ g_pl.load_edges_from_df(
+ data=dataframes["polars"]["edges"],
+ time="timestamp",
+ src="source",
+ dst="destination",
+ )
+ g_pl.load_nodes_from_df(
+ data=dataframes["polars"]["nodes"],
+ time="timestamp",
+ id="server_id",
+ )
+ # update metadata
+ g_pl.load_node_metadata_from_df(
+ data=dataframes["polars"]["nodes"],
+ id="server_id",
+ metadata=["primary_function", "server_name", "hardware_type"],
+ )
+ g_pl.load_edge_metadata_from_df(
+ data=dataframes["polars"]["edges"],
+ src="source",
+ dst="destination",
+ metadata=["is_encrypted"],
+ )
+ assert g_pd == g_pl, "Polars metadata ingestion failed equivalence check"
+
+ # Arrow
+ g_arrow = graph_type()
+ g_arrow.load_edges_from_df(
+ data=dataframes["arrow"]["edges"],
+ time="timestamp",
+ src="source",
+ dst="destination",
+ )
+ g_arrow.load_nodes_from_df(
+ data=dataframes["arrow"]["nodes"],
+ time="timestamp",
+ id="server_id",
+ )
+ # update metadata
+ g_arrow.load_node_metadata_from_df(
+ data=dataframes["arrow"]["nodes"],
+ id="server_id",
+ metadata=["primary_function", "server_name", "hardware_type"],
+ )
+ g_arrow.load_edge_metadata_from_df(
+ data=dataframes["arrow"]["edges"],
+ src="source",
+ dst="destination",
+ metadata=["is_encrypted"],
+ )
+ assert g_pd == g_arrow, "Arrow metadata ingestion failed equivalence check"
+
+ # DuckDB
+ g_duckdb = graph_type()
+ con = dataframes["duckdb"]["con"]
+ g_duckdb.load_edges_from_df(
+ data=duck_query(con, "SELECT * FROM edges_df"),
+ time="timestamp",
+ src="source",
+ dst="destination",
+ )
+ g_duckdb.load_nodes_from_df(
+ data=duck_query(con, "SELECT * FROM nodes_df"),
+ time="timestamp",
+ id="server_id",
+ )
+ # update metadata
+ g_duckdb.load_node_metadata_from_df(
+ data=duck_query(con, "SELECT * FROM nodes_df"),
+ id="server_id",
+ metadata=["primary_function", "server_name", "hardware_type"],
+ )
+ g_duckdb.load_edge_metadata_from_df(
+ data=duck_query(con, "SELECT * FROM edges_df"),
+ src="source",
+ dst="destination",
+ metadata=["is_encrypted"],
+ )
+ assert g_pd == g_duckdb, "DuckDB metadata ingestion failed equivalence check"
+
+ if fpd:
+ # FireDucks
+ g_fd = graph_type()
+ g_fd.load_edges_from_df(
+ data=dataframes["fireducks"]["edges"],
+ time="timestamp",
+ src="source",
+ dst="destination",
+ )
+ g_fd.load_nodes_from_df(
+ data=dataframes["fireducks"]["nodes"],
+ time="timestamp",
+ id="server_id",
+ )
+ # update metadata
+ g_fd.load_node_metadata_from_df(
+ data=dataframes["fireducks"]["nodes"],
+ id="server_id",
+ metadata=["primary_function", "server_name", "hardware_type"],
+ )
+ g_fd.load_edge_metadata_from_df(
+ data=dataframes["fireducks"]["edges"],
+ src="source",
+ dst="destination",
+ metadata=["is_encrypted"],
+ )
+ assert g_pd == g_fd, "FireDucks metadata ingestion failed equivalence check"
diff --git a/python/tests/test_load_from_df.py b/python/tests/test_load_from_df.py
new file mode 100644
index 0000000000..968876d597
--- /dev/null
+++ b/python/tests/test_load_from_df.py
@@ -0,0 +1,96 @@
+import polars as pl
+from raphtory import Graph, PersistentGraph
+import pytest
+
+try:
+ import fireducks.pandas as fpd
+except ModuleNotFoundError:
+ fpd = None
+
+
+def _collect_edges(g: Graph):
+ return sorted((e.history.t[0], e.src.id, e.dst.id, e["value"]) for e in g.edges)
+
+
+@pytest.mark.parametrize("graph_type", [Graph, PersistentGraph])
+def test_load_edges_from_polars_df(graph_type):
+ df = pl.DataFrame(
+ {
+ "time": [1, 2, 3],
+ "src": [1, 2, 3],
+ "dst": [2, 3, 4],
+ "value": [10.0, 20.0, 30.0],
+ }
+ )
+
+ g_to_pandas = graph_type()
+ g_to_pandas.load_edges_from_pandas(
+ df=df.to_pandas(), time="time", src="src", dst="dst", properties=["value"]
+ )
+
+ g_from_df = graph_type()
+ g_from_df.load_edges_from_df(
+ data=df, time="time", src="src", dst="dst", properties=["value"]
+ )
+
+ expected = [(1, 1, 2, 10.0), (2, 2, 3, 20.0), (3, 3, 4, 30.0)]
+ assert _collect_edges(g_to_pandas) == _collect_edges(g_from_df)
+ assert _collect_edges(g_to_pandas) == expected
+ assert _collect_edges(g_from_df) == expected
+
+
+if fpd:
+ import pandas
+
+ @pytest.mark.parametrize("graph_type", [Graph, PersistentGraph])
+ def test_load_edges_from_fireducks_df(graph_type):
+ # FireDucks DataFrame (pandas-compatible API)
+ df = fpd.DataFrame(
+ {
+ "time": [1, 2, 3],
+ "src": [1, 2, 3],
+ "dst": [2, 3, 4],
+ "value": [10.0, 20.0, 30.0],
+ }
+ )
+
+ g = graph_type()
+ g.load_edges_from_df(
+ data=df, time="time", src="src", dst="dst", properties=["value"]
+ )
+ assert [(1, 1, 2, 10.0), (2, 2, 3, 20.0), (3, 3, 4, 30.0)] == _collect_edges(g)
+
+ @pytest.mark.parametrize("graph_type", [Graph, PersistentGraph])
+ def test_fireducks_matches_pandas_for_same_edges(graph_type):
+ df_fireducks = fpd.DataFrame(
+ {
+ "time": [1, 2, 3],
+ "src": [1, 2, 3],
+ "dst": [2, 3, 4],
+ "value": [10.0, 20.0, 30.0],
+ }
+ )
+ df_pandas = pandas.DataFrame(
+ {
+ "time": [1, 2, 3],
+ "src": [1, 2, 3],
+ "dst": [2, 3, 4],
+ "value": [10.0, 20.0, 30.0],
+ }
+ )
+
+ g_fireducks = graph_type()
+ g_fireducks.load_edges_from_df(
+ data=df_fireducks, time="time", src="src", dst="dst", properties=["value"]
+ )
+
+ g_pandas = graph_type()
+ g_pandas.load_edges_from_pandas(
+ df=df_pandas, time="time", src="src", dst="dst", properties=["value"]
+ )
+
+ expected = [(1, 1, 2, 10.0), (2, 2, 3, 20.0), (3, 3, 4, 30.0)]
+
+ assert _collect_edges(g_fireducks) == _collect_edges(g_pandas)
+ assert _collect_edges(g_fireducks) == expected
+ assert _collect_edges(g_pandas) == expected
diff --git a/raphtory-api/src/core/entities/edges/edge_ref.rs b/raphtory-api/src/core/entities/edges/edge_ref.rs
index 413514f8ba..ceafb31a40 100644
--- a/raphtory-api/src/core/entities/edges/edge_ref.rs
+++ b/raphtory-api/src/core/entities/edges/edge_ref.rs
@@ -1,6 +1,6 @@
use crate::core::{
entities::{EID, VID},
- storage::timeindex::{AsTime, TimeIndexEntry},
+ storage::timeindex::{AsTime, EventTime},
};
use std::cmp::Ordering;
@@ -10,7 +10,7 @@ pub struct EdgeRef {
src_pid: VID,
dst_pid: VID,
e_type: Dir,
- time: Option,
+ time: Option,
layer_id: Option,
}
@@ -71,7 +71,7 @@ impl EdgeRef {
}
#[inline(always)]
- pub fn time(&self) -> Option {
+ pub fn time(&self) -> Option {
self.time
}
@@ -117,7 +117,7 @@ impl EdgeRef {
}
#[inline]
- pub fn at(&self, time: TimeIndexEntry) -> Self {
+ pub fn at(&self, time: EventTime) -> Self {
let mut e_ref = *self;
e_ref.time = Some(time);
e_ref
diff --git a/raphtory-api/src/core/entities/properties/tprop.rs b/raphtory-api/src/core/entities/properties/tprop.rs
index b987adb498..f5510f1885 100644
--- a/raphtory-api/src/core/entities/properties/tprop.rs
+++ b/raphtory-api/src/core/entities/properties/tprop.rs
@@ -1,11 +1,11 @@
use crate::core::{
entities::properties::prop::Prop,
- storage::timeindex::{AsTime, TimeIndexEntry},
+ storage::timeindex::{AsTime, EventTime},
};
use std::ops::Range;
pub trait TPropOps<'a>: Clone + Send + Sync + Sized + 'a {
- fn active(&self, w: Range) -> bool {
+ fn active(&self, w: Range) -> bool {
self.clone().iter_window(w).next().is_some()
}
@@ -14,11 +14,11 @@ pub trait TPropOps<'a>: Clone + Send + Sync + Sized + 'a {
self.clone().iter_window_t(w).next().is_some()
}
- fn last_before(&self, t: TimeIndexEntry) -> Option<(TimeIndexEntry, Prop)> {
- self.clone().iter_window(TimeIndexEntry::MIN..t).next_back()
+ fn last_before(&self, t: EventTime) -> Option<(EventTime, Prop)> {
+ self.clone().iter_window(EventTime::MIN..t).next_back()
}
- fn iter(self) -> impl DoubleEndedIterator- + Send + Sync + 'a;
+ fn iter(self) -> impl DoubleEndedIterator
- + Send + Sync + 'a;
fn iter_t(self) -> impl DoubleEndedIterator
- + Send + Sync + 'a {
self.iter().map(|(t, v)| (t.t(), v))
@@ -26,23 +26,23 @@ pub trait TPropOps<'a>: Clone + Send + Sync + Sized + 'a {
fn iter_window(
self,
- r: Range
,
- ) -> impl DoubleEndedIterator- + Send + Sync + 'a;
+ r: Range
,
+ ) -> impl DoubleEndedIterator- + Send + Sync + 'a;
fn iter_window_t(
self,
r: Range
,
) -> impl DoubleEndedIterator- + Send + Sync + 'a {
- self.iter_window(TimeIndexEntry::range(r))
+ self.iter_window(EventTime::range(r))
.map(|(t, v)| (t.t(), v))
}
fn iter_window_te(
self,
- r: Range
,
+ r: Range,
) -> impl DoubleEndedIterator- + Send + Sync + 'a {
self.iter_window(r).map(|(t, v)| (t.t(), v))
}
- fn at(&self, ti: &TimeIndexEntry) -> Option
;
+ fn at(&self, ti: &EventTime) -> Option;
}
diff --git a/raphtory-api/src/core/storage/timeindex.rs b/raphtory-api/src/core/storage/timeindex.rs
index d83ba4a38b..954ea4c16f 100644
--- a/raphtory-api/src/core/storage/timeindex.rs
+++ b/raphtory-api/src/core/storage/timeindex.rs
@@ -3,15 +3,49 @@ use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::{fmt, ops::Range};
+/// Error type for timestamp to chrono::DateTime conversion operations
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum TimeError {
+ /// The timestamp value is out of range for chrono::DateTime conversion
+ OutOfRange(i64),
+}
+
+impl fmt::Display for TimeError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let min = DateTime::::MIN_UTC.timestamp_millis();
+ let max = DateTime::::MAX_UTC.timestamp_millis();
+ match self {
+ TimeError::OutOfRange(timestamp) => {
+ write!(f, "Timestamp '{}' is out of range for DateTime conversion. Valid range is from {} to {}", timestamp, min, max)
+ }
+ }
+ }
+}
+
+impl std::error::Error for TimeError {}
+
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Ord, PartialOrd, Eq, Hash)]
-pub struct TimeIndexEntry(pub i64, pub usize);
+pub struct EventTime(pub i64, pub usize);
+
+impl PartialEq for EventTime {
+ fn eq(&self, other: &i64) -> bool {
+ self.0 == *other
+ }
+}
+
+impl fmt::Display for EventTime {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "EventTime(timestamp={}, event_id={})", self.0, self.1)
+ }
+}
pub trait AsTime: fmt::Debug + Copy + Ord + Eq + Send + Sync + 'static {
fn t(&self) -> i64;
- fn dt(&self) -> Option> {
+ /// Tries to convert the timestamp into a UTC DateTime.
+ fn dt(&self) -> Result, TimeError> {
let t = self.t();
- DateTime::from_timestamp_millis(t)
+ DateTime::from_timestamp_millis(t).ok_or(TimeError::OutOfRange(t))
}
fn range(w: Range) -> Range;
@@ -171,20 +205,31 @@ impl<'a, L: TimeIndexOps<'a>, R: TimeIndexOps<'a, IndexType = L::IndexType>> Tim
}
}
-impl From for TimeIndexEntry {
+impl From for EventTime {
fn from(value: i64) -> Self {
Self::start(value)
}
}
-impl TimeIndexEntry {
- pub const MIN: TimeIndexEntry = TimeIndexEntry(i64::MIN, 0);
+impl EventTime {
+ pub const MIN: EventTime = EventTime(i64::MIN, 0);
- pub const MAX: TimeIndexEntry = TimeIndexEntry(i64::MAX, usize::MAX);
+ pub const MAX: EventTime = EventTime(i64::MAX, usize::MAX);
pub fn new(t: i64, s: usize) -> Self {
Self(t, s)
}
+ /// Sets the event id of the EventTime.
+ /// Note that this mutates the EventTime in place rather than create and return a new one.
+ pub fn set_event_id(mut self, i: usize) -> Self {
+ self.1 = i;
+ self
+ }
+
+ pub fn as_tuple(&self) -> (i64, usize) {
+ (self.0, self.1)
+ }
+
pub fn start(t: i64) -> Self {
Self(t, 0)
}
@@ -228,7 +273,7 @@ impl AsTime for i64 {
}
}
-impl AsTime for TimeIndexEntry {
+impl AsTime for EventTime {
fn t(&self) -> i64 {
self.0
}
diff --git a/raphtory-api/src/core/utils/mod.rs b/raphtory-api/src/core/utils/mod.rs
index 9f0346076f..3a2402d266 100644
--- a/raphtory-api/src/core/utils/mod.rs
+++ b/raphtory-api/src/core/utils/mod.rs
@@ -1,2 +1,3 @@
pub mod hashing;
pub mod logging;
+pub mod time;
diff --git a/raphtory-api/src/core/utils/time.rs b/raphtory-api/src/core/utils/time.rs
new file mode 100644
index 0000000000..f81cdc726f
--- /dev/null
+++ b/raphtory-api/src/core/utils/time.rs
@@ -0,0 +1,222 @@
+use crate::core::storage::timeindex::{AsTime, EventTime};
+#[cfg(feature = "python")]
+use crate::python::error::adapt_err_value;
+use chrono::{DateTime, NaiveDate, NaiveDateTime, ParseError, TimeZone};
+#[cfg(feature = "python")]
+use pyo3::PyErr;
+use std::{convert::Infallible, num::ParseIntError};
+
+#[derive(thiserror::Error, Debug, Clone, PartialEq)]
+pub enum ParseTimeError {
+ #[error("The interval string doesn't contain a complete number of number-unit pairs.")]
+ InvalidPairs,
+ #[error(
+ "One of the tokens in the interval string supposed to be a number couldn't be parsed."
+ )]
+ ParseInt {
+ #[from]
+ source: ParseIntError,
+ },
+ #[error("'{0}' is not a valid unit. Valid units are year(s), month(s), week(s), day(s), hour(s), minute(s), second(s) and millisecond(s).")]
+ InvalidUnit(String),
+ #[error("'{0}' is not a valid unit. Valid units are year(s), month(s), week(s), day(s), hour(s), minute(s), second(s), millisecond(s), and unaligned.")]
+ InvalidAlignmentUnit(String),
+ #[error(transparent)]
+ ParseError(#[from] ParseError),
+ #[error("Negative interval is not supported.")]
+ NegativeInt,
+ #[error("0 size step is not supported.")]
+ ZeroSizeStep,
+ #[error("'{0}' is not a valid datetime. Valid formats are RFC3339, RFC2822, %Y-%m-%d, %Y-%m-%dT%H:%M:%S%.3f, %Y-%m-%dT%H:%M:%S%, %Y-%m-%d %H:%M:%S%.3f and %Y-%m-%d %H:%M:%S%")]
+ InvalidDateTimeString(String),
+}
+
+impl From for ParseTimeError {
+ fn from(value: Infallible) -> Self {
+ match value {}
+ }
+}
+
+#[cfg(feature = "python")]
+impl From for PyErr {
+ fn from(value: ParseTimeError) -> Self {
+ adapt_err_value(&value)
+ }
+}
+
+pub trait IntoTime {
+ fn into_time(self) -> EventTime;
+}
+
+impl IntoTime for i64 {
+ fn into_time(self) -> EventTime {
+ EventTime::from(self)
+ }
+}
+
+impl IntoTime for DateTime {
+ fn into_time(self) -> EventTime {
+ EventTime::from(self.timestamp_millis())
+ }
+}
+
+impl IntoTime for NaiveDateTime {
+ fn into_time(self) -> EventTime {
+ EventTime::from(self.and_utc().timestamp_millis())
+ }
+}
+
+impl IntoTime for EventTime {
+ fn into_time(self) -> EventTime {
+ self
+ }
+}
+
+impl IntoTime for (T, usize) {
+ fn into_time(self) -> EventTime {
+ self.0.into_time().set_event_id(self.1)
+ }
+}
+
+pub trait TryIntoTime {
+ fn try_into_time(self) -> Result;
+}
+
+impl TryIntoTime for T {
+ fn try_into_time(self) -> Result {
+ Ok(self.into_time())
+ }
+}
+
+impl TryIntoTime for &str {
+ /// Tries to parse the timestamp as RFC3339 and then as ISO 8601 with local format and all
+ /// fields mandatory except for milliseconds and allows replacing the T with a space
+ fn try_into_time(self) -> Result {
+ let rfc_result = DateTime::parse_from_rfc3339(self);
+ if let Ok(datetime) = rfc_result {
+ return Ok(EventTime::from(datetime.timestamp_millis()));
+ }
+
+ let result = DateTime::parse_from_rfc2822(self);
+ if let Ok(datetime) = result {
+ return Ok(EventTime::from(datetime.timestamp_millis()));
+ }
+
+ let result = NaiveDate::parse_from_str(self, "%Y-%m-%d");
+ if let Ok(date) = result {
+ let timestamp = date
+ .and_hms_opt(00, 00, 00)
+ .unwrap()
+ .and_utc()
+ .timestamp_millis();
+ return Ok(EventTime::from(timestamp));
+ }
+
+ let result = NaiveDateTime::parse_from_str(self, "%Y-%m-%dT%H:%M:%S%.3f");
+ if let Ok(datetime) = result {
+ return Ok(EventTime::from(datetime.and_utc().timestamp_millis()));
+ }
+
+ let result = NaiveDateTime::parse_from_str(self, "%Y-%m-%dT%H:%M:%S%");
+ if let Ok(datetime) = result {
+ return Ok(EventTime::from(datetime.and_utc().timestamp_millis()));
+ }
+
+ let result = NaiveDateTime::parse_from_str(self, "%Y-%m-%d %H:%M:%S%.3f");
+ if let Ok(datetime) = result {
+ return Ok(EventTime::from(datetime.and_utc().timestamp_millis()));
+ }
+
+ let result = NaiveDateTime::parse_from_str(self, "%Y-%m-%d %H:%M:%S%");
+ if let Ok(datetime) = result {
+ return Ok(EventTime::from(datetime.and_utc().timestamp_millis()));
+ }
+
+ Err(ParseTimeError::InvalidDateTimeString(self.to_string()))
+ }
+}
+
+pub trait TryIntoTimeNeedsEventId: TryIntoTime {}
+
+impl TryIntoTimeNeedsEventId for i64 {}
+
+impl TryIntoTimeNeedsEventId for DateTime {}
+
+impl TryIntoTimeNeedsEventId for NaiveDateTime {}
+
+impl TryIntoTimeNeedsEventId for &str {}
+
+/// Used to handle automatic injection of event id if not explicitly provided.
+/// In many cases, we will want different behaviour if an event id was provided or not.
+pub enum InputTime {
+ Simple(i64),
+ Indexed(i64, usize),
+}
+
+impl InputTime {
+ pub fn set_index(self, index: usize) -> Self {
+ match self {
+ InputTime::Simple(time) => InputTime::Indexed(time, index),
+ InputTime::Indexed(time, _) => InputTime::Indexed(time, index),
+ }
+ }
+
+ pub fn as_time(&self) -> EventTime {
+ match self {
+ InputTime::Simple(t) => EventTime::new(*t, 0),
+ InputTime::Indexed(t, s) => EventTime::new(*t, *s),
+ }
+ }
+}
+
+/// Single time input only refers to the i64 component of an EventTime (no event id).
+pub trait AsSingleTimeInput {
+ fn try_into_input_time(self) -> Result;
+}
+
+impl AsSingleTimeInput for T {
+ fn try_into_input_time(self) -> Result {
+ Ok(InputTime::Simple(self.try_into_time()?.t()))
+ }
+}
+
+pub trait TryIntoInputTime {
+ fn try_into_input_time(self) -> Result;
+}
+
+impl TryIntoInputTime for InputTime {
+ fn try_into_input_time(self) -> Result {
+ Ok(self)
+ }
+}
+
+impl TryIntoInputTime for T {
+ fn try_into_input_time(self) -> Result {
+ self.try_into_input_time()
+ }
+}
+
+impl TryIntoInputTime for EventTime {
+ fn try_into_input_time(self) -> Result {
+ Ok(InputTime::Indexed(self.t(), self.i()))
+ }
+}
+
+impl TryIntoInputTime for (T, usize) {
+ fn try_into_input_time(self) -> Result {
+ Ok(self.0.try_into_input_time()?.set_index(self.1))
+ }
+}
+
+pub trait IntoTimeWithFormat {
+ fn parse_time(&self, fmt: &str) -> Result;
+}
+
+impl IntoTimeWithFormat for &str {
+ fn parse_time(&self, fmt: &str) -> Result {
+ let timestamp = NaiveDateTime::parse_from_str(self, fmt)?
+ .and_utc()
+ .timestamp_millis();
+ Ok(timestamp)
+ }
+}
diff --git a/raphtory-api/src/python/mod.rs b/raphtory-api/src/python/mod.rs
index b723716262..f3889213e5 100644
--- a/raphtory-api/src/python/mod.rs
+++ b/raphtory-api/src/python/mod.rs
@@ -3,5 +3,6 @@ mod direction;
pub mod error;
mod gid;
mod prop;
+pub mod timeindex;
pub use prop::*;
diff --git a/raphtory-api/src/python/timeindex.rs b/raphtory-api/src/python/timeindex.rs
new file mode 100644
index 0000000000..1dcb3774a2
--- /dev/null
+++ b/raphtory-api/src/python/timeindex.rs
@@ -0,0 +1,566 @@
+use crate::core::{
+ storage::timeindex::{AsTime, EventTime, TimeError},
+ utils::time::{
+ InputTime, IntoTime, ParseTimeError, TryIntoInputTime, TryIntoTime, TryIntoTimeNeedsEventId,
+ },
+};
+use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, Utc};
+use pyo3::{
+ basic::CompareOp,
+ exceptions::{PyRuntimeError, PyTypeError},
+ prelude::*,
+ types::{PyDate, PyDateAccess, PyDateTime, PyList, PyTuple},
+};
+use serde::Serialize;
+use std::hash::{DefaultHasher, Hash, Hasher};
+
+impl<'py> IntoPyObject<'py> for EventTime {
+ type Target = PyEventTime;
+ type Output = Bound<'py, Self::Target>;
+ type Error = PyErr;
+
+ fn into_pyobject(self, py: Python<'py>) -> Result {
+ PyEventTime::from(self).into_pyobject(py)
+ }
+}
+
+impl<'source> FromPyObject<'source> for EventTime {
+ fn extract_bound(time: &Bound<'source, PyAny>) -> PyResult {
+ InputTime::extract_bound(time).map(|input_time| input_time.as_time())
+ }
+}
+
+/// Components that can make an EventTime. They can be used as the event id as well.
+/// Extract them from Python as individual components so we can support tuples for EventTime.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
+pub struct EventTimeComponent {
+ component: i64,
+}
+
+impl IntoTime for EventTimeComponent {
+ fn into_time(self) -> EventTime {
+ EventTime::from(self.component)
+ }
+}
+
+impl TryIntoTimeNeedsEventId for EventTimeComponent {}
+
+impl EventTimeComponent {
+ pub fn new(component: i64) -> Self {
+ Self { component }
+ }
+
+ pub fn t(&self) -> i64 {
+ self.component
+ }
+}
+
+impl<'source> FromPyObject<'source> for EventTimeComponent {
+ fn extract_bound(component: &Bound<'source, PyAny>) -> PyResult {
+ extract_time_index_component(component).map_err(|e| match e {
+ ParsingError::Matched(err) => err,
+ ParsingError::Unmatched => {
+ let message = format!(
+ "Time component '{component}' must be a str, datetime, float, or an integer."
+ );
+ PyTypeError::new_err(message)
+ }
+ })
+ }
+}
+enum ParsingError {
+ Matched(PyErr),
+ Unmatched,
+}
+
+fn extract_time_index_component<'source>(
+ component: &Bound<'source, PyAny>,
+) -> Result {
+ if let Ok(string) = component.extract::() {
+ let timestamp = string.as_str();
+ let parsing_result = timestamp.try_into_time().or_else(|e| {
+ parse_email_timestamp(timestamp).map_err(|_| ParsingError::Matched(e.into()))
+ })?;
+ return Ok(EventTimeComponent::new(parsing_result.0));
+ }
+ if let Ok(number) = component.extract::() {
+ return Ok(EventTimeComponent::new(number));
+ }
+ if let Ok(float_time) = component.extract::() {
+ // seconds since Unix epoch as returned by python `timestamp`
+ let float_ms = float_time * 1000.0;
+ let float_ms_trunc = float_ms.round();
+ let rel_err = (float_ms - float_ms_trunc).abs() / (float_ms.abs() + f64::EPSILON);
+ if rel_err > 4.0 * f64::EPSILON {
+ return Err(ParsingError::Matched(PyRuntimeError::new_err(
+ "Float timestamps with more than millisecond precision are not supported.",
+ )));
+ }
+ return Ok(EventTimeComponent::new(float_ms_trunc as i64));
+ }
+ if let Ok(parsed_datetime) = component.extract::>() {
+ return Ok(EventTimeComponent::new(parsed_datetime.timestamp_millis()));
+ }
+ if let Ok(parsed_datetime) = component.extract::() {
+ // Important, this is needed to prevent inconsistencies by ensuring that naive DateTime objects are always treated as UTC and not local time.
+ // EventId and History objects use UTC so everything should be extracted as UTC.
+ return Ok(EventTimeComponent::new(
+ parsed_datetime.and_utc().timestamp_millis(),
+ ));
+ }
+ if let Ok(py_datetime) = component.downcast::() {
+ let time = (py_datetime
+ .call_method0("timestamp")
+ .map_err(ParsingError::Matched)?
+ .extract::()
+ .map_err(ParsingError::Matched)?
+ * 1000.0) as i64;
+ return Ok(EventTimeComponent::new(time));
+ }
+ // NaiveDate/Date checks come after all the DateTime checks to avoid a type matching Date/NaiveDate
+ // when it can match PyDateTime/NaiveDatetime/DateTime, which potentially have time/timezone information
+ if let Ok(naive_date) = component.extract::() {
+ let naive_dt = naive_date.and_hms_opt(0, 0, 0).ok_or_else(|| {
+ ParsingError::Matched(PyRuntimeError::new_err(format!(
+ "Failed to ingest date: {naive_date}"
+ )))
+ })?;
+ return Ok(EventTimeComponent::new(
+ naive_dt.and_utc().timestamp_millis(),
+ ));
+ }
+ if let Ok(py_date) = component.downcast::() {
+ let year: i32 = py_date.get_year();
+ let month: u32 = py_date.get_month() as u32;
+ let day: u32 = py_date.get_day() as u32;
+
+ let naive_dt = NaiveDate::from_ymd_opt(year, month, day)
+ .ok_or_else(|| {
+ ParsingError::Matched(PyRuntimeError::new_err(format!(
+ "Failed to ingest date: {year}-{month}-{day}"
+ )))
+ })?
+ .and_hms_opt(0, 0, 0)
+ .ok_or_else(|| {
+ ParsingError::Matched(PyRuntimeError::new_err(format!(
+ "Failed to construct datetime internally: {year}-{month}-{day}"
+ )))
+ })?;
+ return Ok(EventTimeComponent::new(
+ naive_dt.and_utc().timestamp_millis(),
+ ));
+ }
+ Err(ParsingError::Unmatched)
+}
+
+fn parse_email_timestamp(timestamp: &str) -> PyResult {
+ Python::with_gil(|py| {
+ let email_utils = PyModule::import(py, "email.utils")?;
+ let datetime = email_utils.call_method1("parsedate_to_datetime", (timestamp,))?;
+ let py_seconds = datetime.call_method1("timestamp", ())?;
+ let seconds = py_seconds.extract::()?;
+ Ok(EventTime::from(seconds as i64 * 1000))
+ })
+}
+
+/// Raphtory’s EventTime.
+/// Represents a unique timepoint in the graph’s history as (timestamp, event_id).
+///
+/// - timestamp: Number of milliseconds since the Unix epoch.
+/// - event_id: ID used for ordering between equal timestamps.
+///
+/// Unless specified manually, the event ids are generated automatically by Raphtory to
+/// maintain a unique ordering of events.
+/// EventTime can be converted into a timestamp or a Python datetime, and compared
+/// either by timestamp (against ints/floats/datetimes/strings), by tuple of (timestamp, event_id),
+/// or against another EventTime.
+#[pyclass(name = "EventTime", module = "raphtory", frozen)]
+#[derive(Debug, Clone, Copy, Serialize, PartialEq, Ord, PartialOrd, Eq)]
+pub struct PyEventTime {
+ time: EventTime,
+}
+
+impl PyEventTime {
+ pub fn inner(&self) -> EventTime {
+ self.time
+ }
+
+ pub fn new(time: EventTime) -> Self {
+ Self { time }
+ }
+
+ pub const MIN: PyEventTime = PyEventTime {
+ time: EventTime::MIN,
+ };
+ pub const MAX: PyEventTime = PyEventTime {
+ time: EventTime::MAX,
+ };
+}
+
+#[pymethods]
+impl PyEventTime {
+ /// Returns the UTC datetime representation of this EventTime's timestamp.
+ ///
+ /// Returns:
+ /// datetime: The UTC datetime.
+ ///
+ /// Raises:
+ /// TimeError: Returns TimeError on timestamp conversion errors (e.g. out-of-range timestamp).
+ #[getter]
+ pub fn dt(&self) -> Result, TimeError> {
+ self.time.dt()
+ }
+
+ /// Returns the event id used to order events within the same timestamp.
+ ///
+ /// Returns:
+ /// int: The event id.
+ #[getter]
+ pub fn event_id(&self) -> usize {
+ self.time.i()
+ }
+
+ /// Returns the timestamp in milliseconds since the Unix epoch.
+ ///
+ /// Returns:
+ /// int: Milliseconds since the Unix epoch.
+ #[getter]
+ pub fn t(&self) -> i64 {
+ self.time.t()
+ }
+
+ /// Return this entry as a tuple of (timestamp, event_id), where the timestamp is in milliseconds.
+ ///
+ /// Returns:
+ /// tuple[int,int]: (timestamp, event_id).
+ #[getter]
+ pub fn as_tuple(&self) -> (i64, usize) {
+ self.time.as_tuple()
+ }
+
+ pub fn __richcmp__(&self, other: &Bound, op: CompareOp) -> PyResult {
+ // extract TimeIndexComponent first. If we're dealing with a single i64 (or something that can be converted to an i64), we only compare timestamps
+ if let Ok(component) = other.extract::() {
+ match op {
+ CompareOp::Eq => Ok(self.t() == component.t()),
+ CompareOp::Ne => Ok(self.t() != component.t()),
+ CompareOp::Gt => Ok(self.t() > component.t()),
+ CompareOp::Lt => Ok(self.t() < component.t()),
+ CompareOp::Ge => Ok(self.t() >= component.t()),
+ CompareOp::Le => Ok(self.t() <= component.t()),
+ }
+ // If an EventTime was passed, we then compare the event id
+ } else if let Ok(time_index) = other.extract::() {
+ match op {
+ CompareOp::Eq => Ok(self.time == time_index),
+ CompareOp::Ne => Ok(self.time != time_index),
+ CompareOp::Gt => Ok(self.time > time_index),
+ CompareOp::Lt => Ok(self.time < time_index),
+ CompareOp::Ge => Ok(self.time >= time_index),
+ CompareOp::Le => Ok(self.time <= time_index),
+ }
+ // Support comparison with Option type
+ } else if let Ok(opt_event_time) = other.extract::() {
+ match opt_event_time.inner {
+ Some(other_time) => match op {
+ CompareOp::Eq => Ok(self.time == other_time),
+ CompareOp::Ne => Ok(self.time != other_time),
+ CompareOp::Gt => Ok(self.time > other_time),
+ CompareOp::Lt => Ok(self.time < other_time),
+ CompareOp::Ge => Ok(self.time >= other_time),
+ CompareOp::Le => Ok(self.time <= other_time),
+ },
+ None => match op {
+ CompareOp::Eq => Ok(false),
+ CompareOp::Ne => Ok(true),
+ CompareOp::Gt | CompareOp::Ge => Ok(true),
+ CompareOp::Lt | CompareOp::Le => Ok(false),
+ },
+ }
+ } else {
+ Err(PyTypeError::new_err("Unsupported comparison: EventTime can only be compared with a str, datetime, float, integer, a tuple/list of two of those types, or another EventTime."))
+ }
+ }
+
+ pub fn __repr__(&self) -> String {
+ self.time.to_string()
+ }
+
+ pub fn __hash__(&self) -> isize {
+ let mut hasher = DefaultHasher::new();
+ self.time.hash(&mut hasher);
+ hasher.finish() as isize
+ }
+
+ pub fn __int__(&self) -> i64 {
+ self.t()
+ }
+
+ /// Creates a new EventTime.
+ ///
+ /// Arguments:
+ /// timestamp (int | float | datetime | str): A time input convertible to an EventTime.
+ /// event_id (int | float | datetime | str | None): Optionally, specify the event id. Defaults to 0.
+ ///
+ /// Returns:
+ /// EventTime:
+ #[new]
+ #[pyo3(signature = (timestamp, event_id=None))]
+ pub fn py_new(timestamp: EventTimeComponent, event_id: Option) -> Self {
+ let event_id = event_id.map(|t| t.t() as usize).unwrap_or(0);
+ Self {
+ time: EventTime::new(timestamp.t(), event_id),
+ }
+ }
+}
+
+impl IntoTime for PyEventTime {
+ fn into_time(self) -> EventTime {
+ self.time
+ }
+}
+
+impl TryIntoInputTime for PyEventTime {
+ fn try_into_input_time(self) -> Result {
+ Ok(InputTime::Indexed(self.t(), self.event_id()))
+ }
+}
+
+impl From for PyEventTime {
+ fn from(time: EventTime) -> Self {
+ Self { time }
+ }
+}
+
+impl From for EventTime {
+ fn from(value: PyEventTime) -> Self {
+ value.inner()
+ }
+}
+
+/// Raphtory’s optional EventTime type. Instances of OptionalEventTime may contain an EventTime, or be empty.
+/// This is used for functions that may not return data (such as earliest_time and latest_time) because the data is unavailable.
+///
+/// If data is contained, OptionalEventTime instances can be used similarly to EventTime.
+/// If empty, time operations (such as .t, .dt, .event_id) will return None.
+/// An empty OptionalEventTime is considered smaller than (<) any EventTime or OptionalEventTime with data.
+#[pyclass(name = "OptionalEventTime", module = "raphtory", frozen)]
+#[derive(Debug, Clone, Copy, Serialize, PartialEq, Ord, PartialOrd, Eq)]
+pub struct PyOptionalEventTime {
+ inner: Option,
+}
+
+impl PyOptionalEventTime {
+ // Repr trait is in raphtory crate so it can't be implemented here
+ pub fn repr(&self) -> String {
+ match self.inner {
+ Some(v) => v.to_string(), // couldn't use .repr() because it's in the raphtory crate
+ None => "None".to_string(),
+ }
+ }
+}
+
+#[pymethods]
+impl PyOptionalEventTime {
+ /// Returns the timestamp in milliseconds since the Unix epoch if an EventTime is contained, or else None.
+ ///
+ /// Returns:
+ /// int | None: Milliseconds since the Unix epoch.
+ #[getter]
+ pub fn t(&self) -> Option {
+ self.inner.map(|t| t.t())
+ }
+
+ /// Returns the UTC datetime representation of this EventTime's timestamp if an EventTime is contained, or else None.
+ ///
+ /// Returns:
+ /// datetime | None: The UTC datetime.
+ ///
+ /// Raises:
+ /// TimeError: Returns TimeError on timestamp conversion errors (e.g. out-of-range timestamp).
+ #[getter]
+ pub fn dt(&self) -> PyResult>> {
+ self.inner.map(|t| t.dt().map_err(PyErr::from)).transpose()
+ }
+
+ /// Returns the event id used to order events within the same timestamp if an EventTime is contained, or else None.
+ ///
+ /// Returns:
+ /// int | None: The event id.
+ #[getter]
+ pub fn event_id(&self) -> Option {
+ self.inner.map(|t| t.i())
+ }
+
+ /// Returns true if the OptionalEventTime doesn't contain an EventTime.
+ ///
+ /// Returns:
+ /// bool:
+ pub fn is_none(&self) -> bool {
+ self.inner.is_none()
+ }
+
+ /// Returns true if the OptionalEventTime contains an EventTime.
+ ///
+ /// Returns:
+ /// bool:
+ pub fn is_some(&self) -> bool {
+ self.inner.is_some()
+ }
+
+ /// Returns the contained EventTime if it exists, or else None.
+ ///
+ /// Returns:
+ /// EventTime | None:
+ pub fn get_event_time(&self) -> Option {
+ self.inner
+ }
+
+ /// Return this entry as a tuple of (timestamp, event_id), where the timestamp is in milliseconds if an EventTime is contained, or else None.
+ ///
+ /// Returns:
+ /// tuple[int,int] | None: (timestamp, event_id).
+ #[getter]
+ pub fn as_tuple(&self) -> Option<(i64, usize)> {
+ self.inner.map(|t| t.as_tuple())
+ }
+
+ pub fn __bool__(&self) -> bool {
+ self.inner.is_some()
+ }
+
+ // we assume None < Some(_)
+ pub fn __richcmp__(&self, other: &Bound, op: CompareOp) -> PyResult {
+ if other.is_none() {
+ match op {
+ CompareOp::Eq => Ok(self.inner.is_none()),
+ CompareOp::Ne => Ok(self.inner.is_some()),
+ CompareOp::Gt => Ok(self.inner.is_some()),
+ CompareOp::Ge => Ok(true), // Some >= None (true) && None >= None (true)
+ CompareOp::Lt => Ok(false),
+ CompareOp::Le => Ok(self.inner.is_none()),
+ }
+ }
+ // extract TimeIndexComponent first. If we're dealing with a single i64 (or something that can be converted to an i64), we only compare timestamps
+ else if let Ok(component) = other.extract::() {
+ match self.inner {
+ Some(t) => match op {
+ CompareOp::Eq => Ok(t.t() == component.t()),
+ CompareOp::Ne => Ok(t.t() != component.t()),
+ CompareOp::Gt => Ok(t.t() > component.t()),
+ CompareOp::Lt => Ok(t.t() < component.t()),
+ CompareOp::Ge => Ok(t.t() >= component.t()),
+ CompareOp::Le => Ok(t.t() <= component.t()),
+ },
+ None => match op {
+ CompareOp::Eq => Ok(false),
+ CompareOp::Ne => Ok(true),
+ CompareOp::Lt | CompareOp::Le => Ok(true),
+ CompareOp::Gt | CompareOp::Ge => Ok(false),
+ },
+ }
+ // If an EventTime was passed, we then compare the event id
+ } else if let Ok(time_index) = other.extract::() {
+ match self.inner {
+ Some(t) => match op {
+ CompareOp::Eq => Ok(t == time_index),
+ CompareOp::Ne => Ok(t != time_index),
+ CompareOp::Gt => Ok(t > time_index),
+ CompareOp::Lt => Ok(t < time_index),
+ CompareOp::Ge => Ok(t >= time_index),
+ CompareOp::Le => Ok(t <= time_index),
+ },
+ None => match op {
+ CompareOp::Eq => Ok(false),
+ CompareOp::Ne => Ok(true),
+ CompareOp::Lt | CompareOp::Le => Ok(true),
+ CompareOp::Gt | CompareOp::Ge => Ok(false),
+ },
+ }
+ } else if let Ok(opt_event_time) = other.extract::() {
+ match op {
+ CompareOp::Eq => Ok(self.inner == opt_event_time.inner),
+ CompareOp::Ne => Ok(self.inner != opt_event_time.inner),
+ CompareOp::Gt => Ok(self.inner > opt_event_time.inner),
+ CompareOp::Lt => Ok(self.inner < opt_event_time.inner),
+ CompareOp::Ge => Ok(self.inner >= opt_event_time.inner),
+ CompareOp::Le => Ok(self.inner <= opt_event_time.inner),
+ }
+ } else {
+ Err(PyTypeError::new_err("Unsupported comparison: EventTime can only be compared with a str, datetime, float, integer, a tuple/list of two of those types, or another EventTime."))
+ }
+ }
+}
+
+impl From> for PyOptionalEventTime {
+ fn from(value: Option) -> Self {
+ Self { inner: value }
+ }
+}
+
+impl From for Option {
+ fn from(value: PyOptionalEventTime) -> Self {
+ value.inner
+ }
+}
+
+impl<'source> FromPyObject<'source> for InputTime {
+ fn extract_bound(input: &Bound<'source, PyAny>) -> PyResult {
+ if let Ok(py_time) = input.downcast::() {
+ return Ok(py_time.get().try_into_input_time()?);
+ } else if let Ok(opt_py_time) = input.extract::() {
+ return match opt_py_time.inner {
+ Some(t) => Ok(t.try_into_input_time()?),
+ None => Err(PyTypeError::new_err("OptionalEventTime is None")),
+ };
+ }
+ // Handle list/tuple case: [timestamp, event_id]
+ if input.downcast::().is_ok() || input.downcast::().is_ok() {
+ let py = input.py();
+ if let Ok(items) = input.extract::>() {
+ let len = items.len();
+ if len != 2 {
+ return Err(PyTypeError::new_err(format!(
+ "List/tuple for time input must have exactly 2 elements [timestamp, event_id], got {} elements.",
+ len
+ )));
+ }
+ let first = items[0].bind(py);
+ let second = items[1].bind(py);
+ let first_entry = extract_time_index_component(first).map_err(|e| match e {
+ ParsingError::Matched(err) => err,
+ ParsingError::Unmatched => {
+ let message = format!("Time component '{first}' must be a str, datetime, float, or an integer.");
+ PyTypeError::new_err(message)
+ }
+ })?;
+ let second_entry = extract_time_index_component(second).map_err(|e| match e {
+ ParsingError::Matched(err) => err,
+ ParsingError::Unmatched => {
+ let message = format!("Time component '{second}' must be a str, datetime, float, or an integer.");
+ PyTypeError::new_err(message)
+ }
+ })?;
+ return Ok(InputTime::Indexed(
+ first_entry.t(),
+ second_entry.t() as usize,
+ ));
+ }
+ }
+ // allow errors from EventTimeComponent extraction to pass through (except if no type has matched at all)
+ match extract_time_index_component(input) {
+ Ok(component) => Ok(InputTime::Simple(component.t())),
+ Err(ParsingError::Matched(err)) => Err(err),
+ Err(ParsingError::Unmatched) => {
+ let message = format!("Time '{input}' must be a str, datetime, float, integer, or a tuple/list of two of those types.");
+ Err(PyTypeError::new_err(message))
+ }
+ }
+ }
+}
+
+impl From for PyErr {
+ fn from(err: TimeError) -> Self {
+ PyRuntimeError::new_err(err.to_string())
+ }
+}
diff --git a/raphtory-benchmark/benches/base.rs b/raphtory-benchmark/benches/base.rs
index 4037cfb126..c6f65479c3 100644
--- a/raphtory-benchmark/benches/base.rs
+++ b/raphtory-benchmark/benches/base.rs
@@ -1,5 +1,6 @@
use criterion::{criterion_group, criterion_main, Criterion, Throughput};
use raphtory::{graph_loader::lotr_graph::lotr_graph, prelude::*};
+use raphtory_api::core::storage::timeindex::AsTime;
use raphtory_benchmark::common::{
bootstrap_graph, run_graph_ops_benches, run_large_ingestion_benchmarks,
run_proto_decode_benchmark, run_proto_encode_benchmark,
@@ -37,7 +38,7 @@ pub fn base(c: &mut Criterion) {
for t in edge.history() {
layered_graph
.add_edge(
- t,
+ t.t(),
edge.src().name().clone(),
edge.dst().name().clone(),
NO_PROPS,
diff --git a/raphtory-benchmark/src/common/mod.rs b/raphtory-benchmark/src/common/mod.rs
index 51f8d4ff6d..edf973889d 100644
--- a/raphtory-benchmark/src/common/mod.rs
+++ b/raphtory-benchmark/src/common/mod.rs
@@ -7,7 +7,7 @@ use criterion::{
};
use rand::{distributions::Uniform, seq::*, Rng, SeedableRng};
use raphtory::{db::api::view::StaticGraphViewOps, prelude::*};
-use raphtory_api::core::utils::logging::global_info_logger;
+use raphtory_api::core::{storage::timeindex::AsTime, utils::logging::global_info_logger};
use std::collections::HashSet;
use tempfile::TempDir;
use tracing::info;
@@ -301,7 +301,10 @@ pub fn run_analysis_benchmarks(
bench(group, "has_edge_existing", parameter, |b: &mut Bencher| {
let mut rng = rand::thread_rng();
- let (src, dst) = edges.iter().choose(&mut rng).expect("non-empty graph");
+ let (src, dst) = edges
+ .iter()
+ .choose(&mut rng)
+ .expect("has_edge_existing: non-empty graph (graph().edges().iter() is empty)");
b.iter(|| graph.has_edge(src, dst))
});
@@ -313,8 +316,8 @@ pub fn run_analysis_benchmarks(
let mut rng = rand::thread_rng();
let edge = loop {
let edge: (&GID, &GID) = (
- nodes.iter().choose(&mut rng).expect("non-empty graph"),
- nodes.iter().choose(&mut rng).expect("non-empty graph"),
+ nodes.iter().choose(&mut rng).expect("has_edge_nonexisting: non-empty graph (graph().nodes().id().iter() is empty)"),
+ nodes.iter().choose(&mut rng).expect("has_edge_nonexisting: non-empty graph (graph().nodes().id().iter() is empty)"),
);
if !edges.contains(&(edge.0.clone(), edge.1.clone())) {
break edge;
@@ -328,7 +331,7 @@ pub fn run_analysis_benchmarks(
let mut rng = rand::thread_rng();
let (edge, active_t) = edges_t
.choose(&mut rng)
- .and_then(|(src, dst, t)| graph.edge(src, dst).map(|e| (e, *t)))
+ .and_then(|(src, dst, t)| graph.edge(src, dst).map(|e| (e, t.t())))
.expect("active edge");
b.iter(|| {
edge.window(active_t.saturating_sub(5), active_t + 5)
@@ -362,7 +365,10 @@ pub fn run_analysis_benchmarks(
bench(group, "has_node_existing", parameter, |b: &mut Bencher| {
let mut rng = rand::thread_rng();
- let v = nodes.iter().choose(&mut rng).expect("non-empty graph");
+ let v = nodes
+ .iter()
+ .choose(&mut rng)
+ .expect("has_node_existing: non-empty graph (graph().nodes().id().iter() is empty)");
b.iter(|| graph.has_node(v))
});
@@ -482,8 +488,14 @@ pub fn run_graph_ops_benches(
// graph windowed
let group_name = format!("{graph_name}_window_10");
let mut graph_window_group_10 = c.benchmark_group(group_name);
- let latest = graph.latest_time().expect("non-empty graph");
- let earliest = graph.earliest_time().expect("non-empty graph");
+ let latest = graph
+ .latest_time()
+ .expect("windowed graph latest time error: non-empty graph")
+ .t();
+ let earliest = graph
+ .earliest_time()
+ .expect("windowed graph earliest time error: non-empty graph")
+ .t();
let start = latest - (latest - earliest) / 10;
// graph_window_group_10.sample_size(10);
let make_graph = || graph.window(start, latest + 1);
@@ -535,8 +547,14 @@ pub fn run_graph_ops_benches(
let graph = layered_graph;
let group_name = format!("{graph_name}_window_50_layered");
let mut graph_window_layered_group_50 = c.benchmark_group(group_name);
- let latest = graph.latest_time().expect("non-empty graph");
- let earliest = graph.earliest_time().expect("non-empty graph");
+ let latest = graph
+ .latest_time()
+ .expect("layered windowed graph latest time error: non-empty graph")
+ .t();
+ let earliest = graph
+ .earliest_time()
+ .expect("layered windowed graph earliest time error: non-empty graph")
+ .t();
let start = latest - (latest - earliest) / 2;
let make_graph = || {
graph
@@ -556,8 +574,14 @@ pub fn run_graph_ops_benches(
let group_name = format!("{graph_name}_persistent_window_50_layered");
let mut graph_window_layered_group_50 = c.benchmark_group(group_name);
- let latest = graph.latest_time().expect("non-empty graph");
- let earliest = graph.earliest_time().expect("non-empty graph");
+ let latest = graph
+ .latest_time()
+ .expect("layered windowed persistent graph latest time error: non-empty graph")
+ .t();
+ let earliest = graph
+ .earliest_time()
+ .expect("layered windowed persistent graph earliest time error: non-empty graph")
+ .t();
let start = latest - (latest - earliest) / 2;
let make_graph = || {
graph
diff --git a/raphtory-core/src/entities/edges/edge_store.rs b/raphtory-core/src/entities/edges/edge_store.rs
index fa9e57c049..d05a920f47 100644
--- a/raphtory-core/src/entities/edges/edge_store.rs
+++ b/raphtory-core/src/entities/edges/edge_store.rs
@@ -5,7 +5,7 @@ use crate::{
},
storage::{
raw_edges::EdgeShard,
- timeindex::{TimeIndex, TimeIndexEntry},
+ timeindex::{EventTime, TimeIndex},
},
utils::iter::GenLockedIter,
};
@@ -67,12 +67,7 @@ impl EdgeLayer {
self.props
}
- pub fn add_prop(
- &mut self,
- t: TimeIndexEntry,
- prop_id: usize,
- prop: Prop,
- ) -> Result<(), TPropError> {
+ pub fn add_prop(&mut self, t: EventTime, prop_id: usize, prop: Prop) -> Result<(), TPropError> {
let props = self.props.get_or_insert_with(Props::new);
props.add_prop(t, prop_id, prop)
}
@@ -163,11 +158,11 @@ impl<'a> MemEdge<'a> {
self.edges.internal_num_layers()
}
- pub fn get_additions(self, layer_id: usize) -> Option<&'a TimeIndex> {
+ pub fn get_additions(self, layer_id: usize) -> Option<&'a TimeIndex> {
self.edges.additions(self.offset, layer_id)
}
- pub fn get_deletions(self, layer_id: usize) -> Option<&'a TimeIndex> {
+ pub fn get_deletions(self, layer_id: usize) -> Option<&'a TimeIndex> {
self.edges.deletions(self.offset, layer_id)
}
diff --git a/raphtory-core/src/entities/graph/tgraph.rs b/raphtory-core/src/entities/graph/tgraph.rs
index 839bce2f75..8822c164ca 100644
--- a/raphtory-core/src/entities/graph/tgraph.rs
+++ b/raphtory-core/src/entities/graph/tgraph.rs
@@ -12,7 +12,7 @@ use crate::{
},
storage::{
raw_edges::EdgeWGuard,
- timeindex::{AsTime, TimeIndexEntry},
+ timeindex::{AsTime, EventTime},
NodeEntry, PairEntryMut,
},
};
@@ -236,7 +236,7 @@ impl TemporalGraph {
}
#[inline]
- pub fn update_time(&self, time: TimeIndexEntry) {
+ pub fn update_time(&self, time: EventTime) {
let t = time.t();
self.earliest_time.update(t);
self.latest_time.update(t);
@@ -246,7 +246,7 @@ impl TemporalGraph {
&self,
node_pair: &mut PairEntryMut,
edge_id: EID,
- t: TimeIndexEntry,
+ t: EventTime,
layer: usize,
is_deletion: bool,
) {
@@ -269,7 +269,7 @@ impl TemporalGraph {
pub fn link_edge(
&self,
eid: EID,
- t: TimeIndexEntry,
+ t: EventTime,
layer: usize,
is_deletion: bool,
) -> EdgeWGuard<'_> {
@@ -288,7 +288,7 @@ impl TemporalGraph {
&self,
src_id: VID,
dst_id: VID,
- t: TimeIndexEntry,
+ t: EventTime,
layer: usize,
is_deletion: bool,
) -> MaybeNew> {
diff --git a/raphtory-core/src/entities/nodes/node_store.rs b/raphtory-core/src/entities/nodes/node_store.rs
index 0d17de970b..dbfd327775 100644
--- a/raphtory-core/src/entities/nodes/node_store.rs
+++ b/raphtory-core/src/entities/nodes/node_store.rs
@@ -9,7 +9,7 @@ use crate::{
LayerIds, EID, GID, VID,
},
storage::{
- timeindex::{TimeIndexEntry, TimeIndexWindow},
+ timeindex::{EventTime, TimeIndexWindow},
NodeEntry,
},
utils::iter::GenLockedIter,
@@ -58,8 +58,8 @@ impl NodeTimestamps {
}
impl<'a> TimeIndexOps<'a> for &'a NodeTimestamps {
- type IndexType = TimeIndexEntry;
- type RangeType = TimeIndexWindow<'a, TimeIndexEntry, NodeTimestamps>;
+ type IndexType = EventTime;
+ type RangeType = TimeIndexWindow<'a, EventTime, NodeTimestamps>;
#[inline]
fn active(&self, w: Range) -> bool {
@@ -211,7 +211,7 @@ impl NodeStore {
}
#[inline]
- pub fn update_time(&mut self, t: TimeIndexEntry, eid: ELID) {
+ pub fn update_time(&mut self, t: EventTime, eid: ELID) {
self.timestamps.edge_ts.set(t, eid);
}
@@ -230,7 +230,7 @@ impl NodeStore {
props.update_metadata(prop_id, prop)
}
- pub fn update_t_prop_time(&mut self, t: TimeIndexEntry, prop_i: Option) {
+ pub fn update_t_prop_time(&mut self, t: EventTime, prop_i: Option) {
self.timestamps.props_ts.set(t, prop_i);
}
diff --git a/raphtory-core/src/entities/properties/graph_meta.rs b/raphtory-core/src/entities/properties/graph_meta.rs
index ba981af740..42e89afce2 100644
--- a/raphtory-core/src/entities/properties/graph_meta.rs
+++ b/raphtory-core/src/entities/properties/graph_meta.rs
@@ -3,7 +3,7 @@ use crate::{
props::MetadataError,
tprop::{IllegalPropType, TProp},
},
- storage::{locked_view::LockedView, timeindex::TimeIndexEntry},
+ storage::{locked_view::LockedView, timeindex::EventTime},
};
use raphtory_api::core::{
entities::properties::{
@@ -92,7 +92,7 @@ impl GraphMeta {
pub fn add_prop(
&self,
- t: TimeIndexEntry,
+ t: EventTime,
prop_id: usize,
prop: Prop,
) -> Result<(), IllegalPropType> {
diff --git a/raphtory-core/src/entities/properties/props.rs b/raphtory-core/src/entities/properties/props.rs
index b875decb8b..59028aecc1 100644
--- a/raphtory-core/src/entities/properties/props.rs
+++ b/raphtory-core/src/entities/properties/props.rs
@@ -2,7 +2,7 @@ use crate::{
entities::properties::tprop::{IllegalPropType, TProp},
storage::{
lazy_vec::{IllegalSet, LazyVec},
- timeindex::TimeIndexEntry,
+ timeindex::EventTime,
},
};
use raphtory_api::core::entities::properties::prop::Prop;
@@ -47,12 +47,7 @@ impl Props {
}
}
- pub fn add_prop(
- &mut self,
- t: TimeIndexEntry,
- prop_id: usize,
- prop: Prop,
- ) -> Result<(), TPropError> {
+ pub fn add_prop(&mut self, t: EventTime, prop_id: usize, prop: Prop) -> Result<(), TPropError> {
self.temporal_props.update(prop_id, |p| Ok(p.set(t, prop)?))
}
diff --git a/raphtory-core/src/entities/properties/tcell.rs b/raphtory-core/src/entities/properties/tcell.rs
index c81474e9f3..dc6fd3abea 100644
--- a/raphtory-core/src/entities/properties/tcell.rs
+++ b/raphtory-core/src/entities/properties/tcell.rs
@@ -1,4 +1,4 @@
-use crate::storage::timeindex::{AsTime, TimeIndexEntry, TimeIndexOps, TimeIndexWindow};
+use crate::storage::timeindex::{AsTime, EventTime, TimeIndexOps, TimeIndexWindow};
use either::Either;
use iter_enum::{DoubleEndedIterator, ExactSizeIterator, Extend, FusedIterator, Iterator};
use raphtory_api::core::storage::{sorted_vec_map::SVM, timeindex::TimeIndexLike};
@@ -10,9 +10,9 @@ use std::{collections::BTreeMap, fmt::Debug, ops::Range};
pub enum TCell {
#[default]
Empty,
- TCell1(TimeIndexEntry, A),
- TCellCap(SVM),
- TCellN(BTreeMap),
+ TCell1(EventTime, A),
+ TCellCap(SVM),
+ TCellN(BTreeMap),
}
#[derive(Iterator, DoubleEndedIterator, ExactSizeIterator, FusedIterator, Extend)]
@@ -26,12 +26,12 @@ enum TCellVariants {
const BTREE_CUTOFF: usize = 128;
impl TCell {
- pub fn new(t: TimeIndexEntry, value: A) -> Self {
+ pub fn new(t: EventTime, value: A) -> Self {
TCell::TCell1(t, value)
}
#[inline]
- pub fn set(&mut self, t: TimeIndexEntry, value: A) {
+ pub fn set(&mut self, t: EventTime, value: A) {
match self {
TCell::Empty => {
*self = TCell::TCell1(t, value);
@@ -53,7 +53,7 @@ impl TCell {
svm.insert(t, value);
} else {
let svm = std::mem::take(svm);
- let mut btm: BTreeMap = BTreeMap::new();
+ let mut btm: BTreeMap = BTreeMap::new();
for (k, v) in svm.into_iter() {
btm.insert(k, v);
}
@@ -67,7 +67,7 @@ impl TCell {
}
}
- pub fn at(&self, ti: &TimeIndexEntry) -> Option<&A> {
+ pub fn at(&self, ti: &EventTime) -> Option<&A> {
match self {
TCell::Empty => None,
TCell::TCell1(t, v) => (t == ti).then_some(v),
@@ -77,7 +77,7 @@ impl TCell {
}
}
impl TCell {
- pub fn iter(&self) -> impl DoubleEndedIterator- + Send + Sync {
+ pub fn iter(&self) -> impl DoubleEndedIterator
- + Send + Sync {
match self {
TCell::Empty => TCellVariants::Empty(std::iter::empty()),
TCell::TCell1(t, value) => TCellVariants::TCell1(std::iter::once((t, value))),
@@ -92,8 +92,8 @@ impl
TCell {
pub fn iter_window(
&self,
- r: Range,
- ) -> impl DoubleEndedIterator- + Send + Sync {
+ r: Range
,
+ ) -> impl DoubleEndedIterator- + Send + Sync {
match self {
TCell::Empty => TCellVariants::Empty(std::iter::empty()),
TCell::TCell1(t, value) => TCellVariants::TCell1(if r.contains(t) {
@@ -110,22 +110,16 @@ impl
TCell {
&self,
r: Range,
) -> impl DoubleEndedIterator- + Send + Sync + '_ {
- self.iter_window(TimeIndexEntry::range(r))
+ self.iter_window(EventTime::range(r))
.map(|(t, a)| (t.t(), a))
}
- pub fn last_before(&self, t: TimeIndexEntry) -> Option<(TimeIndexEntry, &A)> {
+ pub fn last_before(&self, t: EventTime) -> Option<(EventTime, &A)> {
match self {
TCell::Empty => None,
TCell::TCell1(t2, v) => (*t2 < t).then_some((*t2, v)),
- TCell::TCellCap(map) => map
- .range(TimeIndexEntry::MIN..t)
- .last()
- .map(|(ti, v)| (*ti, v)),
- TCell::TCellN(map) => map
- .range(TimeIndexEntry::MIN..t)
- .last()
- .map(|(ti, v)| (*ti, v)),
+ TCell::TCellCap(map) => map.range(EventTime::MIN..t).last().map(|(ti, v)| (*ti, v)),
+ TCell::TCellN(map) => map.range(EventTime::MIN..t).last().map(|(ti, v)| (*ti, v)),
}
}
@@ -144,7 +138,7 @@ impl
TCell {
}
impl<'a, A: Send + Sync> TimeIndexOps<'a> for &'a TCell {
- type IndexType = TimeIndexEntry;
+ type IndexType = EventTime;
type RangeType = TimeIndexWindow<'a, Self::IndexType, TCell >;
#[inline]
@@ -270,12 +264,12 @@ impl<'a, A: Send + Sync> TimeIndexLike<'a> for &'a TCell {
#[cfg(test)]
mod tcell_tests {
use super::TCell;
- use crate::storage::timeindex::{AsTime, TimeIndexEntry};
+ use crate::storage::timeindex::{AsTime, EventTime};
#[test]
fn set_new_value_for_tcell_initialized_as_empty() {
let mut tcell = TCell::default();
- tcell.set(TimeIndexEntry::start(16), String::from("lobster"));
+ tcell.set(EventTime::start(16), String::from("lobster"));
assert_eq!(
tcell.iter().map(|(_, v)| v).collect::>(),
@@ -285,8 +279,8 @@ mod tcell_tests {
#[test]
fn every_new_update_to_the_same_prop_is_recorded_as_history() {
- let mut tcell = TCell::new(TimeIndexEntry::start(1), "Pometry");
- tcell.set(TimeIndexEntry::start(2), "Pometry Inc.");
+ let mut tcell = TCell::new(EventTime::start(1), "Pometry");
+ tcell.set(EventTime::start(2), "Pometry Inc.");
assert_eq!(
tcell.iter_t().collect::>(),
@@ -296,8 +290,8 @@ mod tcell_tests {
#[test]
fn new_update_with_the_same_time_to_a_prop_is_ignored() {
- let mut tcell = TCell::new(TimeIndexEntry::start(1), "Pometry");
- tcell.set(TimeIndexEntry::start(1), "Pometry Inc.");
+ let mut tcell = TCell::new(EventTime::start(1), "Pometry");
+ tcell.set(EventTime::start(1), "Pometry Inc.");
assert_eq!(
tcell.iter_t().collect::>(),
@@ -315,17 +309,17 @@ mod tcell_tests {
assert_eq!(tcell.iter_t().collect::>(), vec![]);
- let tcell = TCell::new(TimeIndexEntry::start(3), "Pometry");
+ let tcell = TCell::new(EventTime::start(3), "Pometry");
assert_eq!(
tcell.iter().collect::>(),
- vec![(&TimeIndexEntry::start(3), &"Pometry")]
+ vec![(&EventTime::start(3), &"Pometry")]
);
assert_eq!(tcell.iter_t().collect::>(), vec![(3, &"Pometry")]);
- let mut tcell = TCell::new(TimeIndexEntry::start(2), "Pometry");
- tcell.set(TimeIndexEntry::start(1), "Inc. Pometry");
+ let mut tcell = TCell::new(EventTime::start(2), "Pometry");
+ tcell.set(EventTime::start(1), "Inc. Pometry");
assert_eq!(
// Results are ordered by time
@@ -337,14 +331,14 @@ mod tcell_tests {
// Results are ordered by time
tcell.iter().collect::>(),
vec![
- (&TimeIndexEntry::start(1), &"Inc. Pometry"),
- (&TimeIndexEntry::start(2), &"Pometry")
+ (&EventTime::start(1), &"Inc. Pometry"),
+ (&EventTime::start(2), &"Pometry")
]
);
let mut tcell: TCell = TCell::default();
for n in 1..130 {
- tcell.set(TimeIndexEntry::start(n), n)
+ tcell.set(EventTime::start(n), n)
}
assert_eq!(tcell.iter_t().count(), 129);
@@ -357,7 +351,7 @@ mod tcell_tests {
let tcell: TCell = TCell::default();
let actual = tcell
- .iter_window(TimeIndexEntry::MIN..TimeIndexEntry::MAX)
+ .iter_window(EventTime::MIN..EventTime::MAX)
.collect::>();
let expected = vec![];
assert_eq!(actual, expected);
@@ -367,13 +361,13 @@ mod tcell_tests {
vec![]
);
- let tcell = TCell::new(TimeIndexEntry::start(3), "Pometry");
+ let tcell = TCell::new(EventTime::start(3), "Pometry");
assert_eq!(
tcell
- .iter_window(TimeIndexEntry::range(3..4))
+ .iter_window(EventTime::range(3..4))
.collect::>(),
- vec![(&TimeIndexEntry(3, 0), &"Pometry")]
+ vec![(&EventTime(3, 0), &"Pometry")]
);
assert_eq!(
@@ -381,13 +375,13 @@ mod tcell_tests {
vec![(3, &"Pometry")]
);
- let mut tcell = TCell::new(TimeIndexEntry::start(3), "Pometry");
- tcell.set(TimeIndexEntry::start(1), "Pometry Inc.");
- tcell.set(TimeIndexEntry::start(2), "Raphtory");
+ let mut tcell = TCell::new(EventTime::start(3), "Pometry");
+ tcell.set(EventTime::start(1), "Pometry Inc.");
+ tcell.set(EventTime::start(2), "Raphtory");
- let one = TimeIndexEntry::start(1);
- let two = TimeIndexEntry::start(2);
- let three = TimeIndexEntry::start(3);
+ let one = EventTime::start(1);
+ let two = EventTime::start(2);
+ let three = EventTime::start(3);
assert_eq!(
tcell.iter_window_t(2..3).collect::>(),
@@ -397,14 +391,14 @@ mod tcell_tests {
let expected = vec![];
assert_eq!(
tcell
- .iter_window(TimeIndexEntry::range(4..5))
+ .iter_window(EventTime::range(4..5))
.collect::>(),
expected
);
assert_eq!(
tcell
- .iter_window(TimeIndexEntry::range(1..i64::MAX))
+ .iter_window(EventTime::range(1..i64::MAX))
.collect::>(),
vec![
(&one, &"Pometry Inc."),
@@ -415,14 +409,14 @@ mod tcell_tests {
assert_eq!(
tcell
- .iter_window(TimeIndexEntry::range(3..i64::MAX))
+ .iter_window(EventTime::range(3..i64::MAX))
.collect::>(),
vec![(&three, &"Pometry")]
);
assert_eq!(
tcell
- .iter_window(TimeIndexEntry::range(2..i64::MAX))
+ .iter_window(EventTime::range(2..i64::MAX))
.collect::>(),
vec![(&two, &"Raphtory"), (&three, &"Pometry")]
);
@@ -430,14 +424,14 @@ mod tcell_tests {
let expected = vec![];
assert_eq!(
tcell
- .iter_window(TimeIndexEntry::range(5..i64::MAX))
+ .iter_window(EventTime::range(5..i64::MAX))
.collect::>(),
expected
);
assert_eq!(
tcell
- .iter_window(TimeIndexEntry::range(i64::MIN..4))
+ .iter_window(EventTime::range(i64::MIN..4))
.collect::>(),
vec![
(&one, &"Pometry Inc."),
@@ -449,21 +443,21 @@ mod tcell_tests {
let expected = vec![];
assert_eq!(
tcell
- .iter_window(TimeIndexEntry::range(i64::MIN..1))
+ .iter_window(EventTime::range(i64::MIN..1))
.collect::>(),
expected
);
let mut tcell: TCell = TCell::default();
for n in 1..130 {
- tcell.set(TimeIndexEntry::start(n), n)
+ tcell.set(EventTime::start(n), n)
}
assert_eq!(tcell.iter_window_t(i64::MIN..i64::MAX).count(), 129);
assert_eq!(
tcell
- .iter_window(TimeIndexEntry::range(i64::MIN..i64::MAX))
+ .iter_window(EventTime::range(i64::MIN..i64::MAX))
.count(),
129
)
diff --git a/raphtory-core/src/entities/properties/tprop.rs b/raphtory-core/src/entities/properties/tprop.rs
index cb9635e5e9..66f63b0596 100644
--- a/raphtory-core/src/entities/properties/tprop.rs
+++ b/raphtory-core/src/entities/properties/tprop.rs
@@ -1,6 +1,6 @@
use crate::{
entities::properties::tcell::TCell,
- storage::{timeindex::TimeIndexEntry, TPropColumn},
+ storage::{timeindex::EventTime, TPropColumn},
};
use bigdecimal::BigDecimal;
use chrono::{DateTime, NaiveDateTime, Utc};
@@ -105,7 +105,7 @@ impl<'a> TPropCell<'a> {
}
impl<'a> TPropOps<'a> for TPropCell<'a> {
- fn iter(self) -> impl DoubleEndedIterator- + Send + Sync + 'a {
+ fn iter(self) -> impl DoubleEndedIterator
- + Send + Sync + 'a {
let log = self.log;
self.t_cell.into_iter().flat_map(move |t_cell| {
t_cell
@@ -116,8 +116,8 @@ impl<'a> TPropOps<'a> for TPropCell<'a> {
fn iter_window(
self,
- r: Range
,
- ) -> impl DoubleEndedIterator- + Send + Sync + 'a {
+ r: Range
,
+ ) -> impl DoubleEndedIterator- + Send + Sync + 'a {
self.t_cell.into_iter().flat_map(move |t_cell| {
t_cell
.iter_window(r.clone())
@@ -125,13 +125,13 @@ impl<'a> TPropOps<'a> for TPropCell<'a> {
})
}
- fn at(&self, ti: &TimeIndexEntry) -> Option
{
+ fn at(&self, ti: &EventTime) -> Option {
self.t_cell?.at(ti).and_then(|&id| self.log?.get(id?))
}
}
impl TProp {
- pub(crate) fn from(t: TimeIndexEntry, prop: Prop) -> Self {
+ pub(crate) fn from(t: EventTime, prop: Prop) -> Self {
match prop {
Prop::Str(value) => TProp::Str(TCell::new(t, value)),
Prop::I32(value) => TProp::I32(TCell::new(t, value)),
@@ -176,7 +176,7 @@ impl TProp {
}
}
- pub(crate) fn set(&mut self, t: TimeIndexEntry, prop: Prop) -> Result<(), IllegalPropType> {
+ pub(crate) fn set(&mut self, t: EventTime, prop: Prop) -> Result<(), IllegalPropType> {
if matches!(self, TProp::Empty) {
*self = TProp::from(t, prop);
} else {
@@ -245,7 +245,7 @@ impl TProp {
}
impl<'a> TPropOps<'a> for &'a TProp {
- fn last_before(&self, t: TimeIndexEntry) -> Option<(TimeIndexEntry, Prop)> {
+ fn last_before(&self, t: EventTime) -> Option<(EventTime, Prop)> {
match self {
TProp::Empty => None,
TProp::Str(cell) => cell.last_before(t).map(|(t, v)| (t, Prop::Str(v.clone()))),
@@ -272,7 +272,7 @@ impl<'a> TPropOps<'a> for &'a TProp {
}
}
- fn iter(self) -> impl DoubleEndedIterator- + Send + Sync + 'a {
+ fn iter(self) -> impl DoubleEndedIterator
- + Send + Sync + 'a {
match self {
TProp::Empty => TPropVariants::Empty(iter::empty()),
TProp::Str(cell) => {
@@ -332,8 +332,8 @@ impl<'a> TPropOps<'a> for &'a TProp {
fn iter_window(
self,
- r: Range
,
- ) -> impl DoubleEndedIterator- + Send + Sync + 'a {
+ r: Range
,
+ ) -> impl DoubleEndedIterator- + Send + Sync + 'a {
match self {
TProp::Empty => TPropVariants::Empty(iter::empty()),
TProp::Str(cell) => TPropVariants::Str(
@@ -403,7 +403,7 @@ impl<'a> TPropOps<'a> for &'a TProp {
}
}
- fn at(&self, ti: &TimeIndexEntry) -> Option
{
+ fn at(&self, ti: &EventTime) -> Option {
match self {
TProp::Empty => None,
TProp::Str(cell) => cell.at(ti).map(|v| Prop::Str(v.clone())),
@@ -438,11 +438,11 @@ mod tprop_tests {
let col = TPropColumn::Bool(LazyVec::from(0, true));
assert_eq!(col.get(0), Some(Prop::Bool(true)));
- let t_prop = TPropCell::new(&TCell::TCell1(TimeIndexEntry(0, 0), Some(0)), Some(&col));
+ let t_prop = TPropCell::new(&TCell::TCell1(EventTime(0, 0), Some(0)), Some(&col));
let actual = t_prop.iter().collect::>();
- assert_eq!(actual, vec![(TimeIndexEntry(0, 0), Prop::Bool(true))]);
+ assert_eq!(actual, vec![(EventTime(0, 0), Prop::Bool(true))]);
}
#[test]
diff --git a/raphtory-core/src/python/time.rs b/raphtory-core/src/python/time.rs
index 0a78c09a0d..4c988dcee9 100644
--- a/raphtory-core/src/python/time.rs
+++ b/raphtory-core/src/python/time.rs
@@ -1,12 +1,5 @@
-use crate::utils::time::{AlignmentUnit, Interval, ParseTimeError};
-use pyo3::{exceptions::PyTypeError, prelude::*, Bound, FromPyObject, PyAny, PyErr, PyResult};
-use raphtory_api::python::error::adapt_err_value;
-
-impl From for PyErr {
- fn from(value: ParseTimeError) -> Self {
- adapt_err_value(&value)
- }
-}
+use crate::utils::time::{AlignmentUnit, Interval};
+use pyo3::{exceptions::PyTypeError, prelude::*, Bound, FromPyObject, PyAny, PyResult};
impl<'source> FromPyObject<'source> for Interval {
fn extract_bound(interval: &Bound<'source, PyAny>) -> PyResult {
diff --git a/raphtory-core/src/storage/node_entry.rs b/raphtory-core/src/storage/node_entry.rs
index 23a3e5730e..ee62ac8e74 100644
--- a/raphtory-core/src/storage/node_entry.rs
+++ b/raphtory-core/src/storage/node_entry.rs
@@ -7,7 +7,7 @@ use raphtory_api::core::{
properties::{prop::Prop, tprop::TPropOps},
LayerIds,
},
- storage::timeindex::TimeIndexEntry,
+ storage::timeindex::EventTime,
Direction,
};
use std::{
@@ -109,7 +109,7 @@ impl<'a> NodePtr<'a> {
.filter_map(|(id, col)| (!col.is_empty()).then_some(id))
}
- pub fn into_rows(self) -> impl Iterator- )> {
+ pub fn into_rows(self) -> impl Iterator
- )> {
self.node
.timestamps()
.props_ts
@@ -117,7 +117,7 @@ impl<'a> NodePtr<'a> {
.map(move |(t, &row)| (*t, MemRow::new(self.t_props_log, row)))
}
- pub fn last_before_row(self, t: TimeIndexEntry) -> Vec<(usize, Prop)> {
+ pub fn last_before_row(self, t: EventTime) -> Vec<(usize, Prop)> {
self.t_props_log
.iter()
.enumerate()
@@ -130,8 +130,8 @@ impl<'a> NodePtr<'a> {
pub fn into_rows_window(
self,
- w: Range
,
- ) -> impl Iterator- )> + Send + Sync {
+ w: Range
,
+ ) -> impl Iterator- )> + Send + Sync {
let tcell = &self.node.timestamps().props_ts;
tcell
.iter_window(w)
diff --git a/raphtory-core/src/storage/raw_edges.rs b/raphtory-core/src/storage/raw_edges.rs
index 434b6785c2..b7dd07373c 100644
--- a/raphtory-core/src/storage/raw_edges.rs
+++ b/raphtory-core/src/storage/raw_edges.rs
@@ -6,7 +6,7 @@ use crate::{
use itertools::Itertools;
use lock_api::ArcRwLockReadGuard;
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
-use raphtory_api::core::{entities::EID, storage::timeindex::TimeIndexEntry};
+use raphtory_api::core::{entities::EID, storage::timeindex::EventTime};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use std::{
@@ -22,8 +22,8 @@ use std::{
pub struct EdgeShard {
edge_ids: Vec
,
props: Vec>,
- additions: Vec>>,
- deletions: Vec>>,
+ additions: Vec>>,
+ deletions: Vec>>,
}
#[must_use]
@@ -67,11 +67,11 @@ impl EdgeShard {
self.additions.len().max(self.deletions.len())
}
- pub fn additions(&self, index: usize, layer_id: usize) -> Option<&TimeIndex> {
+ pub fn additions(&self, index: usize, layer_id: usize) -> Option<&TimeIndex> {
self.additions.get(layer_id).and_then(|add| add.get(index))
}
- pub fn deletions(&self, index: usize, layer_id: usize) -> Option<&TimeIndex> {
+ pub fn deletions(&self, index: usize, layer_id: usize) -> Option<&TimeIndex> {
self.deletions.get(layer_id).and_then(|del| del.get(index))
}
@@ -245,7 +245,7 @@ impl<'a> MutEdge<'a> {
&mut self.guard.edge_ids[self.i]
}
- pub fn deletions_mut(&mut self, layer_id: usize) -> &mut TimeIndex {
+ pub fn deletions_mut(&mut self, layer_id: usize) -> &mut TimeIndex {
if layer_id >= self.guard.deletions.len() {
self.guard
.deletions
@@ -270,7 +270,7 @@ impl<'a> MutEdge<'a> {
}
false
}
- pub fn additions_mut(&mut self, layer_id: usize) -> &mut TimeIndex {
+ pub fn additions_mut(&mut self, layer_id: usize) -> &mut TimeIndex {
if layer_id >= self.guard.additions.len() {
self.guard
.additions
diff --git a/raphtory-core/src/utils/iter.rs b/raphtory-core/src/utils/iter.rs
index 9d04b0af6c..1c49f05c5a 100644
--- a/raphtory-core/src/utils/iter.rs
+++ b/raphtory-core/src/utils/iter.rs
@@ -1,5 +1,5 @@
use ouroboros::self_referencing;
-use raphtory_api::iter::{BoxedLDIter, BoxedLIter};
+pub use raphtory_api::iter::{BoxedLDIter, BoxedLIter};
#[self_referencing]
pub struct GenLockedIter<'a, O, OUT> {
diff --git a/raphtory-core/src/utils/time.rs b/raphtory-core/src/utils/time.rs
index e8396a0c33..1c8e053282 100644
--- a/raphtory-core/src/utils/time.rs
+++ b/raphtory-core/src/utils/time.rs
@@ -1,14 +1,8 @@
-use chrono::{DateTime, Datelike, Duration, Months, NaiveDate, NaiveDateTime, TimeZone};
+use chrono::{DateTime, Datelike, Duration, Months, NaiveDate};
use itertools::Itertools;
+use raphtory_api::core::{storage::timeindex::EventTime, utils::time::ParseTimeError};
use regex::Regex;
-use std::{
- convert::Infallible,
- ops::{Add, Sub},
-};
-
-use chrono::ParseError;
-use raphtory_api::core::storage::timeindex::{AsTime, TimeIndexEntry};
-use std::{num::ParseIntError, ops::Mul};
+use std::ops::{Add, Mul, Sub};
pub(crate) const SECOND_MS: i64 = 1000;
pub(crate) const MINUTE_MS: i64 = 60 * SECOND_MS;
@@ -16,160 +10,6 @@ pub(crate) const HOUR_MS: i64 = 60 * MINUTE_MS;
pub(crate) const DAY_MS: i64 = 24 * HOUR_MS;
pub(crate) const WEEK_MS: i64 = 7 * DAY_MS;
-#[derive(thiserror::Error, Debug, Clone, PartialEq)]
-pub enum ParseTimeError {
- #[error("the interval string doesn't contain a complete number of number-unit pairs")]
- InvalidPairs,
- #[error("one of the tokens in the interval string supposed to be a number couldn't be parsed")]
- ParseInt {
- #[from]
- source: ParseIntError,
- },
- #[error("'{0}' is not a valid unit. Valid units are year(s), month(s), week(s), day(s), hour(s), minute(s), second(s) and millisecond(s).")]
- InvalidUnit(String),
- #[error("'{0}' is not a valid unit. Valid units are year(s), month(s), week(s), day(s), hour(s), minute(s), second(s), millisecond(s), and unaligned.")]
- InvalidAlignmentUnit(String),
- #[error(transparent)]
- ParseError(#[from] ParseError),
- #[error("negative interval is not supported")]
- NegativeInt,
- #[error("0 size step is not supported")]
- ZeroSizeStep,
- #[error("'{0}' is not a valid datetime. Valid formats are RFC3339, RFC2822, %Y-%m-%d, %Y-%m-%dT%H:%M:%S%.3f, %Y-%m-%dT%H:%M:%S%, %Y-%m-%d %H:%M:%S%.3f and %Y-%m-%d %H:%M:%S%")]
- InvalidDateTimeString(String),
-}
-
-impl From for ParseTimeError {
- fn from(value: Infallible) -> Self {
- match value {}
- }
-}
-
-pub trait IntoTime {
- fn into_time(self) -> i64;
-}
-
-impl IntoTime for i64 {
- fn into_time(self) -> i64 {
- self
- }
-}
-
-impl IntoTime for DateTime {
- fn into_time(self) -> i64 {
- self.timestamp_millis()
- }
-}
-
-impl IntoTime for NaiveDateTime {
- fn into_time(self) -> i64 {
- self.and_utc().timestamp_millis()
- }
-}
-
-pub trait TryIntoTime {
- fn try_into_time(self) -> Result;
-}
-
-impl TryIntoTime for T {
- fn try_into_time(self) -> Result {
- Ok(self.into_time())
- }
-}
-
-impl TryIntoTime for &str {
- /// Tries to parse the timestamp as RFC3339 and then as ISO 8601 with local format and all
- /// fields mandatory except for milliseconds and allows replacing the T with a space
- fn try_into_time(self) -> Result