Sitemap

Checking idle time by Python

3 min readNov 25, 2022

--

My boss made me to do some uncomfortable job..

Press enter or click to view image in full size

One day, my boss secretly told me that he needed a program to check idle time of each employees during working hour. I felt that It was a kind of surveillance to us, but it seemed there was no way to refuse it.

So I decided to write down how I made the program in open place.

The program collects computer name, IP address and record_date to create an unique observation. and add up foreground program name as key and usage time of the program as value.

So the program create initial dictionary like below.

process_time = {'comp_name': comp_name,
'record_date': curr_date,
'last_save': '0000',
'ip_address': ip_address,
'idle': 0,
'error': 0}

Boss also wanted to know what program each employee uses and how much time he or she uses each program.

Below codes get foreground program name.
Using re package, we can remove filename extension.
But this codes sometimes lose current window, so exception should be set.

from win32gui import GetForegroundWindow
from win32process import GetWindowThreadProcessId
import re

curr_window = GetForegroundWindow()
curr_pid = GetWindowThreadProcessId(curr_window)
insensitive_exe = re.compile(re.escape('.exe'), re.IGNORECASE)
current_app = insensitive_exe.sub('', app_name)

The Idle time means no keyboard or mouse activity moments.
And below code helps to collect the no movement time.

from win32api import GetLastInputInfo, GetTickCount

def get_idle_time():
return (GetTickCount() - GetLastInputInfo()) / 1000.0

win32api package provides methods to check operating time and last input time in milliseconds, so as subtracting last input time from operating time, idle time can be checked.

Get Kaizin’s stories in your inbox

Join Medium for free to get updates from this writer.

Since everyone has some moments that they just think without moving, I had to provide some interval. It was 30 seconds. If there is no movement after the 30 seconds, Idle time starts to increase, otherwise process time dictionary would add each program name as key and add up value to the foreground program key every 2 seconds.

If it is out of working hour range that is between 09:00 and 18:00, program only checks foreground program time when there is movement.

idle_allow_time = 0.5
timestamp = {}
idle_t = get_idle_time()

time.sleep(2)

if current_app not in process_time.keys():
process_time[current_app] = 0

if (int(curr_time) >= 1800) | (int(curr_time) <= 900):
if idle_t <= 60 * idle_allow_time:
process_time[current_app] = process_time[current_app] + int(time.time()) - timestamp[current_app]
else:
if idle_t <= 60 * idle_allow_time:
process_time[current_app] = process_time[current_app] + int(time.time()) - timestamp[current_app]
else:
process_time['idle'] = process_time['idle'] + int(time.time()) - timestamp[current_app]

These results need to be stored at local place to restart from the last record in case the computer restarts. But also the user should not recognize what this program is doing. I had to encrypt and decode the results for each purpose.

Below class uses cryptography package to generate a key for encryption and save as a text file. Since I had to make the program as executable, path for key text file had to be different by the condition.

from cryptography.fernet import Fernet

class HideSeekMessage:
def __init__(self):
# path must be different if app is a script file or frozen exe
if getattr(sys, 'frozen', False):
pth = os.path.dirname(sys.executable)
else:
pth = os.path.dirname(os.path.realpath(__file__))
k_name = "k.txt"
address = os.path.join(pth, k_name)
if os.path.exists(address):
with open('k.txt', 'rb') as f:
self.key = f.read()
else:
self.key = Fernet.generate_key()
with open(address, 'wb') as f:
f.write(self.key)

self.fernet = Fernet(self.key)

def hide(self, message):
return self.fernet.encrypt(message.encode())

def seek(self, message):
return self.fernet.decrypt(message).decode()

In the end of line, the program insert result into a database table. I used timedelta to control the timing for calling API.

save_interval = 10
curr_timestamp = datetime.strptime(curr_time, "%H%M")
save_interval_limit = datetime.strptime(process_time['last_save'], "%H%M") + timedelta(minutes=save_interval)

if curr_timestamp >= save_interval_limit:
process_time['last_save'] = curr_time
call_api(hs_message, f_address)

try:
with open(f_address, 'wb') as f:
f.write(hided_message)
except Exception as e:
print(e)
print("unable to save log..")
pass

The program reads last saving time and if it is more than 10 min ago, it calls API to insert a record. And this overall procedure never stops.

I also set some codes to insert previous records and delete the records from local. glob package is useful to get file or directory list. I selected text file that name starts as “abc” and that does not include current date.

import glob, os
prev_abc_file_list = [p for p in glob.glob('./abc**.txt') if p.find(curr_date) == -1]

# if the files exists, inserts them into table and deletes them.
for prev_file_path in prev_abc_file_list:
call_api(hs_message, prev_file_path)
os.remove(prev_file_path)

I will write other post to explain how I made an API to insert and how I write a program to register the program as starting program secretly.

--

--

Kaizin
Kaizin

Written by Kaizin

Interested in data science. Recently keen into LLM related stuff.