Repository: seeq12/seeq-correlation
Pull request #55: CRAB-48351: Correlation: Formula Package is not an element of the Add-on
Author: login: AlexDorobantiu
Created: 2025-04-07T14:07:43Z | Merged: 2025-04-11T16:57:00Z

This PR adds the formula package with the correlation formulas as an element in the add-on package.

NOTE: The formulas will be installed now with the add-on. If the add-on was previously installed, installing or updating may not work and will require manual removing of the formula package `CrossCorrelations`.

Exploratory testing notes:
I created a package with the correlation add-on using the `python build.py -a` command line. 
[correlation.addon.zip](https://github.com/user-attachments/files/19667643/correlation.addon.zip)
 Remove the .zip extension to use it.
I installed it locally and played with permissions for non admin users. The visibility seems as expected ✅

Notes for testers:
Update Cross Correlation Add-on using the package on a system which already has Cross Correlation installed.
Using AoM, uninstall the Cross Correlation Add-on from a system which already has it, and then install the new package.


Diff:
diff --git a/.gitignore b/.gitignore
index 6e5cdb5..2a74a54 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,6 @@
 /bin
 /seeq_causality.egg-info
 /.spyproject
-.idea
 .ptest_cache
 *.pyc
 *.egg-info
@@ -98,4 +97,6 @@ Generated\ Files/
 *.pdf
 
 # credential files
-*.key
\ No newline at end of file
+*.key
+
+/.venv/
\ No newline at end of file
diff --git a/README.md b/README.md
index 7d6e33f..adf813d 100644
--- a/README.md
+++ b/README.md
@@ -41,15 +41,23 @@ section [Use Cases](https://seeq12.github.io/seeq-correlation/examples.html).
 
 # Installation
 
-The backend of **seeq-correlation** requires **Python 3.7** or later.
+The easiest way to install **seeq-correlation** is to use Add-on Manager from Seeq Workbench. 
+Correlation is included in the default list of available Add-ons. Simply search for "Correlation" in the manager
+and click "Install".
+
+## Manual Installation
+If you want to install **seeq-correlation** manually or Add-on Manager is not available, you can do so by 
+following the steps below.
 
-## Dependencies
+### Dependencies
+
+The backend of **seeq-correlation** requires **Python 3.7** or later.
 
 See [`requirements.txt`](https://github.com/seeq12/seeq-correlation/tree/master/requirements.txt) file for a list of
 dependencies and versions. Additionally, you will need to install the `seeq` module with the appropriate version that
 matches your Seeq server. For more information on the `seeq` module see [seeq at pypi](https://pypi.org/project/seeq/)
 
-## User Installation Requirements (Seeq Data Lab)
+### User Installation Requirements (Seeq Data Lab)
 
 If you want to install **seeq-correlation** as a Seeq Add-on Tool, you will need:
 
@@ -58,7 +66,7 @@ If you want to install **seeq-correlation** as a Seeq Add-on Tool, you will need
 - Seeq administrator access
 - Enable Add-on Tools in the Seeq server
 
-## User Installation (Seeq Data Lab)
+### User Installation (Seeq Data Lab)
 
 The latest build of the project can be found [here](https://pypi.org/project/seeq-correlation/) as a wheel file. The
 file is published as a courtesy to the user, and it does not imply any obligation for support from the publisher.
diff --git a/addon.json b/addon.json
index d324851..8e65ea0 100644
--- a/addon.json
+++ b/addon.json
@@ -9,17 +9,25 @@
   "tags": {
     "documentation": "Documentation website here https://seeq12.github.io/seeq-correlation/introduction.html"
   },
-  "previews": ["additional_content/LargeMatrixExample.png"],
+  "previews": [
+    "additional_content/LargeMatrixExample.png"
+  ],
   "elements": [
     {
       "name": "Correlation Analysis",
       "description": "Determine cross correlations and time shifts to maximize correlations among signals",
       "identifier": "com.seeq.addon.correlation.correlation",
-      "known_aliases": ["Correlation"],
+      "known_aliases": [
+        "Correlation"
+      ],
       "type": "AddOnTool",
       "path": "data-lab-functions",
       "notebook_file_path": "correlation_analysis_master.ipynb",
-      "extensions": ["ipyvuetify", "widgetsnbextension", "ipyvue"],
+      "extensions": [
+        "ipyvuetify",
+        "widgetsnbextension",
+        "ipyvue"
+      ],
       "configuration_schema": {
         "type": "object",
         "properties": {
@@ -31,7 +39,11 @@
                 "default": "fa fa-th"
               },
               "linkType": {
-                "enum": ["window", "tab", "none"],
+                "enum": [
+                  "window",
+                  "tab",
+                  "none"
+                ],
                 "default": "window"
               },
               "sortKey": {
@@ -73,8 +85,17 @@
             ]
           }
         },
-        "required": ["display", "advanced_project_configuration"]
+        "required": [
+          "display",
+          "advanced_project_configuration"
+        ]
       }
+    },
+    {
+      "name": "CrossCorrelationAddOn",
+      "identifier": "com.seeq.addon.correlation.formulapackage",
+      "type": "FormulaPackage",
+      "path": "correlation_formulas"
     }
   ]
 }
diff --git a/build.py b/build.py
index 0d12409..1a2f851 100644
--- a/build.py
+++ b/build.py
@@ -83,7 +83,7 @@
     addon_meta = os.path.join(bin, f'{name}.addonmeta')
 
     # Build addon
-    with zipfile.ZipFile(addon, 'w') as z:
+    with zipfile.ZipFile(addon, 'w', compression=zipfile.ZIP_DEFLATED, compresslevel=9) as z:
         z.write(source_wheel, arcname=os.path.join('data-lab-functions', source_wheel_name))
         z.writestr('data-lab-functions/requirements.txt', f"./{source_wheel_name}")
         with z.open("addon.json", "w") as c:
@@ -92,12 +92,15 @@
         for file in directory.rglob('*ipynb'):
             z.write(file, arcname=os.path.join('data-lab-functions', file.name))
         directory = pathlib.Path("./additional_content/")
+        for file in directory.iterdir():
+            z.write(file)
+        directory = pathlib.Path("./correlation_formulas/")
         for file in directory.iterdir():
             z.write(file)
         addon_manager_artifacts.append(addon)
     # Build addonmeta
     print(f'Creating {name}.addonmeta')
-    with zipfile.ZipFile(addon_meta, 'w') as z:
+    with zipfile.ZipFile(addon_meta, 'w', compression=zipfile.ZIP_DEFLATED, compresslevel=9) as z:
         with z.open("addon.json", "w") as c:
             c.write(json.dumps(parsed_json, indent=2).encode("utf-8"))
         directory = pathlib.Path("./additional_content/")
diff --git a/correlation_formulas/formula_package.json b/correlation_formulas/formula_package.json
new file mode 100644
index 0000000..aa62084
--- /dev/null
+++ b/correlation_formulas/formula_package.json
@@ -0,0 +1,148 @@
+{	
+	"formulaPackage": {
+		"name": "CrossCorrelationAddOn",
+		"creatorName": "Seeq Corporation",
+		"creatorContactInfo": "support@seeq.com"
+	},
+	"functions": [
+		{
+			"id": "f253c5ca-6eed-43e0-993e-5696ecd441ea",
+			"name": "correlationCoefficient",			
+			"description": "<p>Calculates the rolling window Pearson's correlation coefficients between two signals.\nOne correlation coefficient is calculated for the data contained within each <code>window</code> of time.\nThe <code>period</code> dictates the step the <code>window</code> is moved (or rolled) for the next\ncoefficient calculation.</p>",
+			"formula": "$condition = periods($window, $period)\n$s1 = $signal1.setUnits('')\n$s2 = $signal2.setUnits('')\n// intermediate calculations\n$s1_sq = $s1^2\n$s2_sq = $s2^2\n$s1s2_product = ($s1 * $s2)\n$N = $s1.aggregate(count(), $condition, middleKey(), 0h)\n// Pearson's coefficient\n$numerator = $N * $s1s2_product.aggregate(sum(), $condition, middleKey(), 0h) - $s1.aggregate(sum(), $condition,\nmiddleKey(), 0h) * $s2.aggregate(sum(), $condition, middleKey(), 0h)\n$denominator = sqrt(($N * $s1_sq.aggregate(sum(), $condition, middleKey(), 0h) - ($s1.aggregate(sum(), $condition,\nmiddleKey(), 0h))^2) * ($N * $s2_sq.aggregate(sum(), $condition, middleKey(), 0h) - ($s2.aggregate(sum(),\n$condition, middleKey(), 0h))^2))\n($numerator / $denominator).aggregate(average(), $condition, middleKey())",
+			"type": "UserDefinedFormulaFunction",
+			"parameters": [
+				{
+					"unbound": true,
+					"name": "signal1",
+					"formula": "1.toSignal()"
+				},
+				{
+					"unbound": true,
+					"name": "signal2",
+					"formula": "1.toSignal()"
+				},
+				{
+					"unbound": true,
+					"name": "window",
+					"formula": "1h"
+				},
+				{
+					"unbound": true,
+					"name": "period",
+					"formula": "1h"
+				}
+			]
+		},
+		{
+			"id": "d32366ff-b9f0-494f-8c8e-cced58386968",
+			"name": "correlationCoefficientWithTimeShifts",			
+			"description": "<p>Calculates the rolling window maximum Pearson's correlation coefficients between two sliding signals. \nFor each <code>window</code>, the time shift that maximizes the cross-correlation between the two \nsignals is determined first. Then, the samples inside the window are time shifted \n(the <code>goal</code> signal is kept fixed and the other <code>window</code> is shifted). Finally, \nthe maximum correlation coefficient coefficient for the <code>window</code> is calculated and its value \nbecomes one sample of the output signal. The process repeats after rolling the window a given \n<code>period</code>.</p> \n<p>This formula uses the <sq-link href=\"/formulas/docs/Seeq/correlationOffset\"> \n<a href=\"\" ng-click=\"$ctrl.sqPowerSearch.requestDocumentation($ctrl.href)\" \nng-transclude=\"\">correlationOffset() </a></sq-link> formula to calculate the time shifts for each window. \nSee <sq-link href=\"/formulas/docs/Seeq/correlationOffset\"> \n<a href=\"\" ng-click=\"$ctrl.sqPowerSearch.requestDocumentation($ctrl.href)\" \nng-transclude=\"\">correlationOffset() </a></sq-link> \nfor more information on the <code>maxOffset</code> and <code>correlation_threshold</code> parameters</p> \n<p><i>Note: The <strong>correlationCoefficientWithTimeShifts</strong> assumes the <code>minOffset</code> in \n<sq-link href=\"/formulas/docs/Seeq/correlationOffset\"> \n<a href=\"\" ng-click=\"$ctrl.sqPowerSearch.requestDocumentation($ctrl.href)\" \nng-transclude=\"\">correlationOffset() </a></sq-link> to be equal to <code>-maxOffset</code>.</i></p>",
+			"formula": "$s1_o = $signal.setUnits('')\n$s2 = $goal.setUnits('')\n\n$Wind = periods($window, $period)\n$Wind.tosamples( $cap -> {\n// get the time shift to maximize cross-correlation\n$time_shift = $signal.correlationOffset($goal, group($cap), -$maxOffset , $maxOffset, $correlation_threshold)\n// shift the signal\n$s1 = $s1_o.move($time_shift)\n$s1_sq = $s1^2\n$s2_sq = $s2^2\n$s1s2_product = ($s1 * $s2)\n$N = $s1.count($cap)\n// Pearson's coefficient\n$numerator = $N * $s1s2_product.sum($cap) - $s1.sum($cap) * $s2.sum($cap)\n$denominator = sqrt(($N * $s1_sq.sum($cap) - ($s1.sum($cap))^2) * ($N * $s2_sq.sum($cap) - ($s2.sum($cap))^2))\n$pearson = ($numerator / $denominator)\nsample($cap.middleKey(),$pearson)},$window)",
+			"type": "UserDefinedFormulaFunction",
+			"parameters": [
+				{
+					"unbound": true,
+					"name": "signal",
+					"formula": "1.toSignal()"
+				},
+				{
+					"unbound": true,
+					"name": "goal",
+					"formula": "1.toSignal()"
+				},
+				{
+					"unbound": true,
+					"name": "window",
+					"formula": "1h"
+				},
+				{
+					"unbound": true,
+					"name": "period",
+					"formula": "1h"
+				},
+				{
+					"unbound": true,
+					"name": "correlation_threshold",
+					"formula": "1"
+				},
+				{
+					"unbound": true,
+					"name": "maxOffset",
+					"formula": "1h"
+				}
+			]
+		},
+		{
+			"id": "3ff61fcb-bfb2-45e0-b5e5-586c7858ca17",
+			"name": "timeShifts",			
+			"description": "<p>Calculates the rolling window \n<sq-link href=\"/formulas/docs/Seeq/correlationOffset\"> \n<a href=\"\" ng-click=\"$ctrl.sqPowerSearch.requestDocumentation($ctrl.href)\" \nng-transclude=\"\">correlationOffset() </a></sq-link> between <code>signal</code> and <code>goal</code> \nThat is, the time shift that maximizes the cross-correlation between the two signals is determined for \neach <code>window</code> using the <sq-link href=\"/formulas/docs/Seeq/correlationOffset\"> \n<a href=\"\" ng-click=\"$ctrl.sqPowerSearch.requestDocumentation($ctrl.href)\" \nng-transclude=\"\">correlationOffset() \n</a></sq-link> formula. The process repeats after rolling the window a given <code>period</code>.</p>",
+			"formula": "$Wind = periods($window, $period)\n$Wind.tosamples( $cap -> {\n$value = $signal.correlationOffset($goal,group($cap), -$maxOffset, $maxOffset, $correlation_threshold)\nsample($cap.middleKey(),$value)},$window)",
+			"type": "UserDefinedFormulaFunction",
+			"parameters": [
+				{
+					"unbound": true,
+					"name": "signal",
+					"formula": "1.toSignal()"
+				},
+				{
+					"unbound": true,
+					"name": "goal",
+					"formula": "1.toSignal()"
+				},
+				{
+					"unbound": true,
+					"name": "window",
+					"formula": "1h"
+				},
+				{
+					"unbound": true,
+					"name": "period",
+					"formula": "1h"
+				},
+				{
+					"unbound": true,
+					"name": "correlation_threshold",
+					"formula": "1"
+				},
+				{
+					"unbound": true,
+					"name": "maxOffset",
+					"formula": "1h"
+				}
+			]
+		}
+	],
+	"docs": [
+		{
+			"name": "index",
+			"description": "Computes the cross-correlations between two signals and allows signals to time shift to maximize the \ncross-correlation",
+			"title": "Cross Correlation Formula Package",
+			"examples": {
+				"examples": [
+					{
+						"description": "Calculate the Pearson's correlation coefficients between <code>$signal1</code> and \n<code>$signal2</code> during a 24 hour interval and move the window every 6 hours",
+						"formula": "CrossCorrelationAddOn_correlationCoefficient($signal1, $signal2, 24h, 6h)"
+					},
+					{
+						"description": "Calculate the maximum Pearson's correlation coefficients between <code>$signal</code> \nand <code>$goal</code> in 24 h intervals that step every 6 hours, if <code>$signal</code>\n is allowed to shift in time with respect to <code>$goal</code>",
+						"formula": "CrossCorrelationAddOn_correlationCoefficientWithTimeShifts($signal, $goal, 24h, 6h, 0.8, 2h)"
+					},
+					{
+						"description": "Calculate the dynamic time shifts to maximize cross-correlation between <code>$signal</code>\n and <code>$goal</code> in 24 h intervals  every 6 hours, if <code>$signal</code> is allowed\nto shift in time with respect to <code>$goal</code>",
+						"formula": "CrossCorrelationAddOn_timeShifts($signal, $goal, 24h, 6h, 0.8, 2h)"
+					}
+				]
+			},
+			"searchKeywords": {
+				"keywords": [
+					"cross-correlation",
+					"correlation",
+					"Pearson",
+					"offset",
+					"time shifts"
+				]
+			}			
+		}
+	]
+}
\ No newline at end of file
diff --git a/docs/_modules/seeq/addons/correlation/_seeq_formulas.html b/docs/_modules/seeq/addons/correlation/_seeq_formulas.html
index bd31226..abe79aa 100644
--- a/docs/_modules/seeq/addons/correlation/_seeq_formulas.html
+++ b/docs/_modules/seeq/addons/correlation/_seeq_formulas.html
@@ -152,7 +152,7 @@ <h1>Source code for seeq.addons.correlation._seeq_formulas</h1><div class="highl
              <span class="n">sdk</span><span class="o">.</span><span class="n">FormulaDocExampleInputV1</span><span class="p">(</span>
                  <span class="n">description</span><span class="o">=</span><span class="s2">&quot;Calculate the Pearson&#39;s correlation coefficients between &lt;code&gt;$signal1&lt;/code&gt; and &quot;</span>
                              <span class="s2">&quot;&lt;code&gt;$signal2&lt;/code&gt; during a 24 hour interval and move the window every 6 hours&quot;</span><span class="p">,</span>
-                 <span class="n">formula</span><span class="o">=</span><span class="s1">&#39;CrossCorrelations_correlationCoefficient($signal1, $signal2, 24h, 6h)&#39;</span><span class="p">)</span>
+                 <span class="n">formula</span><span class="o">=</span><span class="s1">&#39;CrossCorrelationAddOn_correlationCoefficient($signal1, $signal2, 24h, 6h)&#39;</span><span class="p">)</span>
          <span class="p">]</span>
          <span class="p">),</span>
     <span class="nb">dict</span><span class="p">(</span><span class="n">function_name</span><span class="o">=</span><span class="s1">&#39;correlationCoefficientWithTimeShifts&#39;</span><span class="p">,</span>
@@ -184,7 +184,7 @@ <h1>Source code for seeq.addons.correlation._seeq_formulas</h1><div class="highl
                  <span class="n">description</span><span class="o">=</span><span class="s2">&quot;Calculate the maximum Pearson&#39;s correlation coefficients between &lt;code&gt;$signal&lt;/code&gt; &quot;</span>
                              <span class="s2">&quot;and &lt;code&gt;$goal&lt;/code&gt; in 24 h intervals that step every 6 hours, if &lt;code&gt;$signal&lt;/code&gt;&quot;</span>
                              <span class="s2">&quot; is allowed to shift in time with respect to &lt;code&gt;$goal&lt;/code&gt;&quot;</span><span class="p">,</span>
-                 <span class="n">formula</span><span class="o">=</span><span class="s1">&#39;CrossCorrelations_correlationCoefficientWithTimeShifts($signal, $goal, 24h, 6h, 0.8, 2h)&#39;</span><span class="p">)</span>
+                 <span class="n">formula</span><span class="o">=</span><span class="s1">&#39;CrossCorrelationAddOn_correlationCoefficientWithTimeShifts($signal, $goal, 24h, 6h, 0.8, 2h)&#39;</span><span class="p">)</span>
          <span class="p">],</span>
          <span class="p">),</span>
     <span class="nb">dict</span><span class="p">(</span><span class="n">function_name</span><span class="o">=</span><span class="s1">&#39;timeShifts&#39;</span><span class="p">,</span>
@@ -212,7 +212,7 @@ <h1>Source code for seeq.addons.correlation._seeq_formulas</h1><div class="highl
 <span class="sd">                     to shift in time with respect to &lt;code&gt;$goal&lt;/code&gt;</span>
 <span class="sd">                     &quot;&quot;&quot;</span>
                  <span class="p">),</span>
-                 <span class="n">formula</span><span class="o">=</span><span class="s1">&#39;CrossCorrelations_timeShifts($signal, $goal, 24h, 6h, 0.8, 2h)&#39;</span><span class="p">)</span>
+                 <span class="n">formula</span><span class="o">=</span><span class="s1">&#39;CrossCorrelationAddOn_timeShifts($signal, $goal, 24h, 6h, 0.8, 2h)&#39;</span><span class="p">)</span>
          <span class="p">],</span>
          <span class="p">)</span>
 <span class="p">]</span>
@@ -235,9 +235,9 @@ <h1>Source code for seeq.addons.correlation._seeq_formulas</h1><div class="highl
 <span class="sd">    package.id: str</span>
 <span class="sd">        The ID of the Seeq UDF package</span>
 <span class="sd">    &quot;&quot;&quot;</span>
-    <span class="n">package_name</span> <span class="o">=</span> <span class="s1">&#39;CrossCorrelations&#39;</span>
-    <span class="n">creator_name</span> <span class="o">=</span> <span class="s1">&#39;Alberto Rivas&#39;</span>
-    <span class="n">creator_contact_info</span> <span class="o">=</span> <span class="s1">&#39;applied.research@seeq.com&#39;</span>
+    <span class="n">package_name</span> <span class="o">=</span> <span class="s1">&#39;CrossCorrelationAddOn&#39;</span>
+    <span class="n">creator_name</span> <span class="o">=</span> <span class="s1">&#39;Seeq Corporation&#39;</span>
+    <span class="n">creator_contact_info</span> <span class="o">=</span> <span class="s1">&#39;support@seeq.com&#39;</span>
     <span class="n">formulas_api</span> <span class="o">=</span> <span class="n">sdk</span><span class="o">.</span><span class="n">FormulasApi</span><span class="p">(</span><span class="n">api_client</span><span class="p">)</span>
     <span class="k">try</span><span class="p">:</span>
         <span class="n">pkg</span> <span class="o">=</span> <span class="n">formulas_api</span><span class="o">.</span><span class="n">get_package</span><span class="p">(</span><span class="n">package_name</span><span class="o">=</span><span class="n">package_name</span><span class="p">)</span>
@@ -318,7 +318,7 @@ <h1>Source code for seeq.addons.correlation._seeq_formulas</h1><div class="highl
             <span class="sa">f</span><span class="s2">&quot;&quot;&quot;</span>
 <span class="s2">            $window = </span><span class="si">{</span><span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;window&#39;</span><span class="p">]</span><span class="si">}</span>
 <span class="s2">            $period = </span><span class="si">{</span><span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;period&#39;</span><span class="p">]</span><span class="si">}</span>
-<span class="s2">            CrossCorrelations_correlationCoefficient($signal, $goal, $window, $period)</span>
+<span class="s2">            CrossCorrelationAddOn_correlationCoefficient($signal, $goal, $window, $period)</span>
 <span class="s2">        &quot;&quot;&quot;</span>
         <span class="p">)</span>
         <span class="n">signal_name_prefix</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;Correlation Coefficient:&quot;</span>
@@ -332,7 +332,7 @@ <h1>Source code for seeq.addons.correlation._seeq_formulas</h1><div class="highl
 <span class="s2">            $correlation_threshold = </span><span class="si">{</span><span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;corr_threshold&#39;</span><span class="p">]</span><span class="si">}</span>
 <span class="s2">            $max_time_shift = </span><span class="si">{</span><span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;max_time_shift&#39;</span><span class="p">]</span><span class="si">}</span>
 <span class="s2">            $time_unit = &#39;</span><span class="si">{</span><span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;output_time_unit&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">&#39;</span>
-<span class="s2">            CrossCorrelations_timeShifts($signal, $goal, $window, $period, $correlation_threshold, </span>
+<span class="s2">            CrossCorrelationAddOn_timeShifts($signal, $goal, $window, $period, $correlation_threshold, </span>
 <span class="s2">            $max_time_shift).convertUnits($time_unit)</span>
 <span class="s2">        &quot;&quot;&quot;</span>
         <span class="p">)</span>
@@ -345,7 +345,7 @@ <h1>Source code for seeq.addons.correlation._seeq_formulas</h1><div class="highl
 <span class="s2">            $period = </span><span class="si">{</span><span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;period&#39;</span><span class="p">]</span><span class="si">}</span>
 <span class="s2">            $correlation_threshold = </span><span class="si">{</span><span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;corr_threshold&#39;</span><span class="p">]</span><span class="si">}</span>
 <span class="s2">            $max_time_shift = </span><span class="si">{</span><span class="n">kwargs</span><span class="p">[</span><span class="s1">&#39;max_time_shift&#39;</span><span class="p">]</span><span class="si">}</span>
-<span class="s2">            CrossCorrelations_correlationCoefficientWithTimeShifts($signal, $goal, $window, $period, $correlation_threshold, </span>
+<span class="s2">            CrossCorrelationAddOn_correlationCoefficientWithTimeShifts($signal, $goal, $window, $period, $correlation_threshold, </span>
 <span class="s2">            $max_time_shift)</span>
 <span class="s2">        &quot;&quot;&quot;</span>
         <span class="p">)</span>
diff --git a/docs_src/source/changelog.md b/docs_src/source/changelog.md
index c7b0648..541f205 100644
--- a/docs_src/source/changelog.md
+++ b/docs_src/source/changelog.md
@@ -1,4 +1,9 @@
 # Changelog
+
+v0.3.0
+---
+Bundled the formula package element in the add-on package
+
 v0.1.96
 ---
 Bug fix for long signal name leading to duplicates dropped in heatmap plot
@@ -9,7 +14,7 @@ Minor updates to addon manager packaging
 
 v0.1.9
 ---
-Fucntions to package Correlation for use in addon manager
+Functions to package Correlation for use in addon manager
 
 v0.1.8
 ---
diff --git a/docs_src/source/conf.py b/docs_src/source/conf.py
index e2f50d9..88d2ffc 100644
--- a/docs_src/source/conf.py
+++ b/docs_src/source/conf.py
@@ -21,7 +21,7 @@
 
 project = 'seeq-correlation'
 copyright = '2021, Seeq Corporation'
-author = 'Alberto Rivas'
+author = 'Seeq Corporation'
 
 # The full version, including alpha/beta/rc tags
 version = correlation.__version__
diff --git a/seeq/addons/correlation/_seeq_formulas.py b/seeq/addons/correlation/_seeq_formulas.py
index e49d534..d8e79b8 100644
--- a/seeq/addons/correlation/_seeq_formulas.py
+++ b/seeq/addons/correlation/_seeq_formulas.py
@@ -1,6 +1,8 @@
 import textwrap
 from seeq import sdk, spy
 from seeq.sdk.rest import ApiException
+
+from . import _version
 from .utils import check_udf_package, get_user_group, get_user, DEFAULT_USERS, DEFAULT_GROUP
 
 pearson_formula = textwrap.dedent(
@@ -64,6 +66,7 @@
 
 formulas_info = [
     dict(function_name='correlationCoefficient',
+         data_id="f253c5ca-6eed-43e0-993e-5696ecd441ea", # Must match the IDs in formula_package.json
          formula=pearson_formula,
          args=[signal_input1, signal_input2, window_input, period_input],
          description=textwrap.dedent(
@@ -77,10 +80,11 @@
              sdk.FormulaDocExampleInputV1(
                  description="Calculate the Pearson's correlation coefficients between <code>$signal1</code> and "
                              "<code>$signal2</code> during a 24 hour interval and move the window every 6 hours",
-                 formula='CrossCorrelations_correlationCoefficient($signal1, $signal2, 24h, 6h)')
+                 formula='CrossCorrelationAddOn_correlationCoefficient($signal1, $signal2, 24h, 6h)')
          ]
          ),
     dict(function_name='correlationCoefficientWithTimeShifts',
+         data_id="d32366ff-b9f0-494f-8c8e-cced58386968",
          formula=pearson_timeshifted_formula,
          args=[signal_input, goal_input, window_input, period_input, corr_thrs_input, maxtimeshift_input],
          description=textwrap.dedent(
@@ -109,10 +113,11 @@
                  description="Calculate the maximum Pearson's correlation coefficients between <code>$signal</code> "
                              "and <code>$goal</code> in 24 h intervals that step every 6 hours, if <code>$signal</code>"
                              " is allowed to shift in time with respect to <code>$goal</code>",
-                 formula='CrossCorrelations_correlationCoefficientWithTimeShifts($signal, $goal, 24h, 6h, 0.8, 2h)')
+                 formula='CrossCorrelationAddOn_correlationCoefficientWithTimeShifts($signal, $goal, 24h, 6h, 0.8, 2h)')
          ],
          ),
     dict(function_name='timeShifts',
+         data_id="3ff61fcb-bfb2-45e0-b5e5-586c7858ca17",
          formula=time_shifts_formula,
          args=[signal_input, goal_input, window_input, period_input, corr_thrs_input, maxtimeshift_input],
          description=textwrap.dedent(
@@ -137,7 +142,7 @@
                      to shift in time with respect to <code>$goal</code>
                      """
                  ),
-                 formula='CrossCorrelations_timeShifts($signal, $goal, 24h, 6h, 0.8, 2h)')
+                 formula='CrossCorrelationAddOn_timeShifts($signal, $goal, 24h, 6h, 0.8, 2h)')
          ],
          )
 ]
@@ -160,40 +165,57 @@ def correlation_udfs(api_client):
     package.id: str
         The ID of the Seeq UDF package
     """
-    package_name = 'CrossCorrelations'
-    creator_name = 'Alberto Rivas'
-    creator_contact_info = 'applied.research@seeq.com'
+    package_name = 'CrossCorrelationAddOn'
+    creator_name = 'Seeq Corporation'
+    creator_contact_info = 'support@seeq.com'
+    version = _version.__version__
     formulas_api = sdk.FormulasApi(api_client)
     found = check_udf_package(package_name, api_client)
     if found:
         print(f"Overwriting CrossCorrelation package")
         formulas_api.delete_package(package_name=package_name)
 
-    # Create the Formula Package
-    package_input = sdk.FormulaPackageInputV1(creator_name=creator_name, creator_contact_info=creator_contact_info)
-    package_output = formulas_api.put_package(package_name=package_name, body=package_input)
+    # Define the Formula Package
+    import_input = sdk.FormulaPackageImportInputV1()
+    import_input.formula_package = sdk.FormulaPackageInputV1(name=package_name,
+                                                             creator_name=creator_name,
+                                                             creator_contact_info=creator_contact_info,
+                                                             version_string=version)
 
-    # Create the individual formulas
+    # Define the individual formulas
+    import_input.functions = list()
     for formula in formulas_info:
         print(f"Creating formula {formula['function_name']}")
-        create_udf_formulas(formulas_api, package_name, formula['function_name'], formula['formula'], *formula['args'])
+        udf_input = create_udf_formulas(package_name, formula['function_name'],
+                                        formula['data_id'], formula['formula'], *formula['args'])
+        import_input.functions.append(udf_input)
+
+    # Define the formula docs for the top-level and for each function
+    import_input.docs = list()
+    pkg_doc_input = formula_pkg_doc(package_name)
+    import_input.docs.append(pkg_doc_input)
+    for formula in formulas_info:
+        formula_doc_input = formula_doc(formula['function_name'], formula['description'],
+                                        sdk.FormulaDocExampleListInputV1(examples=formula['examples']))
+        import_input.docs.append(formula_doc_input)
 
-    # Create the formula docs
-    formula_pkg_docs(package_name, formulas_api)
+    # Actually create everything
+    package_output = formulas_api.import_package(body=import_input)
 
-    return package_output.id
+    return package_output.formula_package.id
 
 
-def create_udf_formulas(formulas_api, pkg_name, function_name, formula, *args):
+def create_udf_formulas(pkg_name, function_name, data_id, formula, *args):
     udf_input = sdk.FunctionInputV1(package_name=pkg_name,
                                     name=function_name,
+                                    data_id=data_id,
                                     type='UserDefinedFormulaFunction',
                                     formula=formula,
                                     parameters=list(args))
-    formulas_api.create_function(body=udf_input)
+    return udf_input
 
 
-def formula_pkg_docs(package_name, formulas_api: sdk.FormulasApi):
+def formula_pkg_doc(package_name):
     # Create formula docs
     index_title = 'Cross Correlation'
     index_description = textwrap.dedent(
@@ -202,29 +224,20 @@ def formula_pkg_docs(package_name, formulas_api: sdk.FormulasApi):
         cross-correlation
         """)
     index_doc_input = sdk.FormulaDocInputV1(
+        name='index',
         title=index_title,
         description=index_description,
         search_keywords=sdk.FormulaDocKeywordListInputV1(keywords=[
             'cross-correlation', 'correlation', 'Pearson', 'offset', 'time shifts']))
-    formulas_api.put_formula_doc(
-        package_name=package_name,
-        doc_name='index',
-        body=index_doc_input)
-
-    # Create the docs for each formula
-    for formula in formulas_info:
-        formula_docs(package_name, formula['function_name'], formula['description'],
-                     sdk.FormulaDocExampleListInputV1(examples=formula['examples']), formulas_api)
+    return index_doc_input
 
 
-def formula_docs(pkg_name, function_name, description, examples: sdk.FormulaDocExampleListInputV1,
-                 formulas_api: sdk.FormulasApi):
+def formula_doc(function_name, description, examples: sdk.FormulaDocExampleListInputV1):
     # Examples are the best way to demonstrate using the UDF.
-    doc_input = sdk.FormulaDocInputV1(description=description,
+    doc_input = sdk.FormulaDocInputV1(name=function_name,
+                                      description=description,
                                       examples=examples)
-    formulas_api.put_formula_doc(package_name=pkg_name,
-                                 doc_name=function_name,
-                                 body=doc_input)
+    return doc_input
 
 
 def signals_from_formula(signal1_id, signal_ref_id, workbook_id, formula_type=None, output_signal_name=None,
@@ -237,7 +250,7 @@ def signals_from_formula(signal1_id, signal_ref_id, workbook_id, formula_type=No
             f"""
             $window = {kwargs['window']}
             $period = {kwargs['period']}
-            CrossCorrelations_correlationCoefficient($signal, $goal, $window, $period)
+            CrossCorrelationAddOn_correlationCoefficient($signal, $goal, $window, $period)
         """
         )
         signal_name_prefix = f"Correlation Coefficient:"
@@ -251,7 +264,7 @@ def signals_from_formula(signal1_id, signal_ref_id, workbook_id, formula_type=No
             $correlation_threshold = {kwargs['corr_threshold']}
             $max_time_shift = {kwargs['max_time_shift']}
             $time_unit = '{kwargs['output_time_unit']}'
-            CrossCorrelations_timeShifts($signal, $goal, $window, $period, $correlation_threshold, 
+            CrossCorrelationAddOn_timeShifts($signal, $goal, $window, $period, $correlation_threshold, 
             $max_time_shift).convertUnits($time_unit)
         """
         )
@@ -264,7 +277,7 @@ def signals_from_formula(signal1_id, signal_ref_id, workbook_id, formula_type=No
             $period = {kwargs['period']}
             $correlation_threshold = {kwargs['corr_threshold']}
             $max_time_shift = {kwargs['max_time_shift']}
-            CrossCorrelations_correlationCoefficientWithTimeShifts($signal, $goal, $window, $period, $correlation_threshold, 
+            CrossCorrelationAddOn_correlationCoefficientWithTimeShifts($signal, $goal, $window, $period, $correlation_threshold, 
             $max_time_shift)
         """
         )
@@ -308,9 +321,9 @@ def create_udfs(api_client, *, permissions_groups: list = None, permissions_user
 
     permissions_groups = permissions_groups if permissions_groups else DEFAULT_GROUP
     permissions_users = permissions_users if permissions_users else DEFAULT_USERS
-    print("\n\nCreating CrossCorrelation UDFs...")
+    print("\n\nCreating CrossCorrelationAddOn UDFs...")
     user_groups_api = sdk.UserGroupsApi(api_client)
-    users_api = sdk.UsersApi(spy.client)
+    users_api = sdk.UsersApi(api_client)
     items_api = sdk.ItemsApi(api_client)
     pkg_id = correlation_udfs(api_client)
 
diff --git a/seeq/addons/correlation/_version.py b/seeq/addons/correlation/_version.py
index 7fd229a..0404d81 100644
--- a/seeq/addons/correlation/_version.py
+++ b/seeq/addons/correlation/_version.py
@@ -1 +1 @@
-__version__ = '0.2.0'
+__version__ = '0.3.0'
diff --git a/seeq/addons/correlation/utils/_sdl.py b/seeq/addons/correlation/utils/_sdl.py
index 740466d..65b19e5 100644
--- a/seeq/addons/correlation/utils/_sdl.py
+++ b/seeq/addons/correlation/utils/_sdl.py
@@ -97,11 +97,9 @@ def get_seeq_url():
 
     return None
 
-def check_udf_package(name="CrossCorrelations", api_client=None):
-    if api_client:
-        formulas_api = sdk.FormulasApi(api_client)
-    else:
-        formulas_api = sdk.FormulasApi(spy.client)
+def check_udf_package(name="CrossCorrelationAddOn", api_client=None):
+    client = api_client if api_client else spy.client
+    formulas_api = sdk.FormulasApi(client)
     try:
         formulas_api.get_package(package_name=name)
         return True
diff --git a/tests/test_common.py b/tests/test_common.py
index 2bbf9eb..dac0c34 100644
--- a/tests/test_common.py
+++ b/tests/test_common.py
@@ -113,8 +113,8 @@ def delete_correlation_udfs():
     r = formulas_api.get_packages()
     package_exists = False
     for item in r.items:
-        if item.name == 'CrossCorrelations':
+        if item.name == 'CrossCorrelationAddOn':
             package_exists = True
             break
     if package_exists:
-        formulas_api.delete_package(package_name='CrossCorrelations')
+        formulas_api.delete_package(package_name='CrossCorrelationAddOn')
diff --git a/tests/test_integration.py b/tests/test_integration.py
index 7eb0eab..44cb830 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -38,7 +38,7 @@ def test_create_worksheet():
 @pytest.mark.integration
 def test_create_correlation_udf_package():
     test_common.delete_correlation_udfs()
-    print("\n\nCreating CrossCorrelation UDFs...")
+    print("\n\nCreating CrossCorrelationAddOn UDFs...")
     pkg_id = correlation.correlation_udfs(spy.client)
     print(f"DONE. Created UDF package with id: {pkg_id}")
 


Reviews:
user: login: nanophd
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-07T14:48:21Z
user: login: AlexDorobantiu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T14:49:34Z
user: login: monstrorivas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T18:29:14Z
user: login: monstrorivas
body: LGTM, didn't test
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-07T18:31:01Z
user: login: monstrorivas
body: LGTM, didn't test
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-07T18:31:03Z
user: login: dakkanner
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T22:40:33Z
user: login: dakkanner
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T22:45:48Z
user: login: matthewgasbarro
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-08T15:43:43Z
user: login: matthewgasbarro
body: LGTM did not test
state: APPROVED
author_association: NONE
submitted_at: 2025-04-08T15:47:11Z
user: login: codeabit
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-08T16:12:49Z
user: login: monstrorivas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-08T17:01:08Z
user: login: monstrorivas
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-08T17:03:14Z
user: login: AlexDorobantiu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-08T17:27:13Z
user: login: AlexDorobantiu
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-09T12:42:22Z
user: login: codeabit
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-09T15:42:07Z
user: login: wizzle3d
body: There's a cell in the [`correlation_analysis_master.ipynb`](https://github.com/seeq12/seeq-correlation/blob/master/seeq/addons/correlation/deployment_notebook/correlation_analysis_master.ipynb) that checks for the old UDF and if not found, It creates it. I think that should be removed or better still, we can decide to remove the old UDF in addition to the check if found.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-10T16:34:31Z
user: login: dakkanner
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T15:24:10Z

Review comments:
diff_hunk: @@ -9,17 +9,25 @@
   "tags": {
     "documentation": "Documentation website here https://seeq12.github.io/seeq-correlation/introduction.html"
   },
-  "previews": ["additional_content/LargeMatrixExample.png"],
+  "previews": [
+    "additional_content/LargeMatrixExample.png"
+  ],
   "elements": [
     {
       "name": "Correlation Analysis",
       "description": "Determine cross correlations and time shifts to maximize correlations among signals",
       "identifier": "com.seeq.addon.correlation.correlation",
-      "known_aliases": ["Correlation"],
+      "known_aliases": [
+        "Correlation"
+      ],
       "type": "AddOnTool",
       "path": "data-lab-functions",
       "notebook_file_path": "correlation_analysis_master.ipynb",
-      "extensions": ["ipyvuetify", "widgetsnbextension", "ipyvue"],
+      "extensions": [
+        "ipyvuetify",
+        "widgetsnbextension",
+        "ipyvue"
+      ],
path: addon.json
user: login: nanophd
body: Possible follow on ticket:  Do Add-on still require 'extensions'?  I was under the impression that these are obsolete with the upgrade Jupyter. @matthewgasbarro, thoughts? 
created_at: 2025-04-07T14:46:54Z
author_association: NONE
diff_hunk: @@ -9,17 +9,25 @@
   "tags": {
     "documentation": "Documentation website here https://seeq12.github.io/seeq-correlation/introduction.html"
   },
-  "previews": ["additional_content/LargeMatrixExample.png"],
+  "previews": [
+    "additional_content/LargeMatrixExample.png"
+  ],
   "elements": [
     {
       "name": "Correlation Analysis",
       "description": "Determine cross correlations and time shifts to maximize correlations among signals",
       "identifier": "com.seeq.addon.correlation.correlation",
-      "known_aliases": ["Correlation"],
+      "known_aliases": [
+        "Correlation"
+      ],
       "type": "AddOnTool",
       "path": "data-lab-functions",
       "notebook_file_path": "correlation_analysis_master.ipynb",
-      "extensions": ["ipyvuetify", "widgetsnbextension", "ipyvue"],
+      "extensions": [
+        "ipyvuetify",
+        "widgetsnbextension",
+        "ipyvue"
+      ],
path: addon.json
user: login: AlexDorobantiu
body: we no longer require those, no. I think they are already getting removed in Ayo's PR to migrate to python 3.11
created_at: 2025-04-07T14:49:34Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,148 @@
+{	
+	"formulaPackage": {
+		"name": "CrossCorrelations",
+		"creatorName": "Alberto Rivas",
+		"creatorContactInfo": "applied.research@seeq.com"
path: correlation_formulas/formula_package.json
user: login: monstrorivas
body: I think we have to remove this email. It is or will be disabled soon
created_at: 2025-04-07T18:29:14Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,148 @@
+{	
+	"formulaPackage": {
+		"name": "CrossCorrelations",
+		"creatorName": "Alberto Rivas",
+		"creatorContactInfo": "applied.research@seeq.com"
path: correlation_formulas/formula_package.json
user: login: dakkanner
body: Just generically support@seeq.com then? 
created_at: 2025-04-07T22:40:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -6,7 +6,6 @@
 /bin
 /seeq_causality.egg-info
 /.spyproject
-.idea
path: .gitignore
user: login: dakkanner
body: Nit: Why remove `.idea`? 
created_at: 2025-04-07T22:45:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -101,6 +104,9 @@
         with z.open("addon.json", "w") as c:
             c.write(json.dumps(parsed_json, indent=2).encode("utf-8"))
         directory = pathlib.Path("./additional_content/")
+        for file in directory.iterdir():
+            z.write(file)
+        directory = pathlib.Path("./correlation_formulas/")
path: build.py
user: login: matthewgasbarro
body: nit: Is it worth to DRY up the generation of the .addon and .addonmeta files. 
created_at: 2025-04-08T15:43:42Z
author_association: NONE
diff_hunk: @@ -0,0 +1,148 @@
+{	
+	"formulaPackage": {
+		"name": "CrossCorrelations",
+		"creatorName": "Alberto Rivas",
path: correlation_formulas/formula_package.json
user: login: codeabit
body: We should consider changing the creator to a more generic "Seeq". @monstrorivas what do you think?
created_at: 2025-04-08T16:12:48Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,148 @@
+{	
+	"formulaPackage": {
+		"name": "CrossCorrelations",
+		"creatorName": "Alberto Rivas",
+		"creatorContactInfo": "applied.research@seeq.com"
path: correlation_formulas/formula_package.json
user: login: monstrorivas
body: yes
created_at: 2025-04-08T17:01:08Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,148 @@
+{	
+	"formulaPackage": {
+		"name": "CrossCorrelations",
+		"creatorName": "Alberto Rivas",
path: correlation_formulas/formula_package.json
user: login: monstrorivas
body: No, I want to be the creator! Just kidding!

I agree, maybe `Seeq Corporation`?
created_at: 2025-04-08T17:03:13Z
author_association: COLLABORATOR
diff_hunk: @@ -6,7 +6,6 @@
 /bin
 /seeq_causality.egg-info
 /.spyproject
-.idea
path: .gitignore
user: login: AlexDorobantiu
body: It's a duplicate entry (see line 45)
created_at: 2025-04-08T17:25:47Z
author_association: MEMBER
diff_hunk: @@ -101,6 +104,9 @@
         with z.open("addon.json", "w") as c:
             c.write(json.dumps(parsed_json, indent=2).encode("utf-8"))
         directory = pathlib.Path("./additional_content/")
+        for file in directory.iterdir():
+            z.write(file)
+        directory = pathlib.Path("./correlation_formulas/")
path: build.py
user: login: AlexDorobantiu
body: I'll extract a function
created_at: 2025-04-08T17:26:07Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,148 @@
+{	
+	"formulaPackage": {
+		"name": "CrossCorrelations",
+		"creatorName": "Alberto Rivas",
path: correlation_formulas/formula_package.json
user: login: AlexDorobantiu
body: I'll go for `Seeq Corporation` with the email `support@seeq.com`
created_at: 2025-04-08T17:26:37Z
author_association: MEMBER
diff_hunk: @@ -101,6 +104,9 @@
         with z.open("addon.json", "w") as c:
             c.write(json.dumps(parsed_json, indent=2).encode("utf-8"))
         directory = pathlib.Path("./additional_content/")
+        for file in directory.iterdir():
+            z.write(file)
+        directory = pathlib.Path("./correlation_formulas/")
path: build.py
user: login: AlexDorobantiu
body: actually, add-on metadata doesn't need the correlation formula
created_at: 2025-04-09T12:42:21Z
author_association: MEMBER

PR comments:
user: login: dakkanner
created_at: 2025-04-07T22:59:44Z
body: When installing to Develop where 0.2.0 had already been installed, I received this error. Maybe we should switch the package name to `CrossCorrelation` going forward or update AoM to handle this case? 
```
Error summary:
A formula package with the same name 'CrossCorrelations' is already installed
Error updating add-on with identifier com.seeq.addon.correlation. 
```
author_association: CONTRIBUTOR
user: login: AlexDorobantiu
created_at: 2025-04-08T14:23:36Z
body: > When installing to Develop where 0.2.0 had already been installed, I received this error. Maybe we should switch the package name to `CrossCorrelation` going forward or update AoM to handle this case?

This is what I expected when the Add-on was already installed. I am happy with renaming the package to CrossCorrelation and this will prevent the error from happening on update or uninstall/install. I wonder who should get the final decision on this?
author_association: MEMBER
user: login: matthewgasbarro
created_at: 2025-04-08T15:37:26Z
body: > I am happy with renaming the package to CrossCorrelation

If the package is renamed, does the original package `CrossCorrelations` get removed or is it orphaned?
author_association: NONE
user: login: AlexDorobantiu
created_at: 2025-04-08T15:39:33Z
body: > If the package is renamed, does the original package `CrossCorrelations` get removed or is it orphaned?

The original will be orphaned in the sense that it's going to be there without any purpose, visible to only those who used to see it previously. It will have no connection to the add-on any more.
author_association: MEMBER
user: login: dakkanner
created_at: 2025-04-10T21:47:50Z
body: Exploratory testing notes:

I tested including the changes in https://github.com/seeq12/seeq-correlation/pull/56

Starting from 0.2.0 via AoM. I was able to upgrade both to the WHL version and the ADDON version of this and I verified that several of the mathmatical functions worked. I did encounter https://seeq.atlassian.net/browse/CRAB-48431 when going from the 0.2.0 .ADDON to the 0.3.0 .ADDON, but a basic uninstall fixed the issue. 
author_association: CONTRIBUTOR