Access bus booking services using Django & AfricasTalking API

I am very certain that each one of us has, in the past took a long journey and you had to find a vehicle that goes express without dropping you midway. Chances are you went to their booking office, acquired a ticket and then waited for your bus to arrive ,board it then take off. Notice that you had to manually go to the station, queue then get your flight ticket.
This is all fine given the circumstances but what if I told you that we could digitize this process of acquiring bus ticket such that you can book a bus while seated in your house and only have to go and start the journey when the time for your bus has come? We are going to see how.
In this post we are going use Django to create a USSD bus booking system where:
  • Users can query all buses in the system
  • Users can book a bus
  • Users can check the status of their flight
  • Users can cancel their flight
  • Users can report complains
  • The USSD service is made possible thanks to Africa'sTalking. We will use their sandbox environment to simulate a real life scenario of our application. Without further talk let's start!
    PROJECT SETUP
    Let's quicky create a new project:
    mkdir django_ussd && cd django_ussd
    
    virtualenv env
    
    source env/bin/activate
    
    pip install django
    
    django-admin startproject mysite .
    
    python manage.py startapp core
    Next add core to the list of installed apps in settings.py.
    Now let's create the following models to hold our data:
    from django.db import models
    # Create your models here.
    class Bus(models.Model):
        number_plate=models.CharField(
            max_length=10
        )
        start=models.CharField(
            max_length=250
        )
        finish=models.CharField(
            max_length=250
        )
        price = models.FloatField()
        seats=models.IntegerField()
        is_available=models.BooleanField(
            default=True
        )
        def __str__(self) -> str:
            return self.number_plate
    
    class Booking(models.Model):
        STATUS_CHOICES = [ 
        ("Awaiting departure", "Awaiting departure"),
        ("Journey complete", "Journey complete"),
        ("Cancelled", "Cancelled"),
        ]
        bus = models.ForeignKey(Bus, on_delete=models.CASCADE)
        customer = models.CharField(max_length = 150)
        seat=models.IntegerField(default=0)
        date = models.DateTimeField(auto_now_add=True)
        departure=models.DateTimeField(null=True, blank=True)
        status = models.CharField(
            max_length = 20,
            choices = STATUS_CHOICES,
            default = 'Awaiting departure'
            )
        def __str__(self) -> str:
            return self.customer
    
    from django.contrib import admin
    admin.site.register(Bus)
    admin.site.register(Booking)
    So our Bus class will have all details related to a given bus while Booking will store user tickets. Go ahead and makemigrations then migrate.
    Create an admin user then go to the admin section and add a few buses that we will use for testing.
    Next let's write our application logic. Open views.py and add the code below to it. If need be, by all means take a minute to go through it and understand. It is pretty straight forward.
    import random
    from datetime import datetime, timedelta
    from core.models import Booking, Bus
    from django.views.decorators.csrf import csrf_exempt
    from django.http import HttpResponse
    
    # Create your views here.
    @csrf_exempt
    def index(request):
        if request.method == 'POST':
            session_id = request.POST.get('sessionId')
            service_code = request.POST.get('serviceCode')
            phone_number = request.POST.get('phoneNumber')
            text = request.POST.get('text')
    
            response = ""
    
            if text == "":
                response = "CON Welcome! \n Which service would you like to access? \n"  
                response += "1. List all our buses  \n"
                response += "2. Check ticket status \n"
                response += "3. Book a bus seat \n"
                response += "4. Cancel a booking \n"
                response += "5. Report an issue"
    
             #User needs a list of all buses   
            elif text == "1":
                results=Bus.objects.all()
                for i in results:
                    response += f"END {i}:{i.start}-{i.finish} @KSHS{i.price} \n \n"
    
            elif text == "2":
                response = "CON Choose an option \n"
                response += "1. All tickets \n"
                response += "2. Today active tickets"
    
             #Follow up
            elif text == '2*1':
                tickets=Booking.objects.filter(
                    customer=phone_number
                )
                for tkt in tickets:
                    response += f"END Ticket {tkt.id} on {tkt.departure:%Y-%m-%d %H:%M}"
    
             #Follow up
            elif text == '2*2':
                now = datetime.now()
                tickets=Booking.objects.filter(
                    customer=phone_number,
                    departure__date=now
                )
                if tickets:
                    for tkt in tickets:
                        response += f"END Ticket {tkt.id} on {tkt.departure:%Y-%m-%d %H:%M}"
                response ='END No tickets found'
    
            #User wants to book a seat
            elif text == "3":
                response = "CON Okay, pick a route \n"
                response += "1. Kericho-Nairobi \n"
                response += "2. Kisumu-Eldoret \n"
                response += "3. Nakuru-Mombasa \n"
                response += "4. Narok-Naivasha "
    
            #Follow up
            elif text == '3*1' or '3*2' or '3*3' or '3*4':
                seat=random.randint(1,30)
                buses=Bus.objects.filter(is_available=True)
                buses=[bus for bus in buses]
                bus=random.choices(buses)
                for i in bus:
                    bus=i
                departure=datetime.now() + timedelta(hours=1)
                new_booking=Booking.objects.create(
                    bus=bus,
                    customer=phone_number,
                    seat=seat,
                    departure=departure
    
                )
                response = f"END  Alright! Here is your booking info: \n TICKET NO {new_booking.id} \n Bus Number is {bus} \n Your seat number is {seat} \n Your bus leaves at {departure:%H:%M:%S}"  
            elif text == "4":
                response = "END Feature work in progress, check again soon"
            elif text == "5":
                response = "END Feature work in progress, check again soon"
    
            return HttpResponse(response)
    With that figured out we can move on to create the route for it. Update the project urls.py like so:
    from django.contrib import admin
    from django.urls import path
    from core import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', views.index),
    ]
    And that should be all we need. Now the next part is to expose our application to the internet because AfricasTalking cannot find our localhost. A great tool for such is Ngrok . Follow its instructions to get installed on your machine if you haven't already.
    To tunnel our app:
    ngrok http 8000
    Result:
    ngrok
    Copy that https link we'll need it next up.
    AFRICASTALKING SETUP
    Log in to your AfricasTalking account then head over to the simulator section click on USSD part and click to create a new session. Now add the ngrok link that you created before to act as our callback. In case you need to know what a callback is, then here is a post. Now the form should be like shown below:
    session
    Save the form:
    form
    So for me, the app will be invoked by dialing *384*1827# yours may be a bit different but should work.
    SIMULATING
    We now have an application baked and we have configured our sandbox environment. Go ahead and launch the simulator then pick 'USSD' when the phone shows up. Enter your code, for me it is *384*1827#. The behavior should be:
    WRAP UP
    If you are still here then hats off to you. Congratulations. In this tutorial we were solving a transport problem. We built a solution from scratch to help passengers book buses and access a range of related services offline via USSD. Thus whether on featured phones(Kabambe) or smartphone, they can have at it with no problem.
    That is all I had for you guys. Let me know if you encounter an issue or have a suggestion in the comments below. Also if possible show some love to this post.
    As usual you can follow me here or on Twitter for more awesome content.
    Till next time, cheers!

    38

    This website collects cookies to deliver better user experience

    Access bus booking services using Django & AfricasTalking API