diff --git a/.dockerignore b/.dockerignore index cef21b7..675d74b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,3 +8,4 @@ orconfig .readthedocs.yml .travis.yml venv +data diff --git a/passfinder/events/api/serializers.py b/passfinder/events/api/serializers.py index f0ffd45..be94b47 100644 --- a/passfinder/events/api/serializers.py +++ b/passfinder/events/api/serializers.py @@ -172,7 +172,7 @@ class InputRouteSerializer(serializers.Serializer): class ListUserRouteSerializer(serializers.ModelSerializer): class Meta: model = UserRoute - fields = ["id", "created"] + fields = ["id", "name", "description", "created"] class UserRouteDateSerializer(serializers.ModelSerializer): diff --git a/passfinder/events/migrations/0028_userroute_description_userroute_title.py b/passfinder/events/migrations/0028_userroute_description_userroute_title.py new file mode 100644 index 0000000..638116b --- /dev/null +++ b/passfinder/events/migrations/0028_userroute_description_userroute_title.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.1 on 2023-05-28 10:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("events", "0027_city_temperature_city_weather_condition"), + ] + + operations = [ + migrations.AddField( + model_name="userroute", + name="description", + field=models.TextField(default="описание"), + preserve_default=False, + ), + migrations.AddField( + model_name="userroute", + name="title", + field=models.CharField(default="маршрут", max_length=250), + preserve_default=False, + ), + ] diff --git a/passfinder/events/models.py b/passfinder/events/models.py index 53cb224..efd5caf 100644 --- a/passfinder/events/models.py +++ b/passfinder/events/models.py @@ -238,6 +238,8 @@ class RestaurantType(models.TextChoices): class UserRoute(models.Model): + title = models.CharField(max_length=250) + description = models.TextField() user = models.ForeignKey( "users.User", related_name="routes", on_delete=models.CASCADE ) diff --git a/passfinder/events/services.py b/passfinder/events/services.py index 7a2f32a..35b537b 100644 --- a/passfinder/events/services.py +++ b/passfinder/events/services.py @@ -5,15 +5,17 @@ api_url = "https://api.weather.yandex.ru/v2/forecast" -def get_position_weather(lat: float, lon: float) -> (int, str): - url = api_url + f"?lat={lat}&lon={lon}&lang=ru_RU&limit=1&hours=false" +def get_position_weather(lat: float, lon: float) -> list[(str, str)]: + url = api_url + f"?lat={lat}&lon={lon}&lang=ru_RU&limit=3&hours=false" response = requests.get( url=url, headers={"X-Yandex-API-Key": settings.YANDEX_TOKEN} ) - temp_feels = 20 - weather = "clear" + if response.status_code == 200: data = response.json() - temp_feels = data["forecasts"][0]["parts"]["day"]["feels_like"] - weather = data["forecasts"][0]["parts"]["day_short"]["condition"] - return temp_feels, weather + days = [] + + for d in data["forecasts"]: + days.append((d["date"], d["parts"]["day_short"]["condition"])) + return days + return [] diff --git a/passfinder/events/tasks.py b/passfinder/events/tasks.py index a8cb6f6..68be8fc 100644 --- a/passfinder/events/tasks.py +++ b/passfinder/events/tasks.py @@ -1,15 +1,72 @@ -from celery import shared_task +from datetime import timedelta -from passfinder.events.models import City +from celery import shared_task +from django.conf import settings +from django.core.mail import send_mail +from django.template.loader import render_to_string +from django.utils.timezone import now +from django.utils.dateparse import parse_date + +from passfinder.events.models import City, UserRoute from passfinder.events.services import get_position_weather -api_url = "https://api.weather.yandex.ru/v2/forecast" +weathers_labels = { + "drizzle": "морось", + "light-rain": "небольшой дождь", + "rain": "дождь", + "moderate-rain": "дождь", + "heavy-rain": "сильный дождь", + "continuous-heavy-rain": "сильный дождь", + "showers": "ливень", + "wet-snow": "дождь со снегом", + "snow-showers": "снегопад", + "hail": "град", + "thunderstorm": "гроза", + "thunderstorm-with-rain": "дождь с грозой", + "thunderstorm-with-hail": "дождь с грозой", +} @shared_task def check_temperature(): - for city in City.objects.all(): - temperature, weather_condition = get_position_weather(city.lat, city.lon) - city.temperature = temperature - city.weather_condition = weather_condition - city.save() + cities_temp = {} + + for route in UserRoute.objects.filter( + dates__date__gte=now().date(), dates__date__lte=now().date() + timedelta(days=3) + ): + points = route.dates.objects.get(date=now().date()).points.all() + cities = points.values_list("city_id").distinct() + alerts = [] + for city in cities: + if city: + city = City.objects.get(oid=city) + if city in cities_temp: + weather_conditions = cities_temp[city] + else: + weather_conditions = get_position_weather(city.lat, city.lon) + weather_conditions = [ + (parse_date(x[0]), x[1]) for x in weather_conditions + ] + cities_temp[city] = weather_conditions + + for date, weather in weather_conditions: + if weather in weathers_labels: + alerts.append( + f"В городе {city.title} {date.strftime('%d.%m.%Y')} предстоит {weathers_labels[weather]}" + ) + if alerts: + context = { + "user": route.user, + "route": route, + "alerts": alerts, + # TODO: change to frontend link + "link": f"http://127.0.0.1:8000/path/{route.id}/change", + } + + email_plaintext_message = render_to_string("weather_alert.txt", context) + send_mail( + "Предупреждение о погоде", + email_plaintext_message, + settings.DEFAULT_FROM_EMAIL, + [route.user.email], + ) diff --git a/passfinder/templates/weather_alert.txt b/passfinder/templates/weather_alert.txt new file mode 100644 index 0000000..786640d --- /dev/null +++ b/passfinder/templates/weather_alert.txt @@ -0,0 +1,8 @@ +Доброе утро, {{ user.username }}! + +Уведомляем вас, что во время вашей поездки в {{ route.title }} ожидаются осадки: +{% for alert in alerts %} + - {{ alert }} +{% endfor %} + +Вы можете изменить маршрут по ссылке: {{ link }}