Commit 41df78b3ffd3fdc6ac55bfbb4c685e9633ebd28b

  • avatar
  • arvind
  • Mon Mar 10 18:34:55 IST 2014
Code refactor and other improvements:
     Use the config file to import IP address of channels.
     Moving away from template based printing.  Generate a dict and
     print it as a template.
     Queries now return values rounded up to 4 decimal places.
  • query.py 41 --------+++++++++++++++++++++++++++++++++
  • report.py 94 --------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  • Diff rendering mode:
  • inline
  • side by side

query.py

1import logger1import logger
2from sqlalchemy.sql import func2from sqlalchemy.sql import func
3import datetime3import datetime
4import decimal
45
6def toStr(fn):
7 def wrapped(*args, **kwargs):
8 response = fn(*args, **kwargs)
9 if type(response) is decimal.Decimal:
10 return str(response)
11 if type(response) is dict:
12 return repr(response)
13 else:
14 return str(response)
15 return wrapped
16
5class Query:17class Query:
6 """Objects of query class can be used to run specific queries."""18 """Objects of query class can be used to run specific queries."""
7 def __init__(self, table):19 def __init__(self, table):
27 def recordings(self, date, dateRange):27 def recordings(self, date, dateRange):
28 return self.t.lt.query.filter(self.t.lt.posted.between(date,28 return self.t.lt.query.filter(self.t.lt.posted.between(date,
29 dateRange)).count()29 dateRange)).count()
30
30 def filter_by_title(self, title, date, dateRange):31 def filter_by_title(self, title, date, dateRange):
31 return self.t.lt.query.filter(self.t.lt.title.like(title+'%'),32 return self.t.lt.query.filter(self.t.lt.title.like(title+'%'),
32 self.t.lt.posted.between(date, dateRange)).count()33 self.t.lt.posted.between(date, dateRange)).count()
3334
34 def load(self, channel, date, dateRange):
35
36 def totalMinutes(self, channel, date, dateRange):
35 query = self.t.lt.query.with_entities(func.sum(self.t.lt.duration).label('sum')).filter(self.t.lt.dcontext == "callback", self.t.lt.channel.like(channel+'%'), self.t.lt.calldate.between(date, dateRange))37 query = self.t.lt.query.with_entities(func.sum(self.t.lt.duration).label('sum')).filter(self.t.lt.dcontext == "callback", self.t.lt.channel.like(channel+'%'), self.t.lt.calldate.between(date, dateRange))
36 sum = 038 sum = 0
37 for res in query:39 for res in query:
38 sum = res.sum/60
39 return sum
40 if res.sum is not None:
41 sum = res.sum/60
42 return round(sum, 4)
4043
44
41 def average(self, date, dateRange):45 def average(self, date, dateRange):
42 query = self.t.lt.query.with_entities(func.avg(self.t.lt.duration).label('average')).filter(self.t.lt.dcontext == "callback", self.t.lt.calldate.between(date, dateRange))46 query = self.t.lt.query.with_entities(func.avg(self.t.lt.duration).label('average')).filter(self.t.lt.dcontext == "callback", self.t.lt.calldate.between(date, dateRange))
43 average = 047 average = 0
44 for res in query:48 for res in query:
45 average = res.average/60
46 return average
49 if res.average is not None:
50 average = res.average/60
51 return round(average, 4)
4752
48 def sum(self, date, dateRange):53 def sum(self, date, dateRange):
49 query = self.t.lt.query.with_entities(func.sum(self.t.lt.duration).label('sum')).filter(self.t.lt.dcontext == "callback", self.t.lt.calldate.between(date, dateRange))54 query = self.t.lt.query.with_entities(func.sum(self.t.lt.duration).label('sum')).filter(self.t.lt.dcontext == "callback", self.t.lt.calldate.between(date, dateRange))
50 sum = 055 sum = 0
51 for res in query:56 for res in query:
52 sum = res.sum/60
53 return sum
57 if res.sum is not None:
58 sum = res.sum/60
59 return round(sum, 4)
5460
61
55 def missedCalls(self, date, dateRange, modem=None):62 def missedCalls(self, date, dateRange, modem=None):
56 if modem is None:63 if modem is None:
57 return self.t.lt.query.filter(((self.t.lt.dcontext == 'mobilink') |64 return self.t.lt.query.filter(((self.t.lt.dcontext == 'mobilink') |
67 else:67 else:
68 return self.t.lt.query.filter(self.t.lt.dcontext == modem, self.t.lt.calldate.between(date, dateRange)).count()68 return self.t.lt.query.filter(self.t.lt.dcontext == modem, self.t.lt.calldate.between(date, dateRange)).count()
6969
70
70 def answeredCalls(self, date, dateRange):71 def answeredCalls(self, date, dateRange):
71 return self.t.lt.query.filter(self.t.lt.dcontext == 'callback',72 return self.t.lt.query.filter(self.t.lt.dcontext == 'callback',
72 self.t.lt.calldate.between(date, dateRange)).count()73 self.t.lt.calldate.between(date, dateRange)).count()
7374
75
74 def filter_calls_by_duration(self, date, dateRange, duration):76 def filter_calls_by_duration(self, date, dateRange, duration):
75 return self.t.lt.query.filter(self.t.lt.dcontext == 'callback', self.t.lt.duration < duration, self.t.lt.calldate.between(date, dateRange)).count()77 return self.t.lt.query.filter(self.t.lt.dcontext == 'callback', self.t.lt.duration < duration, self.t.lt.calldate.between(date, dateRange)).count()
7678
79
77 def call_distribution(self, date, dateRange, dcontext):80 def call_distribution(self, date, dateRange, dcontext):
78 startTimeStamp = datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S')81 startTimeStamp = datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S')
79 endTimeStamp = datetime.datetime.strptime(dateRange, '%Y-%m-%d %H:%M:%S')82 endTimeStamp = datetime.datetime.strptime(dateRange, '%Y-%m-%d %H:%M:%S')
98 minLoad.append(slot)98 minLoad.append(slot)
99 return {"maxLoad": maxLoad, "maxCalls": slots[maxLoad], "minLoad": repr(minLoad)}99 return {"maxLoad": maxLoad, "maxCalls": slots[maxLoad], "minLoad": repr(minLoad)}
100100
101
101 def calls_unanswered(self, date, dateRange):102 def calls_unanswered(self, date, dateRange):
102 return self.t.lt.query.filter(self.t.lt.dcontext=='default', self.t.lt.calldate.between(date,dateRange)).count()103 return self.t.lt.query.filter(self.t.lt.dcontext=='default', self.t.lt.calldate.between(date,dateRange)).count()
103104
105
104 def max_duration_UC(self, date, dateRange):106 def max_duration_UC(self, date, dateRange):
105 query = self.t.lt.query.with_entities(func.max(self.t.lt.duration).label('duration')).filter(self.t.lt.dcontext=='default', self.t.lt.calldate.between(date,dateRange))107 query = self.t.lt.query.with_entities(func.max(self.t.lt.duration).label('duration')).filter(self.t.lt.dcontext=='default', self.t.lt.calldate.between(date,dateRange))
106 duration = 0108 duration = 0
107 for result in query:109 for result in query:
108 duration = result.duration
110 if result.duration is not None:
111 duration = result.duration
109 return duration112 return duration

report.py

2import datetime2import datetime
3import query3import query
4import gmail4import gmail
5import config as conf
5import mailConfig6import mailConfig
6from string import Template
7import decimal
8
7parser = argparse.ArgumentParser(description="""Generate report for the date specified.9parser = argparse.ArgumentParser(description="""Generate report for the date specified.
8 Start and end date default to present day.10 Start and end date default to present day.
9 Start time defaults to 00:00:00.11 Start time defaults to 00:00:00.
26callDetails = query.Query('cdr')26callDetails = query.Query('cdr')
27average_call_length = callDetails.average(startDate, endDate)27average_call_length = callDetails.average(startDate, endDate)
28audio_minutes = callDetails.sum(startDate, endDate)28audio_minutes = callDetails.sum(startDate, endDate)
29channel1_minutes = callDetails.load('SIP/10.0.0.30', startDate, endDate)
30channel2_minutes = callDetails.load('SIP/10.0.0.31', startDate, endDate)
31channel3_minutes = callDetails.load('SIP/10.0.0.33', startDate, endDate)
32mobilink_load = callDetails.call_distribution(startDate, endDate, 'mobilink')29mobilink_load = callDetails.call_distribution(startDate, endDate, 'mobilink')
33mobilink_tata_load = callDetails.call_distribution(startDate, endDate, 'mobilinktata')30mobilink_tata_load = callDetails.call_distribution(startDate, endDate, 'mobilinktata')
3431
3532
36def genReport():
33def individual_audio_minutes():
34 """Return the total number of minutes on outgoing calls for every IP
35 address of modems specified in conf.py
3736
38 posts = '{0}: {1}'.format("Number of postings published", postings.posts(startDate, endDate))
37 """
38 minutes = {}
39 for ip in conf.ip_addr:
40 minutes[ip] = callDetails.totalMinutes(ip, startDate, endDate)
41 return minutes
3942
40 recordings = '{0}: {1}'.format("Number of recordings made", postings.recordings(startDate, endDate))
43def individual_load():
44 """ Return the fraction of load on each modem."""
45 minutes = individual_audio_minutes()
46 for ip in minutes:
47 minutes[ip] = round(minutes[ip]/audio_minutes, 4)
48 return minutes
4149
42 impact_stories = '{0}: {1}'.format("Number of impact stories", postings.filter_by_title('impact', startDate, endDate))
50def genJSON():
51 return ({"Number_of_postings_published": postings.posts(startDate,
52 endDate),
4353
44 missed_calls = '{0}: {1}'.format("Number of missed calls", callDetails.missedCalls(startDate, endDate))
54 "Number_of_recordings_made": postings.recordings(startDate,
55 endDate),
4556
46 missed_calls_mobilink = '{0}: {1}'.format("Number of missed calls on 'mobilink'", callDetails.missedCalls(startDate, endDate, 'mobilink'))
57 "Number_of_impact_stories": postings.filter_by_title('impact',
58 startDate, endDate),
4759
48 missed_calls_mobilink_tata = '{0}: {1}'.format("Number of missed calls on 'mobilinktata'", callDetails.missedCalls(startDate, endDate,'mobilinktata'))
60 "Number_of_missed_calls": callDetails.missedCalls(startDate,
61 endDate),
4962
50 answered_calls = '{0}: {1}'.format("Number of calls answered", callDetails.answeredCalls(startDate, endDate))
63 "Number_of_missed_calls_on_mobilink":
64 callDetails.missedCalls(startDate, endDate, 'mobilink'),
5165
52 filtered_calls = '{0}: {1}'.format("Number of calls lasting less than 30 seconds", callDetails.filter_calls_by_duration(startDate, endDate, 30))
66 "Number_of_missed_calls_on_mobilinktata":
67 callDetails.missedCalls(startDate, endDate,'mobilinktata'),
5368
54 average_Call_Length = '{0}: {1:.4f}'.format("Average length of calls", average_call_length)
69 "Number_of_calls_answered": callDetails.answeredCalls(startDate,
70 endDate),
5571
56 audio_Minutes = '{0}: {1:.4f}'.format("Total number of audio minutes played", audio_minutes)
72 "Number_of_calls_lasting_less_than_30_seconds": callDetails.filter_calls_by_duration(startDate, endDate, 30),
5773
58 ch1_minutes = '{0}: {1:.4f}'.format("Audio minutes on channel 10.0.0.20", channel1_minutes)
74 "Average_length_of_calls": average_call_length,
5975
60 ch2_minutes = '{0}: {1:.4f}'.format("Audio minutes on channel 10.0.0.21", channel2_minutes)
76 "Total_number_of_audio_minutes_played": str(audio_minutes),
6177
62 ch3_minutes = '{0}: {1:.4f}'.format("Audio minutes on channel 10.0.0.22", channel3_minutes)
78 "Audio_minutes_on_channel": individual_audio_minutes(),
6379
64 ch1_load = '{0}: {1:.4f}'.format("Load on channel 10.0.0.20", channel1_minutes/audio_minutes)
80 "Load_on_channel": individual_load(),
6581
66 ch2_load = '{0}: {1:.4f}'.format("Load on channel 10.0.0.21", channel2_minutes/audio_minutes)
82 "Busiest_hour_for_mobilink": mobilink_load["maxLoad"],
6783
68 ch3_load = '{0}: {1:.4f}'.format("Load on channel 10.0.0.22", channel3_minutes/audio_minutes)
84 "Number_of_calls_in_busiest_hour_for_mobilink":
85 mobilink_load["maxCalls"],
6986
70 mobilink_max_load = '{0}: {1}'.format("Busiest hour for mobilink",mobilink_load["maxLoad"])
87 "Least_active_hour_for_mobilink": mobilink_load["minLoad"],
7188
72 mobilink_max_calls = '{0}: {1}'.format("Number of calls in busiest hour for mobilink",mobilink_load["maxCalls"])
89 "Busiest_hour_for_mobilinktata": mobilink_tata_load["maxLoad"],
7390
74 mobilink_min_load = '{0}: {1}'.format("Least active hour for mobilink",mobilink_load["minLoad"])
91 "Number_of_calls_in_busiest_hour_for_mobilinktata":
92 mobilink_tata_load["maxCalls"],
7593
76 mobilink_tata_max_load = '{0}: {1}'.format("Busiest hour for mobilinktata",mobilink_tata_load["maxLoad"])
94 "Least_active_hour_for_mobilinktata":
95 mobilink_tata_load["minLoad"],
7796
78 mobilink_tata_max_calls = '{0}: {1}'.format("Number of calls in busiest hour for mobilinktata",mobilink_tata_load["maxCalls"])
97 "Number_of_calls_unanswered":
98 callDetails.calls_unanswered(startDate, endDate),
7999
80 mobilink_tata_min_load = '{0}: {1}'.format("Least active hour for mobilinktata",mobilink_tata_load["minLoad"])
100 "Maximum_duration_of_unanswered_call":
101 callDetails.max_duration_UC(startDate, endDate)
102 })
81103
82 unanswered_calls = '{0}: {1}'.format("Number of calls unanswered", callDetails.calls_unanswered(startDate, endDate))
104def genReport():
83105
84 max_duration_UC = '{0}: {1}'.format("Maximum duration of unanswered call(in seconds)", callDetails.max_duration_UC(startDate, endDate))
106 rep = genJSON()
107 keys = rep.keys()
108 template = ''
109 for key in keys:
110 if type(rep[key]) is dict:
111 for item in rep[key]:
112 rep[key][item] = str(rep[key][item])
113 template+= key.replace('_', ' ') + ': ' + str(rep[key]) + '\n'
85114
86 t =Template('$posts\n$recordings\n$impact_stories\n$missed_calls\n$missed_calls_mobilink\n$missed_calls_mobilink_tata\n$answered_calls\n$unanswered_calls\n$max_duration_uc\n$filtered_calls\n$average_call_length\n$audio_minutes\n$ch1_minutes\n$ch2_minutes\n$ch3_minutes\n$ch1_load\n$ch2_load\n$ch3_load\n$mobilink_max_load\n$mobilink_max_calls\n$mobilink_min_load\n$mobilink_tata_max_load\n$mobilink_tata_max_calls\n$mobilink_tata_min_load')
87 report = t.substitute(posts=posts,recordings=recordings,impact_stories=impact_stories,missed_calls=missed_calls,missed_calls_mobilink=missed_calls_mobilink,missed_calls_mobilink_tata=missed_calls_mobilink_tata,answered_calls=answered_calls,unanswered_calls=unanswered_calls,max_duration_uc=max_duration_UC,filtered_calls=filtered_calls,average_call_length=average_Call_Length,audio_minutes=audio_Minutes,ch1_minutes=ch1_minutes,ch2_minutes=ch2_minutes,ch3_minutes=ch3_minutes,ch1_load=ch1_load,ch2_load=ch2_load,ch3_load=ch3_load,mobilink_max_load=mobilink_max_load,mobilink_max_calls=mobilink_max_calls,mobilink_min_load=mobilink_min_load,mobilink_tata_max_load=mobilink_tata_max_load,mobilink_tata_max_calls=mobilink_tata_max_calls,mobilink_tata_min_load=mobilink_tata_min_load)
88 return report
115 return template
116
89117
90if args.mail is False:118if args.mail is False:
91 report = genReport()119 report = genReport()