1
0
mirror of https://github.com/ssb22/wm6-utils.git synced 2022-12-13 08:35:29 +00:00
wm6-utils/SBminutes.py

161 lines
6.9 KiB
Python

# SBMinutes version 1.2, Silas S. Brown 2011-2013,2015, Public Domain, no warranty
# Requires Python to be installed on the phone (see Gradint page).
# This is Python 2 code; Python 3 was not available for WM phones.
# Checks call logs and calculates outgoing usage,
# accounting for minimum call times (unlike WM-Smartphone's built-in counter)
# Displays mins used so far, and mins that "should be" used so far if the
# monthly allowance were being used at an equal rate
# (so you can see if you're "ahead" or "behind" with your minutes)
# I wrote SBMinutes because I couldn't get LCMinutes to work
# and wm6minutecount requires a touch-screen phone.
# You can change the values below according to your contract:
start_day_of_month = 14
monthly_allowance = 600
minimum_secs_per_call = 60
secs_increment = 1
allow_phone_error = 3 # seconds added to each call just in case
numbers_not_affecting_allowance = [
# List here any numbers you might call that shouldn't affect the
# minute count. A prefix of a longer number is also allowed.
# (Note: these numbers are not necessarily free - in fact some
# of them are very expensive - but they don't affect the minutes.)
"1","05","08","076","070","07744","07755","09","44555",
]
strip_prefixes = [ "141" ] # to be stripped before testing the above
# If your logs are incomplete but you know that your usage (including
# roundup) does not exceed N minutes up to and including date Y-M-D,
# you can set known_usage = ((YYYY,M,D), N) (do NOT prefix m/d with 0)
# and this will then be used instead of the logs on or before that date.
known_usage = None
# If you know you will be without phone access for a future period, you
# can set avoid_usage = ((YYYY,M,D), (YYYY,M,D)) with the start and end dates
# of the no-usage period. This will be factored into the report that tells
# you if you're "ahead" or "behind" with your minutes.
avoid_usage = None
# Where to find history:
# on GitHub at https://github.com/ssb22/wm6-utils
# and on GitLab at https://gitlab.com/ssb22/wm6-utils
# and on BitBucket https://bitbucket.org/ssb22/wm6-utils
# and at https://gitlab.developers.cam.ac.uk/ssb22/wm6-utils
# and in China: https://gitee.com/ssb22/wm6-utils
# ----------------------------------------------------------------------
import ctypes, math
import ctypes.wintypes as wintypes
IOM_MISSED,IOM_INCOMING,IOM_OUTGOING = range(3) # enum
CALLLOGSEEK_BEGINNING = 2
CallLogEntrySize = 48
class CallLogEntry(ctypes.Structure): _fields_ = [
("cbSize",wintypes.DWORD),
("ftStartTimeL",wintypes.DWORD),
("ftStartTimeH",wintypes.DWORD),
("ftEndTimeL",wintypes.DWORD),
("ftEndTimeH",wintypes.DWORD),
("iom",wintypes.DWORD), # enum
("flags",wintypes.DWORD), # (fOutgoing, fConnected, fEnded, fRoam)
("callerIDtype",wintypes.DWORD),
("number",wintypes.LPWSTR),
("name",wintypes.LPWSTR),
("nameType",wintypes.LPWSTR),
("note",wintypes.LPWSTR)
]
def ftToUnixTime(ft): return (ft-116444736000000000)/10000000
import time
timeNow = time.time()
y,m,d = time.localtime(timeNow)[:3]
timeNow = time.mktime((y,m,d,23,59,59,0,0,-1)) # by just before midnight tonight
yUp,mUp,dUp = time.localtime(timeNow)[:3]
if dUp >= start_day_of_month:
mUp += 1
if mUp>12: mUp,yUp = 1,yUp+1
yStart,mStart,dStart = time.localtime(timeNow)[:3]
if dStart < start_day_of_month:
mStart -= 1
if mStart==0: mStart,yStart = 12,yStart-1
dUp=dStart=start_day_of_month
tUp = time.mktime((yUp,mUp,dUp,0,0,0,0,0,-1))
tStart = time.mktime((yStart,mStart,dStart,0,0,0,0,0,-1))
if avoid_usage:
avoidStart = time.mktime(avoid_usage[0]+(0,0,0,0,0,-1))
avoidEnd = time.mktime(avoid_usage[1]+(0,0,0,0,0,-1)) + 24*3600 # end of that day
if avoidEnd > tStart and avoidStart < tUp: # overlaps current period
if avoidEnd > tUp: avoidEnd = tUp
if avoidStart < timeNow: avoidStart = timeNow # no point accounting for scheduled avoids in the past (if changing, need to fix some of the formulae below)
if avoidStart < avoidEnd <= tUp: tUp -= avoidEnd-avoidStart
if tUp>timeNow: proportionLeft = (tUp-timeNow)*1.0/(tUp-tStart)
else: proportionLeft = 0 # probably in an avoid_usage period at the end of an allowance
minsUsed_ifEven = math.ceil(monthly_allowance*(1-proportionLeft))
seconds = added = addedCalls = totalCalls = errorCalls = 0
if known_usage:
tStart2 = time.mktime(known_usage[0]+(0,0,0,0,0,-1)) + 24*3600 # end of that day
if tStart2 > tStart:
tStart = tStart2
seconds += known_usage[1]*60
def rounding(length):
if length < minimum_secs_per_call:
return minimum_secs_per_call - length
if secs_increment > 1:
remainder = length % secs_increment
if remainder: return secs_increment-remainder
return 0
def inAllowance(number):
for n in strip_prefixes:
if number.startswith(n): number=number[len(n):]
for n in numbers_not_affecting_allowance:
if number.startswith(n): return 0
return 1
handle = ctypes.c_long()
if ctypes.cdll.phone.PhoneOpenCallLog(ctypes.byref(handle))==0:
# 3rd param of PhoneSeekCallLog is the Nth item (asking for 0)
iRecord = ctypes.c_long() # the index value of the record actually found
ctypes.cdll.phone.PhoneSeekCallLog(handle,CALLLOGSEEK_BEGINNING,0,ctypes.byref(iRecord))
entry = CallLogEntry() ; entry.cbSize = CallLogEntrySize
ape = 0 # allowed phone error
while ctypes.cdll.phone.PhoneGetCallLogEntry(handle,ctypes.byref(entry))==0:
start,end = ftToUnixTime(entry.ftStartTimeL|(entry.ftStartTimeH<<32)),ftToUnixTime(entry.ftEndTimeL|(entry.ftEndTimeH<<32))
if start < tStart: break # we're into calls that happened before start_day_of_month
if (entry.flags & 3) == 3 and inAllowance(entry.number): # 3 = fOutgoing + fConnected
rTmp = rounding(end-start)
total_if_phone_correct = end-start + rTmp
length = end-start + allow_phone_error
ar = rounding(length)
total_if_phone_wrong = length + ar
# Don't double-count allow_phone_error and rounding in the report:
if total_if_phone_correct == total_if_phone_wrong:
# rounded-up value would be the same anyway, so ignore phone error
length = end-start ; ar = rTmp # rounding(end-start)
else:
ape += allow_phone_error # stet length and ar
errorCalls += 1
added += ar
if ar: addedCalls += 1
seconds += length ; totalCalls += 1
ctypes.cdll.phone.PhoneCloseCallLog(handle)
minsUsed = int((seconds+added+59)/60)
if minsUsed < minsUsed_ifEven: stuff=" (%dm behind)" % (minsUsed_ifEven-minsUsed)
elif minsUsed > minsUsed_ifEven: stuff=" (%dm AHEAD)" % (minsUsed-minsUsed_ifEven)
else: stuff = " (on schedule)"
stuff += ", incl %d:%02d+%d:%02d round+error (%d,%d calls out of %d)" % (added/60,added%60,ape/60,ape%60,addedCalls,errorCalls,totalCalls) # might be only partially visible
raw_input("Time used = %dm%s included" % (minsUsed,stuff))
if start > tStart: raw_input("Warning: logs go back only to %d-%02d %d:%02d (incomplete?)" % time.localtime(start)[1:5])
else: raw_input("Could not open call logs")