Skip to content

Commit

Permalink
Merge pull request #19 from Funtech-3/ft/email
Browse files Browse the repository at this point in the history
Ft/email
  • Loading branch information
floks41 committed Apr 15, 2024
2 parents 81e5e99 + 3a3e97a commit 7e676b3
Show file tree
Hide file tree
Showing 21 changed files with 280 additions and 82 deletions.
18 changes: 14 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
SECRET_KEY=django_secret_key_example18sd2skw2sxsq
DEBUG=False
ALLOWED_HOSTS=127.0.0.1, 0.0.0.0, localhost, example.host.com, IP_адрес_сервера
POSTGRES_USER=DB_USER
POSTGRES_PASSWORD=DB_PASSWORD
POSTGRES_DB=DB_APPS
DB_NAME=DB_NAME
DB_NAME=foods
DB_HOST=db
DB_PORT=5432
SECRET_KEY=django_secret_key_example18sd2skw2sxsq
DEBUG=False
ALLOWED_HOSTS=127.0.0.1, 0.0.0.0, localhost, example.host.com, IP_адрес_сервера
CSRF_DOMAIN=https://example.sites.com/
SUPERUSER_USERNAME=admin_example
SUPERUSER_PASSWORD=password_admin_example
SUPERUSER_EMAIL=admin@example.net

# Email settings
EMAIL_HOST='smtp.supermail.me'
EMAIL_PORT='4651'
EMAIL_HOST_USER='apushkin8888@yahoo.puk'
EMAIL_HOST_PASSWORD='ddfbgdfgbdgrf'
4 changes: 4 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ jobs:
REDIS_HOST=${{ secrets.REDIS_HOST }}
REDIS_PORT=${{ secrets.REDIS_PORT }}
CSRF_DOMAIN=${{ secrets.CSRF_DOMAIN }}
EMAIL_HOST=${{ secrets.EMAIL_HOST }}
EMAIL_PORT=${{ secrets.EMAIL_PORT }}
EMAIL_HOST_USER=${{ secrets.EMAIL_HOST_USER }}
EMAIL_HOST_PASSWORD=${{ secrets.EMAIL_HOST_PASSWORD }}
_EOF_
- name: Copy docker-compose.production.yml via ssh
uses: appleboy/scp-action@master
Expand Down
45 changes: 17 additions & 28 deletions backend/api/management/commands/load_test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,20 +107,18 @@ def load_speaker(self):
for row in DictReader(f=open(SPEAKER_CSV, encoding=UTF)):
counter += 1
object, created = Speaker.objects.update_or_create(**row)
img_path = os.path.join(
SPEAKER_IMAGE_PATH, SPEAKER_IMAGES.get(counter)
)
f = open(
os.path.join(SPEAKER_IMAGE_PATH, SPEAKER_IMAGES.get(counter)),
# f'{SPEAKER_IMAGE_PATH}{SPEAKER_IMAGES.get(counter)}',
img_path,
mode="rb",
)
image_file = File(f)
object.image.save(
SPEAKER_IMAGES.get(counter), image_file, save=True
)
self.stdout.write(
self.style.SUCCESS(
f"{os.path.join(SPEAKER_IMAGE_PATH, SPEAKER_IMAGES.get(counter))} {MESSAGE}"
)
)
self.stdout.write(self.style.SUCCESS(f"{img_path} {MESSAGE}"))
self.stdout.write(self.style.SUCCESS(f"{SPEAKER_CSV} {MESSAGE}"))

def load_users(self):
Expand All @@ -129,17 +127,14 @@ def load_users(self):
for row in DictReader(f=open(USERS_CSV, encoding=UTF)):
counter += 1
object, created = User.objects.update_or_create(**row)
img_path = os.path.join(USER_IMAGE_PATH, USER_IMAGES.get(counter))
f = open(
os.path.join(USER_IMAGE_PATH, USER_IMAGES.get(counter)),
img_path,
mode="rb",
)
image_file = File(f)
object.avatar.save(USER_IMAGES.get(counter), image_file, save=True)
self.stdout.write(
self.style.SUCCESS(
f"{os.path.join(USER_IMAGE_PATH, USER_IMAGES.get(counter))} {MESSAGE}"
)
)
self.stdout.write(self.style.SUCCESS(f"{img_path} {MESSAGE}"))
self.stdout.write(self.style.SUCCESS(f"{USERS_CSV} {MESSAGE}"))

def load_users_tags(self):
Expand Down Expand Up @@ -190,11 +185,11 @@ def load_event(self):
city=city,
**row,
)

img_path = os.path.join(
EVENT_IMAGE_PATH, EVENT_PREVIEW_IMAGES.get(counter)
)
f = open(
os.path.join(
EVENT_IMAGE_PATH, EVENT_PREVIEW_IMAGES.get(counter)
),
img_path,
mode="rb",
)
preview_image_file = File(f)
Expand All @@ -203,25 +198,19 @@ def load_event(self):
preview_image_file,
save=True,
)
self.stdout.write(
self.style.SUCCESS(
f"{os.path.join(EVENT_IMAGE_PATH, EVENT_PREVIEW_IMAGES.get(counter))} {MESSAGE}"
)
self.stdout.write(self.style.SUCCESS(f"{img_path} {MESSAGE}"))
img_path = os.path.join(
EVENT_IMAGE_PATH, EVENT_FULL_IMAGES.get(counter)
)

f = open(
os.path.join(EVENT_IMAGE_PATH, EVENT_FULL_IMAGES.get(counter)),
img_path,
mode="rb",
)
full_image_file = File(f)
event_object.image.save(
EVENT_FULL_IMAGES.get(counter), full_image_file, save=True
)
self.stdout.write(
self.style.SUCCESS(
f"{os.path.join(EVENT_IMAGE_PATH, EVENT_FULL_IMAGES.get(counter))} {MESSAGE}"
)
)
self.stdout.write(self.style.SUCCESS(f"{img_path} {MESSAGE}"))
event_object.save()

self.stdout.write(self.style.SUCCESS(f"{EVENT_CSV} {MESSAGE}"))
Expand Down
5 changes: 4 additions & 1 deletion backend/api/v1/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
from events.views import EventViewSet, FavoriteView
from rest_framework import routers
from tickets.views import UserTicketViewSet
from tickets.views import CheckTicketViewSet, UserTicketViewSet
from users.views import (
CitiesListView,
CustomUserViewSet,
Expand All @@ -13,6 +13,9 @@

router_v1 = routers.DefaultRouter()
router_v1.register(r"events", EventViewSet, basename="event")
router_v1.register(
"user/ticket_check", CheckTicketViewSet, basename="ticket_check"
)
router_v1.register("user/ticket", UserTicketViewSet, basename="ticket")
router_v1.register("user", CustomUserViewSet, basename="user")

Expand Down
16 changes: 16 additions & 0 deletions backend/funtech/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,19 @@
]

CSRF_TRUSTED_ORIGINS = [os.getenv("CSRF_DOMAIN", "http://localhost")]

# Email settings

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = os.getenv("EMAIL_HOST")
EMAIL_PORT = os.getenv("EMAIL_PORT", "465")
EMAIL_USE_SSL = True

EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", "")

EMAIL_SERVER = EMAIL_HOST_USER
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
EMAIL_ADMIN = [
EMAIL_HOST_USER,
]
2 changes: 1 addition & 1 deletion backend/pytest.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[pytest]
DJANGO_SETTINGS_MODULE = funtech.settings
testpaths = tests/
testpaths = tests/
1 change: 1 addition & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ PyJWT==2.8.0
python-dotenv==1.0.1
python3-openid==3.2.0
PyYAML==6.0.1
qrcode[pil]==7.4.2
referencing==0.34.0
requests==2.31.0
requests-oauthlib==2.0.0
Expand Down
6 changes: 3 additions & 3 deletions backend/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pytest_plugins = [
'tests.fixtures.fixture_user',
'tests.fixtures.fixture_data',
'tests.fixtures.fixture_urls',
"tests.fixtures.fixture_user",
"tests.fixtures.fixture_data",
"tests.fixtures.fixture_urls",
]
34 changes: 16 additions & 18 deletions backend/tests/fixtures/fixture_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,50 @@
@pytest.fixture
def cities_list(db):
return City.objects.bulk_create(
City(name=f'Город {index}')
for index in range(LEN_OBJ_IN_LIST)
City(name=f"Город {index}") for index in range(LEN_OBJ_IN_LIST)
)


@pytest.fixture
def city(db):
return City.objects.create(name='Тестовый город')
return City.objects.create(name="Тестовый город")


@pytest.fixture
def tags_list(db):
return Tag.objects.bulk_create(
Tag(title=f'Тег {index}')
for index in range(LEN_OBJ_IN_LIST)
Tag(title=f"Тег {index}") for index in range(LEN_OBJ_IN_LIST)
)


@pytest.fixture
def tag(db):
return Tag.objects.create(title='Тестовый тег')
return Tag.objects.create(title="Тестовый тег")


@pytest.fixture
def speaker(db):
return Speaker.objects.create(
first_name='Иван',
last_name='Иванов',
work_place='ACME',
position='Работник'
first_name="Иван",
last_name="Иванов",
work_place="ACME",
position="Работник",
)


@pytest.fixture
def image():
return NamedTemporaryFile(suffix='.jpg').name
return NamedTemporaryFile(suffix=".jpg").name


@pytest.fixture
def event(db, city, image, tag):
event = Event.objects.create(
title='Тестовое событие',
description='Тестовое',
slug='test-event',
title="Тестовое событие",
description="Тестовое",
slug="test-event",
city=city,
address='Зимний дворец',
address="Зимний дворец",
date=date.today() + timedelta(days=7),
image=image,
)
Expand Down Expand Up @@ -84,10 +82,10 @@ def event_list(db, city, image):
def event_steps(db, events, speaker):
steps = EventStep.objects.bulk_create(
EventStep(
title=f'Этап события {index}',
title=f"Этап события {index}",
start_time=time(hour=index),
description=f'Событие {index}',
event=event
description=f"Событие {index}",
event=event,
)
for index, event in enumerate(events)
)
Expand Down
2 changes: 1 addition & 1 deletion backend/tests/fixtures/fixture_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

@pytest.fixture
def url_event_list():
return reverse('event-list')
return reverse("event-list")


@pytest.fixture
Expand Down
8 changes: 2 additions & 6 deletions backend/tests/fixtures/fixture_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,12 @@

@pytest.fixture
def user(django_user_model):
return django_user_model.objects.create(
yandex_id=11111
)
return django_user_model.objects.create(yandex_id=11111)


@pytest.fixture
def another_user(django_user_model):
return django_user_model.objects.create(
yandex_id=22222
)
return django_user_model.objects.create(yandex_id=22222)


@pytest.fixture
Expand Down
14 changes: 7 additions & 7 deletions backend/tests/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ class TestEventAPI:

def test_events_not_found(self, client, url_event_list):
response = client.get(url_event_list)
assert response.status_code != HTTPStatus.NOT_FOUND, (
f'Эндпоинт {url_event_list} не найден.'
)
assert (
response.status_code != HTTPStatus.NOT_FOUND
), f"Эндпоинт {url_event_list} не найден."

def test_event_list_not_auth(self, client, url_event_list):
response = client.get(url_event_list)
assert response.status_code == HTTPStatus.OK, (
f'Эндпоинт {url_event_list} должен быть доступен'
f'неавторизованным пользователям.'
f"Эндпоинт {url_event_list} должен быть доступен"
f"неавторизованным пользователям."
)

def test_event_list_auth(self, user_client, url_event_list):
Expand Down Expand Up @@ -60,8 +60,8 @@ def test_events_get_paginated(self, client, url_event_list, event_list):
def test_event_detail_not_auth(self, client, url_event_detail, event):
response = client.get(url_event_detail)
assert response.status_code == HTTPStatus.OK, (
f'Эндпоинт {url_event_detail} должен быть доступен'
f'неавторизованным пользователям.'
f"Эндпоинт {url_event_detail} должен быть доступен"
f"неавторизованным пользователям."
)
data = response.json()
assert isinstance(data, dict), (
Expand Down
6 changes: 6 additions & 0 deletions backend/tickets/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ class TicketsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "tickets"
verbose_name = "Регистрация билетов"

def ready(self):
"""Активация сигналов при включении приложения."""
from . import signals

pass
17 changes: 17 additions & 0 deletions backend/tickets/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,20 @@

STATUS_MAX_LENGTH = 25
TICKET_CODE_LENGTH = 36

# Для модуля email_utils.py
QR_CODE_CONTENT_ID = "qrcode"
TICKET_LETTER_TEMPLATE = (
"<HTML><BODY>"
"<h1>Ваш билет</h1>"
"<h2>{}</h2>"
"<h3>{}</h3>"
"<h2>{}</h2>"
"<h3>{}</h3>"
'<img src="cid:{}">'
"</BODY></HTML>"
)
TICKET_LETTER_SUBJECT = "Ваш билет"
TICKET_CHECK_URL_TEMPLATE = (
"https://funtech.myddns.me/api/v1/user/ticket_check/{}/"
)
Loading

0 comments on commit 7e676b3

Please sign in to comment.