10 Commits

Author SHA1 Message Date
oguzhankoral b148e50438 Mark template function as success 2024-11-13 20:59:49 +00:00
oguzhankoral 92d8303834 Remove business logic 2024-11-13 14:20:08 +00:00
oguzhankoral 1c8fe8927e Fix typo
build and deploy Speckle functions / publish-automate-function-version (push) Has been cancelled
2024-11-13 09:25:49 +00:00
oguzhankoral b2b8791d28 Replace window dict with wall
build and deploy Speckle functions / publish-automate-function-version (push) Has been cancelled
2024-11-13 09:17:33 +00:00
oguzhankoral cbfdd67306 Fix the condition
build and deploy Speckle functions / publish-automate-function-version (push) Has been cancelled
2024-11-13 09:09:05 +00:00
oguzhankoral a62633974f Clean up
build and deploy Speckle functions / publish-automate-function-version (push) Has been cancelled
2024-11-12 16:19:17 +00:00
oguzhankoral c04328bfa4 Fix enum
build and deploy Speckle functions / publish-automate-function-version (push) Has been cancelled
2024-11-09 23:19:29 +00:00
oguzhankoral 63e96f93a1 Polish
build and deploy Speckle functions / publish-automate-function-version (push) Has been cancelled
2024-11-09 23:08:35 +00:00
oguzhankoral e8d7a936c2 WIP 2024-11-09 19:38:53 +00:00
oguzhankoral 51ad4a8ff9 Test enum function input
build and deploy Speckle functions / publish-automate-function-version (push) Has been cancelled
2024-11-01 18:07:15 +03:00
7 changed files with 390 additions and 44 deletions
+150 -19
View File
@@ -3,6 +3,16 @@ using Speckle.Automate.Sdk;
using Speckle.Automate.Sdk.Schema;
using Speckle.Core.Models;
using Speckle.Core.Models.Extensions;
using Speckle.Core.Models.GraphTraversal;
namespace TestAutomateFunction;
public enum SpeckleType
{
Wall,
Window,
Roof,
}
public static class AutomateFunction
{
@@ -13,35 +23,156 @@ public static class AutomateFunction
{
Console.WriteLine("Starting execution");
_ = typeof(ObjectsKit).Assembly; // INFO: Force objects kit to initialize
var threshold = 0.15;
Console.WriteLine("Receiving version");
var commitObject = await automationContext.ReceiveVersion();
var values = commitObject
.Flatten()
.Where(b => b.speckle_type == "Objects.BuiltElements.Revit.RevitElement" && (string)b["category"]! == "Windows")
.Select(GetThermalResistance);
// 0- Get climate zone from function inputs
var failedObjectIds = values.Where(val => val.value > threshold).Select(v => v.id).ToList();
// 1- Receive version from automation context
if (failedObjectIds.Count > 0)
// 2- Flatten the objects in received root object
// 3- Get the objects we need
// 4- Check the compliance for given object types
// 5- Attach report to failed objects to be able to highlight them in viewer or Revit connector
// 6- Report the automation result as SUCCESS/FAIL
automationContext.MarkRunSuccess(
"We are going to fail successfully in a bit, don't worry!"
);
}
private static void AttachReportToFailedObjects(
AutomationContext automationContext,
IEnumerable<ObjectToCheck> failedObjects
)
{
foreach (var failedObject in failedObjects)
{
automationContext.AttachResultToObjects(ObjectResultLevel.Error, "test", failedObjectIds);
automationContext.MarkRunFailed("FAILED");
var speckleTypeString = failedObject.SpeckleType.ToString();
string message = "";
if (failedObject.UValue == 0)
{
message =
$"{speckleTypeString} has no any material that have thermal properties.";
}
else
{
message =
$"{speckleTypeString} expected to have maximum {failedObject.ExpectedUValue} U-value but it is {failedObject.UValue}.";
}
automationContext.AttachResultToObjects(
ObjectResultLevel.Error,
speckleTypeString,
new[] { failedObject.Id },
message
);
}
}
private static void ReportStatus(
AutomationContext automationContext,
FunctionInputs functionInputs,
int numberOfWalls,
int numberOfFailedWalls,
int numberOfWindows,
int numberOfFailedWindows,
int numberOfRoofs,
int numberOfFailedRoofs
)
{
if (numberOfFailedWalls + numberOfFailedWindows + numberOfFailedRoofs > 0)
{
var message = "";
if (true) // TODO: Check the whether walls included or not from function inputs
{
message += "WALLS:\n";
if (numberOfWalls > 0)
{
message += $"{numberOfFailedWalls}/{numberOfWalls} wall(s) failed.\n";
}
else
{
message += "There are no walls\n\n";
}
}
if (true) // TODO: Check the whether windows included or not from function inputs
{
message += "WINDOWS:\n";
if (numberOfWindows > 0)
{
message += $"{numberOfFailedWindows}/{numberOfWindows} window(s) failed.\n";
}
else
{
message += "There are no windows\n\n";
}
}
if (true) // TODO: Check the whether roofs included or not from function inputs
{
message += "ROOFS:\n";
if (numberOfRoofs > 0)
{
message += $"{numberOfFailedRoofs}/{numberOfRoofs} roof(s) failed.\n";
}
else
{
message += "There are no roofs\n\n";
}
}
automationContext.MarkRunFailed(message);
}
else
{
automationContext.MarkRunSuccess($"SUCCESS");
automationContext.MarkRunSuccess(
$"Your building is compliant with selected climate zone!"
);
}
}
private static (string id, double value) GetThermalResistance(Base obj)
private static double GetExpectedValue(
ClimateZone climateZone,
SpeckleType speckleType
)
{
var properties = obj["properties"] as Dictionary<string, object>;
var typeParameters = properties!["Type Parameters"] as Dictionary<string, object>;
var analyticalProperties = typeParameters!["Analytical Properties"] as Dictionary<string, object>;
var thermalResistance = analyticalProperties!["Thermal Resistance (R)"] as Dictionary<string, object>;
var value = thermalResistance!["value"] is double ? (double)thermalResistance!["value"] : 0;
return (obj.id, value);
switch (speckleType)
{
case SpeckleType.Wall:
return UValues.Wall[climateZone];
case SpeckleType.Window:
return UValues.Window[climateZone];
case SpeckleType.Roof:
return UValues.Roof[climateZone];
default:
return 0;
}
}
private static IEnumerable<ObjectToCheck> CheckCompliance(
IEnumerable<Base> objects,
ClimateZone climateZone,
SpeckleType speckleType
)
{
var expectedValue = GetExpectedValue(climateZone, speckleType);
var objectsToCheck = objects.Select(o => new ObjectToCheck(
o,
expectedValue,
speckleType
));
return objectsToCheck.Where(obj => obj.UValue > expectedValue || obj.UValue == 0);
}
private static ClimateZone GetClimateZone(string climateZoneString)
{
if (Enum.TryParse(climateZoneString, out ClimateZone climateZoneEnum))
{
return climateZoneEnum;
}
// Handle the case where the ClimateZone string is not a valid ClimateZones value
throw new ArgumentException($"Invalid ClimateZone: {climateZoneString}");
}
}
+143
View File
@@ -0,0 +1,143 @@
namespace TestAutomateFunction;
public enum ClimateZone
{
// Tropical Climates
Af_TropicalRainforest,
Am_TropicalMonsoon,
Aw_TropicalSavanna,
As_TropicalSavanna,
// Dry Climates
BWh_HotDesert,
BWk_ColdDesert,
BSh_HotSemiArid,
BSk_ColdSemiArid,
// Temperate Climates
Cfa_HumidSubtropical,
Cfb_Oceanic,
Cfc_SubpolarOceanic,
Csa_MediterraneanHotSummer,
Csb_MediterraneanWarmSummer,
Csc_MediterraneanCoolSummer,
// Continental Climates
Dfa_HumidContinentalHotSummer,
Dfb_HumidContinentalMildSummer,
Dfc_Subarctic,
Dfd_SubarcticExtremeWinter,
Dsa_MediterraneanInfluenceSnowyWinter,
Dsb_MediterraneanInfluenceSnowyWinter,
Dsc_MediterraneanInfluenceSnowyWinter,
Dsd_MediterraneanInfluenceSnowyWinter,
// Polar Climates
ET_Tundra,
EF_IceCap,
}
public static class UValues
{
public static Dictionary<ClimateZone, double> Window =>
new()
{
// Tropical Climates
{ ClimateZone.Af_TropicalRainforest, 0.9 },
{ ClimateZone.Am_TropicalMonsoon, 1.0 },
{ ClimateZone.Aw_TropicalSavanna, 1.1 },
{ ClimateZone.As_TropicalSavanna, 1.1 },
// Dry Climates
{ ClimateZone.BWh_HotDesert, 1.0 },
{ ClimateZone.BWk_ColdDesert, 1.2 },
{ ClimateZone.BSh_HotSemiArid, 1.2 },
{ ClimateZone.BSk_ColdSemiArid, 1.5 },
// Temperate Climates
{ ClimateZone.Cfa_HumidSubtropical, 1.4 },
{ ClimateZone.Cfb_Oceanic, 1.3 },
{ ClimateZone.Cfc_SubpolarOceanic, 1.2 },
{ ClimateZone.Csa_MediterraneanHotSummer, 1.51 },
{ ClimateZone.Csb_MediterraneanWarmSummer, 1.4 },
{ ClimateZone.Csc_MediterraneanCoolSummer, 1.3 },
// Continental Climates
{ ClimateZone.Dfa_HumidContinentalHotSummer, 1.3 },
{ ClimateZone.Dfb_HumidContinentalMildSummer, 1.2 },
{ ClimateZone.Dfc_Subarctic, 0.7 },
{ ClimateZone.Dfd_SubarcticExtremeWinter, 0.6 },
{ ClimateZone.Dsa_MediterraneanInfluenceSnowyWinter, 1.2 },
{ ClimateZone.Dsb_MediterraneanInfluenceSnowyWinter, 1.1 },
{ ClimateZone.Dsc_MediterraneanInfluenceSnowyWinter, 0.9 },
{ ClimateZone.Dsd_MediterraneanInfluenceSnowyWinter, 0.8 },
// Polar Climates
{ ClimateZone.ET_Tundra, 0.5 },
{ ClimateZone.EF_IceCap, 0.4 },
};
public static Dictionary<ClimateZone, double> Wall =>
new()
{
// Tropical Climates
{ ClimateZone.Af_TropicalRainforest, 0.8 },
{ ClimateZone.Am_TropicalMonsoon, 0.8 },
{ ClimateZone.Aw_TropicalSavanna, 0.9 },
{ ClimateZone.As_TropicalSavanna, 0.9 },
// Dry Climates
{ ClimateZone.BWh_HotDesert, 0.7 },
{ ClimateZone.BWk_ColdDesert, 0.9 },
{ ClimateZone.BSh_HotSemiArid, 0.8 },
{ ClimateZone.BSk_ColdSemiArid, 0.85 },
// Temperate Climates
{ ClimateZone.Cfa_HumidSubtropical, 0.6 },
{ ClimateZone.Cfb_Oceanic, 0.7 },
{ ClimateZone.Cfc_SubpolarOceanic, 0.75 },
{ ClimateZone.Csa_MediterraneanHotSummer, 0.55 },
{ ClimateZone.Csb_MediterraneanWarmSummer, 0.65 },
{ ClimateZone.Csc_MediterraneanCoolSummer, 0.7 },
// Continental Climates
{ ClimateZone.Dfa_HumidContinentalHotSummer, 0.75 },
{ ClimateZone.Dfb_HumidContinentalMildSummer, 0.8 },
{ ClimateZone.Dfc_Subarctic, 0.5 },
{ ClimateZone.Dfd_SubarcticExtremeWinter, 0.45 },
{ ClimateZone.Dsa_MediterraneanInfluenceSnowyWinter, 0.7 },
{ ClimateZone.Dsb_MediterraneanInfluenceSnowyWinter, 0.65 },
{ ClimateZone.Dsc_MediterraneanInfluenceSnowyWinter, 0.55 },
{ ClimateZone.Dsd_MediterraneanInfluenceSnowyWinter, 0.5 },
// Polar Climates
{ ClimateZone.ET_Tundra, 0.3 },
{ ClimateZone.EF_IceCap, 0.25 },
};
public static Dictionary<ClimateZone, double> Roof =>
new()
{
// Tropical Climates
{ ClimateZone.Af_TropicalRainforest, 1.2 },
{ ClimateZone.Am_TropicalMonsoon, 1.3 },
{ ClimateZone.Aw_TropicalSavanna, 1.4 },
{ ClimateZone.As_TropicalSavanna, 1.4 },
// Dry Climates
{ ClimateZone.BWh_HotDesert, 1.1 },
{ ClimateZone.BWk_ColdDesert, 1.3 },
{ ClimateZone.BSh_HotSemiArid, 1.2 },
{ ClimateZone.BSk_ColdSemiArid, 1.3 },
// Temperate Climates
{ ClimateZone.Cfa_HumidSubtropical, 1.1 },
{ ClimateZone.Cfb_Oceanic, 1.0 },
{ ClimateZone.Cfc_SubpolarOceanic, 0.9 },
{ ClimateZone.Csa_MediterraneanHotSummer, 1.2 },
{ ClimateZone.Csb_MediterraneanWarmSummer, 1.1 },
{ ClimateZone.Csc_MediterraneanCoolSummer, 1.0 },
// Continental Climates
{ ClimateZone.Dfa_HumidContinentalHotSummer, 1.0 },
{ ClimateZone.Dfb_HumidContinentalMildSummer, 0.9 },
{ ClimateZone.Dfc_Subarctic, 0.6 },
{ ClimateZone.Dfd_SubarcticExtremeWinter, 0.5 },
{ ClimateZone.Dsa_MediterraneanInfluenceSnowyWinter, 0.9 },
{ ClimateZone.Dsb_MediterraneanInfluenceSnowyWinter, 0.8 },
{ ClimateZone.Dsc_MediterraneanInfluenceSnowyWinter, 0.7 },
{ ClimateZone.Dsd_MediterraneanInfluenceSnowyWinter, 0.6 },
// Polar Climates
{ ClimateZone.ET_Tundra, 0.4 },
{ ClimateZone.EF_IceCap, 0.35 },
};
}
+7 -19
View File
@@ -1,7 +1,8 @@
using Speckle.Automate.Sdk.DataAnnotations;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace TestAutomateFunction;
/// <summary>
/// This class describes the user specified variables that the function wants to work with.
/// </summary>
@@ -9,24 +10,11 @@ using System.ComponentModel.DataAnnotations;
/// are valid and match the required schema.
public struct FunctionInputs
{
/// <summary>
/// The object type to count instances of in the given model version.
/// </summary>
[Required]
public string SpeckleTypeToCount;
// 0- Create dropdown for available climate zones as "ClimateZones"
/// <summary>
/// The total number of the specified type expected.
/// </summary>
[DefaultValue(10)]
[Range(1, 100)]
[Required]
public int SpeckleTypeTargetCount;
// 1- Create toggle for whether including walls or not
/// <summary>
/// An arbitrary example of using a secret input value.
/// </summary>
[Required]
[Secret]
public string ExternalServiceKey;
// 2- Create toggle for whether including windows or not
// 3- Create toggle for whether including roofs or not
}
@@ -0,0 +1,32 @@
using Speckle.Core.Models;
namespace TestAutomateFunction;
public class ObjectToCheck
{
public string Id { get; }
public double UValue { get; }
public double ExpectedUValue { get; }
public SpeckleType SpeckleType { get; }
public ObjectToCheck(Base obj, double expectedUValue, SpeckleType speckleType)
{
Id = obj.id;
ExpectedUValue = expectedUValue;
SpeckleType = speckleType;
var properties = obj["properties"] as Dictionary<string, object>;
if (properties is null)
{
UValue = 0;
return;
}
var typeParameters = properties!["Type Parameters"] as Dictionary<string, object>;
var analyticalProperties =
typeParameters!["Analytical Properties"] as Dictionary<string, object>;
var u =
analyticalProperties!["Heat Transfer Coefficient (U)"]
as Dictionary<string, object>;
var value = u!["value"] is double ? (double)u!["value"] : 0;
UValue = value;
}
}
+1
View File
@@ -1,4 +1,5 @@
using Speckle.Automate.Sdk;
using TestAutomateFunction;
// WARNING do not delete this call, this is the actual execution of your function
return await AutomationRunner
@@ -0,0 +1,50 @@
using Speckle.Core.Models;
using Speckle.Core.Models.GraphTraversal;
namespace TestAutomateFunction;
public class SpeckleTypeUtilities
{
public static IEnumerable<Base> GetByType(
IEnumerable<Base> objects,
SpeckleType speckleType
)
{
return objects.Where(b =>
b.speckle_type == SpeckleTypes[speckleType]
&& (string)b["category"]! == SpeckleCategories[speckleType]
);
}
public static IEnumerable<Base> GetByType<T>(Base root)
where T : Base
{
var traversal = new GraphTraversal();
return traversal
.Traverse(root)
.Where(obj => obj.Current is T)
.Select(obj => obj.Current);
}
private static readonly Dictionary<SpeckleType, string> SpeckleTypes =
new()
{
{
SpeckleType.Wall,
"Objects.BuiltElements.Wall:Objects.BuiltElements.Revit.RevitWall"
},
{ SpeckleType.Window, "Objects.BuiltElements.Revit.RevitElement" },
{
SpeckleType.Roof,
"Objects.BuiltElements.Roof:Objects.BuiltElements.Revit.RevitRoof.RevitRoof:Objects.BuiltElements.Revit.RevitRoof.RevitExtrusionRoof"
},
};
private static readonly Dictionary<SpeckleType, string> SpeckleCategories =
new()
{
{ SpeckleType.Wall, "Walls" },
{ SpeckleType.Window, "Windows" },
{ SpeckleType.Roof, "Roofs" },
};
}
@@ -1,14 +1,13 @@
namespace TestAutomateFunction;
using Speckle.Automate.Sdk;
using Speckle.Automate.Sdk.Test;
using Speckle.Core.Api;
using Speckle.Core.Credentials;
namespace TestAutomateFunction;
[TestFixture]
public sealed class AutomationContextTest : IDisposable
{
private Client client;
private Account account;
@@ -18,7 +17,10 @@ public sealed class AutomationContextTest : IDisposable
account = new Account
{
token = TestAutomateEnvironment.GetSpeckleToken(),
serverInfo = new ServerInfo { url = TestAutomateEnvironment.GetSpeckleServerUrl().ToString() }
serverInfo = new ServerInfo
{
url = TestAutomateEnvironment.GetSpeckleServerUrl().ToString(),
},
};
client = new Client(account);
}
@@ -28,8 +30,7 @@ public sealed class AutomationContextTest : IDisposable
{
var inputs = new FunctionInputs
{
SpeckleTypeToCount = "Base",
SpeckleTypeTargetCount = 1
// TODO: Define test inputs
};
var automationRunData = await TestAutomateUtils.CreateTestRun(client);