mirror of
https://github.com/ssb22/wm6-utils.git
synced 2022-12-13 08:35:29 +00:00
161 lines
6.9 KiB
Python
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")
|