Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
b7b1683
Update assembly file version to 9.3.0.0
BHoMBot Jul 6, 2026
86e33a6
Start of milestone changes for 9.3 (#223)
sakanni Jul 9, 2026
0ea01ae
build dir if not exists + migration recovery for the long lost logs
Felix-Mallinder Jul 16, 2026
7eabb79
shuffled order of events; ensure we only migrate if the main logs fol…
Felix-Mallinder Jul 16, 2026
d3ddacf
Fix missing logs folder bug (#226)
Felix-Mallinder Jul 16, 2026
2a20b4c
fix typos and add __Time__ field
Tom-Kingstone Jul 20, 2026
9c0a8da
use shorthand BHoM version and add _bhomVersion field to be more cons…
Tom-Kingstone Jul 20, 2026
236eafc
use left split instead of right split to be more robust
Tom-Kingstone Jul 21, 2026
b3cb7cc
Apply suggestion from @Tom-Kingstone
Tom-Kingstone Jul 21, 2026
1b2324c
Fix typos in log record summarisation, and add __Time__ field. (#227)
Tom-Kingstone Jul 22, 2026
4d8a777
add some unit enums with converters
Tom-Kingstone Jul 23, 2026
0425544
fix conversions for speed
Tom-Kingstone Jul 24, 2026
7204ab3
remove partial example in favour of doing it in future
Tom-Kingstone Jul 24, 2026
8a66544
add area and power units
Tom-Kingstone Jul 24, 2026
448e208
minor order of operations wrangling
Tom-Kingstone Jul 24, 2026
a7f1e0c
Unit converters (#231)
Tom-Kingstone Jul 28, 2026
c1d20d3
make median and mean lines optional
Tom-Kingstone Jul 28, 2026
4bf1ff2
make quantile_range an explicit key word argument and document it
Tom-Kingstone Jul 29, 2026
6288387
fix redefining variables
Tom-Kingstone Aug 10, 2026
c48dfa7
Add ability to specify median and mean lines for diurnal plots (#232)
jamesramsden-bh Aug 12, 2026
a7cc69e
upgraded calendar widget for rigid sizing
Felix-Mallinder Aug 12, 2026
ebc9f19
Added bhom base child window, and refactored dependant windows
Felix-Mallinder Aug 12, 2026
c597e7e
BHoM Tkinter Child Windows (#238)
Felix-Mallinder Aug 13, 2026
ee84dd7
upgraded calendar widget for rigid sizing (#237)
Felix-Mallinder Aug 13, 2026
abe3501
trimmed some fat from the styling sizing
Felix-Mallinder Aug 21, 2026
d12260f
added binning to heatmap to ensure bounds include full series
Felix-Mallinder Aug 21, 2026
9199050
added binning to heatmap to ensure bounds include full series (#242)
Felix-Mallinder Aug 21, 2026
6b24873
ci: onboard new CI checks (beta tier)
sakanni Aug 23, 2026
b5bb0ca
added python version
Felix-Mallinder Aug 26, 2026
c244b7c
add pythonnet as a dependency and make sure that c# is only called in…
Tom-Kingstone Aug 26, 2026
082bc2f
Onboard new CI checks (beta tier) (#244)
Tom-Kingstone Aug 26, 2026
873e88e
make bhom path directory agnostic
Tom-Kingstone Aug 26, 2026
7c4521d
Re-styled bhom kinter UIs (#246)
Felix-Mallinder Aug 27, 2026
32437f9
added bhom version to bhom tkinter UIs (#245)
Felix-Mallinder Aug 27, 2026
d566b5f
start with a simple copy of BH.oM.BHoMObject with some simple helper …
Tom-Kingstone May 21, 2026
2c32f57
further develop BHoMObject class and helper methods, and add some tests
Tom-Kingstone May 26, 2026
fb4cc5d
refine tests a little and add debug and slightly improve an error mes…
Tom-Kingstone May 26, 2026
f448e2f
added extra class to handle non bhom objects serialised by the bhom s…
Tom-Kingstone May 28, 2026
aa0a241
added warning if mismatched bhom versions
Tom-Kingstone May 28, 2026
19b9ef2
I hate not having an interpreter available
Tom-Kingstone May 28, 2026
0c08130
use two version numbers instead of one :)
Tom-Kingstone May 28, 2026
6ffbddf
improve error messages and fix bugs found by new test scripts/methods…
Tom-Kingstone Jun 1, 2026
6e43f8f
add from and to_dict methods to IObject to enable subclasses to use d…
Tom-Kingstone Jun 1, 2026
59279c0
save point as changing laptops
Tom-Kingstone Jun 2, 2026
cef5c1b
add a few default converters to json format for common non-serialisab…
Tom-Kingstone Jun 2, 2026
12fc88b
add extra method called by both decoder and encoder to more easily al…
Tom-Kingstone Jun 4, 2026
477b9a9
minor edits
Tom-Kingstone Jun 16, 2026
8f273c0
Add decorator for bhom callable methods, unfinished
Tom-Kingstone Jul 2, 2026
227c5fd
change decorator to also record which methods have been decorated (wh…
Tom-Kingstone Jul 31, 2026
9c19842
Apply suggestion from @Tom-Kingstone
Tom-Kingstone Aug 12, 2026
de03cc9
throw error if a command can't be found instead of returning None
Tom-Kingstone Aug 18, 2026
3ce420d
fix date serialisation to/from bhom
Tom-Kingstone Aug 18, 2026
eaddefe
fix double adding segments for camel case conversion, and make decora…
Tom-Kingstone Aug 24, 2026
941979c
update dockerfile to include aspdotnet and BHoM assemblies folder
Tom-Kingstone Sep 3, 2026
91ba94f
Add BHoMObject class to python (#233)
jamesramsden-bh Sep 8, 2026
8fbed44
add UpdateBHoMPackages method
Tom-Kingstone Sep 9, 2026
f201b60
add documentation attributes
Tom-Kingstone Sep 9, 2026
2e207eb
copyright compliance
Tom-Kingstone Sep 9, 2026
607e4bd
check executable directly instead.
Tom-Kingstone Sep 9, 2026
8d38f59
use pip based check for dependencies to be updated
Tom-Kingstone Sep 9, 2026
81699b2
ensure that when getting environments, they are fully updated.
Tom-Kingstone Sep 10, 2026
41c39a8
remove carriage return from stdout before reading it as a json array
Tom-Kingstone Sep 10, 2026
f8a63a6
Added UpdateBHoMPackages method to update missing/old python packages…
jamesramsden-bh Sep 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions .github/workflows/ci-beta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Beta-tier proxy, BHoM variant. Bundles the tier's checks plus
# copyright-compliance (BHoM enforces OSS copyright; the BHE variant omits it).
# Copy to .github/workflows/ci-beta.yml in each BHoM beta repo.
# Runs on pull_request, non-blocking until a ruleset requires its checks.
# Workflow name CI keeps check contexts bare: required contexts are the job names.

name: CI

on:
pull_request:
branches:
- develop

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true

jobs:
ci-build:
runs-on: windows-2025-vs2026
timeout-minutes: 30
steps:
- name: Run build
uses: BHoM/CI_Toolkit/.github/actions/ci-build@develop
with:
app_id: ${{ secrets.BHOM_APP_ID }}
private_key: ${{ secrets.BHOM_APP_PRIVATE_KEY }}

ci-code-compliance:
runs-on: windows-2025-vs2026
timeout-minutes: 20
permissions:
contents: read
pull-requests: read
actions: write
steps:
- name: Run code compliance
uses: BHoM/CI_Toolkit/.github/actions/ci-compliance@develop
with:
check_type: code
patterns: '*.cs'
app_id: ${{ secrets.BHOM_APP_ID }}
private_key: ${{ secrets.BHOM_APP_PRIVATE_KEY }}

ci-copyright-compliance:
runs-on: windows-2025-vs2026
timeout-minutes: 20
permissions:
contents: read
pull-requests: read
actions: write
steps:
- name: Run copyright compliance
uses: BHoM/CI_Toolkit/.github/actions/ci-compliance@develop
with:
check_type: copyright
patterns: '*.cs'
app_id: ${{ secrets.BHOM_APP_ID }}
private_key: ${{ secrets.BHOM_APP_PRIVATE_KEY }}

ci-dataset-compliance:
runs-on: windows-2025-vs2026
timeout-minutes: 20
permissions:
contents: read
pull-requests: read
actions: write
steps:
- name: Run dataset compliance
uses: BHoM/CI_Toolkit/.github/actions/ci-compliance@develop
with:
check_type: dataset
patterns: ':(icase)*datasets*.json'
app_id: ${{ secrets.BHOM_APP_ID }}
private_key: ${{ secrets.BHOM_APP_PRIVATE_KEY }}

ci-documentation-compliance:
runs-on: windows-2025-vs2026
timeout-minutes: 20
permissions:
contents: read
pull-requests: read
actions: write
steps:
- name: Run documentation compliance
uses: BHoM/CI_Toolkit/.github/actions/ci-compliance@develop
with:
check_type: documentation
patterns: '*.cs'
app_id: ${{ secrets.BHOM_APP_ID }}
private_key: ${{ secrets.BHOM_APP_PRIVATE_KEY }}

ci-project-compliance:
runs-on: windows-2025-vs2026
timeout-minutes: 20
permissions:
contents: read
pull-requests: read
actions: write
steps:
- name: Run project compliance
uses: BHoM/CI_Toolkit/.github/actions/ci-compliance@develop
with:
check_type: project
patterns: '*AssemblyInfo.cs *.csproj'
app_id: ${{ secrets.BHOM_APP_ID }}
private_key: ${{ secrets.BHOM_APP_PRIVATE_KEY }}

ci-dataset-tests:
runs-on: windows-2025-vs2026
timeout-minutes: 30
steps:
- name: Run dataset tests
uses: BHoM/CI_Toolkit/.github/actions/ci-dataset-tests@develop
with:
app_id: ${{ secrets.BHOM_APP_ID }}
private_key: ${{ secrets.BHOM_APP_PRIVATE_KEY }}

ci-serialisation:
runs-on: windows-2025-vs2026
timeout-minutes: 90
steps:
- name: Run serialisation
uses: BHoM/CI_Toolkit/.github/actions/ci-serialisation@develop
with:
app_id: ${{ secrets.BHOM_APP_ID }}
private_key: ${{ secrets.BHOM_APP_PRIVATE_KEY }}
base_ref: ${{ github.base_ref }}

ci-versioning:
runs-on: windows-2025-vs2026
timeout-minutes: 90
steps:
- name: Run versioning
uses: BHoM/CI_Toolkit/.github/actions/ci-versioning@develop
with:
app_id: ${{ secrets.BHOM_APP_ID }}
private_key: ${{ secrets.BHOM_APP_PRIVATE_KEY }}
6 changes: 5 additions & 1 deletion Python_Engine/Compute/BasePythonEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ public static PythonEnvironment BasePythonEnvironment(
bool exists = File.Exists(targetExecutable);

if (exists && reload)
return new PythonEnvironment() { Name = Query.ToolkitName(), Executable = targetExecutable };
{
PythonEnvironment env = new PythonEnvironment() { Name = Query.ToolkitName(), Executable = targetExecutable };
UpdateBHoMPackages(env);
return env;
}

if (exists && !reload)
// remove all existing environments and kernels
Expand Down
110 changes: 110 additions & 0 deletions Python_Engine/Compute/UpdateBHoMPackages.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2026, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using BH.oM.Base;
using BH.oM.Base.Attributes;
using BH.oM.Python;
using BH.oM.Python.Enums;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;

namespace BH.Engine.Python
{
public static partial class Compute
{
[Description("Using the environment name (and version if the environment name is the base python environment), directly install/update missing BHoM packages.")]
[Input("environmentName", "The name of the environment to update.")]
[Input("version", "If the environment name is the name of the base python environment, the version that needs updating.")]
public static void UpdateBHoMPackages(this string environmentName, PythonVersion version = PythonVersion.Undefined)
{
//construct the expected PythonEnvironment and pass to UpdateBHoMPackages
PythonEnvironment env = new PythonEnvironment()
{
Name = environmentName,
};

//python version is only used if the environment name is this toolkits name.
if (environmentName == Query.ToolkitName())
{
if (version == PythonVersion.Undefined)
{
BH.Engine.Base.Compute.RecordError("The version must be specified when updating BHoM packages for a base python environment. No updates have occurred.");
return;
}

env.Executable = Path.Combine(Query.DirectoryBaseEnvironment(version), "python.exe");
}
else
env.Executable = Query.VirtualEnvironmentExecutable(environmentName);

UpdateBHoMPackages(env);
}

/***************************************************/

[Description("Using a PythonEnvironment, directly install/update missing BHoM packages.")]
[Input("environment", "The python environment to update.")]
public static void UpdateBHoMPackages(this PythonEnvironment environment)
{
if (!File.Exists(environment.Executable))
{
BH.Engine.Base.Compute.RecordError($"Given environment or base install {environment.Executable} does not exist.");
return;
}

//`python -m pip list -e --format json` lists all environment packages that are editable installs.
//As all BHoM packages are editable installs, this handily lists all the packages that should be updated if necessary.
//side effect of updating other editable installs if they have changed, but I suspect this won't be a problem as anyone who has these would update packages manually instead.
System.Diagnostics.Process process = new System.Diagnostics.Process()
{
StartInfo = new System.Diagnostics.ProcessStartInfo()
{
FileName = environment.Executable,
Arguments = $"-m pip list -e --format json",
UseShellExecute = false,
RedirectStandardOutput = true,
}
};

process.StartInfo.Environment["PYTHONHOME"] = "";
string stdOut;

using (Process p = Process.Start(process.StartInfo))
{
stdOut = p.StandardOutput.ReadToEnd();
p.WaitForExit();
}

stdOut = stdOut.TrimEnd('\r', '\n');

IEnumerable<CustomObject> objs = Serialiser.Convert.FromJsonArray(stdOut).OfType<CustomObject>();

foreach (CustomObject obj in objs)
InstallPackageLocal(environment, (string)obj.CustomData["editable_project_location"]);

Check warning on line 107 in Python_Engine/Compute/UpdateBHoMPackages.cs

View check run for this annotation

BHoMBot-CI / beta-code-compliance

Python_Engine/Compute/UpdateBHoMPackages.cs#L106-L107

The use of CustomData within the code is discouraged except in circumstances where volatile data is being used. - For more information see https://bhom.xyz/documentation/DevOps/Code%20Compliance%20and%20CI/Compliance%20Checks/IsUsingCustomData
}
}
}
6 changes: 5 additions & 1 deletion Python_Engine/Compute/VirtualEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ public static PythonEnvironment VirtualEnvironment(this PythonVersion version, s
bool exists = Query.VirtualEnvironmentExists(name);

if (exists && reload)
return new PythonEnvironment() { Name = name, Executable = targetExecutable };
{
PythonEnvironment env = new PythonEnvironment() { Name = name, Executable = targetExecutable };
UpdateBHoMPackages(env);
return env;
}

if (exists && !reload)
{
Expand Down
17 changes: 16 additions & 1 deletion Python_Engine/Python/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
FROM python:3.10-slim

COPY . /Python_Toolkit

#requires build argument --build-context bhom-assemblies C:/ProgramData/BHoM/Assemblies
COPY --from=bhom-assemblies . /bin/BHoM/Assemblies

#install .net core 8.0, and set the pythonnet runtime to the .net core CLR
RUN apt-get update \
&& apt-get install -y wget

RUN wget https://packages.microsoft.com/config/debian/13/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
RUN dpkg -i packages-microsoft-prod.deb
RUN rm packages-microsoft-prod.deb

RUN apt-get update \
&& apt-get install -y aspnetcore-runtime-10.0

ENV PYTHONNET_RUNTIME="coreclr"

RUN pip3 install ./Python_Toolkit

RUN rm ./Python_Toolkit -rf
1 change: 1 addition & 0 deletions Python_Engine/Python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies = [
"pytest-cov>=6.0.0",
"pytest-order",
"virtualenv",
"pythonnet",
]

[urls]
Expand Down
25 changes: 22 additions & 3 deletions Python_Engine/Python/src/python_toolkit/bhom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@
import tempfile
import importlib.metadata

if os.name == 'nt':
from .installer_info import INSTALLER_INFO
else:
INSTALLER_INFO = {"Version": importlib.metadata.version("python_toolkit")}

BHOM_LOG_FOLDER = Path(path.expandvars("%PROGRAMDATA%/BHoM/Logs"))
TEMP_LOG_FOLDER = Path(tempfile.gettempdir()) / "BHoM" / "Logs"
TOOLKIT_NAME = "Python_Toolkit"
BHOM_VERSION = importlib.metadata.version("python_toolkit")
BHOM_VERSION = INSTALLER_INFO["Version"]

#Environment variable that if set disables BHoM analytics logging.
DISABLE_ANALYTICS = os.environ.get("DISABLE_BHOM_ANALYTICS", None)
Expand All @@ -18,5 +24,18 @@
DISABLE_ANALYTICS = True

if not BHOM_LOG_FOLDER.exists():
BHOM_LOG_FOLDER = Path(tempfile.gettempdir()) / "BHoM" / "Logs"
BHOM_LOG_FOLDER.mkdir(exist_ok=True, parents=True)

try:
BHOM_LOG_FOLDER.mkdir(exist_ok=True, parents=True)

#migration recovery for any logs in the temp folder
if TEMP_LOG_FOLDER.exists():
for file in TEMP_LOG_FOLDER.glob("*.log"):
file.rename(BHOM_LOG_FOLDER / file.name)

except Exception as e:
BHOM_LOG_FOLDER = TEMP_LOG_FOLDER
BHOM_LOG_FOLDER.mkdir(exist_ok=True, parents=True)



14 changes: 9 additions & 5 deletions Python_Engine/Python/src/python_toolkit/bhom/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def summarise_usage_logs(usage_log_entries:List[UsageLogEntry]) -> List[Dict]:

usage_log_entries.sort(key=lambda x: x.ProjectID)

short_bhom_version = ".".join(BHOM_VERSION.split(".", 2)[0:2])

for file_id, filegroup in groupby(usage_log_entries, lambda x: x.FileId):
filegroup = list(filegroup)
project_id = filegroup[0].ProjectID
Expand All @@ -89,15 +91,17 @@ def summarise_usage_logs(usage_log_entries:List[UsageLogEntry]) -> List[Dict]:
"CallerName": first_entry.CallerName,
"SelectedItem": first_entry.SelectedItem,
"Computer": socket.gethostname(),
"UserName": os.environ.get("USERNAME"),
"BHoMVersion": BHOM_VERSION,
"Username": os.environ.get("USERNAME"),
"BHoMVersion": short_bhom_version,
"FileId": file_id,
"FileName": filename,
"ProjectID": project_id,
"NbCallingComponents": len(set([a.ComponentId for a in methodgroup])),
"TotalNbCals": len(methodgroup),
"TotalNbCalls": len(methodgroup),
"Errors": list(itertools.chain.from_iterable([x.Errors for x in methodgroup])),
"_t": "BH.oM.BHoMAnalytics.UsageEntry"
"_t": "BH.oM.BHoMAnalytics.UsageEntry",
"__Time__": datetime.now(),
"_bhomVersion": short_bhom_version
})

return db_entries
Expand Down Expand Up @@ -150,7 +154,7 @@ def decorator(function: Callable):
@wraps(function)
def wrapper(*args, **kwargs) -> Any:
"""A wrapper around the function that captures usage analytics."""

if disable:
CONSOLE_LOGGER.debug("bhom_analytics is curently disabled.")
return function(*args, **kwargs)
Expand Down
Loading