Category: Programming and Scripting

Home / Category: Programming and Scripting

Python: Biorhythm Implementation

June 26, 2019 | Python | 2 Comments

1 Background and Introduction

Sigmund Freud and Wilhelm Fliess, in late 1800 were working on a research to emphasis that mental and physical fitness has a distinguishable pattern. As a result of that, they could came up with a conclusion that human life has two cycle in their lives, 23 days and 28 days, where 23 days cycle is subjected men and 28 days cycle is subjected to women.

Followed by these findings and additional researches which conducted by scientist later on, it was happened to found that human physical strength and well-being seems to showing 23 days cycle, emotional potentials showing 28 days cycle and intellectual well-being showing 33 days cycle.

Theses patterns could demonstrate by sin or cosine function.

t=Number days count from the birth

Physical (Py) =

Emotional (Ey) =

Intellectual (Iy) =

Though in 80’s Biorhythms were so much popular, later on, most of the studies showed that this concept is not valid. Either way to overcome the negative potentials, self-confident is the key.

2 Classes and Functions

In this section, shall we discuss the class and function on our sample code.

2.1       Biorhythm

Main purposes of this class is to facilitate users to create the Biorhythm objects and using these objects derive the data sets to draw the Biorhythm graph in three major aspect. Further in class constuctor there are two default values are defined for user’s convenience.

2.1.1       dateReformat

As the default date format, within this class, it has used “DD/MM/YYYY”. Either user’s input or system derived dates should be stored and used in a standardize format. This function cast/convert and return date string to date object which ought to be used in other functions.

2.1.2       countDates

By providing two argument (Date one, Date two), calling party can derive how many days are there in between mentioned dates.

2.1.3       physicalRhythm

The function to calculate physical well being functional data set. This function use following algorithm to calculate the dataset for physical well-being.

Further this will return a python dictionary, particular date as the key and the well being ratio as the value.

2.1.4       intellectualRhythm

The function to calculate intellectual well being functional data set. This function use following algorithm to calculate the dataset for intellectual well-being.

Further this will return a python dictionary, particular date as the key and the well being ratio as the value.

2.1.5       emotionalRhythm

The function to calculate emotional well being functional data set. This function use following algorithm to calculate the dataset for emotional well-being.

Further this will return a python dictionary, particular date as the key and the well being ratio as the value.

2.1.6       compositRhythm

Based on above three major calculation this function calculate the mean value for given date and return as a python dictionary.

Since this function is not a major priority, if any error occurs when processing the composite dataset, these errors will be discarded. Further, to use this function all the other data sets should be created prematurely.

2.2       Plots

This class is to create a multiple line plot by using any number of data sets provided. This class has only one constructor. As the first argument, user can pass, general data related to the graph using python dictionary.

<Variable Name >={‘title’: “<Title of the graph>” #Mandatory

,’axis_X’: “<Description for X axis” #Mandatory

,’axis_Y’: “<Description for Y axis”  #Mandatory

,’figSize’:[<Length: Horizontal>,<Length: Vatical>] #Optional

,‘grid’:<Show grid in the graph True/False> #Optional

,’toggleAxis’:<Change X and Y axis True/False> #Optional

}

As the last argument (non-keyworded variable length argument), the constructor accepts, any number datasets (as python dictionaries) to create the graph.

<Variable Name>= {‘<Name of the Line graph 1>’: {‘axis_X’:<Data set for X axis>, ‘axis_Y’:<Data set for Y axis>},

.

.

‘<Name of the Line graph n>‘: {‘axis_X’:<Data set for X axis>, ‘axis_Y’:<Data set for Y axis>}, ….}

2.3       Runner Program

This program integrates above two class to draw the biorhythm plot. In case of a interactive user input, it is required to uncomment line number 11 to 15 and comment line 18 and 19.

3 pseudo codes

3.1 biorhythm

Start
     Get date of birth
     Get targeted date
     Get date count (from the target date)
     Covert Date string to Date Object

 PHYSICAL

     From “target date” to “target date + date count”
          Calculate Physical well-being ratio
          sin⁡(2πt/23)
     Return Data dictionary [Key=> date,Value=>ratio]

 INTELLECTUAL

     From “target date” to “target date + date count”
          Calculate Intellectual well-being ratio
          sin⁡(2πt/28)
     Return Data dictionary [Key=> date,Value=>ratio]

 EMOTIONAL

     From “target date” to “target date + date count”
          Calculate Emotional well-being ratio
          sin⁡(2πt/33)
     Return Data dictionary [Key=> date,Value=>ratio]
 COMPOSITE
     Until last element of physical well-being Data set
          Calculate the mean values for similar indexes; Physical, Emotional, Intellectual data sets
          Store and return a Data Dictionary [Key=> date, Value=>relevant mean value]
Exit

3.2 Plots

Start
     Get general details of the graph
     Get datasets
     Until last dataset read all the datasets
     If toggle flag is flagged
          Until last dataset read all the datasets
               Store values for X axis
               Store values for Y axis
          Toggle X and Y axis
          Rotate X axis label for 60 degree
     If toggle flag is not flagged
          Until last dataset read all the datasets
               Store values for X axis
               Store values for Y axis
     If grid flag is flagged
          Show grids on the plot
     If custom figure size is provided
          Set dimension of the plot
     Set the title
     Set the legends
     Draw the plot
Exit

3.3 Execution (Runner)

Start
     Provide User name
     Create Biorhythm object [birthday, target date, date count]
     Set and configure graph properties => Dataset #1
     Configure data sets => Dataset #2
     Create Plot object using Dataset #1 and #2
Exit

4 bio.py

# -*- coding: utf-8 -*-
#import datetime
import math 
from datetime import date
import datetime
import statistics
import sys

class Biorhythm:

    def  __init__(self,birthday,startDate=str(datetime.date.today().strftime('%d/%m/%Y')),length=30):
        self.emotional = {}  # emotional well-being list 
        self.intellectual = {}  # intellectual well-being list 
        self.composit = {}
        self.physical = {}
        self.startDate=startDate
        self.birthday=birthday
        self.length=length     
                
    def dateReformat(self,dateStr): #Covert to system redable date format
        try:
            reformatedDate = datetime.datetime.strptime(dateStr,'%d/%m/%Y') # convert to format datetime.date(year, month, day)
            return reformatedDate.date()
        except:
            sys.exit("Please insert a valid date[DD/MM/YYYY]")
            
    def countDates(self,firstDate,lastDate):
        try:
            splitedFirstDate=firstDate.split('/')
            splitedLastDate=lastDate.split('/')
            return (date(int(splitedFirstDate[2]),int(splitedFirstDate[1]),int(splitedFirstDate[0]))-date(int(splitedLastDate[2]),int(splitedLastDate[1]),int(splitedLastDate[0]))).days
        except:
            sys.exit("Something went wrong when couting the dates.")
            
    def physicalRhythm(self):
        date=self.dateReformat(self.startDate) - datetime.timedelta(days=4)
        try:
            for datePointer in range(int(self.countDates(self.birthday,self.startDate))-4,self.countDates(self.birthday,self.startDate)+self.length):
#                self.physical[date.strftime('%d/%m/%Y-%a')] = math.sin(2*math.pi*datePointer/23)
                self.physical[date] = math.sin(2*math.pi*datePointer/23)
                date += datetime.timedelta(days=1)                
            return self.physical
        except:
            sys.exit("Something went wrong! Please insert valid data followed by the format.")

    def intellectualRhythm(self):
        date=self.dateReformat(self.startDate) - datetime.timedelta(days=4)
        try:
            for datePointer in range(int(self.countDates(self.birthday,self.startDate))-4,self.countDates(self.birthday,self.startDate)+self.length):
 #               self.intellectual[date.strftime('%d/%m/%Y-%a')]=math.sin(2*math.pi*datePointer/28)
                self.intellectual[date]=math.sin(2*math.pi*datePointer/28)
                date += datetime.timedelta(days=1)
            return self.intellectual
        except:
            sys.exit("Something went wrong! Please insert valid data followed by the format.")
    
    def emotionalRhythm(self):        
        date=self.dateReformat(self.startDate) - datetime.timedelta(days=4)
        try:
            for datePointer in range(int(self.countDates(self.birthday,self.startDate))-4,self.countDates(self.birthday,self.startDate)+self.length):
#                self.emotional[date.strftime('%d/%m/%Y-%a')]=math.sin(2*math.pi*datePointer/33)
                self.emotional[date]=math.sin(2*math.pi*datePointer/33)                
                date += datetime.timedelta(days=1)
            return self.emotional
        except:
            sys.exit("Something went wrong! Please insert valid data followed by the format.")

    def compositRhythm(self):
        try:
            for physicalElement_key,physicalElement_value in  self.physical.items():
                tmpList=[physicalElement_value,self.intellectual[physicalElement_key],self.emotional[physicalElement_key]]
                self.composit[physicalElement_key]=statistics.mean(tmpList)
                tmpList.clear()
            return self.composit
        except:
            pass

5 plots.py

from tqdm import tqdm
#from matplotlib.pyplot import figure
import matplotlib.pyplot as plt 
class Plots:
    def __init__(self,plotDisc,*args):
        
        if  ('toggleAxis' in plotDisc.keys() and plotDisc['toggleAxis']):
            plt.xlabel(plotDisc['axis_Y'])
            plt.ylabel(plotDisc['axis_X'])
            for dataSet in tqdm(args):
                for key, val in dataSet.items():
                    plt.plot(val['axis_Y'], val['axis_X'], label = key)
            plt.xticks(rotation=60)
        
        else:
            plt.xlabel(plotDisc['axis_X'])
            plt.ylabel(plotDisc['axis_Y'])
            for dataSet in tqdm(args):
                for key, val in dataSet.items():
                    plt.plot(val['axis_X'], val['axis_Y'], label = key) 
                    
        #Show/Hide Grid
        if ('grid' in plotDisc.keys() and plotDisc['grid']):
            plt.grid('on')
            
        #Set Plot size
        if ('figSize' in plotDisc.keys()):
            plt.rcParams["figure.figsize"] = plotDisc['figSize']
            
        #Hide Axis
        if ('hideAxis_X' in plotDisc.keys() and plotDisc['hideAxis_X']):
            frame = plt.gca()
            frame.axes.get_xaxis().set_ticks([])
            
        if ('hideAxis_Y' in plotDisc.keys() and plotDisc['hideAxis_Y']):
            frame = plt.gca()
            frame.axes.get_yaxis().set_ticks([])
            
        #Set Plot title and Legend
        plt.title(plotDisc['title']) 
        plt.legend()
        
        #Set tick Limit
        if ('tickLimit_X' in plotDisc.keys()):
            plt.locator_params(nbins=plotDisc['tickLimit_X'],axis='x')
        
        if ('tickLimit_Y' in plotDisc.keys()):
            plt.locator_params(nbins=plotDisc['tickLimit_Y'],axis='y')
            
        #Draw plot
        plt.show()

6 runner.py

from plots import Plots
from bio import Biorhythm


bio=Biorhythm("10/2/1993"   #Mandatory
              ,"1/8/1990"   #Optional
              ,100          #Optional
              )

user="binbash"
#Data set to make the graph
dataSet = {'physical': {'axis_X':list(bio.physicalRhythm().values()), 'axis_Y':list(bio.physicalRhythm().keys())}
      ,'emotional': {'axis_X':list(bio.emotionalRhythm().values()), 'axis_Y':list(bio.emotionalRhythm().keys())}
      ,'intellectual': {'axis_X':list(bio.intellectualRhythm().values()), 'axis_Y':list(bio.intellectualRhythm().keys())}
      ,'composit': {'axis_X':list(bio.compositRhythm().values()), 'axis_Y':list(bio.compositRhythm().keys())}
     }
#Change properties of the graph
plotDisc={'title':'{}\'s-Biorhythm'.format(user) #Mandatary
    ,'axis_X':'Well-Being' #Mandatary 
    ,'axis_Y':'Date' #Mandatary
    ,'figSize':[20,5] #Optional
    ,'grid':True #Optional
    ,'toggleAxis':True #Optional
    ,'hideAxis_X':False #Optional
    ,'hideAxis_Y':False #Optional

#    ,'tickLimit_X':1 #Optional
#    ,'tickLimit_Y':8 #Optional
    } 

plts=Plots(plotDisc,dataSet)

7 Reference

3.7.3 Documentation. (2019). Retrieved from https://docs.python.org/3/

Hunter, J., Dale, D., Droettboom, M., & Firing, E. (2019). matplotlib.pyplot.show — Matplotlib 3.1.0 documentation. Retrieved from https://matplotlib.org/api/_as_gen/matplotlib.pyplot.show.html

Zundel, J. (2019). Biorhythms: Definition, History & Calculation | Study.com. Retrieved from https://study.com/academy/lesson/biorhythms-definition-history-calculation.html

Database Transactions with Perl

To understand what is a database transaction lets look at a simple bank withdrawal and deposit scenario.
Assume that you have two bank accounts call A and B, and you need withdraw some amount from account A and Deposit it on account B.
In this scenario what happen if you couldn’t withdraw money from your account A, the deposit part won’t carry out.
Again if you couldn’t deposit the money to your account B you have to deposit them back on account A (Which means a roll back).
So in the  context of Database, Transaction is refer to  a sequence of jobs which is supposed to run as a whole. So in other words, it should happen as whole or not.
So as in our following example, we have 3 Database queries which should perform as a whole. Further assume that our first Database query is supposed to perform a insert if successful, second query  should update a table if successful, third query  should delete an entry from a table.  So if any of them fails due to any circumstances everything should rollback to initial state.
Anyway the concept behind the Transaction is your data will be in sensible state no matter what happens.
There are four types of properties the context of Database transaction (know as ACID).
  1. Atomic :  If one part of the transaction fails, then the entire transaction fails, and the database state is left unchanged.
  2. Constraints : Only valid changes will be accepted, If changes are invalid system leave with the previous state.
  3. Isolated : Until transactions are committed no one else is supposed to see the changes.
  4. Durable : Once a valid change is committed, it will remain so.  
You can find a pretty good explanation about ACID in here.
Okay now lets looks in to our perl example.
First of all lets install perl-mysql dependencies for our Debian system
        
#apt-get install libdbd-mysql-perl 
Afterwards lets install the DBI module for perl,
#cpan
>install DBI

   

Now lets look in to our perl script, (Following content should resides on your perl script)
I have use a hash to store database configuration (MYSQL), and another Hash to store connection attributes (ATTRIB) (You can also store these attributes on MYSQL hash, depends on your requirement)

 

#!/usr/bin/perl
use strict;                                                              
use warnings;                                                       
use DBI;                     #Including DBI module for databases
my %MYSQL = (
         hostname   =>  "localhost",
         username   =>  "root",
         password   =>  "password",
         database   =>  "customers"
);
my %ATTRIB = (RaiseError  =>  1,      #Enable error handling
              AutoCommit  =>  0       #Enabling Transactions
);
Now we shall establish our Databases connections (Since its so obvious I’m not going to explain database connection string),
 my $DB_CON = DBI->connect("dbi:mysql:$MYSQL{database}:$MYSQL{hostname}","$MYSQL{username}","$MYSQL{password}",%ATTRIB) || die("Couldn't connect to the Database!n");
Now lets execute our sample transaction (In following example we wrap our db quires inside a transaction),
In following example, I’ve use eval function, Generally eval interprets a string as code further eval lets a perl program run a perl code within itself.
eval {

#Update quarry 
 $SQL_STR = "UPDATE cus_info SET cus_tp='$CUS_TP' WHERE cus_id=$CUS_ID";
        $SQL_EXEC = $DB_CON->prepare($SQL_STR);
 $SQL_EXEC->execute();
 print "Update Quarry Executedn";

#Insert quarry 
 $SQL_STR = "INSERT INTO product_main(prod_name,prod_stock) VALUES ('$PRODUCT_NAME','$PRODUCT_STOCK')";
        $SQL_EXEC = $DB_CON->prepare($SQL_STR);
        $SQL_EXEC->execute();
        print "Insert Quarry Executedn";

#Delete quarry
 $SQL_STR = "DELETE FROM product_info WHERE prod_id='$PRODUCT_ID'";
        $SQL_EXEC = $DB_CON->prepare($SQL_STR);
        $SQL_EXEC->execute();
        print "Delete Quarry Executedn";

#Commit If everything went well
 $DB_CON->commit();
};
According to our scenario if there is any unexpected behavior occurred, it should rollback any changes and database should be keep previous state.
if($@){
        print "Transactions were Rolled Baskn";   
        $DB_CON->rollback();
}
PS:  ‘$@’ will set if our eval did not compile. Please click here for further information from perl docs.
Now lets close our connection to the database,
$DB_CON->disconnect();

Click  here to download the  sample program including database dump.

Conclusion

  • Database transaction is a sequence of jobs which is supposed to run as a whole.
  • There are four type of properties regards to Database transactions
    • Atomic
    • Constraints
    • Isolated
    • Durable
 

Asterisk Gateway Interface

1. What is Asterisk Gateway Interface? 

 
In simple word AGI is Language Independent API to programmers to control the call flow on their Asterisk PBXs.
 
Asterisk provides more than its own dial-plan, to control to the call flow or lets say call logics. So which means you may use either one of
 
  1. Dialplan
  2. Asterisk Manager Interface (AMI)
  3. Asterisk Gateway Interface (AGI)
to manipulate your call logics.
 
Before we move on to AGI lets briefly discuss about each one of above,
 


Dialplan

Dial plan is Asterisk native call logics performer, it’s fast, easy to learn and efficient. But this configuration script is more closer to assembly program (If you have any previous experience on assembly), the main drawback of the Asterisk Dialplan in it’s lack of support on standard procedural  language as an example when you want create a loop. 
 
Any way in the following tutorials we will only discuss about the AGI, But we can’t avoid the fact that we need Dialplan to call out AGI script.
 


Asterisk Manager Interface (AMI)

What if you have a remote controller to your Asterisk PBX, it is AMI. It is more sophisticate with regards to Dialplan. So in essence you can control your PBX by using TCP socket. 
 
You can use a remote control tool to control your PBX but beware there are more security concerns would be followed up with this.


Asterisk Gateway Interface (AGI)

If you consider AMI and Dialplan regards to AGI, AGI should lay between them. AGI can’t be completely independent, we have to get the support of the Dialplan, also you may use your desired language on AGI script. 
 
In this tutorial I’ll use Perl on our AGI script…
 
 
But please note that if your PBX system is pure outbound AGI is not for you, AGI is only for inbound call  handling.
 
There are four type of AGI’s you can find,
  • Standard AGI
It is the Simplest of all, and use standard inputs(STDIN) and outputs(STDOUT) to communicate with the PBX. In this tutorial are about to use this one. 
  • Dead AGI
Dead AGI is to handle the call logics after call hangup. Some AGI commands will not be able to use under this. 
  • Fast AGI
Using Fast AGI you can transfer your AGI processing burden to another server. You may use TCP sockets for the communications. It’s Provide all the features in Standard AGI. 
  • EAGI
If Developers need to access/communicate beyond the STDIN and STDOUT which if they need to access the media channel, they may use EAGI.
 


2. Okay Now it’s time to discuss our scenario,

I have two SIP peers call, peter and bob. We need them to able call with each other, and if one of them is not available, calling party should be able to drop a voice mail.
Later the other party may listen to that voice mail by accessing their voice mail.
By Dialing an extension with ‘*’ (eg: *101) can listen their Voice Mail Boxes 
 

Create two sip peers for bob and peter

  • sip.conf
          [peter]             ;Peer name
          type=friend         ;Allow both incoming and outgoing 
          secret=123          ;Password for SIP peer
          host=dynamic        ;IP address of the SIP peer 
          allow=alaw,ulaw     ;Allowed coddecs
          context=users       ;Dialplan context for the SIP peer

          [bob]
          type=friend
          secret=123
          host=dynamic
          allow=alaw,ulaw
          context=users
         
Configure Dialplan to use our AGI script 

  • extensions.conf
   [users]
          exten => _[*0-9]X.,1,NoOp("Dialiing AGI") ; This will accept dial pattern which are starting from * or from a number
          same => n,AGI(dialplan.pl,${EXTEN})            ; run Our AGI script which resides in agi-bin, and also we are providing the dialled extension number as a command line input.

Configure voice mail boxes

  • voicemail.conf
          [sales]                                       ;VM Context
          101 => 123,Bob's Mailbox,bob@example.com ;vmbox => [password],[Description], [Mail address to use]
          102 => 321,Peter's Mailbox,peter@example.com
         
Now please reload your configuration through asterisk CLI and Verify them
                   #rasterisk
                     > sip reload
                     > sip show peers
                     > dialplan reload
                     > voicemail reload
                     > voicemail show users
              
 
Now lets look at our Perl script
#!/usr/bin/perl
use warnings;
use strict;

#apt-get install build-essential

use Asterisk::AGI;

Asterisk::AGI for perl : 
First you may install cpan which is use to manage perl module by using 
#apt-get install build-essential
Then install Asterisk:AGI module using cpan
#cpan
>install Asterisk::AGI

Now in here I’ve define a global variable and assign the extension number which has passed as a command line argument from the Dialplan.

our $DPLAN_EXTEN=$ARGV[0];



Now depends on the number that dialed, if number start with ‘*’ it should dialed to relevant voice mail box or else if it’s a number it should directly dial the relevant sip peer.

if ($DPLAN_EXTEN =~ m/^*/){
       vm_box();
} else {
       main();
}
####Call Routes between two peers, Bob and Peter 
sub main{

#-->In here I have created a hash(a nested hash) to store relevant information to use
 my %EXTEN_CONF = (
                '101' => {
                        'CHAN'          =>'SIP',
                        'PEER'          =>'bob',
                        'MAXWAIT'       =>5,
                        'VM_CONTEXT'    =>'sales',
                },
                '102' => {
                        'CHAN'          =>'SIP',
                        'PEER'          =>'peter',
                        'MAXWAIT'       =>5,
                        'VM_CONTEXT'    =>'sales',
                },
 );

#-->Now here we create our AGI object
my $AGI = new Asterisk::AGI;


#-->Use exec to use any Dialplan Application in your AGI script. Using exec you can execute the relevant application and pass the argument to the relevant application.
#-->$AGI->exec($app, $options)
#--> As per the following example, execute dial function and pass relevant Chanel , Peer and wait time. If you execute the following on the Dialplan it would be "exten => 101,1,Dial(SIP/bob,5)"
$AGI->exec('Dial',"$EXTEN_CONF{$DPLAN_EXTEN}{'CHAN'}/$EXTEN_CONF{$DPLAN_EXTEN}{'PEER'},$EXTEN_CONF{$DPLAN_EXTEN}{'MAXWAIT'}");

#-->Now in order to put a voice mail use voice mail function
$AGI->exec('VoiceMail',"$DPLAN_EXTEN@$EXTEN_CONF{$DPLAN_EXTEN}{'VM_CONTEXT'}");


#-->To Hangup
$AGI->hangup();
}
####Listen to the Voice Mails
sub vm_box{
#-->In order to store the informations regarding to the voice mail boxes here I've use a hash again
my %VM_CONF = (
                '*101' => {
                        'VM_BOX'        =>'101',
                        'VM_CONTEXT'    =>'sales',
                },
                '*102' => {
                        'VM_BOX'        =>'102',
                        'VM_CONTEXT'    =>'sales',
                },
);
my $AGI = new Asterisk::AGI;
$AGI->exec('VoiceMailMain',"$VM_CONF{$DPLAN_EXTEN}{VM_BOX}@$VM_CONF{$DPLAN_EXTEN}{VM_CONTEXT}");
$AGI->hangup();
You can put your AGI file in asterisk agi-bin. in my Debian system its on 
#cd /var/lib/asterisk/agi-bin


In order to properly run the script change the ownership to your Asterisk user and grant the execute permission.

#chown asterisk:asterisk dialplan.pl
#chmod u+x dialplan.pl

Click here to download configuration file.

 
 
To get the asterisk Dialplan application information and how to use them you may refer asterisk CLI or you can search them on https://www.voip-info.org.
Installing Perl modules,

Thats it…. Enjoy….