Tutorial

This tutorial walks you through building progressively more complex applications with gntplib.

Tutorial 1: Simple Task Manager

Let’s build a task manager that sends notifications when tasks are completed.

Basic Version

from gntplib import Publisher, Event

class TaskManager:
    def __init__(self):
        events = [Event('task_complete', 'Task Completed')]
        self.publisher = Publisher('TaskManager', events)
        self.publisher.register()

    def complete_task(self, task_name):
        """Mark a task as complete and notify."""
        print(f"Completing task: {task_name}")
        self.publisher.publish(
            'task_complete',
            'Task Done',
            f'"{task_name}" has been completed!'
        )

# Usage
manager = TaskManager()
manager.complete_task('Write documentation')
manager.complete_task('Fix bug #123')

Adding Priority

Let’s add priority levels for tasks:

from gntplib import Publisher, Event

class TaskManager:
    def __init__(self):
        events = [
            Event('low_priority', 'Low Priority Task'),
            Event('normal_priority', 'Normal Priority Task'),
            Event('high_priority', 'High Priority Task'),
        ]
        self.publisher = Publisher('TaskManager', events)
        self.publisher.register()

    def complete_task(self, task_name, priority='normal'):
        """Complete task with priority level."""
        priority_map = {
            'low': ('low_priority', -1),
            'normal': ('normal_priority', 0),
            'high': ('high_priority', 1),
        }

        event_name, notification_priority = priority_map[priority]

        self.publisher.publish(
            event_name,
            f'{priority.title()} Priority Task Done',
            f'"{task_name}" has been completed!',
            priority=notification_priority
        )

# Usage
manager = TaskManager()
manager.complete_task('Update README', priority='low')
manager.complete_task('Review code', priority='normal')
manager.complete_task('Fix production bug', priority='high')

Adding Icons

Make it visual with icons:

from gntplib import Publisher, Event, Resource

class TaskManager:
    def __init__(self):
        # Define events with icons
        events = [
            Event('low_priority', 'Low Priority',
                  icon=Resource.from_file('icons/low.png')),
            Event('normal_priority', 'Normal Priority',
                  icon=Resource.from_file('icons/normal.png')),
            Event('high_priority', 'High Priority',
                  icon=Resource.from_file('icons/high.png')),
        ]

        # App icon
        app_icon = Resource.from_file('icons/app.png')

        self.publisher = Publisher('TaskManager', events, icon=app_icon)
        self.publisher.register()

    def complete_task(self, task_name, priority='normal'):
        priority_map = {
            'low': ('low_priority', -1),
            'normal': ('normal_priority', 0),
            'high': ('high_priority', 1),
        }

        event_name, notification_priority = priority_map[priority]

        self.publisher.publish(
            event_name,
            f'{priority.title()} Priority Task',
            f'✓ "{task_name}" completed!',
            priority=notification_priority
        )

Tutorial 2: Build Monitor

A CI/CD build monitoring system with notifications.

Basic Build Monitor

from gntplib import Publisher, Event, Resource
from datetime import datetime

class BuildMonitor:
    def __init__(self, project_name):
        self.project_name = project_name

        # Define build events
        events = [
            Event('build_started', 'Build Started'),
            Event('build_success', 'Build Succeeded'),
            Event('build_failed', 'Build Failed'),
        ]

        self.publisher = Publisher(f'BuildMonitor-{project_name}', events)
        self.publisher.register()

    def build_started(self, build_number):
        """Notify that build has started."""
        self.publisher.publish(
            'build_started',
            f'Build #{build_number} Started',
            f'Building {self.project_name}...',
            priority=0
        )

    def build_completed(self, build_number, success, duration):
        """Notify build completion."""
        if success:
            event = 'build_success'
            title = f'Build #{build_number} Succeeded ✓'
            priority = 0
        else:
            event = 'build_failed'
            title = f'Build #{build_number} Failed ✗'
            priority = 2  # Emergency

        message = f'{self.project_name} - Duration: {duration}s'

        self.publisher.publish(
            event,
            title,
            message,
            priority=priority,
            sticky=not success  # Keep failures visible
        )

# Usage
monitor = BuildMonitor('MyProject')

import time
monitor.build_started(42)
time.sleep(2)  # Simulate build
monitor.build_completed(42, success=True, duration=120)

With Detailed Information

Add more details to notifications:

from gntplib import Publisher, Event
from datetime import datetime

class BuildMonitor:
    def __init__(self, project_name):
        self.project_name = project_name

        events = [
            Event('build_started', 'Build Started'),
            Event('build_success', 'Build Succeeded'),
            Event('build_failed', 'Build Failed'),
            Event('tests_failed', 'Tests Failed'),
        ]

        self.publisher = Publisher(f'BuildMonitor-{project_name}', events)
        self.publisher.register()

    def build_started(self, build_number, branch='main', commit=None):
        """Notify build start with details."""
        details = [
            f'Project: {self.project_name}',
            f'Branch: {branch}',
            f'Time: {datetime.now().strftime("%H:%M:%S")}'
        ]

        if commit:
            details.append(f'Commit: {commit[:8]}')

        self.publisher.publish(
            'build_started',
            f'Build #{build_number} Started',
            '\n'.join(details),
            priority=0,
            id_=f'build-{build_number}'
        )

    def build_completed(self, build_number, success, duration,
                       tests_passed=None, tests_failed=None, errors=None):
        """Notify completion with test results."""

        if not success:
            # Build failed
            event = 'build_failed'
            title = f'Build #{build_number} Failed ✗'
            priority = 2

            details = [
                f'Project: {self.project_name}',
                f'Duration: {duration}s',
            ]

            if errors:
                details.append(f'\nErrors:\n{errors}')

        elif tests_failed and tests_failed > 0:
            # Build succeeded but tests failed
            event = 'tests_failed'
            title = f'Build #{build_number} - Tests Failed'
            priority = 1

            details = [
                f'Project: {self.project_name}',
                f'Duration: {duration}s',
                f'Tests Passed: {tests_passed}',
                f'Tests Failed: {tests_failed}',
            ]
        else:
            # Everything succeeded
            event = 'build_success'
            title = f'Build #{build_number} Succeeded ✓'
            priority = 0

            details = [
                f'Project: {self.project_name}',
                f'Duration: {duration}s',
                f'Tests Passed: {tests_passed}',
            ]

        self.publisher.publish(
            event,
            title,
            '\n'.join(details),
            priority=priority,
            sticky=(priority > 0),  # Keep if issues
            coalescing_id=f'build-{build_number}'
        )

# Usage
monitor = BuildMonitor('MyProject')

monitor.build_started(42, branch='feature-x', commit='abc123def')
# ... build happens ...
monitor.build_completed(
    42,
    success=True,
    duration=145,
    tests_passed=150,
    tests_failed=0
)

Tutorial 3: System Monitor

Monitor system resources and send alerts.

from gntplib import Publisher, Event
import psutil
import time

class SystemMonitor:
    def __init__(self, thresholds=None):
        # Default thresholds
        self.thresholds = thresholds or {
            'cpu': 80.0,
            'memory': 85.0,
            'disk': 90.0,
        }

        # Define events
        events = [
            Event('cpu_alert', 'CPU Alert'),
            Event('memory_alert', 'Memory Alert'),
            Event('disk_alert', 'Disk Alert'),
            Event('system_normal', 'System Normal'),
        ]

        self.publisher = Publisher('SystemMonitor', events)
        self.publisher.register()

        # Track alert state to avoid spam
        self.alert_active = {
            'cpu': False,
            'memory': False,
            'disk': False,
        }

    def check_cpu(self):
        """Check CPU usage."""
        usage = psutil.cpu_percent(interval=1)

        if usage > self.thresholds['cpu'] and not self.alert_active['cpu']:
            self.publisher.publish(
                'cpu_alert',
                'High CPU Usage',
                f'CPU usage is at {usage:.1f}%',
                priority=1,
                sticky=True
            )
            self.alert_active['cpu'] = True

        elif usage <= self.thresholds['cpu'] and self.alert_active['cpu']:
            self.publisher.publish(
                'system_normal',
                'CPU Normal',
                f'CPU usage back to {usage:.1f}%',
                priority=0
            )
            self.alert_active['cpu'] = False

    def check_memory(self):
        """Check memory usage."""
        memory = psutil.virtual_memory()
        usage = memory.percent

        if usage > self.thresholds['memory'] and not self.alert_active['memory']:
            self.publisher.publish(
                'memory_alert',
                'High Memory Usage',
                f'Memory usage is at {usage:.1f}%\n'
                f'Available: {memory.available / (1024**3):.1f} GB',
                priority=1,
                sticky=True
            )
            self.alert_active['memory'] = True

        elif usage <= self.thresholds['memory'] and self.alert_active['memory']:
            self.publisher.publish(
                'system_normal',
                'Memory Normal',
                f'Memory usage back to {usage:.1f}%',
                priority=0
            )
            self.alert_active['memory'] = False

    def check_disk(self):
        """Check disk usage."""
        disk = psutil.disk_usage('/')
        usage = disk.percent

        if usage > self.thresholds['disk'] and not self.alert_active['disk']:
            self.publisher.publish(
                'disk_alert',
                'Low Disk Space',
                f'Disk usage is at {usage:.1f}%\n'
                f'Free space: {disk.free / (1024**3):.1f} GB',
                priority=2,
                sticky=True
            )
            self.alert_active['disk'] = True

        elif usage <= self.thresholds['disk'] and self.alert_active['disk']:
            self.publisher.publish(
                'system_normal',
                'Disk Space Normal',
                f'Disk usage back to {usage:.1f}%',
                priority=0
            )
            self.alert_active['disk'] = False

    def monitor_loop(self, interval=60):
        """Run monitoring loop."""
        print(f"Starting system monitor (interval: {interval}s)")
        print("Press Ctrl+C to stop")

        try:
            while True:
                self.check_cpu()
                self.check_memory()
                self.check_disk()
                time.sleep(interval)
        except KeyboardInterrupt:
            print("\nMonitoring stopped")

# Usage
if __name__ == '__main__':
    # Custom thresholds
    monitor = SystemMonitor(thresholds={
        'cpu': 75.0,
        'memory': 80.0,
        'disk': 85.0,
    })

    # Start monitoring
    monitor.monitor_loop(interval=30)

Tutorial 4: Application with Callbacks

Handle user interactions with callbacks.

from gntplib import Publisher, Event, SocketCallback

class InteractiveApp:
    def __init__(self):
        events = [Event('question', 'User Question')]
        self.publisher = Publisher('InteractiveApp', events)
        self.publisher.register()

        self.user_responses = []

    def on_clicked(self, response):
        """Handle click event."""
        print(f"User clicked the notification!")
        context = response.headers.get('Notification-Callback-Context')
        self.user_responses.append(('clicked', context))
        return f"Handled click: {context}"

    def on_closed(self, response):
        """Handle close event."""
        print(f"User closed the notification")
        context = response.headers.get('Notification-Callback-Context')
        self.user_responses.append(('closed', context))
        return f"Handled close: {context}"

    def ask_question(self, question, question_id):
        """Ask user a question via notification."""
        callback = SocketCallback(
            context=question_id,
            context_type='question',
            on_click=self.on_clicked,
            on_close=self.on_closed
        )

        self.publisher.publish(
            'question',
            'Question',
            question,
            priority=1,
            sticky=True,
            gntp_callback=callback
        )

# Usage
app = InteractiveApp()
app.ask_question('Do you want to continue?', 'q1')
app.ask_question('Save changes?', 'q2')

Next Steps

Continue learning: