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
DJANGO_DEBUG=T
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
SECRET_KEY=wevras-vberagv-rebavertf-wefgre
DJANGO_DEBUG=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:
db_table = 'films_genres'
verbose_name = 'Жанр'
verbose_name = 'жанр'
verbose_name_plural = 'Жанры'
@ -102,7 +102,7 @@ class Director(Model):
class Meta:
db_table = 'films_directors'
verbose_name = 'Режиссер'
verbose_name = 'режиссер'
verbose_name_plural = 'Режиссеры'
@ -124,7 +124,7 @@ class Actor(Model):
class Meta:
db_table = 'films_actors'
verbose_name = 'Актер'
verbose_name = 'актер'
verbose_name_plural = 'Актеры'
@ -140,7 +140,7 @@ class Country(Model):
class Meta:
db_table = 'films_countries'
verbose_name = 'Страна'
verbose_name = 'страна'
verbose_name_plural = 'Страны'
@ -245,5 +245,5 @@ class Film(Model):
class Meta:
db_table = 'films_films'
verbose_name = 'Фильм'
verbose_name = 'фильм'
verbose_name_plural = 'Фильмы'

View File

@ -2,7 +2,7 @@ from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render
from films.models import Film
from timetable.models import FilmSession
from core.functions import get_film_to_sessions
def films_list(request: HttpResponse) -> HttpResponse:
@ -19,28 +19,7 @@ def film_details(request: HttpResponse, film_id: int) -> HttpResponse:
Film.objects.released(),
id=film_id,
)
film_sessions = FilmSession.objects.nearest_timetable().filter(
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,
)
sessions_by_date_and_film = get_film_to_sessions()
context = {
'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 films.models import Film
from timetable.models import FilmSession
from core.functions import get_film_to_sessions
def homepage(request):
films = Film.objects.on_main()
film_sessions = FilmSession.objects.nearest_timetable()
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,
)
sessions_by_date_and_film = get_film_to_sessions()
template = render(
request,
'home/homepage.html',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -38,6 +38,9 @@
<div id="help" class="form-text">{{ field.help_text }}</div>
</div>
{% endfor %}
{% if user.profile.image %}
<img class="user_image" src="{{ user.profile.image.url }}">
{% endif %}
{% if messages %}
{% for message in messages %}
<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:
db_table = 'tickets_orders'
verbose_name = 'Заказ'
verbose_name = 'заказ'
verbose_name_plural = 'Заказы'
@ -72,5 +72,5 @@ class Ticket(Model):
class Meta:
db_table = 'tickets_tickets'
verbose_name = 'Билет'
verbose_name = 'билет'
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:
db_table = 'timetable_auditoriums'
verbose_name = 'Зал'
verbose_name = 'зал'
verbose_name_plural = 'Залы'
@ -79,7 +79,7 @@ class Row(Model):
class Meta:
db_table = 'timetable_rows'
verbose_name = 'Место'
verbose_name = 'место'
verbose_name_plural = 'Места'
@ -134,5 +134,5 @@ class FilmSession(Model):
class Meta:
db_table = 'timetable_film_sessions'
verbose_name = 'Сеанс'
verbose_name = 'сеанс'
verbose_name_plural = 'Сеансы'

View File

@ -15,23 +15,20 @@ from timetable.models import FilmSession
def timetable_view(request):
film_sessions = FilmSession.objects.all_timetable()
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] = {}
session_film = session.film
sessions_by_date_and_film.setdefault(session_date, {}).setdefault(
session_film, []
).append(session)
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:
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 sorted_session: sorted_session.start_datetime,
key=lambda x: x.start_datetime
)
template = render(

View File

@ -53,6 +53,17 @@ class SignUpForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
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):
def __init__(self, *args, **kwargs) -> None:
@ -99,3 +110,14 @@ class UserForm(forms.ModelForm):
model.first_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']
class Meta:
verbose_name = 'Данные пользователя'
verbose_name = 'данные пользователя'
verbose_name_plural = 'Данные пользователей'
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/
## Перед запуском
Необходимо также задать переменные окружения, для этого нужно создать файл `.env`, и указать там необходимые переменны,
ознакомится с ними можно в [примере файла](.example_env).
## Обратите внимание
Проект предусматривает возможность запуска проекта, как на sqlite, так и на postgres. В зависимости от этого нужно
устанавливать разные зависимости (ниже описаны какие) и задавать разные переменные окружения.
## Инструкция к локальному запуску
1) Скачать проект или склонировать репозиторий:
@ -44,16 +54,21 @@
pip3 install -r requirements/test.txt
```
- Для продакшена:
- Для продакшена на sqlite:
```bash
pip3 install -r requirements/prod.txt
pip3 install -r requirements/base.txt
```
- Для продакшена на postgres:
```bash
pip3 install -r requirements/prod.txt
```
5) Применить миграции:
```bash
python3 manage.py makemigrations
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
django-debug-toolbar==4.3.0

View File

@ -1,7 +1,2 @@
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
-r base.txt
psycopg2-binary==2.9.1

View File

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