Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@
"editor.rulers": [
120
],
"explorer.excludeGitIgnore": true
}
10 changes: 5 additions & 5 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
# pip-compile --extra=dev --output-file=requirements/dev.txt pyproject.toml
#
asgiref==3.10.0
asgiref==3.11.0
# via django
astroid==4.0.2
# via pylint
Expand All @@ -14,15 +14,15 @@ charset-normalizer==3.4.4
# via
# reportlab
# requests
coverage==7.11.3
coverage==7.12.0
# via ida (pyproject.toml)
dill==0.4.0
# via pylint
django==5.2.8
# via
# django-telegram-app
# ida (pyproject.toml)
django-telegram-app==0.3.0
django-telegram-app==1.0.0
# via ida (pyproject.toml)
django-types==0.22.0
# via ida (pyproject.toml)
Expand Down Expand Up @@ -50,11 +50,11 @@ pyright==1.1.407
# via ida (pyproject.toml)
python-dotenv==1.2.1
# via ida (pyproject.toml)
reportlab==4.4.4
reportlab==4.4.5
# via ida (pyproject.toml)
requests==2.32.5
# via django-telegram-app
ruff==0.14.5
ruff==0.14.6
# via ida (pyproject.toml)
sqlparse==0.5.3
# via django
Expand Down
6 changes: 3 additions & 3 deletions requirements/main.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
# pip-compile --output-file=requirements/main.txt pyproject.toml
#
asgiref==3.10.0
asgiref==3.11.0
# via django
certifi==2025.11.12
# via requests
Expand All @@ -16,7 +16,7 @@ django==5.2.8
# via
# django-telegram-app
# ida (pyproject.toml)
django-telegram-app==0.3.0
django-telegram-app==1.0.0
# via ida (pyproject.toml)
envyronment==0.4.0
# via ida (pyproject.toml)
Expand All @@ -28,7 +28,7 @@ packaging==25.0
# via gunicorn
pillow==12.0.0
# via reportlab
reportlab==4.4.4
reportlab==4.4.5
# via ida (pyproject.toml)
requests==2.32.5
# via django-telegram-app
Expand Down
1 change: 1 addition & 0 deletions src/apps/telegram/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Telegram app."""
10 changes: 10 additions & 0 deletions src/apps/telegram/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""Configuration for the telegram app."""

from django.apps import AppConfig


class TelegramConfig(AppConfig):
"""Configuration for the telegram app."""

default_auto_field = "django.db.models.BigAutoField"
name = "apps.telegram"
1 change: 1 addition & 0 deletions src/apps/telegram/management/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Management package for the telegram app."""
32 changes: 32 additions & 0 deletions src/apps/telegram/management/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Base command for telegram management commands."""

from __future__ import annotations

from typing import TYPE_CHECKING

from django.utils.translation import override
from django_telegram_app.bot.bot import handle_update
from django_telegram_app.management.base import BaseManagementCommand

from apps.telegram.models import TelegramSettings
from apps.users.models import IdaUser

if TYPE_CHECKING:
from django_telegram_app.models import AbstractTelegramSettings


class ManagementCommand(BaseManagementCommand):
"""Base command for telegram management commands."""

def get_telegram_settings_filter(self):
"""Filter to get only active users."""
return {"user__is_active": True}

def handle_command(self, telegram_settings: AbstractTelegramSettings, command_text: str):
"""Handle the update in the current user's language."""
assert isinstance(telegram_settings, TelegramSettings)
assert isinstance(telegram_settings.user, IdaUser)

update = {"message": {"chat": {"id": telegram_settings.chat_id}, "text": command_text}}
with override(telegram_settings.user.language):
handle_update(update, telegram_settings)
31 changes: 31 additions & 0 deletions src/apps/telegram/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 5.2.8 on 2025-11-18 23:38

import django.core.serializers.json
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='TelegramSettings',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('chat_id', models.IntegerField(unique=True, verbose_name='chat id')),
('data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder, verbose_name='data')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='updated at')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user')),
],
options={
'abstract': False,
},
),
]
Empty file.
12 changes: 12 additions & 0 deletions src/apps/telegram/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""Models for the telegram app."""

from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_telegram_app.models import AbstractTelegramSettings


class TelegramSettings(AbstractTelegramSettings):
"""Custom Telegram settings model."""

user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name=_("user"))
1 change: 1 addition & 0 deletions src/apps/telegram/telegrambot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Telegrambot package for the telegram app."""
43 changes: 43 additions & 0 deletions src/apps/telegram/telegrambot/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Base telegram settings."""

from abc import ABC

from django_telegram_app.bot.base import BaseBotCommand, Step

from apps.telegram.models import TelegramSettings


class TelegramCommand(BaseBotCommand, ABC):
"""Project specific base class for telegram commands."""

settings: TelegramSettings


class TelegramStep(Step, ABC):
"""Project specific base class for telegram command steps."""

command: TelegramCommand

def __init__(self, command: TelegramCommand, unique_id: str | None = None, steps_back: int = 0):
"""Initialize the telegram step.

Steps back is configured on the step level since we re-use steps in multiple commands, so the amount of
steps to go back might differ depending on the command.
"""
self.steps_back = steps_back
super().__init__(command, unique_id)

def maybe_add_previous_button(self, keyboard: list[list[dict]], data: dict, **kwargs):
"""Add a previous button if steps_back is set."""
if self.steps_back <= 0:
return
keyboard.append(
[
{
"text": "⬅️ Previous step",
"callback_data": self.previous_step_callback(
steps_back=self.steps_back, original_data=data, **kwargs
),
}
]
)
1 change: 1 addition & 0 deletions src/apps/telegram/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for the Telegram app."""
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Django command to start the CompleteTimesheet command for active users with a chat_id."""

from django.utils import timezone
from django_telegram_app.management.base import BaseTelegramCommand

from apps.telegram.management.base import ManagementCommand
from apps.timesheets.telegrambot.commands.completetimesheet import Command as CompleteTimesheetCommand


class Command(BaseTelegramCommand):
class Command(ManagementCommand):
"""Start the CompleteTimesheet command."""

help = "Start the CompleteTimesheet command to let users complete their timesheets."
Expand Down
4 changes: 2 additions & 2 deletions src/apps/timesheets/management/commands/startregisterwork.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"""Django command to start a RegisterWork command for active users with a chat_id."""

from django.utils import timezone
from django_telegram_app.management.base import BaseTelegramCommand

from apps.telegram.management.base import ManagementCommand
from apps.timesheets.telegrambot.commands.registerwork import Command as RegisterWorkCommand


class Command(BaseTelegramCommand):
class Command(ManagementCommand):
"""Start a RegisterWork command."""

help = "Start a RegisterWork command to let users register their work hours."
Expand Down
7 changes: 3 additions & 4 deletions src/apps/timesheets/telegrambot/commands/completetimesheet.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
"""Complete timesheet command for the Telegram bot."""

from django_telegram_app.bot.base import BaseCommand, Step

from apps.telegram.telegrambot.base import TelegramCommand
from apps.timesheets.telegrambot.steps import Confirm, MarkTimesheetAsCompleted, SelectTimesheet


class Command(BaseCommand):
class Command(TelegramCommand):
"""Represent the complete timesheet command."""

description = "Mark a timesheet as completed"

@property
def steps(self) -> list[Step]:
def steps(self):
"""Return the steps of the command."""
return [SelectTimesheet(self), Confirm(self, steps_back=1), MarkTimesheetAsCompleted(self)]
7 changes: 3 additions & 4 deletions src/apps/timesheets/telegrambot/commands/editwork.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
"""Edit work command for the Telegram bot."""

from django_telegram_app.bot.base import BaseCommand, Step

from apps.telegram.telegrambot.base import TelegramCommand
from apps.timesheets.telegrambot.steps import EditWorkedHours, SelectExistingDay, SelectWorkedHours


class Command(BaseCommand):
class Command(TelegramCommand):
"""Represent the edit work command."""

description = "Edit previously registered working hours"

@property
def steps(self) -> list[Step]:
def steps(self):
"""Return the steps of the command."""
return [SelectExistingDay(self), SelectWorkedHours(self, steps_back=1), EditWorkedHours(self)]
7 changes: 3 additions & 4 deletions src/apps/timesheets/telegrambot/commands/registerovertime.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Register overtime command for the Telegram bot."""

from django_telegram_app.bot.base import BaseCommand, Step

from apps.telegram.telegrambot.base import TelegramCommand
from apps.timesheets.telegrambot.steps import (
CombineDateTime,
Confirm,
Expand All @@ -14,13 +13,13 @@
)


class Command(BaseCommand):
class Command(TelegramCommand):
"""Represent the register overtime command."""

description = "Register overtime for a specific day on a specific project."

@property
def steps(self) -> list[Step]:
def steps(self):
"""Return the steps of the command."""
return [
SelectProject(self),
Expand Down
7 changes: 3 additions & 4 deletions src/apps/timesheets/telegrambot/commands/registerwork.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
"""Register work command for the Telegram bot."""

from django_telegram_app.bot.base import BaseCommand, Step

from apps.telegram.telegrambot.base import TelegramCommand
from apps.timesheets.telegrambot.steps import RegisterWorkedHours, SelectMissingDay, SelectWorkedHours


class Command(BaseCommand):
class Command(TelegramCommand):
"""Represent the register work command."""

description = "Register working hours for a specific day on a specific project."

@property
def steps(self) -> list[Step]:
def steps(self):
"""Return the steps of the command."""
return [SelectMissingDay(self), SelectWorkedHours(self, steps_back=1), RegisterWorkedHours(self)]
7 changes: 3 additions & 4 deletions src/apps/timesheets/telegrambot/commands/requestoverview.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
"""Request Overview command for the Telegram bot."""

from django_telegram_app.bot.base import BaseCommand, Step

from apps.telegram.telegrambot.base import TelegramCommand
from apps.timesheets.telegrambot.steps import SelectOverviewType, SelectTimesheet, ShowOverview


class Command(BaseCommand):
class Command(TelegramCommand):
"""Represent the request overview command."""

description = "Request an overview of a timesheet and its items."

@property
def steps(self) -> list[Step]:
def steps(self):
"""Return the steps of the command."""
return [
SelectTimesheet(self, filter_kwargs={"user": self.settings.user}),
Expand Down
Loading