Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculating sms count based of text lenght and returning number for metrics #459

Merged
merged 6 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public interface ISmsNotificationRepository
/// <summary>
/// Adds a new sms notification to the database
/// </summary>
public Task AddNotification(SmsNotification notification, DateTime expiry);
public Task AddNotification(SmsNotification notification, DateTime expiry, int smsCount);

/// <summary>
/// Retrieves all sms notifications with status 'New'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public interface ISmsNotificationService
/// <summary>
/// Creates a new sms notification based on the provided orderId and recipient
/// </summary>
public Task CreateNotification(Guid orderId, DateTime requestedSendTime, Recipient recipient);
public Task CreateNotification(Guid orderId, DateTime requestedSendTime, Recipient recipient, int smsCount);

/// <summary>
/// Starts the process of sending all ready sms notifications
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public SmsNotificationService(
}

/// <inheritdoc/>
public async Task CreateNotification(Guid orderId, DateTime requestedSendTime, Recipient recipient)
public async Task CreateNotification(Guid orderId, DateTime requestedSendTime, Recipient recipient, int smsCount)
{
SmsAddressPoint? addressPoint = recipient.AddressInfo.Find(a => a.AddressType == AddressType.Sms) as SmsAddressPoint;

Expand All @@ -54,7 +54,7 @@ public async Task CreateNotification(Guid orderId, DateTime requestedSendTime, R

if (!string.IsNullOrEmpty(addressPoint?.MobileNumber))
{
await CreateNotificationForRecipient(orderId, requestedSendTime, smsRecipient, SmsNotificationResultType.New);
await CreateNotificationForRecipient(orderId, requestedSendTime, smsRecipient, SmsNotificationResultType.New, smsCount);
}
else
{
Expand Down Expand Up @@ -83,7 +83,7 @@ public async Task UpdateSendStatus(SmsSendOperationResult sendOperationResult)
await _repository.UpdateSendStatus(sendOperationResult.NotificationId, sendOperationResult.SendResult, sendOperationResult.GatewayReference);
}

private async Task CreateNotificationForRecipient(Guid orderId, DateTime requestedSendTime, SmsRecipient recipient, SmsNotificationResultType type)
private async Task CreateNotificationForRecipient(Guid orderId, DateTime requestedSendTime, SmsRecipient recipient, SmsNotificationResultType type, int smsCount = 0)
{
var smsNotification = new SmsNotification()
{
Expand All @@ -94,6 +94,6 @@ private async Task CreateNotificationForRecipient(Guid orderId, DateTime request
SendResult = new(type, _dateTime.UtcNow())
};

await _repository.AddNotification(smsNotification, requestedSendTime.AddHours(1));
await _repository.AddNotification(smsNotification, requestedSendTime.AddHours(1), smsCount);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using Altinn.Notifications.Core.Enums;
using System.Runtime.CompilerServices;
using System.Web;

using Altinn.Notifications.Core.Enums;
using Altinn.Notifications.Core.Models;
using Altinn.Notifications.Core.Models.Address;
using Altinn.Notifications.Core.Models.NotificationTemplate;
using Altinn.Notifications.Core.Models.Orders;
using Altinn.Notifications.Core.Models.Recipients;
using Altinn.Notifications.Core.Persistence;
Expand Down Expand Up @@ -28,15 +32,18 @@ public SmsOrderProcessingService(ISmsNotificationRepository smsNotificationRepos
/// <inheritdoc/>
public async Task ProcessOrder(NotificationOrder order)
{
int smsCount = GetSmsCountForOrder(order);

foreach (Recipient recipient in order.Recipients)
{
await _smsService.CreateNotification(order.Id, order.RequestedSendTime, recipient);
await _smsService.CreateNotification(order.Id, order.RequestedSendTime, recipient, smsCount);
}
}

/// <inheritdoc/>
public async Task ProcessOrderRetry(NotificationOrder order)
{
int smsCount = GetSmsCountForOrder(order);
List<SmsRecipient> smsRecipients = await _smsNotificationRepository.GetRecipients(order.Id);
foreach (Recipient recipient in order.Recipients)
{
Expand All @@ -47,8 +54,43 @@ public async Task ProcessOrderRetry(NotificationOrder order)
&& sr.OrganisationNumber == recipient.OrganisationNumber
&& sr.MobileNumber == addressPoint?.MobileNumber))
{
await _smsService.CreateNotification(order.Id, order.RequestedSendTime, recipient);
await _smsService.CreateNotification(order.Id, order.RequestedSendTime, recipient, smsCount);
}
}
}

/// <summary>
/// Calculates the number of messages based on the rules for concatenation of SMS messages in the SMS gateway.
/// </summary>
internal static int CalculateNumberOfMessages(string message)
{
const int maxCharactersPerMessage = 160;
const int maxMessagesPerConcatenation = 16;
const int charactersPerConcatenatedMessage = 134;

string urlEncodedMessage = HttpUtility.UrlEncode(message);
int messageLength = urlEncodedMessage.Length;

if (messageLength <= maxCharactersPerMessage)
{
return 1;
}

// Calculate the number of messages for messages exceeding 160 characters
int numberOfMessages = (int)Math.Ceiling((double)messageLength / charactersPerConcatenatedMessage);

// Check if the total number of messages exceeds the limit
if (numberOfMessages > maxMessagesPerConcatenation)
{
numberOfMessages = maxMessagesPerConcatenation;
}

return numberOfMessages;
}

private static int GetSmsCountForOrder(NotificationOrder order)
{
SmsTemplate? smsTemplate = order.Templates.Find(t => t.Type == NotificationTemplateType.Sms) as SmsTemplate;
return CalculateNumberOfMessages(smsTemplate!.Body);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

CREATE OR REPLACE PROCEDURE notifications.insertsmsnotification(
_orderid uuid,
_alternateid uuid,
_recipientorgno TEXT,
_recipientnin TEXT,
_mobilenumber TEXT,
_result text,
_smscount integer,
_resulttime timestamptz,
_expirytime timestamptz
)
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
__orderid BIGINT := (SELECT _id from notifications.orders
where alternateid = _orderid);
BEGIN

INSERT INTO notifications.smsnotifications(
_orderid,
alternateid,
recipientorgno,
recipientnin,
mobilenumber,
result,
smscount,
resulttime,
expirytime)
VALUES (
__orderid,
_alternateid,
_recipientorgno,
_recipientnin,
_mobilenumber,
_result::smsnotificationresulttype,
_smscount,
_resulttime,
_expirytime);
END;
$BODY$;
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
CREATE OR REPLACE FUNCTION notifications.getmetrics(
month_input int,
year_input int
)
RETURNS TABLE (
org text,
placed_orders bigint,
sent_emails bigint,
succeeded_emails bigint,
sent_sms bigint,
succeeded_sms bigint
)
AS $$
BEGIN
RETURN QUERY
SELECT
o.creatorname,
COUNT(DISTINCT o._id) AS placed_orders,
SUM(CASE WHEN e._id IS NOT NULL THEN 1 ELSE 0 END) AS sent_emails,
SUM(CASE WHEN e.result = 'Succeeded' THEN 1 ELSE 0 END) AS succeeded_emails,
SUM(CASE WHEN s._id IS NOT NULL THEN s.smscount ELSE 0 END) AS sent_sms,
SUM(CASE WHEN s.result = 'Accepted' THEN 1 ELSE 0 END) AS succeeded_sms
FROM notifications.orders o
LEFT JOIN notifications.emailnotifications e ON o._id = e._orderid
LEFT JOIN notifications.smsnotifications s ON o._id = s._orderid
WHERE EXTRACT(MONTH FROM o.requestedsendtime) = month_input
AND EXTRACT(YEAR FROM o.requestedsendtime) = year_input
GROUP BY o.creatorname;
END;
$$ LANGUAGE plpgsql;
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class SmsNotificationRepository : ISmsNotificationRepository
private readonly NpgsqlDataSource _dataSource;
private readonly TelemetryClient? _telemetryClient;

private const string _insertSmsNotificationSql = "call notifications.insertsmsnotification($1, $2, $3, $4, $5, $6, $7, $8)"; // (__orderid, _alternateid, _recipientorgno, _recipientnin, _mobilenumber, _result, _resulttime, _expirytime)
private const string _insertSmsNotificationSql = "call notifications.insertsmsnotification($1, $2, $3, $4, $5, $6, $7, $8, $9)"; // (__orderid, _alternateid, _recipientorgno, _recipientnin, _mobilenumber, _result, _smscount, _resulttime, _expirytime)
private const string _getSmsNotificationsSql = "select * from notifications.getsms_statusnew_updatestatus()";
private const string _getSmsRecipients = "select * from notifications.getsmsrecipients_v2($1)"; // (_orderid)

Expand All @@ -44,7 +44,7 @@ public SmsNotificationRepository(NpgsqlDataSource dataSource, TelemetryClient? t
}

/// <inheritdoc/>
public async Task AddNotification(SmsNotification notification, DateTime expiry)
public async Task AddNotification(SmsNotification notification, DateTime expiry, int smsCount)
{
await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_insertSmsNotificationSql);
using TelemetryTracker tracker = new(_telemetryClient, pgcom);
Expand All @@ -55,6 +55,7 @@ public async Task AddNotification(SmsNotification notification, DateTime expiry)
pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, (object)DBNull.Value); // recipientnin
pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, notification.Recipient.MobileNumber);
pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, notification.SendResult.Result.ToString());
pgcom.Parameters.AddWithValue(NpgsqlDbType.Integer, smsCount);
pgcom.Parameters.AddWithValue(NpgsqlDbType.TimestampTz, notification.SendResult.ResultTime);
pgcom.Parameters.AddWithValue(NpgsqlDbType.TimestampTz, expiry);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public async Task AddNotification()
}
};

await repo.AddNotification(smsNotification, DateTime.UtcNow);
await repo.AddNotification(smsNotification, DateTime.UtcNow, 1);

// Assert
string sql = $@"SELECT count(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public static async Task<NotificationOrder> PopulateDBWithOrderAndEmailNotificat
}

await orderRepo.Create(order);
await notificationRepo.AddNotification(smsNotification, DateTime.UtcNow.AddDays(1));
await notificationRepo.AddNotification(smsNotification, DateTime.UtcNow.AddDays(1), 1);

return (order, smsNotification);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="7.3.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="RandomString4Net" Version="1.8.0" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ public async Task CreateNotifications_NewSmsNotification_RepositoryCalledOnce()
var service = GetTestService(repo: repoMock.Object);

// Act
await service.CreateNotification(Guid.NewGuid(), DateTime.UtcNow, new Recipient(new List<IAddressPoint>() { new SmsAddressPoint("999999999") }, nationalIdentityNumber: "enduser-nin"));
await service.CreateNotification(Guid.NewGuid(), DateTime.UtcNow, new Recipient(new List<IAddressPoint>() { new SmsAddressPoint("999999999") }, nationalIdentityNumber: "enduser-nin"), It.IsAny<int>());

// Assert
repoMock.Verify(r => r.AddNotification(It.IsAny<SmsNotification>(), It.IsAny<DateTime>()), Times.Once);
repoMock.Verify(r => r.AddNotification(It.IsAny<SmsNotification>(), It.IsAny<DateTime>(), It.IsAny<int>()), Times.Once);
}

[Fact]
Expand All @@ -72,15 +72,15 @@ public async Task CreateNotification_RecipientNumberIsDefined_ResultNew()
};

var repoMock = new Mock<ISmsNotificationRepository>();
repoMock.Setup(r => r.AddNotification(It.Is<SmsNotification>(e => AssertUtils.AreEquivalent(expected, e)), It.Is<DateTime>(d => d == expectedExpiry)));
repoMock.Setup(r => r.AddNotification(It.Is<SmsNotification>(e => AssertUtils.AreEquivalent(expected, e)), It.Is<DateTime>(d => d == expectedExpiry), It.IsAny<int>()));

var service = GetTestService(repo: repoMock.Object, guidOutput: id, dateTimeOutput: dateTimeOutput);

// Act
await service.CreateNotification(orderId, requestedSendTime, new Recipient(new List<IAddressPoint>() { new SmsAddressPoint("+4799999999") }));
await service.CreateNotification(orderId, requestedSendTime, new Recipient(new List<IAddressPoint>() { new SmsAddressPoint("+4799999999") }), 1);

// Assert
repoMock.Verify(r => r.AddNotification(It.Is<SmsNotification>(e => AssertUtils.AreEquivalent(expected, e)), It.Is<DateTime>(d => d == expectedExpiry)), Times.Once);
repoMock.Verify(r => r.AddNotification(It.Is<SmsNotification>(e => AssertUtils.AreEquivalent(expected, e)), It.Is<DateTime>(d => d == expectedExpiry), It.IsAny<int>()), Times.Once);
}

[Fact]
Expand All @@ -102,12 +102,12 @@ public async Task CreateNotification_RecipientNumberMissing_ResultFailedRecipien
};

var repoMock = new Mock<ISmsNotificationRepository>();
repoMock.Setup(r => r.AddNotification(It.Is<SmsNotification>(e => AssertUtils.AreEquivalent(expected, e)), It.Is<DateTime>(d => d == expectedExpiry)));
repoMock.Setup(r => r.AddNotification(It.Is<SmsNotification>(e => AssertUtils.AreEquivalent(expected, e)), It.Is<DateTime>(d => d == expectedExpiry), It.IsAny<int>()));

var service = GetTestService(repo: repoMock.Object, guidOutput: id, dateTimeOutput: dateTimeOutput);

// Act
await service.CreateNotification(orderId, requestedSendTime, new Recipient(new List<IAddressPoint>()));
await service.CreateNotification(orderId, requestedSendTime, new Recipient(new List<IAddressPoint>()), It.IsAny<int>());

// Assert
repoMock.Verify();
Expand Down
Loading
Loading