How do I ensure my python script starts when my Ubuntu system starts and stops when my display is on sleep mode using systemd services

I have created a script that monitors the time each application is running on my main window in Ubuntu OS and sends the data to a PostgreSQL db, The script is working fine but I need to manually start the script and need to keep it’s terminal open, and if my display is suspended or I have not closed my system completely the script keeps on recording the duration of application on window. After some searching I realized using systemd services I can ensure the script starts when my system starts and stops if the display is on sleep mode or suspended.

This is my script:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>import subprocess
import psycopg2
import time
def get_friendly_name(class_name):
# Mapping class names to user-friendly names
mapping = {
"Code": "Visual Studio Code",
"notion-snap": "Notion",
}
return mapping.get(class_name, class_name)
def get_app_name(window_id):
`result = subprocess.run(['xprop', '-id', window_id], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True)'
xprop_output = result.stdout
app_name= None
app_class = None
for line in xprop_output.split('n'):
if 'WM_NAME(STRING)' in line:
app_name = line.split('"')[1]
if 'WM_CLASS(STRING)' in line:
app_class = line.split('"')[3]
# Fallback to class name if WM_NAME is not found
if not app_name and app_class:
app_name = get_friendly_name(app_class)
# Display the name of the application
if app_name:
return(app_name)
host = #host
dbname = 'postgres'
user = #user
password = #password
port = #port
def connect():
conn = None
try:
`conn = psycopg2.connect(host=host, dbname=dbname, user=user, password=password, port=port)`
return conn
except (Exception, psycopg2.DatabaseError) as e:
print(e)
return None
def postAppData(app_name, duration):
conn = connect()
if conn is None:
return
try:
cur = conn.cursor()
cur.execute("""
SELECT * FROM screen_time
WHERE app_name = %s AND DATE(timestamp) = CURRENT_DATE;
""", (app_name,))
row = cur.fetchone()
if row:
cur.execute("""
UPDATE screen_time
SET duration = duration + %s
WHERE id = %s;
""", (duration, row[0]))
else:
cur.execute("""
INSERT INTO screen_time (app_name, duration)
VALUES(%s, %s) RETURNING id;
""", (app_name, duration))
conn.commit()
cur.close()
except(Exception, psycopg2.DatabaseError) as error:
print(error)
finally:
if conn is not None:
conn.close()
def format_time(duration):
if duration < 60:
unit = "seconds"
elif duration < 3600:
duration /= 60 # Convert to minutes
unit = "minutes"
else:
duration /= 3600 # Convert to hours
unit = "hours"
formatted_time = f"{duration:.2f}"
return f"{formatted_time} {unit}"
prev_window = None
start_time = time.time()
while True:
try:
`result = subprocess.run(['xdotool', 'getactivewindow'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True)`
window_id = result.stdout.strip()
current_window = get_app_name(window_id)
if current_window != prev_window:
end_time = time.time()
duration = end_time - start_time
if prev_window is not None:
postAppData(prev_window, duration)
# print(f"Window: {prev_window}, Duration: {format_time(duration)}")
prev_window = current_window
start_time = end_time
except Exception as e:
print(f"An error occurred: {e}")
</code>
<code>import subprocess import psycopg2 import time def get_friendly_name(class_name): # Mapping class names to user-friendly names mapping = { "Code": "Visual Studio Code", "notion-snap": "Notion", } return mapping.get(class_name, class_name) def get_app_name(window_id): `result = subprocess.run(['xprop', '-id', window_id], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True)' xprop_output = result.stdout app_name= None app_class = None for line in xprop_output.split('n'): if 'WM_NAME(STRING)' in line: app_name = line.split('"')[1] if 'WM_CLASS(STRING)' in line: app_class = line.split('"')[3] # Fallback to class name if WM_NAME is not found if not app_name and app_class: app_name = get_friendly_name(app_class) # Display the name of the application if app_name: return(app_name) host = #host dbname = 'postgres' user = #user password = #password port = #port def connect(): conn = None try: `conn = psycopg2.connect(host=host, dbname=dbname, user=user, password=password, port=port)` return conn except (Exception, psycopg2.DatabaseError) as e: print(e) return None def postAppData(app_name, duration): conn = connect() if conn is None: return try: cur = conn.cursor() cur.execute(""" SELECT * FROM screen_time WHERE app_name = %s AND DATE(timestamp) = CURRENT_DATE; """, (app_name,)) row = cur.fetchone() if row: cur.execute(""" UPDATE screen_time SET duration = duration + %s WHERE id = %s; """, (duration, row[0])) else: cur.execute(""" INSERT INTO screen_time (app_name, duration) VALUES(%s, %s) RETURNING id; """, (app_name, duration)) conn.commit() cur.close() except(Exception, psycopg2.DatabaseError) as error: print(error) finally: if conn is not None: conn.close() def format_time(duration): if duration < 60: unit = "seconds" elif duration < 3600: duration /= 60 # Convert to minutes unit = "minutes" else: duration /= 3600 # Convert to hours unit = "hours" formatted_time = f"{duration:.2f}" return f"{formatted_time} {unit}" prev_window = None start_time = time.time() while True: try: `result = subprocess.run(['xdotool', 'getactivewindow'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True)` window_id = result.stdout.strip() current_window = get_app_name(window_id) if current_window != prev_window: end_time = time.time() duration = end_time - start_time if prev_window is not None: postAppData(prev_window, duration) # print(f"Window: {prev_window}, Duration: {format_time(duration)}") prev_window = current_window start_time = end_time except Exception as e: print(f"An error occurred: {e}") </code>
import subprocess
import psycopg2
import time 

def get_friendly_name(class_name):
    # Mapping class names to user-friendly names
    mapping = {
        "Code": "Visual Studio Code",
        "notion-snap": "Notion",
    }
    return mapping.get(class_name, class_name)

def get_app_name(window_id):
    `result = subprocess.run(['xprop', '-id', window_id], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True)'
    xprop_output = result.stdout
    
    app_name= None
    app_class = None
    
    for line in xprop_output.split('n'):
                if 'WM_NAME(STRING)' in line:
                    app_name = line.split('"')[1]
                if 'WM_CLASS(STRING)' in line:
                    app_class = line.split('"')[3]
            
            # Fallback to class name if WM_NAME is not found
                if not app_name and app_class:
                    app_name = get_friendly_name(app_class)
            
                # Display the name of the application
                if app_name:
                    return(app_name)



host = #host
dbname = 'postgres'
user = #user
password = #password
port = #port


def connect():
    conn = None
    try:
        `conn = psycopg2.connect(host=host, dbname=dbname, user=user, password=password, port=port)`
        return conn
    except (Exception, psycopg2.DatabaseError) as e:
        print(e)
        return None
        
        
def postAppData(app_name, duration):
    conn = connect()
    if conn is None:
        return
    
    try:
        cur = conn.cursor()
        cur.execute("""
                    SELECT * FROM screen_time
                    WHERE app_name = %s AND DATE(timestamp) = CURRENT_DATE;
                    """, (app_name,))
        row = cur.fetchone()
        if row:
            cur.execute("""
                        UPDATE screen_time
                        SET duration = duration + %s
                        WHERE id = %s;
                        """, (duration, row[0]))
            
        else:
            cur.execute("""
                        INSERT INTO screen_time (app_name, duration)
                        VALUES(%s, %s) RETURNING id;
                        """, (app_name, duration))
    
        conn.commit()
        
        cur.close()
        
    except(Exception, psycopg2.DatabaseError) as error:
        print(error)
        
    finally:
        if conn is not None:
            conn.close()
            

def format_time(duration):

  if duration < 60:
    unit = "seconds"
  elif duration < 3600:
    duration /= 60  # Convert to minutes
    unit = "minutes"
  else:
    duration /= 3600  # Convert to hours
    unit = "hours"

  formatted_time = f"{duration:.2f}"

  return f"{formatted_time} {unit}"


prev_window = None
start_time = time.time()

while True:
    try:
        `result = subprocess.run(['xdotool', 'getactivewindow'], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True)`
        window_id = result.stdout.strip()
        current_window = get_app_name(window_id)
        
        if current_window != prev_window:
            end_time = time.time()
            duration = end_time - start_time
            
            if prev_window is not None:
                postAppData(prev_window, duration)
                # print(f"Window: {prev_window}, Duration: {format_time(duration)}")
            prev_window = current_window
            start_time = end_time
    
    except Exception as e:
        print(f"An error occurred: {e}")


Here are the few services I tried writing:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>`[Unit]`
`Description=My test service`
`After=multi-user.target`
`[Service]`
`Type=simple`
`Restart=always`
`ExecStart=/usr/bin/python3 /path/to/script.py`
`[Install]`
`WantedBy=multi-user.target`
</code>
<code>`[Unit]` `Description=My test service` `After=multi-user.target` `[Service]` `Type=simple` `Restart=always` `ExecStart=/usr/bin/python3 /path/to/script.py` `[Install]` `WantedBy=multi-user.target` </code>
`[Unit]`
`Description=My test service`
`After=multi-user.target`
`[Service]`
`Type=simple`
`Restart=always`
`ExecStart=/usr/bin/python3 /path/to/script.py`
`[Install]`
`WantedBy=multi-user.target`


Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>`But when I checked status of this service, it showed it failed with exit-code`
`process: (code=exited, status=2)`
`systemd[1]: ScreenTimeMonitor.service: Scheduled restart job, restart counter is at 5.`
`systemd[1]: Stopped My test service.`
`systemd[1]: ScreenTimeMonitor.service: Start request repeated too quickly.`
`systemd[1]: ScreenTimeMonitor.service: Failed with result 'exit-code'.`
`systemd[1]: Failed to start My test service.`
</code>
<code>`But when I checked status of this service, it showed it failed with exit-code` `process: (code=exited, status=2)` `systemd[1]: ScreenTimeMonitor.service: Scheduled restart job, restart counter is at 5.` `systemd[1]: Stopped My test service.` `systemd[1]: ScreenTimeMonitor.service: Start request repeated too quickly.` `systemd[1]: ScreenTimeMonitor.service: Failed with result 'exit-code'.` `systemd[1]: Failed to start My test service.` </code>
`But when I checked status of this service, it showed it failed with exit-code`
`process: (code=exited, status=2)`
`systemd[1]: ScreenTimeMonitor.service: Scheduled restart job, restart counter is at 5.`
`systemd[1]: Stopped My test service.`
`systemd[1]: ScreenTimeMonitor.service: Start request repeated too quickly.`
`systemd[1]: ScreenTimeMonitor.service: Failed with result 'exit-code'.`
`systemd[1]: Failed to start My test service.`

I was expecting the script to run normally as service and log data into my db.

New contributor

Azim Khorajiya is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật