← Назад к вопросам

Как проходила работа с внутренними инструментами?

1.2 Junior🔥 101 комментариев
#Soft Skills

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Работа с внутренними инструментами

Внутренние инструменты (internal tools, admin panels, dashboards) — это неотъемлемая часть разработки. Это простой и быстрый способ автоматизировать рутинные задачи.

Типы внутренних инструментов

1. Admin Panels (административные панели)

Для управления данными приложения:

# Django Admin
from django.contrib.admin import AdminSite
from .models import User, Post

@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'email', 'created_at')
    search_fields = ('name', 'email')
    list_filter = ('is_active', 'created_at')
    
    def get_actions(self, request):
        actions = super().get_actions(request)
        # Добавил кастомное действие для рассылки
        def send_newsletter(modeladmin, request, queryset):
            for user in queryset:
                send_email.delay(user.email)  # Async task
        
        send_newsletter.short_description = "Отправить рассылку"
        actions['send_newsletter'] = (send_newsletter, 'send_newsletter', 
                                       send_newsletter.short_description)
        return actions

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'published', 'created_at')
    list_filter = ('published',)
    readonly_fields = ('created_at', 'updated_at')

2. Dashboards (аналитические панели)

Для мониторинга метрик и KPI:

# FastAPI + Streamlit для быстрого создания Dashboard
from fastapi import APIRouter
from sqlalchemy import func
from .models import User, Post

router = APIRouter(prefix="/admin/dashboard", tags=["dashboard"])

@router.get("/stats")
def get_stats(db):
    return {
        "total_users": db.query(User).count(),
        "active_users": db.query(User).filter(User.is_active).count(),
        "total_posts": db.query(Post).count(),
        "posts_today": db.query(Post).filter(
            Post.created_at >= datetime.now().date()
        ).count(),
        "avg_posts_per_user": db.query(
            func.avg(func.count(Post.id))
        ).group_by(Post.author_id).scalar()
    }

@router.get("/charts/user-growth")
def user_growth_chart(db):
    """Данные для графика роста пользователей по дням"""
    result = db.query(
        func.date(User.created_at).label('date'),
        func.count(User.id).label('count')
    ).group_by('date').order_by('date').all()
    
    return [{"date": str(r[0]), "count": r[1]} for r in result]

3. CLI инструменты (командная строка)

Для автоматизации однократных задач:

import click
from django.core.management.base import BaseCommand

# Django Management Command
class Command(BaseCommand):
    help = 'Очистить неактивных пользователей'
    
    def add_arguments(self, parser):
        parser.add_argument('--days', type=int, default=90,
                          help='Дни неактивности')
    
    def handle(self, *args, **options):
        days = options['days']
        cutoff = datetime.now() - timedelta(days=days)
        
        inactive = User.objects.filter(last_login__lt=cutoff)
        count = inactive.delete()[0]
        
        self.stdout.write(
            self.style.SUCCESS(f'Удалено {count} неактивных пользователей')
        )

# Click CLI
@click.group()
def cli():
    pass

@cli.command()
@click.option('--batch-size', default=100, help='Размер батча')
@click.option('--dry-run', is_flag=True, help='Просто показать, что будет удалено')
def cleanup_inactive_users(batch_size, dry_run):
    """Очистить неактивных пользователей"""
    cutoff = datetime.now() - timedelta(days=90)
    
    inactive = User.objects.filter(last_login__lt=cutoff)
    
    if dry_run:
        click.echo(f"Будет удалено: {inactive.count()} пользователей")
        return
    
    deleted = 0
    for i, user in enumerate(inactive):
        user.delete()
        deleted += 1
        if (i + 1) % batch_size == 0:
            click.echo(f"Обработано: {deleted}")
    
    click.echo(click.style(f"✓ Удалено {deleted} пользователей", fg='green'))

if __name__ == '__main__':
    cli()

4. Data Migration Scripts

Для миграции и трансформации данных:

from django.core.management.base import BaseCommand
from tqdm import tqdm

class Command(BaseCommand):
    help = 'Мигрировать данные из старой системы'
    
    def handle(self, *args, **options):
        # Получить данные из старой БД
        legacy_users = get_legacy_users()  # API или SQL к старой БД
        
        created = 0
        skipped = 0
        
        with transaction.atomic():
            for legacy_user in tqdm(legacy_users, desc="Миграция пользователей"):
                try:
                    user, created_flag = User.objects.get_or_create(
                        email=legacy_user['email'],
                        defaults={
                            'name': legacy_user['name'],
                            'legacy_id': legacy_user['id'],
                        }
                    )
                    if created_flag:
                        created += 1
                    else:
                        skipped += 1
                except Exception as e:
                    self.stdout.write(
                        self.style.ERROR(f"Ошибка с {legacy_user['email']}: {e}")
                    )
        
        self.stdout.write(
            self.style.SUCCESS(
                f"Создано: {created}, пропущено: {skipped}"
            )
        )

Мой опыт с внутренними инструментами

Проект 1: Система модерации контента

# Быстро создал админ-панель для модерирования постов
# Django Admin + кастомные actions

class PostModerationAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'status', 'created_at')
    list_filter = ('status', 'created_at')
    actions = ['approve', 'reject', 'send_feedback']
    
    def approve(self, request, queryset):
        count = queryset.update(status='approved')
        notify_users.delay([p.author_id for p in queryset])
        self.message_user(request, f"{count} постов одобрено")
    
    def reject(self, request, queryset):
        count = queryset.update(status='rejected')
        self.message_user(request, f"{count} постов отклонено")
    
    def send_feedback(self, request, queryset):
        """Отправить feedback авторам"""
        for post in queryset:
            send_notification.delay(
                user_id=post.author_id,
                message="Ваш пост нуждается в правках"
            )

Этот инструмент спас ~5 часов в неделю для команды модерации.

Проект 2: Dashboard аналитики

# Создал FastAPI endpoint + Grafana dashboard
# Отслеживаю:
# - Активных пользователей
# - Ошибки в приложении
# - Время ответа API
# - Объём трафика

@router.get("/metrics/api-health")
def api_health(db):
    last_hour = datetime.now() - timedelta(hours=1)
    
    requests = db.query(
        APILog.endpoint,
        func.count().label('count'),
        func.avg(APILog.response_time).label('avg_time'),
        func.max(APILog.response_time).label('max_time')
    ).filter(APILog.created_at > last_hour).group_by(
        APILog.endpoint
    ).all()
    
    return [{"endpoint": r[0], "requests": r[1], 
             "avg_time": r[2], "max_time": r[3]} for r in requests]

Проект 3: Bulk операции

# CLI инструмент для бульк-операций
# Отправка email, обновление статусов, экспорт данных

@cli.command()
@click.argument('query')
@click.option('--action', required=True, help='Действие (email, update, export)')
@click.option('--batch-size', default=100)
def bulk_operation(query, action, batch_size):
    """Выполнить операцию для множества объектов"""
    
    users = User.objects.filter(**parse_query(query))
    total = users.count()
    
    click.echo(f"Найдено {total} пользователей")
    
    if not click.confirm('Продолжить?'):
        click.echo('Отменено')
        return
    
    if action == 'email':
        for batch in batch_iter(users, batch_size):
            send_emails.delay([u.id for u in batch])
    
    elif action == 'export':
        with open('export.csv', 'w') as f:
            writer = csv.DictWriter(f, fieldnames=['id', 'name', 'email'])
            writer.writeheader()
            for user in users:
                writer.writerow(user.to_dict())
        click.echo('Экспортировано в export.csv')

Лучшие практики

✅ Django Admin для быстрого CRUD
✅ CLI scripts для batch операций
✅ Dashboards для мониторинга
✅ Логирование всех действий (audit trail)
✅ Permissions и role-based access control
❌ Не деплой инструменты без review
❌ Не предоставляй доступ к production без необходимости

Внутренние инструменты экономят тонны времени и улучшают качество операций.

Как проходила работа с внутренними инструментами? | PrepBro