Commit 41df78b3ffd3fdc6ac55bfbb4c685e9633ebd28b
- query.py 41 --------+++++++++++++++++++++++++++++++++
- report.py 94 --------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- Diff rendering mode:
- inline
- side by side
query.py
1 | import logger | 1 | import logger |
---|---|---|---|
2 | from sqlalchemy.sql import func | 2 | from sqlalchemy.sql import func |
3 | import datetime | 3 | import datetime |
4 | import decimal | ||
4 | 5 | ||
6 | def 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 | |||
5 | class Query: | 17 | class 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() |
33 | 34 | ||
34 | 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 = 0 | 38 | sum = 0 |
37 | for res in query: | 39 | for res in query: |
38 | |||
39 | 40 | if res.sum is not None: | |
41 | sum = res.sum/60 | ||
42 | return round(sum, 4) | ||
40 | 43 | ||
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 = 0 | 47 | average = 0 |
44 | for res in query: | 48 | for res in query: |
45 | |||
46 | 49 | if res.average is not None: | |
50 | average = res.average/60 | ||
51 | return round(average, 4) | ||
47 | 52 | ||
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 = 0 | 55 | sum = 0 |
51 | for res in query: | 56 | for res in query: |
52 | |||
53 | 57 | if res.sum is not None: | |
58 | sum = res.sum/60 | ||
59 | return round(sum, 4) | ||
54 | 60 | ||
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() |
69 | 69 | ||
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() |
73 | 74 | ||
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() |
76 | 78 | ||
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)} |
100 | 100 | ||
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() |
103 | 104 | ||
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 = 0 | 108 | duration = 0 |
107 | for result in query: | 109 | for result in query: |
108 | 110 | if result.duration is not None: | |
111 | duration = result.duration | ||
109 | return duration | 112 | return duration |
report.py
2 | import datetime | 2 | import datetime |
---|---|---|---|
3 | import query | 3 | import query |
4 | import gmail | 4 | import gmail |
5 | import config as conf | ||
5 | import mailConfig | 6 | import mailConfig |
6 | 7 | import decimal | |
8 | |||
7 | parser = argparse.ArgumentParser(description="""Generate report for the date specified. | 9 | parser = 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. |
… | … | ||
26 | callDetails = query.Query('cdr') | 26 | callDetails = query.Query('cdr') |
27 | average_call_length = callDetails.average(startDate, endDate) | 27 | average_call_length = callDetails.average(startDate, endDate) |
28 | audio_minutes = callDetails.sum(startDate, endDate) | 28 | audio_minutes = callDetails.sum(startDate, endDate) |
29 | |||
30 | |||
31 | |||
32 | mobilink_load = callDetails.call_distribution(startDate, endDate, 'mobilink') | 29 | mobilink_load = callDetails.call_distribution(startDate, endDate, 'mobilink') |
33 | mobilink_tata_load = callDetails.call_distribution(startDate, endDate, 'mobilinktata') | 30 | mobilink_tata_load = callDetails.call_distribution(startDate, endDate, 'mobilinktata') |
34 | 31 | ||
35 | 32 | ||
36 | 33 | def individual_audio_minutes(): | |
34 | """Return the total number of minutes on outgoing calls for every IP | ||
35 | address of modems specified in conf.py | ||
37 | 36 | ||
38 | 37 | """ | |
38 | minutes = {} | ||
39 | for ip in conf.ip_addr: | ||
40 | minutes[ip] = callDetails.totalMinutes(ip, startDate, endDate) | ||
41 | return minutes | ||
39 | 42 | ||
40 | 43 | def 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 | ||
41 | 49 | ||
42 | 50 | def genJSON(): | |
51 | return ({"Number_of_postings_published": postings.posts(startDate, | ||
52 | endDate), | ||
43 | 53 | ||
44 | 54 | "Number_of_recordings_made": postings.recordings(startDate, | |
55 | endDate), | ||
45 | 56 | ||
46 | 57 | "Number_of_impact_stories": postings.filter_by_title('impact', | |
58 | startDate, endDate), | ||
47 | 59 | ||
48 | 60 | "Number_of_missed_calls": callDetails.missedCalls(startDate, | |
61 | endDate), | ||
49 | 62 | ||
50 | 63 | "Number_of_missed_calls_on_mobilink": | |
64 | callDetails.missedCalls(startDate, endDate, 'mobilink'), | ||
51 | 65 | ||
52 | 66 | "Number_of_missed_calls_on_mobilinktata": | |
67 | callDetails.missedCalls(startDate, endDate,'mobilinktata'), | ||
53 | 68 | ||
54 | 69 | "Number_of_calls_answered": callDetails.answeredCalls(startDate, | |
70 | endDate), | ||
55 | 71 | ||
56 | 72 | "Number_of_calls_lasting_less_than_30_seconds": callDetails.filter_calls_by_duration(startDate, endDate, 30), | |
57 | 73 | ||
58 | 74 | "Average_length_of_calls": average_call_length, | |
59 | 75 | ||
60 | 76 | "Total_number_of_audio_minutes_played": str(audio_minutes), | |
61 | 77 | ||
62 | 78 | "Audio_minutes_on_channel": individual_audio_minutes(), | |
63 | 79 | ||
64 | 80 | "Load_on_channel": individual_load(), | |
65 | 81 | ||
66 | 82 | "Busiest_hour_for_mobilink": mobilink_load["maxLoad"], | |
67 | 83 | ||
68 | 84 | "Number_of_calls_in_busiest_hour_for_mobilink": | |
85 | mobilink_load["maxCalls"], | ||
69 | 86 | ||
70 | 87 | "Least_active_hour_for_mobilink": mobilink_load["minLoad"], | |
71 | 88 | ||
72 | 89 | "Busiest_hour_for_mobilinktata": mobilink_tata_load["maxLoad"], | |
73 | 90 | ||
74 | 91 | "Number_of_calls_in_busiest_hour_for_mobilinktata": | |
92 | mobilink_tata_load["maxCalls"], | ||
75 | 93 | ||
76 | 94 | "Least_active_hour_for_mobilinktata": | |
95 | mobilink_tata_load["minLoad"], | ||
77 | 96 | ||
78 | 97 | "Number_of_calls_unanswered": | |
98 | callDetails.calls_unanswered(startDate, endDate), | ||
79 | 99 | ||
80 | 100 | "Maximum_duration_of_unanswered_call": | |
101 | callDetails.max_duration_UC(startDate, endDate) | ||
102 | }) | ||
81 | 103 | ||
82 | 104 | def genReport(): | |
83 | 105 | ||
84 | 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' | ||
85 | 114 | ||
86 | |||
87 | |||
88 | 115 | return template | |
116 | |||
89 | 117 | ||
90 | if args.mail is False: | 118 | if args.mail is False: |
91 | report = genReport() | 119 | report = genReport() |