Prioritizing Features using Kano Model

The Product Backlog provides a collection of features that the Product should ideally implement. But not every feature has the same priority. Some of the features are more important than others and of course, the Product Owner doesn’t go around picking random features while prioriterzing the features.

There are various models and in this example, we will explore the Kano Model.

The Kano Model

As per Nariako Kano’s model, the features could be broadly categorized into 3 categories.

  • Thresholds/Must Have Features These represent the minimum set of features that should be present to meet User expectations. Improving the must have features beyond a limit would have little impact on the customer satisfaction. For example, for accommodation, a minimum requirement of User would be a clean room with basic ammenities.
  • Linear Features Features which increases customer satisfaction as it increases is known as Linear Features. This includes the size of the room or bed, freebies in the room etc.
  • Delighters Delighters on other hand are features which adds to the premium quality of product, often adding greatly to customer satisfaction. These could include private pools. These are features which the Customer might not quite miss if not present, but would be delighted if present.

Now that we have understood how we would like to group the features, the process of actually grouping them begins. As per Kano, this could be done by asking two questions per feature to the user group.

  • Assuming the feature is present, also known as Functional question
  • Assuming the feature is not present, also known as Dysfunctional Question.

The answers are questions are typically collected as

  1. I like it that way
  2. I expected it that way
  3. I am neutral
  4. I can live with it that way
  5. I dislike it that way

The answers could be mapped to 3 feature groups using the following table.

The answers from the user groups (typically 20-30 users) are aggregated and their distribution could be observed to determine the priority group.

The responses with high values are considered.

This was one approach for User Story Prioritization. In the next post, we will explore Relative Weighting approach.

CRUD Operations with Azure Queue Storage in an Azure Function – Retrieve

In the previous blog post, we explored how to Enqueue and Dequeue items from Azure Storage. Let us continue our exploration of Queue storage and write code to “peek” from a queue.

Peek/Retrieve From Queue

For retrieve from Queue, you could use the CloudQueue.PeekMessageAsync() method. For example,

[FunctionName("PeekItemFromQueue")]
public static async Task<IActionResult> PeekItemFromQueue(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest request,
[Queue("SampleQueue")] CloudQueue cloudQueue)
{

    var message = await cloudQueue.PeekMessageAsync();
    

    if (message == null)
        return new OkObjectResult("No item in the queue");

    var resultBuilder = new StringBuilder();
    resultBuilder.AppendLine($"Message : `({message.AsString})`");
    resultBuilder.AppendLine($"Pop Reciept : {message.PopReceipt}");
    resultBuilder.AppendLine($"Dequeue Count : {message.DequeueCount}");
    resultBuilder.AppendLine($"Insertion Time: {message.InsertionTime}");
    resultBuilder.AppendLine($"Next Visible Time: {message.NextVisibleTime}");
    return new OkObjectResult(resultBuilder.ToString());
}

The CloudMessageQueue object provides some interesting properties about the retrieved message. For example, the above query could provide following details.

Message : `SampleMessageForTest`
Pop Reciept : 
Dequeue Count : 0
Insertion Time: 17-04-2021 02:38:55 +00:00
Next Visible Time:

The DequeueCount property specifies the number of times the message has been retrieved without being deleted from the queue. Remember, when a message is retrieved from the Azure Queue, it is not removed from the Queue. The DequeueCount property hints at the number of attempts to retrieve the message by client applications.

While the message is not removed from the Queue entirely, it does disappears from the queue for a designated time. The Next Visible Time indicates the time at which the message would appear back once is has disappeared temperarly.

You could also in effect use the CloudQueue.GetMessageAsync method as well, since by default the mesage is not removed from the original queue. However, the difference lies in the fact that the GetMessageAsync method would remove the message from queue temporarly, and increment the DequeueCount. It would also issue a PopReciept. But that’s not the case with PeekMessageAsync, which would not hide the message from the queue nor would be increment the DequeueCount. Following would be the response if we had used GetMessageAsync instead of PeekMessageAsync.

Message : `SampleMessageForTest`
Pop Reciept : cIGyxZkB2QgBAAAA
Dequeue Count : 1
Insertion Time: 17-04-2021 02:38:55 +00:00
Next Visible Time: 17-04-2021 12:10:20 +00:00

A Word about Poison Queue

There could be situations wherein the message in the queue appear as a clog in the system as the message could retried too many times. As a guard against this, the concept was poison queue was introduced. Any message that has been retried over 5 times, is considered as poison message (could harm the application), are moved to different queue called the Poison Queue, usually named as queuename-poison.

Since you know the name of the poison queue, you could still process the messages in the queue. Depending on your application/business needs you can decide on the stratergy to process the poison queue messages. Irrespective of your business needs, one of the important point to consider when processing the poison messages is attempting to understand the reason why the message was moved to poison queue in first place.

That’s all for now. We will continue exploring the Azure Storage with this byte sized tutorial series.

CRUD Operations with Azure Queue Storage in an Azure Function – Create And Delete

An Azure queue is ideal for storing large number of messages, with each message having an upper cap of 64 Kb. This is ideal for providing asynchronous message queueing facility between application clients.

Queue concepts can be broken down into 4 components.

  • Storage Account : Like all other Azure Storage facilities, Queue is also linked with a Storage Account.
  • Queue : As the name suggest, it is a queue containing a set of messages. One key point to note here is that the queue needs to be named in lowercase.
  • URL : The queue is accessed via the URL associated. URL has a special format
https://<storage account>.queue.core.windows.net/<queue>

  • Message : Message could be of any formated, though size of each message is limited to 64Kb. The default Time-To-Live for a message is 7 days, but it could be configured. You could also make a message non-expiring with a configuration value of -1.

Create

Let us straightaway hit some code to create our items in Queue.

[FunctionName("AddMessageToQueue")]
public static async Task<IActionResult> AddMessageToQueue(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest request,
    [Queue("samplequeue")] CloudQueue cloudQueue)
{
    var message = request.Query["item"];

    var queueMessage = new CloudQueueMessage(message);
    await cloudQueue.AddMessageAsync(queueMessage);
    return new OkObjectResult($"Message :(`{message}`) added to tbe queue");
}

As with earlier posts in this series, we will stick with Azure Functions for the demonstrations. As observed from the code above, we are using CloudQueue class to refer to the Queue in question (“samplequeue“).

The insertion process is pretty straightforward here. You need to create an instance of the CloudQueueMessage object and use the CloudQueue.AddMessageAsync to add the message to the queue. That seems to be pretty simple right.

There are some important characterstics of the inserted message that would be quite useful to be aware of. Each of the inserted messages as the following properties.

  • ID – An Unique Guid
  • Message Text – Message itself
  • Insertion Time – Represents the time message was added to the queue
  • Expiration Time – Represents the time when the message is expected to expire
  • Dequeue Count – Number of times the message has been dequeued.
  • Size – Actual Size of the message

You can set the Expiration Time with an overload of CloudQueue.AddMessageAsync method. Another interesting property to note is Dequeue count. Unlike traditional queue, the Azure Queue doesn’t remove the message as soon as one dequeue it. Instead, it becomes invisible for a specified amount of time and then reappear again. This is a fail-safe mechanism build by Microsoft so that if your application fails to process the message due to any reason, it could still retrieve it again. The Dequeue Count property provides the number of times a message has been dequeued.

Here is another interesting aspect – If a message has been retrieved/dequeued 5 or more time without being removed from the queue, the message would be considered a posionous message and moved to a separate queue named “Posion Queue”. We will go in detail of the posion queue in a later post.

So how do one remove an item from the queue ? You delete it of course.

Delete

[FunctionName("PopMessageFromQueue")]
public static async Task<IActionResult> PopMessageFromQueue(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest request,
[Queue("SampleQueue")] CloudQueue cloudQueue)
{
    var message = await cloudQueue.PeekMessageAsync();
    await cloudQueue.DeleteIfExistsAsync();

    return new OkObjectResult(message == null ? "No Message found to be removed":$"Message :(`{message.AsString}`) has been removed");
}

The code above uses the CloudQueue.PeekMessageAsync method to retrieve the first message in the queue read to be processed. It then uses the CloudQueue.DeleteIfExistsAsync to remove the item.

Additional Note

You could read fetch the count of Messages in queue. For the purpose, you could use the CloudQueue.FetchAttributesAsync method.

[FunctionName("NumberOfItemsInQueue")]
public static async Task<IActionResult> NumberOfItemsInQueue(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest request,
    [Queue("SampleQueue")] CloudQueue cloudQueue)
{
    await cloudQueue.FetchAttributesAsync();
    return new OkObjectResult($"Approx. number of Items in Queue :{cloudQueue.ApproximateMessageCount}");
}

The call to CloudQueue.FetchAttributesAsync updates the property Cloud.ApproximateMessageCount with the number of items in the queue.

In the next post, we will look into Retrieve and Update part of the Queue. We will also have a further look at the Poison Queue.

How CLR Handles Static, Non-Virtual Instance, and Virtual Instance method calls

How does method execution differs when CLR handles static, instance and virtual instance methods ? To answer them, one needs to begin by understanding a bit more on how CLR handles Managed Heap during execution of a method.

Memory Allocation in Managed Heap

When the application execution begins, as the CLR gets loaded, the thread stack and managed heap gets allocated. Prior to method execution, JIT (Just In Time) Compiler converts the IL code to native instructions. At this moment, it also detects all the types used in the method and also loads all the required/dependend assemblies needed to support the execution.

The CLR also gathers information about the data types involved from the metadata and create the required Data structures. Each time a new Type is encountered, a data structure representing Type Object is created. The Type Object contains two special fields called Type Object Pointer and Sync Block Index. The Type Object also contains static fields and a method reference table. The Method table contains an entry for each method exposed by the type.

For each instance of a type, an instance object data structure is create in the Managed Heap. The instance object would memory space for storing the instance fields in the type. Similiar to the Type Object , the instance object would also contain the two special fields – Type Object Pointer and Sync Block Index.

The Type Object Pointer of the instance object points to the corresponding type object. At this point, one might wonder what would the Type Object Pointer of the type object point to ? Well, it points to a special type object of type System.Type. The next question that might arise in your mind would be, what would type object pointer of the System.Type type object refers to ? The answer is – it refers to self. Interesting.

Once the local variables are created in Thread Stack, the compiled native instruction is executed. After the method exection completes, the CPU instruction is set to the return address.

A common question that might arise would be what happens when a new object is created using the new operator ? During the object creation, the CLR calculates the size of instance fields in the type. It then allocates memory for the calculated size, including fields for Type Object Pointer and Sync Block Index. It also resets the memory to zero. At this point, the constructor is invoked and any fields that could be initialized are assign initial values. It then returns the reference(address) to the newly created object.

Let us now see how CLR handles method execution and how the data structures we discussed in earlier section are related.

Static Method Execution

During the execution of Static Methods, CLR locates the concerned Type’s Type Object data structure in the heap (it would create it if not already created). It then proceed to locate the entry in the method table for the method to be invoked. The compiler then JIT Compiles the method code (only if required – if the method has already been invoked and the native code is already available, this step is skipped) and start executing the native code.

Non Virtual Instance Methods

The execution of a non-virtual instance method is slightly different from the execution of Static Method. It has couple of additional steps.

During the execution of non-virtual instance methods, the compiler detects the Type Object that corresponds to the type of variable being used in the method call. If the method entry is not found in the method table, then it walks through the class heirarchy, checking each of the base type objects for an entry in the method table.

Once it finds the method, follows the steps mentioned for static method. It JIT compiles (only if required) the method code and invokes the native code.

Virtual Instance Method

In the case of virtual instance methods, the compiler begins by refering to the address of instance object the variable points to. The Type object pointer of the instance object would be pointing to the actual type of the object. It then locates the method entry in the Method table, JIT compiles if required and start executing the code.

A summary of the whole process is visualized in the sketch notes below.

Cheat Sheet for Path Markup Syntax

Path Markup Syntax provides a mini-language for describing complex collection of lines and curves. Make no mistake when describing it as mini-language – It is quite powerful and could reduces a lot of nested collection of Xaml elements into a single line.

Having said so, I personally favour the Xaml Element way of constructing elements, mainly due to readability factor. But the language is quite useful in its own ways.

Here is a little cheat sheet for Path Markup Syntax to aid your development cause.

Cheat Sheet Path Markup Syntax

Why be wary of Value Coercion in Dependency Properties

If you are not quite familiar with Value Coercion, it allows you to change/correct value of a property, when it is assigned an unexpected value. This also allows you to ensure relative properties are also kept in sync or in other words, allows you to enforce relation between properties of an object. For example, Date of Birth should not exceed Date of Demise, or Minimum Value in a slider should not exceed the Maximum Value.

You could achieve such a synchronization mechanism using Value Coercion. Consider the following code.

// Note that code has a 'little problem', we will discuss it shortly.
public int MinimumValue
{
    get { return (int)GetValue(MinimumValueProperty); }
    set { SetValue(MinimumValueProperty, value); }
}

public static readonly DependencyProperty MinimumValueProperty = 
    DependencyProperty.Register(nameof(MinimumValue), typeof(int), typeof(Configuration), new PropertyMetadata(0, null, new CoerceValueCallback(OnMinimumValueCoerce)));

private static object OnMinimumValueCoerce(DependencyObject d, object baseValue)
{
    if(d is Configuration config && baseValue is int newValue)
    {
        var maxValue = config.MaximumValue;
        var oldValue = config.MinimumValue;
        return maxValue > newValue ? newValue : oldValue;
    }
    return baseValue;
}

public int MaximumValue
{
    get { return (int)GetValue(MaximumValueProperty); }
    set { SetValue(MaximumValueProperty, value); }
}

public static readonly DependencyProperty MaximumValueProperty =
    DependencyProperty.Register(nameof(MaximumValue), typeof(int), typeof(Configuration), new PropertyMetadata(0,null,new CoerceValueCallback(OnMaximumCoerce)));

private static object OnMaximumCoerce(DependencyObject d, object baseValue)
{
    if(d is Configuration config && baseValue is int newValue)
    {
        var minValue = config.MinimumValue;
        var oldValue = config.MaximumValue;
        return newValue > minValue ? newValue : oldValue;
    }
    return baseValue;
}

In the above code, you are using the CoerceValueCallback within the PropertyMetada to ensure MinimumValue and MaximumValue are kept in sync with each other. Every time a new value is assigned for MinimumValue, it checks if the value is exceeds the MaximumValue. If yes, it reverts the changes and retains the old value.

Little Problem

While the above code looks seemingly harmless, there is a small problem associated with. If you examine the code, we have coercing values for both MinimumValue and Maximum value. Now imagine a scenario when are using the Dependency properties in your code.

<controls:Configuration MinimumValue="{Binding MinValue}"  MaximumValue="{Binding MaxValue}" />

Consider the initial values of MinValue and MaxValue are 1 and 100. At the first glance, this looks valid value, however if you execute the code, you would realize the MinimumValue nevers gets set. Instead, the OnMinimumValueCoerce comes into play and reverts the changes. This is becase, at first the MinimumValue is set (by the order in which is defined in our Xaml – Change the order and behavior would be different).

When a value 1 is assigned to MinimumValue, the OnMinimumValueCoerce notices that it exceeds the current value of MaximumValue (which hasn’t changed yet and is still default of 0). This causes the values to reverts as per our logic in OnMinimumValueCoerce.

PropertyChangedCallback Vs CoerceValueCallback Vs ValidateValueCallback

At this point, it is worth noticing the difference between the 3 seemingly similiar callbacks associated with DependencyProperty – PropertyChangedCallbackCoerceValueCallback and ValidateValueCallback.

The difference could be summarized as

  • PropertyChangedCallback – Reacts to a value change
  • ValidateValueCallback – Determine if the value is valid
  • CoerceValueCallback – Coerce a value.

Order of execution

  1. ValidateValueCallback
  2. CoerceValueCallback
  3. PropertyChangedCallback

Solution

One solution that could be applied in this scenario is

  • Allow MinimumValue to be set to any value
  • In PropertyChangedCallback of MinimumValue, force coercion of MaximumValue.
  • In CoerceValueCallback of MaximumValue, check if it is less than MinimumValue and set it to MinimumValue.

For example,

public int MinimumValue
{
   get { return (int)GetValue(MinimumValueProperty); }
   set { SetValue(MinimumValueProperty, value); }
}

public static readonly DependencyProperty MinimumValueProperty = 
   DependencyProperty.Register(nameof(MinimumValue), typeof(int), typeof(Configuration), new PropertyMetadata(0, new PropertyChangedCallback(OnMinimumValueChanged)));

private static void OnMinimumValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
   if(d is Configuration config)
   {
       config.CoerceValue(MaximumValueProperty);
   }
}

public int MaximumValue
{
   get { return (int)GetValue(MaximumValueProperty); }
   set { SetValue(MaximumValueProperty, value); }
}

public static readonly DependencyProperty MaximumValueProperty =
   DependencyProperty.Register(nameof(MaximumValue), typeof(int), typeof(Configuration), new PropertyMetadata(0,null,new CoerceValueCallback(OnMaximumCoerce)));

private static object OnMaximumCoerce(DependencyObject d, object baseValue)
{
   if(d is Configuration config && baseValue is int newValue)
   {
       var minValue = config.MinimumValue;
       return newValue > minValue ? newValue : minValue;
   }
   return baseValue;
}

As you can see, while 3 muskeeters of PropertyChangedCallbackCoerceValueCallback and ValidateValueCallback are extremly useful, one needs to be aware of similiar issues which could come along and hence one needs to be aware of what is the difference between them and the sequence of execution.

Circular Progressbar in WPF

One of the things I have been working recently required me to use a Circular Progress bar. Incidently, I was surprised there wasn’t something useful in the WPF package, but it wasn’t that hard to do at the hindsight.

The core idea would be to draw two overlapping circles – one for the background circle and other indicating the progress. We will begin by defining certain characterstics of the Cricle (or better called Arc), which we will find useful later on. So, let us first define our model.

public class ProgressArc : PropertyChangedBase
{
    public Point StartPosition { get; set; } = new Point(50, 0);
    public Point EndPosition { get; set; } = new Point(100, 0);
    public Size Radius { get; set; }
    public double Thickness { get; set; } = 2;
    public double Angle { get; set; }
}

Each of the Circle/Arcs are characterized by 5 properties as defined in the Model above.

  • Start Position
  • End Position
  • Radius Of the Arc
  • Thickness of the Stroke
  • Angle

While most of the characterstics are self explanatory, the Angle might need a bit of exaplantion. Let us take a step back and think about what we are attempting to accomplish here. We would like to represent a Value within a Range (Maximum and Minimum Values) just as in regular ProgressBar, but this time as a Circle/Arc. When the Value is equavalent to the Maximum, we would like to see a full circle. But anything short of maximum value (or in other words less than 100%), we would need to draw an arc that ends at an angle, which would represent the percentage of completion. The angle could be calculated pretty easily with the following.

var percent = (currentValue / (maxValue - minValue) * 100);
var valueInAngle = (percent / 100) * 360;

Alright – so with that explanation under our belt, let us proceed to construct our view.

<Viewbox Stretch="Uniform" Margin="10">
    <Grid>
        <Canvas Height="100" Width="100" >
            <Path Stroke="LightGray" StrokeThickness="{Binding BackgroundCircle.Thickness}" >
                <Path.Data>
                    <PathGeometry>
                        <PathFigure  StartPoint="{Binding BackgroundCircle.StartPosition}">
                            <PathFigure.Segments>
                                <ArcSegment RotationAngle="0" SweepDirection="Clockwise"
                                            Size="{Binding BackgroundCircle.Radius}"
                                            IsLargeArc="True"
                                        Point="{Binding BackgroundCircle.EndPosition}"
                                            >

                                </ArcSegment>

                            </PathFigure.Segments>
                        </PathFigure>
                    </PathGeometry>
                </Path.Data>
            </Path>

            <Path Stroke="Blue" StrokeThickness="{Binding ValueCircle.Thickness}" StrokeEndLineCap="Round">
                <Path.Data>
                    <PathGeometry>
                        <PathFigure  StartPoint="{Binding ValueCircle.StartPosition}">
                            <PathFigure.Segments>
                                <ArcSegment RotationAngle="0" SweepDirection="Clockwise"
                                            Size="{Binding ValueCircle.Radius}"
                                            IsLargeArc="{Binding ValueCircle.Angle,Converter={StaticResource AngleToIsLargeConverter}}"
                                        Point="{Binding ValueCircle.EndPosition}"
                                            >

                                </ArcSegment>

                            </PathFigure.Segments>
                        </PathFigure>
                    </PathGeometry>
                </Path.Data>
            </Path>

        </Canvas>
        <TextBlock Text="{Binding CurrentValue}" FontSize="32" VerticalAlignment="Center" HorizontalAlignment="Center" />
    </Grid>
</Viewbox>

Of course, that would look pretty incomplete without our ViewModel. Let us define our ViewModel too before we discuss the finer points.

 public class ShellViewModel : Screen
{
    public ShellViewModel()
    {
        BackgroundCircle.Angle = 360;
        BackgroundCircle.PropertyChanged += OnCircleChanged;
        ValueCircle.PropertyChanged += OnCircleChanged;
        PropertyChanged += OnPropertyChanged; ;
    }

    private void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case nameof(MaxValue):
            case nameof(MinValue):
            case nameof(CurrentValue):
            case nameof(SelectedOverlayMode):
                RefreshControl();
                break;
        }
    }

    private void OnCircleChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        RefreshControl();
    }

    protected override void OnViewAttached(object view, object context)
    {
        base.OnViewAttached(view, context);
        RefreshControl();
    }
    private void RefreshControl()
    {
        ArcCalculatorBase arcCalculator;
        switch (SelectedOverlayMode)
        {
            case OverlayMode.Centered:
                arcCalculator = new CenteredArcCalculator(BackgroundCircle.Thickness, ValueCircle.Thickness); 
                break;
            case OverlayMode.InnerCircle:
                arcCalculator = new InsetArcCalculator(BackgroundCircle.Thickness, ValueCircle.Thickness);
                break;
            case OverlayMode.OuterCircle:
                arcCalculator = new OutsetArcCalculator(BackgroundCircle.Thickness, ValueCircle.Thickness);
                break;
            default:
                arcCalculator = new OutsetArcCalculator(BackgroundCircle.Thickness, ValueCircle.Thickness);
                break;
        }
        arcCalculator.Calculate(MinValue, MaxValue, CurrentValue);

        BackgroundCircle.Radius = arcCalculator.BackgroundCircleRadius;
        BackgroundCircle.StartPosition = arcCalculator.BackgroundCircleStartPosition;
        BackgroundCircle.EndPosition = arcCalculator.BackgroundCircleEndPosition;

        ValueCircle.Radius = arcCalculator.ValueCircleRadius;
        ValueCircle.StartPosition = arcCalculator.ValueCircleStartPosition;
        ValueCircle.EndPosition = arcCalculator.ValueCircleEndPosition;
        ValueCircle.Angle = arcCalculator.ValueAngle;
    }

    public ProgressArc BackgroundCircle { get; set; } = new ProgressArc();
    public ProgressArc ValueCircle { get; set; } = new ProgressArc();
    public double MinValue { get; set; } = 10;
    public double MaxValue { get; set; } = 120;
    public double CurrentValue { get; set; } = 60;

    public OverlayMode SelectedOverlayMode { get; set; }
}

The ViewModel defined is pretty straightforward. We have to instances (BackgroundCircle & ValueCircle) of our model (ProgressArc). These would represent our circles. We have few other properties, including the MinValueMaxValue and CurrentValue, which would be used to configure the ProgressBar.

Each time any of our configuration parameter changes( MinValueMaxValue or CurrentValue), we would need to recalculate the dimentions of the arcs. This is being done in the RefreshControl method. The SelectedOverlayMode property defines how the two circles needs to be aligned with respect to each other.

public enum OverlayMode
{
   InnerCircle,
   OuterCircle,
   Centered
}

Depending on the alignment of the two circles, we would need to alter our calculations. These are defined as the following.

 public abstract class ArcCalculatorBase
{
    protected const double ORIGIN = 50;
    protected double _backgroundCircleThickness;
    protected double _valueCircleThickness;
    public ArcCalculatorBase(double backgroundCircleThickness, double valueCircleThickness)
    {
        _backgroundCircleThickness = backgroundCircleThickness;
        _valueCircleThickness = valueCircleThickness;
    }

    public Size BackgroundCircleRadius { get; protected set; }
    public Size ValueCircleRadius { get; protected set; }

    public Point BackgroundCircleStartPosition { get; protected set; }
    public Point BackgroundCircleEndPosition { get; protected set; }

    public Point ValueCircleStartPosition { get; protected set; }
    public Point ValueCircleEndPosition { get; protected set; }

    public double ValueAngle { get; set; }

    protected double GetAngleForValue(double minValue, double maxValue, double currentValue)
    {
        var percent = (currentValue - minValue) * 100 / (maxValue - minValue);
        var valueInAngle = (percent / 100) * 360;
        return valueInAngle;
    }
    public abstract void Calculate(double minValue, double maxValue, double currentValue);
    protected Point GetPointForAngle(Size radiusInSize,double angle)
    {
        var radius = radiusInSize.Height;
        angle = angle == 360 ? 359.99 : angle;
        double angleInRadians = angle * Math.PI / 180;

        double px = ORIGIN + (Math.Sin(angleInRadians) * radius);
        double py = ORIGIN + (-Math.Cos(angleInRadians) * radius);

        return new Point(px, py);
    }
}

public class OutsetArcCalculator : ArcCalculatorBase
{
    public OutsetArcCalculator(double backgroundCircleThickness, double valueCircleThickness):base(backgroundCircleThickness,valueCircleThickness)
    {

    }

    public override void Calculate(double minValue,double maxValue,double currentValue)
    {
        BackgroundCircleRadius =  new Size(ORIGIN - _backgroundCircleThickness / 2, ORIGIN - _backgroundCircleThickness / 2);
        ValueCircleRadius = new Size(ORIGIN - _valueCircleThickness / 2, ORIGIN - _valueCircleThickness / 2); ;

        BackgroundCircleStartPosition = GetPointForAngle(BackgroundCircleRadius, 0);
        BackgroundCircleEndPosition = GetPointForAngle(BackgroundCircleRadius, 360);

        ValueAngle = GetAngleForValue(minValue, maxValue, currentValue);

        ValueCircleStartPosition = GetPointForAngle(ValueCircleRadius, 0);
        ValueCircleEndPosition = GetPointForAngle(ValueCircleRadius, ValueAngle);
    }
}

public class CenteredArcCalculator : ArcCalculatorBase
{
    public CenteredArcCalculator(double backgroundCircleThickness, double valueCircleThickness) : base(backgroundCircleThickness, valueCircleThickness)
    {

    }

    public override void Calculate(double minValue, double maxValue, double currentValue)
    {
        var maxThickness = Math.Max(_backgroundCircleThickness, _valueCircleThickness);
        
        BackgroundCircleRadius = new Size(ORIGIN - maxThickness / 2, ORIGIN - maxThickness / 2);
        ValueCircleRadius = new Size(ORIGIN - maxThickness / 2, ORIGIN - maxThickness / 2); ;

        BackgroundCircleStartPosition = GetPointForAngle(BackgroundCircleRadius, 0);
        BackgroundCircleEndPosition = GetPointForAngle(BackgroundCircleRadius, 360);

        ValueAngle = GetAngleForValue(minValue, maxValue, currentValue);

        ValueCircleStartPosition = GetPointForAngle(ValueCircleRadius, 0);
        ValueCircleEndPosition = GetPointForAngle(ValueCircleRadius, ValueAngle);
    }
}

public class InsetArcCalculator : ArcCalculatorBase
{
    public InsetArcCalculator(double backgroundCircleThickness, double valueCircleThickness) : base(backgroundCircleThickness, valueCircleThickness)
    {

    }

    public override void Calculate(double minValue, double maxValue, double currentValue)
    {
        var maxThickness = Math.Max(_backgroundCircleThickness, _valueCircleThickness);

        BackgroundCircleRadius = new Size((ORIGIN - maxThickness) + (_backgroundCircleThickness / 2), (ORIGIN - maxThickness) + (_backgroundCircleThickness / 2));
        ValueCircleRadius = new Size((ORIGIN - maxThickness) + (_valueCircleThickness / 2), (ORIGIN - maxThickness) + (_valueCircleThickness / 2));

        BackgroundCircleStartPosition = GetPointForAngle(BackgroundCircleRadius, 0);
        BackgroundCircleEndPosition = GetPointForAngle(BackgroundCircleRadius, 360);

        ValueAngle = GetAngleForValue(minValue, maxValue, currentValue);

        ValueCircleStartPosition = GetPointForAngle(ValueCircleRadius, 0);
        ValueCircleEndPosition = GetPointForAngle(ValueCircleRadius, ValueAngle);
    }
}

The GetPointForAngle() converts the given angle to a point on Canvas using the following formulae

Point = (InitialPosition + (Sin(AngleInRadians) * radius),InitialPosition + (-Cos(AngleInRadians) * radius))

The final outcode, based on different Overlay configuration are shown in figures below

Overlay with Equal Thickness
Center Aligned
Inner Circle Aligned
Outer Circle Aligned

The Complete code could found in here in my Github.

CRUD Operations with Azure Blob Storage in an Azure Function – Update & Deletion

In the preceeding parts of this series, we have delved into how to Create and Retrieve blobs using Azure Web Functions. In this part, we will look into how delete the blobs from Azure Blob Storage.

Wait a minute !! Did that we mean we skip Update ? Not really, we will have a word about it before we start with Deletion.

Update Blobs

The reason I thought Update Blobs doesn’t require its own blog post was it is quite different from what we learned for Creation (for starters, without delving too much into details). In fact, there is no difference at all.

Consider the Block Blobs – most likely, these are single files whose updation implies replacing them with a updated version. This could be an image file or an media file. This is no different from creating a new file.

You could overwrite an existing blob using the same code which learned in the previous post.

using (var stream = data.OpenReadStream())
{
    var blob = blobContainer.GetBlockBlobReference(key);
    await blob.UploadFromStreamAsync(stream);
}

With Append Blob, as the name suggests, the action is more of an “append to end of blob” scenario. This is whatthe Append blob is designed for. You could do exactly that using your learnings in the previous blog.

Deletion

Now that we have briefly spoken about the Updatation process, let us move ahead with the Deletion.

How do one delete a blob ? Well the answer is pretty simple, and is almost the same for Block and Append Blobs.

[FunctionName("DeleteFileBlockBlob")]
public static async Task<IActionResult> DeleteFileBlockBlob(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
    [Blob("todos")] CloudBlobContainer blobContainer,
    ILogger log
    )
{
    string key = req.Query["key"];

    var blob = blobContainer.GetBlockBlobReference(key);
    var result = await blob.DeleteIfExistsAsync();

    return new OkObjectResult($"Item Deletion {(result ? "Success" : "Failed")}");
}

As you notice in the code above, all it takes is to get a reference to the blob using the now familiar CloudBlobContainer.GetBlockBlobReference and use the CloudBlockBlob.DeleteIfExistsAsync to delete it.

With the Append Blob, obviously, one needs to get reference to the Append Blob, instead of Block Blob, but the code feels no different. Take a look at the example below.

[FunctionName("DeleteFileAppendBlob")]
public static async Task<IActionResult> DeleteFileAppendBlob(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
[Blob("sample")] CloudBlobContainer blobContainer,
ILogger log
)
{
    string key = req.Query["key"];

    var blob = blobContainer.GetAppendBlobReference(key);
    var result = await blob.DeleteIfExistsAsync();

    return new OkObjectResult($"Item Deletion {(result ? "Success" : "Failed")}");
}

That’s hardly any different.

So we have so far, looked into basics of CRUD operations into Azure Table Storage and Azure Blob Storage. We will continue our exploration of Azure Storage options and also take time to delve deeper in the configurations/security options in the upcoming blobs.

Until then, keep coding !!! As always, all code discussed here are available in my Github. For complete series on Azure Storage, refer here