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
8 changes: 4 additions & 4 deletions backend/bot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ async def planting(message: Message):
logger.warning("Received message without user information")
return

answer = _("Your future plantings:") + "\n"
answer = _("Your plants ready for transplanting:") + "\n"

plants_found = False
now = timezone.localdate()
Expand All @@ -342,14 +342,14 @@ async def planting(message: Message):
continue

start_date, end_date = parsed_period
if end_date < now:
if now < start_date or end_date < now:
continue

plants_found = True
answer += f"\n🌱 {plant.name}\n"
answer += f" {_('Planned planting period')}: {plant.planting_period}\n"
answer += f" {_('Planned transplanting period')}: {plant.planting_period}\n"

if not plants_found:
answer = _("You don't have any plants yet.")
answer = _("No plants are ready for transplanting right now.")

await message.answer(str(answer))
7 changes: 4 additions & 3 deletions backend/catalogs/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class PlantTypeOperationsInline(admin.TabularInline):

@admin.register(PlantType)
class PlantTypeAdmin(admin.ModelAdmin):
fields = ("name", "slug", "description", "planting_period", "duration_days")
list_display = ("name", "slug", "planting_period", "duration_days")
fields = ("name", "slug", "description", "sowing_period", "planting_period", "duration_days")
list_display = ("name", "slug", "sowing_period", "planting_period", "duration_days")
ordering = ("name",)
prepopulated_fields = {"slug": ("name",)}
search_fields = ("name",)
Expand All @@ -47,10 +47,11 @@ class PlantVarietyAdmin(admin.ModelAdmin):
"name",
"slug",
"description",
"sowing_period",
"planting_period",
"duration_days",
)
list_display = ("full_name", "type", "planting_period", "duration_days")
list_display = ("full_name", "type", "sowing_period", "planting_period", "duration_days")
list_filter = ("type",)
ordering = ("type__name", "name")
prepopulated_fields = {"slug": ("name",)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generated by Django 5.1.7 on 2026-03-03 01:22

import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("catalogs", "0004_alter_planttype_planting_period_and_more"),
]

operations = [
migrations.AddField(
model_name="planttype",
name="sowing_period",
field=models.CharField(
blank=True,
max_length=16,
null=True,
validators=[
django.core.validators.RegexValidator(
message="Неверный формат периода посадки. Используйте формат 'DD.MM-DD.MM', где MM - месяц в римской записи (I-XII).",
regex="^(0[1-9]|[12][0-9]|3[01])\\.(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII)-(0[1-9]|[12][0-9]|3[01])\\.(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII)$",
)
],
verbose_name="Период посадки",
),
),
migrations.AddField(
model_name="plantvariety",
name="sowing_period",
field=models.CharField(
blank=True,
max_length=16,
null=True,
validators=[
django.core.validators.RegexValidator(
message="Неверный формат периода посадки. Используйте формат 'DD.MM-DD.MM', где MM - месяц в римской записи (I-XII).",
regex="^(0[1-9]|[12][0-9]|3[01])\\.(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII)-(0[1-9]|[12][0-9]|3[01])\\.(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII)$",
)
],
verbose_name="Период посадки",
),
),
]
37 changes: 35 additions & 2 deletions backend/catalogs/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from django.utils.translation import gettext_lazy as _


PLANTING_PERIOD_REGEX = r"^(0[1-9]|[12][0-9]|3[01])\.(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII)-(0[1-9]|[12][0-9]|3[01])\.(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII)$"
Comment thread
capcom6 marked this conversation as resolved.


class Step(models.TextChoices):
SEED_PREPARATION = "10-seed_preparation", _("🌱🛠️ Подготовка семян")
SOWING = "20-sowing", _("🌱👐 Посев")
Expand Down Expand Up @@ -30,14 +33,29 @@ class PlantType(models.Model):
name = models.CharField(verbose_name=_("Наименование"), max_length=128)
description = models.TextField(verbose_name=_("Описание"))

sowing_period = models.CharField(
verbose_name=_("Период посадки"),
max_length=16,
null=True,
blank=True,
validators=[
RegexValidator(
regex=PLANTING_PERIOD_REGEX,
message=_(
"Неверный формат периода посадки. Используйте формат 'DD.MM-DD.MM', где MM - месяц в римской записи (I-XII)."
),
),
],
)

planting_period = models.CharField(
verbose_name=_("Период высадки"),
max_length=16,
null=True,
blank=True,
validators=[
RegexValidator(
regex=r"^(0[1-9]|[12][0-9]|3[01])\.(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII)-(0[1-9]|[12][0-9]|3[01])\.(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII)$",
regex=PLANTING_PERIOD_REGEX,
message=_(
"Неверный формат периода высадки. Используйте формат 'DD.MM-DD.MM', где MM - месяц в римской записи (I-XII)."
),
Expand Down Expand Up @@ -69,14 +87,29 @@ class PlantVariety(models.Model):
name = models.CharField(verbose_name=_("Наименование"), max_length=128)
description = models.TextField(verbose_name=_("Описание"))

sowing_period = models.CharField(
verbose_name=_("Период посадки"),
max_length=16,
null=True,
blank=True,
validators=[
RegexValidator(
regex=PLANTING_PERIOD_REGEX,
message=_(
"Неверный формат периода посадки. Используйте формат 'DD.MM-DD.MM', где MM - месяц в римской записи (I-XII)."
),
),
],
)

planting_period = models.CharField(
verbose_name=_("Период высадки"),
max_length=16,
null=True,
blank=True,
validators=[
RegexValidator(
regex=r"^(0[1-9]|[12][0-9]|3[01])\.(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII)-(0[1-9]|[12][0-9]|3[01])\.(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII)$",
regex=PLANTING_PERIOD_REGEX,
message=_(
"Неверный формат периода высадки. Используйте формат 'DD.MM-DD.MM', где MM - месяц в римской записи (I-XII)."
),
Expand Down
4 changes: 4 additions & 0 deletions backend/diary/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ def type_name(self, obj):
def variety_name(self, obj):
return obj.variety

@admin.display(description=_("Период высадки"))
def planting_period(self, obj):
return obj.planting_period

@admin.display(description=_("Дата сбора"))
def planned_harvest_date(self, obj):
return obj.planned_harvest_date
Expand Down
65 changes: 43 additions & 22 deletions backend/diary/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.conf import settings
from django.db import models
from django.utils.functional import cached_property
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from timezone_field import TimeZoneField

Expand Down Expand Up @@ -146,28 +147,15 @@ def save(self, *args, **kwargs):
self.name = self.type.name
if self.variety:
self.name += f" {self.variety.name}"
self.name += f" ({date.today().strftime('%Y-%m-%d')})"
self.name += f" ({timezone.localdate().strftime('%Y-%m-%d')})"
super().save(*args, **kwargs)

@property
def planting_period(self):
if self.variety and self.variety.planting_period:
return self.variety.planting_period
return self.type.planting_period

@cached_property
def parsed_planting_period(self):
"""
Parses the planting_period string in 'DD.MM-DD.MM' format into a tuple of start and end dates.

Returns:
tuple: A tuple containing the start and end dates as datetime.date objects, or None if parsing fails.
"""
period = self.planting_period
@staticmethod
def _parse_period(period: Optional[str], plant_id: Optional[int], period_name: str):
if not period:
return None

romanian_months = {
roman_months = {
"I": 1,
"II": 2,
"III": 3,
Expand All @@ -187,15 +175,48 @@ def parsed_planting_period(self):
start_day, start_month = start_str.split(".")
end_day, end_month = end_str.split(".")

today = date.today()
start_date = date(today.year, romanian_months[start_month], int(start_day))
end_date = date(today.year, romanian_months[end_month], int(end_day))
today = timezone.localdate()
start_day_i = int(start_day)
end_day_i = int(end_day)
start_month_i = roman_months[start_month]
end_month_i = roman_months[end_month]

wraps_year = (end_month_i, end_day_i) < (start_month_i, start_day_i)
start_year = today.year
end_year = today.year

if wraps_year:
if (today.month, today.day) <= (end_month_i, end_day_i):
# We are in Jan/Feb part of a wrapped period: active range started last year.
start_year -= 1
else:
# We are in/approaching Nov-Dec side: end falls next year.
end_year += 1

start_date = date(start_year, start_month_i, start_day_i)
end_date = date(end_year, end_month_i, end_day_i)

return start_date, end_date
except (ValueError, KeyError):
logger.warning("Failed to parse planting period for plant %s", self.id)
except (ValueError, KeyError) as exc:
logger.warning(
"Failed to parse %s for plant %s: value=%r error=%s",
period_name,
plant_id,
period,
exc,
)
return None

@property
def planting_period(self):
if self.variety and self.variety.planting_period:
return self.variety.planting_period
return self.type.planting_period

@cached_property
def parsed_planting_period(self):
return self._parse_period(self.planting_period, self.id, "planting period")

def __str__(self):
return f"{self.name}"

Expand Down