I build a small web-app using flask for my parents to text their customers about promotions. I am using Twilio API. I just finished the myapp.com/new-campaign
page and I would like to have some suggestions as it’s my first full stack applicaton as I just added a new feature: estimated cost of the campaign (coût
)
What are the requirements of this page:
List
: Select a list of clients (I don’t think we will ever have more than 2 lists, one for testing and 1 for customers)Coût
: total estimated cost for the campaignMessage
: message to send
How is the cost calculated?
- number of recipient (which lists I am using)
- message length (basically, between
0
and160
characters it’ssegment 1
;160
to320
it’ssegment 2
and so on).Segment 1
is$0.06
,segment 2
is$0.06 * 2
and so on.
My goal was to give a direct estimate of the total cost of a campaign.
- When the user select a new customer list: making a back-end call to get the length of the list
- For the message input, I told myself that it would be stupid to make a back-end call everytime I am adding a remove a letter in the
textarea
. I just to need to recalculate the cost when it’s reaching a newsegment
base on number of characters. So I am using JS for that and making an AJAX call only when I am in a new segment (upgrade or downgrade)
message-calculator.js
var inputMessageLength = 0;
var currentSegment = 1;
var SelectedCustomerList;
// on load of the page
$(document).ready(function () {
updateList(getSelectedCustomersList());
updateCost();
});
// call the get_cost_estimation to update the price
function updateCost() {
$.ajax({
url: "/get_cost_estimation",
type: "POST",
data: JSON.stringify({
input_length: inputMessageLength,
selected_list: SelectedCustomerList,
}),
contentType: "application/json; charset=utf-8",
dataType: "json",
}).done(function (data) {
var newCost = data('estimated_cost') + ' ' + data('currency');
document.getElementById("total-cost-value").innerHTML = newCost
});
}
function updateList(NewSelectedCustomerList) {
SelectedCustomerList = NewSelectedCustomerList;
updateCost();
}
function getSelectedCustomersList() {
return $("input(name=list):checked").val();
}
function updateSegmentNumber(newSegmentNumber){
currentSegment = newSegmentNumber;
}
// caled everytime the user make a change in the message input area
function updateMessage(body, SegmentCharactersLimit) {
var messageContent = body.value;
inputMessageLength = messageContent.length;
updateSegmentMessage(inputMessageLength, SegmentCharactersLimit);
var newSegment = Math.ceil(inputMessageLength / SegmentCharactersLimit)
if(newSegment != currentSegment){
updateSegmentNumber(newSegment);
updateCost();
}
}
// alert message displayed to tell the client that he already reached the limit of segment 1
function updateSegmentMessage(inputMessageLength, SegmentCharactersLimit) {
if (inputMessageLength > SegmentCharactersLimit) {
document.getElementById("message-cost-alert").style.display = "block";
} else {
document.getElementById("message-cost-alert").style.display = "none";
}
}
views.py
from config.settings import COST_PER_SEGMENT, MAX_CARACTERS_PER_SEGMENT
...
def total_cost_estimation(quantity, input_length):
number_of_segments = math.ceil(input_length / MAX_CARACTERS_PER_SEGMENT)
if number_of_segments < 1:
number_of_segments = 1
estimated_cost_per_sms = number_of_segments * COST_PER_SEGMENT
total_estimated_cost = estimated_cost_per_sms * quantity
print(estimated_cost_per_sms)
print(quantity)
return round(total_estimated_cost, 2)
@user.route("/get_cost_estimation", methods=('GET', 'POST'))
def get_cost_estimation():
currency = '€'
input_length = request.json('input_length')
selected_list = request.json('selected_list')
if selected_list == 'test-list':
count = mongo.db(customers_test).count()
else:
count = mongo.db(customers_production).count()
estimated_cost = str(total_cost_estimation(count, input_length))
return jsonify(
estimated_cost=estimated_cost,
currency=currency,
)
@user.route("/campaigns", methods=('GET'))
@login_required
def campaigns():
return render_template('campaigns.html', currency='€', cost_per_sms = 0, max_caracters = MAX_CARACTERS_PER_SEGMENT)
...
campaigns.html
<div class="container">
<h2>Choisissez votre message</h2>
<form name="sms-form" action="/launch-campaign" method="POST">
<div class="form-group row">
</div>
<div class="form-group row">
</div>
<fieldset class="form-group">
<div class="row">
<legend class="col-form-label col-sm-2 pt-0">Liste</legend>
<div class="col-sm-10">
<div class="form-check">
<input class="form-check-input" onclick="updateList('test-list')" type="radio" name="list" id="gridRadios1" value="test-list" checked>
<label class="form-check-label" for="gridRadios1">
Test
</label>
</div>
<div class="form-check">
<input class="form-check-input" onclick="updateList('production-list')" type="radio" name="list" id="gridRadios2" value="production-list">
<label class="form-check-label" for="gridRadios2">
Clients
</label>
</div>
</div>
</fieldset>
<fieldset class="form-group">
...
</fieldset>
<div class="form-group row">
<div class="col-sm-2">Message</div>
<div class="col-sm-10">
<div class="form-check">
<textarea name="body" oninput="updateMessage(this, {{max_caracters}})" id="form10" class="md-textarea form-control" rows="3"></textarea>
</div>
<div id="message-cost-alert"class="alert alert-warning alert-dismissible fade show">
<strong>Attention!</strong> Votre message dépasse le nombre de {{max_caracters}} caracters
<button type="button" class="close" data-dismiss="alert">×</button>
</div>
<div class="form-group row">
<div class="col-sm-10">
<br>
<button type="submit" class="btn btn-primary" onclick="return confirm('Are you sure?')">Envoyer</button>
</div>
</div>
</form>
</div>