Merge branch 'refs/heads/main' into prod

This commit is contained in:
Андрей Дувакин 2024-04-26 18:28:30 +05:00
commit 536837991c
34 changed files with 254 additions and 83 deletions

View File

@ -1,3 +1,5 @@
SECRET_KEY=amazon SECRET_KEY=wevras-vberagv-rebavertf-wefgre
DJANGO_DEBUG=T DJANGO_DEBUG=1
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1 DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
DJANGO_DB_HOST=188.68.221.227
DJANGO_DB_PASSWORD=127001

View File

View File

@ -0,0 +1,21 @@
from timetable.models import FilmSession
def get_film_to_sessions():
film_sessions = FilmSession.objects.nearest_timetable()
sessions_by_date_and_film = {}
for session in film_sessions:
session_date = session.start_datetime.date()
session_film = session.film
sessions_by_date_and_film.setdefault(session_date, {}).setdefault(
session_film, []
).append(session)
for session_date in sessions_by_date_and_film:
for session_film in sessions_by_date_and_film[session_date]:
sessions_by_date_and_film[session_date][session_film].sort(
key=lambda x: x.start_datetime
)
return sessions_by_date_and_film

View File

View File

@ -0,0 +1,39 @@
# Generated by Django 4.2 on 2024-04-26 04:15
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('films', '0002_alter_film_genres_squashed_0007_alter_film_countries'),
]
operations = [
migrations.AlterModelOptions(
name='actor',
options={'verbose_name': 'актер', 'verbose_name_plural': 'Актеры'},
),
migrations.AlterModelOptions(
name='country',
options={
'verbose_name': 'страна',
'verbose_name_plural': 'Страны',
},
),
migrations.AlterModelOptions(
name='director',
options={
'verbose_name': 'режиссер',
'verbose_name_plural': 'Режиссеры',
},
),
migrations.AlterModelOptions(
name='film',
options={'verbose_name': 'фильм', 'verbose_name_plural': 'Фильмы'},
),
migrations.AlterModelOptions(
name='genre',
options={'verbose_name': 'жанр', 'verbose_name_plural': 'Жанры'},
),
]

View File

@ -80,7 +80,7 @@ class Genre(Model):
class Meta: class Meta:
db_table = 'films_genres' db_table = 'films_genres'
verbose_name = 'Жанр' verbose_name = 'жанр'
verbose_name_plural = 'Жанры' verbose_name_plural = 'Жанры'
@ -102,7 +102,7 @@ class Director(Model):
class Meta: class Meta:
db_table = 'films_directors' db_table = 'films_directors'
verbose_name = 'Режиссер' verbose_name = 'режиссер'
verbose_name_plural = 'Режиссеры' verbose_name_plural = 'Режиссеры'
@ -124,7 +124,7 @@ class Actor(Model):
class Meta: class Meta:
db_table = 'films_actors' db_table = 'films_actors'
verbose_name = 'Актер' verbose_name = 'актер'
verbose_name_plural = 'Актеры' verbose_name_plural = 'Актеры'
@ -140,7 +140,7 @@ class Country(Model):
class Meta: class Meta:
db_table = 'films_countries' db_table = 'films_countries'
verbose_name = 'Страна' verbose_name = 'страна'
verbose_name_plural = 'Страны' verbose_name_plural = 'Страны'
@ -245,5 +245,5 @@ class Film(Model):
class Meta: class Meta:
db_table = 'films_films' db_table = 'films_films'
verbose_name = 'Фильм' verbose_name = 'фильм'
verbose_name_plural = 'Фильмы' verbose_name_plural = 'Фильмы'

View File

@ -2,7 +2,7 @@ from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from films.models import Film from films.models import Film
from timetable.models import FilmSession from core.functions import get_film_to_sessions
def films_list(request: HttpResponse) -> HttpResponse: def films_list(request: HttpResponse) -> HttpResponse:
@ -19,28 +19,7 @@ def film_details(request: HttpResponse, film_id: int) -> HttpResponse:
Film.objects.released(), Film.objects.released(),
id=film_id, id=film_id,
) )
film_sessions = FilmSession.objects.nearest_timetable().filter( sessions_by_date_and_film = get_film_to_sessions()
film_id=film_id,
)
sessions_by_date_and_film = {}
for session in film_sessions:
session_date = session.start_datetime.date()
if session_date not in sessions_by_date_and_film:
sessions_by_date_and_film[session_date] = {}
film_sessions_for_date = sessions_by_date_and_film[session_date]
if session.film not in film_sessions_for_date:
film_sessions_for_date[session.film] = []
film_sessions_for_date[session.film].append(session)
for session_date, session_films in sessions_by_date_and_film.items():
for session_film in session_films:
sessions_by_date_and_film[session_date][session_film].sort(
key=lambda sorted_session: sorted_session.start_datetime,
)
context = { context = {
'film_sessions': sessions_by_date_and_film, 'film_sessions': sessions_by_date_and_film,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,31 +5,12 @@ from django.http import HttpResponse
from django.shortcuts import render from django.shortcuts import render
from films.models import Film from films.models import Film
from timetable.models import FilmSession from core.functions import get_film_to_sessions
def homepage(request): def homepage(request):
films = Film.objects.on_main() films = Film.objects.on_main()
film_sessions = FilmSession.objects.nearest_timetable() sessions_by_date_and_film = get_film_to_sessions()
sessions_by_date_and_film = {}
for session in film_sessions:
session_date = session.start_datetime.date()
if session_date not in sessions_by_date_and_film:
sessions_by_date_and_film[session_date] = {}
film_sessions_for_date = sessions_by_date_and_film[session_date]
if session.film not in film_sessions_for_date:
film_sessions_for_date[session.film] = []
film_sessions_for_date[session.film].append(session)
for session_date, session_films in sessions_by_date_and_film.items():
for session_film in session_films:
sessions_by_date_and_film[session_date][session_film].sort(
key=lambda sorted_session: sorted_session.start_datetime,
)
template = render( template = render(
request, request,
'home/homepage.html', 'home/homepage.html',

View File

@ -24,5 +24,5 @@
.film_card_column::-webkit-scrollbar-thumb { .film_card_column::-webkit-scrollbar-thumb {
background-color: #0d1d3a; background-color: #0d1d3a;
border-radius: 5vw; border-radius: 5vw;
border: 1px solid #ffffff; border: 1px solid #eaeaea;
} }

View File

@ -96,3 +96,10 @@ body::-webkit-scrollbar-thumb {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.not_found_label {
width: 100%;
color: #eaeaea;
text-align: center;
font-size: 2vw;
margin-top: 10vw;
}

View File

@ -21,3 +21,8 @@
.btn { .btn {
margin: 5px !important; margin: 5px !important;
} }
.user_image {
width: 20vw;
border-radius: 2vw;
margin-bottom: 1.5vw;
}

View File

@ -8,6 +8,7 @@
<link href="{% static 'css/films/films_list.css' %}" rel="stylesheet"> <link href="{% static 'css/films/films_list.css' %}" rel="stylesheet">
<div class="films_block"> <div class="films_block">
<h1 class="header_title carousel_title">Смотрите в прокате</h1> <h1 class="header_title carousel_title">Смотрите в прокате</h1>
{% if films %}
<div class="films_list row row-cols-3"> <div class="films_list row row-cols-3">
{% for film in films %} {% for film in films %}
<a class="film_preview_card col" href="{% url 'films:film_details' film_id=film.id %}" <a class="film_preview_card col" href="{% url 'films:film_details' film_id=film.id %}"
@ -19,5 +20,8 @@
</a> </a>
{% endfor %} {% endfor %}
</div> </div>
{% else %}
<p class="not_found_label">Информации о фильмах в прокате не найдена :(</p>
{% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -8,6 +8,7 @@
<link href="{% static 'css/home/homepage.css' %}" rel="stylesheet"> <link href="{% static 'css/home/homepage.css' %}" rel="stylesheet">
<h1 class="header_title carousel_title">Сейчас в прокате</h1> <h1 class="header_title carousel_title">Сейчас в прокате</h1>
<div id="carouselWhite" class="carousel carousel slide carousel_films" data-bs-ride="carousel"> <div id="carouselWhite" class="carousel carousel slide carousel_films" data-bs-ride="carousel">
{% if films_preview %}
<div class="carousel-indicators"> <div class="carousel-indicators">
{% for film in films_preview %} {% for film in films_preview %}
<button type="button" data-bs-target="#carouselWhite" data-bs-slide-to="{{ forloop.counter0 }}" <button type="button" data-bs-target="#carouselWhite" data-bs-slide-to="{{ forloop.counter0 }}"
@ -35,10 +36,14 @@
<span class="carousel-control-next-icon" aria-hidden="true"></span> <span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span> <span class="visually-hidden">Next</span>
</button> </button>
{% else %}
<p class="not_found_label">Информации о фильмах в прокате не найдена :(</p>
{% endif %}
</div> </div>
<div class="timetable_block"> <div class="timetable_block">
<h1 class="header_title">Расписание</h1> <h1 class="header_title">Расписание</h1>
<div class="container"> <div class="container">
{% if films_sessions %}
<ul class="nav nav-tabs" id="myTab" role="tablist"> <ul class="nav nav-tabs" id="myTab" role="tablist">
{% for date in films_sessions %} {% for date in films_sessions %}
{% include "includes/date_timetable_button.html" %} {% include "includes/date_timetable_button.html" %}
@ -56,6 +61,9 @@
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% else %}
<p class="not_found_label">Информации о расписании не найдена :(</p>
{% endif %}
</div> </div>
</div> </div>

View File

@ -9,10 +9,14 @@
<link href="{% static 'css/tickets/my_orders.css' %}" rel="stylesheet"> <link href="{% static 'css/tickets/my_orders.css' %}" rel="stylesheet">
<div class="container"> <div class="container">
<h1 class="header_title">Мои заказы</h1> <h1 class="header_title">Мои заказы</h1>
{% if my_orders %}
<div class="my_orders_block"> <div class="my_orders_block">
{% for order in my_orders %} {% for order in my_orders %}
{% include "includes/my_order_card.html" %} {% include "includes/my_order_card.html" %}
{% endfor %} {% endfor %}
</div> </div>
{% else %}
<p class="not_found_label">Кажется, вы еще не оформили ни одного заказа 🤔</p>
{% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -9,6 +9,7 @@
<div class="timetable_block"> <div class="timetable_block">
<h1 class="header_title">Расписание</h1> <h1 class="header_title">Расписание</h1>
<div class="container"> <div class="container">
{% if films_sessions %}
<ul class="nav nav-tabs" id="myTab" role="tablist"> <ul class="nav nav-tabs" id="myTab" role="tablist">
{% for date in films_sessions %} {% for date in films_sessions %}
{% include "includes/date_timetable_button.html" %} {% include "includes/date_timetable_button.html" %}
@ -26,6 +27,9 @@
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% else %}
<p class="not_found_label">Информации о расписании не найдена :(</p>
{% endif %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -38,6 +38,9 @@
<div id="help" class="form-text">{{ field.help_text }}</div> <div id="help" class="form-text">{{ field.help_text }}</div>
</div> </div>
{% endfor %} {% endfor %}
{% if user.profile.image %}
<img class="user_image" src="{{ user.profile.image.url }}">
{% endif %}
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<p> {{ message }} </p> <p> {{ message }} </p>

View File

@ -0,0 +1,21 @@
# Generated by Django 4.2 on 2024-04-26 04:15
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('tickets', '0002_initial_squashed_0007_alter_order_datetime_order'),
]
operations = [
migrations.AlterModelOptions(
name='order',
options={'verbose_name': 'заказ', 'verbose_name_plural': 'Заказы'},
),
migrations.AlterModelOptions(
name='ticket',
options={'verbose_name': 'билет', 'verbose_name_plural': 'Билеты'},
),
]

View File

@ -42,7 +42,7 @@ class Order(Model):
class Meta: class Meta:
db_table = 'tickets_orders' db_table = 'tickets_orders'
verbose_name = 'Заказ' verbose_name = 'заказ'
verbose_name_plural = 'Заказы' verbose_name_plural = 'Заказы'
@ -72,5 +72,5 @@ class Ticket(Model):
class Meta: class Meta:
db_table = 'tickets_tickets' db_table = 'tickets_tickets'
verbose_name = 'Билет' verbose_name = 'билет'
verbose_name_plural = 'Билеты' verbose_name_plural = 'Билеты'

View File

@ -0,0 +1,37 @@
# Generated by Django 4.2 on 2024-04-26 04:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
'timetable',
'0002_rename_rows_row_filmsession_squashed_0006_remove_auditorium_row_count',
),
]
operations = [
migrations.AlterModelOptions(
name='auditorium',
options={'verbose_name': 'зал', 'verbose_name_plural': 'Залы'},
),
migrations.AlterModelOptions(
name='filmsession',
options={'verbose_name': 'сеанс', 'verbose_name_plural': 'Сеансы'},
),
migrations.AlterModelOptions(
name='row',
options={'verbose_name': 'место', 'verbose_name_plural': 'Места'},
),
migrations.AlterField(
model_name='filmsession',
name='end_datetime',
field=models.DateTimeField(
blank=True,
null=True,
verbose_name='Дата и время окончания сеанса',
),
),
]

View File

@ -50,7 +50,7 @@ class Auditorium(Model):
class Meta: class Meta:
db_table = 'timetable_auditoriums' db_table = 'timetable_auditoriums'
verbose_name = 'Зал' verbose_name = 'зал'
verbose_name_plural = 'Залы' verbose_name_plural = 'Залы'
@ -79,7 +79,7 @@ class Row(Model):
class Meta: class Meta:
db_table = 'timetable_rows' db_table = 'timetable_rows'
verbose_name = 'Место' verbose_name = 'место'
verbose_name_plural = 'Места' verbose_name_plural = 'Места'
@ -134,5 +134,5 @@ class FilmSession(Model):
class Meta: class Meta:
db_table = 'timetable_film_sessions' db_table = 'timetable_film_sessions'
verbose_name = 'Сеанс' verbose_name = 'сеанс'
verbose_name_plural = 'Сеансы' verbose_name_plural = 'Сеансы'

View File

@ -15,23 +15,20 @@ from timetable.models import FilmSession
def timetable_view(request): def timetable_view(request):
film_sessions = FilmSession.objects.all_timetable() film_sessions = FilmSession.objects.all_timetable()
sessions_by_date_and_film = {} sessions_by_date_and_film = {}
for session in film_sessions: for session in film_sessions:
session_date = session.start_datetime.date() session_date = session.start_datetime.date()
if session_date not in sessions_by_date_and_film: session_film = session.film
sessions_by_date_and_film[session_date] = {} sessions_by_date_and_film.setdefault(session_date, {}).setdefault(
session_film, []
).append(session)
film_sessions_for_date = sessions_by_date_and_film[session_date] for session_date in sessions_by_date_and_film:
if session.film not in film_sessions_for_date: for session_film in sessions_by_date_and_film[session_date]:
film_sessions_for_date[session.film] = []
film_sessions_for_date[session.film].append(session)
for session_date, session_films in sessions_by_date_and_film.items():
for session_film in session_films:
sessions_by_date_and_film[session_date][session_film].sort( sessions_by_date_and_film[session_date][session_film].sort(
key=lambda sorted_session: sorted_session.start_datetime, key=lambda x: x.start_datetime
) )
template = render( template = render(

View File

@ -53,6 +53,17 @@ class SignUpForm(UserCreationForm):
class Meta(UserCreationForm.Meta): class Meta(UserCreationForm.Meta):
fields = ('username', 'email') fields = ('username', 'email')
def clean_username(self):
username = self.cleaned_data['username']
if len(username) > 150:
raise forms.ValidationError('Максимальная длина 150 символов.')
if not all(char.isalnum() or char in '@/./+/-/_' for char in username):
raise forms.ValidationError(
'Можно использовать только буквы, цифры и символы @/./+/-/_.'
)
return username
class ProfileForm(ModelForm): class ProfileForm(ModelForm):
def __init__(self, *args, **kwargs) -> None: def __init__(self, *args, **kwargs) -> None:
@ -99,3 +110,14 @@ class UserForm(forms.ModelForm):
model.first_name.field.name, model.first_name.field.name,
model.last_name.field.name, model.last_name.field.name,
] ]
def clean_username(self):
username = self.cleaned_data['username']
if len(username) > 150:
raise forms.ValidationError('Максимальная длина 150 символов.')
if not all(char.isalnum() or char in '@/./+/-/_' for char in username):
raise forms.ValidationError(
'Можно использовать только буквы, цифры и символы @/./+/-/_.'
)
return username

View File

@ -0,0 +1,20 @@
# Generated by Django 4.2 on 2024-04-26 04:15
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('users', '0003_remove_profile_genres_profile_genres'),
]
operations = [
migrations.AlterModelOptions(
name='profile',
options={
'verbose_name': 'данные пользователя',
'verbose_name_plural': 'Данные пользователей',
},
),
]

View File

@ -74,6 +74,6 @@ class Profile(Model):
list_display = ['image_tmb'] list_display = ['image_tmb']
class Meta: class Meta:
verbose_name = 'Данные пользователя' verbose_name = 'данные пользователя'
verbose_name_plural = 'Данные пользователей' verbose_name_plural = 'Данные пользователей'
db_table = 'users_profiles' db_table = 'users_profiles'

BIN
ER.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -10,6 +10,16 @@
авторизации, редактирования профиля. авторизации, редактирования профиля.
Сайт расположен по адресу: https://cinesync.numerum.site/ Сайт расположен по адресу: https://cinesync.numerum.site/
## Перед запуском
Необходимо также задать переменные окружения, для этого нужно создать файл `.env`, и указать там необходимые переменны,
ознакомится с ними можно в [примере файла](.example_env).
## Обратите внимание
Проект предусматривает возможность запуска проекта, как на sqlite, так и на postgres. В зависимости от этого нужно
устанавливать разные зависимости (ниже описаны какие) и задавать разные переменные окружения.
## Инструкция к локальному запуску ## Инструкция к локальному запуску
1) Скачать проект или склонировать репозиторий: 1) Скачать проект или склонировать репозиторий:
@ -44,7 +54,13 @@
pip3 install -r requirements/test.txt pip3 install -r requirements/test.txt
``` ```
- Для продакшена: - Для продакшена на sqlite:
```bash
pip3 install -r requirements/base.txt
```
- Для продакшена на postgres:
```bash ```bash
pip3 install -r requirements/prod.txt pip3 install -r requirements/prod.txt
@ -53,7 +69,6 @@
5) Применить миграции: 5) Применить миграции:
```bash ```bash
python3 manage.py makemigrations
python3 manage.py migrate python3 manage.py migrate
``` ```

6
requirements/base.txt Normal file
View File

@ -0,0 +1,6 @@
Django==4.2
django-ckeditor==6.7.0
django-cleanup==8.1.0
pillow==10.2.0
python-dotenv~=1.0.1
sorl-thumbnail==12.10.0

View File

@ -1,3 +1,3 @@
-r prod.txt -r base.txt
black==24.1.1 black==24.1.1
django-debug-toolbar==4.3.0 django-debug-toolbar==4.3.0

View File

@ -1,7 +1,2 @@
Django==4.2 -r base.txt
django-ckeditor==6.7.0
django-cleanup==8.1.0
pillow==10.2.0
python-dotenv~=1.0.1
sorl-thumbnail==12.10.0
psycopg2-binary==2.9.1 psycopg2-binary==2.9.1

View File

@ -1,4 +1,4 @@
-r prod.txt -r base.txt
flake8==7.0.0 flake8==7.0.0
flake8-bugbear==24.2.6 flake8-bugbear==24.2.6
flake8-clean-block==0.1.2 flake8-clean-block==0.1.2