python – text-messaging app using twilio

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) enter image description here

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 campaign
  • Message: message to send

How is the cost calculated?

  1. number of recipient (which lists I am using)
  2. message length (basically, between 0 and 160 characters it’s segment 1 ; 160 to 320 it’s segment 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.

  1. When the user select a new customer list: making a back-end call to get the length of the list
  2. 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 new segment 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 = '&euro;'
        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">&times;</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>