Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

final #79

Merged
merged 7 commits into from
Mar 11, 2024
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
104 changes: 104 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@

# MVP CRM-системы для Амбассадоров Яндекс Практикума.

## О проекте

Проект команды №10 в хакатоне Яндекс-Практикума.

## Команда разработчиков

- [Рагимов Шериф](https://github.com/ragimov700)
- [Зеленчук Михаил](https://github.com/qwertttyyy)
- [Земцов Антон](https://github.com/antonata-c)
- [Гуржий Вадим](https://github.com/VadimGurzhy)

## Технологии
[![Django](https://img.shields.io/badge/Django-092E20?style=for-the-badge&logo=django&logoColor=white)](https://www.djangoproject.com/)
[![DjangoRESTFramework](https://img.shields.io/badge/Django%20REST%20Framework-092E20?style=for-the-badge&logo=django&logoColor=white)](https://www.django-rest-framework.org/)
[![PostgreSQL](https://img.shields.io/badge/PostgreSQL-316192?style=for-the-badge&logo=postgresql&logoColor=white)](https://www.postgresql.org)
[![Docker](https://img.shields.io/badge/Docker-0db7ed?style=for-the-badge&logo=docker&logoColor=white)](https://www.docker.com)
[![Nginx](https://img.shields.io/badge/nginx-009639?style=for-the-badge&logo=nginx&logoColor=white)](https://www.nginx.org)
![CI/CD](https://img.shields.io/badge/CI%2FCD-2088FF?style=for-the-badge&logo=GitHub-Actions&logoColor=white)


## Начало работы

Эти инструкции позволят вам запустить копию проекта на вашем локальном компьютере для разработки и тестирования.

<details>
<summary><strong>Запуск с использованием Docker</strong></summary>

### Предварительные требования

Убедитесь, что у вас установлены Docker и Docker Compose. Это можно сделать, следуя официальной документации Docker: https://docs.docker.com/get-docker/ и https://docs.docker.com/compose/install/

### Установка и запуск

1. Клонируйте репозиторий на локальный компьютер:
```
git clone [email protected]:Tenth-Team/backend.git
cd backend/infra
```

2. Запустите контейнеры с помощью Docker Compose:
```
docker-compose up --build
```

Теперь приложение должно быть доступно по адресу:

http://localhost:8000

А документация доступна по адресу:

http://localhost:8000/api/v1/swagger/

</details>

<details>
<summary><strong>Локальный запуск через pip</strong></summary>

### Предварительные требования

Убедитесь, что у вас установлен Python и pip. Рекомендуется использовать виртуальное окружение для изоляции зависимостей проекта.

### Установка и запуск

1. Клонируйте репозиторий на локальный компьютер:
```
git clone [email protected]:Tenth-Team/backend.git
cd backend/backend
```

2. Создайте и активируйте виртуальное окружение:
```
python -m venv venv
source venv/bin/activate # На Windows используйте `venv\Scripts\activate`
```

3. Установите зависимости:
```
pip install -r requirements.txt
```

4. Запустите проект (пример для Django):
```
python manage.py migrate
python manage.py runserver
```

Теперь приложение должно быть доступно по адресу:

http://localhost:8000

А документация доступна по адресу:

http://localhost:8000/api/v1/swagger/


</details>

### Ссылка на скриншот документации:

[![Static Badge](https://img.shields.io/badge/Документация_Swagger-Google_Drive-blue?style=for-the-badge)](https://drive.google.com/file/d/1ySTNXQUQZt4djonFki1h1biCSBdJHsIO/view)

18 changes: 18 additions & 0 deletions backend/ambassadors/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,41 @@

@admin.register(Country)
class CountryAdmin(admin.ModelAdmin):
"""
Конфигурация админки для модели стран.
"""
list_display = ('name',)


@admin.register(City)
class CityAdmin(admin.ModelAdmin):
"""
Конфигурация админки для модели города.
"""
list_display = ('name',)


@admin.register(TrainingProgram)
class TrainingProgramAdmin(admin.ModelAdmin):
"""
Конфигурация админки для модели программ обучения.
"""
list_display = ('name',)


@admin.register(AmbassadorGoal)
class AmbassadorGoalAdmin(admin.ModelAdmin):
"""
Конфигурация админки для модели цели амбассадорства.
"""
list_display = ('name',)


@admin.register(Ambassador)
class AmbassadorAdmin(admin.ModelAdmin):
"""
Конфигурация админки для модели амбассадора.
"""
list_display = (
'full_name',
'gender',
Expand All @@ -50,6 +65,9 @@ class AmbassadorAdmin(admin.ModelAdmin):

@admin.register(Content)
class ContentAdmin(admin.ModelAdmin):
"""
Конфигурация админки для модели контента.
"""
list_display = (
'id',
'full_name',
Expand Down
29 changes: 27 additions & 2 deletions backend/ambassadors/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,51 @@
TrainingProgram,
)

fake = Faker(['en-US', 'en_US', 'en_US', 'en-US'])


class CityFactory(DjangoModelFactory):
"""
Фабрика для модели городов.
"""
class Meta:
model = City

name = Faker('city')


class CountryFactory(DjangoModelFactory):
"""
Фабрика для модели стран.
"""
class Meta:
model = Country

name = Faker('country')


class TrainingProgramFactory(DjangoModelFactory):
"""
Фабрика для модели программ обучения.
"""
class Meta:
model = TrainingProgram

name = Faker('sentence', nb_words=4)


class AmbassadorGoalFactory(DjangoModelFactory):
"""
Фабрика для модели целей амбассадорства.
"""
class Meta:
model = AmbassadorGoal

name = Faker('sentence', nb_words=6)


class AmbassadorFactory(DjangoModelFactory):
"""
Фабрика для модели амбассадора.
"""
class Meta:
model = Ambassador

Expand Down Expand Up @@ -89,6 +102,9 @@ class Meta:


class ContentFactory(DjangoModelFactory):
"""
Фабрика для модели контента.
"""
class Meta:
model = Content

Expand All @@ -105,6 +121,9 @@ class Meta:


class PromoCodeFactory(DjangoModelFactory):
"""
Фабрика для модели промокодов.
"""
class Meta:
model = PromoCode

Expand All @@ -117,6 +136,9 @@ class Meta:


class MerchandiseFactory(DjangoModelFactory):
"""
Фабрика для модели мерча.
"""
class Meta:
model = Merchandise

Expand All @@ -131,6 +153,9 @@ class Meta:


class MerchandiseShippingRequestFactory(DjangoModelFactory):
"""
Фабрика для модели заявок на мерч.
"""
class Meta:
model = MerchandiseShippingRequest

Expand Down
4 changes: 4 additions & 0 deletions backend/ambassadors/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

@receiver(pre_save, sender=PromoCode)
def make_other_promo_codes_inactive(sender, instance, **kwargs):
"""
Сигнал, который деактивирует все остальные промокоды
при определении активного.
"""
if instance.status == 'active':
PromoCode.objects.filter(
ambassador=instance.ambassador
Expand Down
5 changes: 3 additions & 2 deletions backend/api/v1/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@


class ContentFilter(FilterSet):
"""Класс для фильтрации Контента."""

"""
Фильтры для контента.
"""
status = ChoiceFilter(choices=CONTENT_STATUS_CHOICES)
full_name = CharFilter(field_name='full_name', lookup_expr='icontains')
ya_edu = CharFilter(
Expand Down
6 changes: 6 additions & 0 deletions backend/api/v1/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@


class AmbassadorPagination(LimitOffsetPagination):
"""
Пагинация для амбассадоров.
"""
default_limit = 30
max_limit = 100


class ContentPagination(AmbassadorPagination):
"""
Пагинация для контента.
"""
pass
4 changes: 4 additions & 0 deletions backend/api/v1/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@


class IsAuthenticatedOrYandexForms(permissions.BasePermission):
"""
Пермишен, который разрешает действия только для авторизованных
пользователей или Яндекс формам.
"""
def has_permission(self, request, view):
api_key = request.headers.get('Authorization')
return (request.user.is_authenticated
Expand Down
3 changes: 3 additions & 0 deletions backend/api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ class Meta:


class AmbassadorUpdateSerializer(serializers.ModelSerializer):
"""
Сериализатор для изменения амбассадора.
"""
city = serializers.CharField(required=False)
country = serializers.CharField(required=False)

Expand Down
9 changes: 7 additions & 2 deletions backend/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ class ContentViewSet(viewsets.ModelViewSet):
@extend_schema(tags=["Заявки"])
@extend_schema_view(**merch_schema)
class MerchandiseShippingRequestViewSet(viewsets.ModelViewSet):
"""
Viewset для модели заявки на мерч.
"""
queryset = MerchandiseShippingRequest.objects.all()
serializer_class = MerchandiseShippingRequestSerializer
http_method_names = (
Expand All @@ -220,8 +223,10 @@ def download(self, request):

@extend_schema(tags=["Программы и цели"], **loyalty_schema)
class AmbassadorLoyaltyViewSet(ListAPIView):
"""Viewset для получения данных для страницы лояльности.
Возвращает список амбассадоров."""
"""
Viewset для получения данных для страницы лояльности.
Возвращает список амбассадоров.
"""

shipped_merch_prefetch = Prefetch(
'merch_shipping_requests',
Expand Down
2 changes: 1 addition & 1 deletion backend/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
DEBUG = os.environ.get('DEBUG', 'True').lower() == 'true'

ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", '*').split()
CSRF_TRUSTED_ORIGINS = ['https://crm.ragimov700.ru']
CSRF_TRUSTED_ORIGINS = ['https://crm.ragimov700.ru', 'http://127.0.0.1:8000']

INSTALLED_APPS = [
'django.contrib.admin',
Expand Down
9 changes: 9 additions & 0 deletions infra/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
DEBUG=True
SECRET_KEY=django-insecure-ssksrb!2)bv^nd%9*=^w=^0h$=0_r-^mb6xo@9yi!)u1d2%)uv
ALLOWED_HOSTS=*

POSTGRES_DB='postgres'
POSTGRES_USER='admin'
POSTGRES_PASSWORD='password'
DB_HOST='db'
DB_PORT=5432
27 changes: 24 additions & 3 deletions infra/docker-compose.local.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
version: '3.3'

services:
db:
image: postgres:13-alpine
volumes:
- postgres:/var/lib/postgresql/data
env_file:
- .env.example
restart: always
backend:
build: ../backend
env_file: ../.env
env_file:
- .env.example
volumes:
- ../backend/:/app/
- backend_static:/app/static
command: >
sh -c "python manage.py collectstatic --no-input &&
sleep 3 &&
python manage.py migrate &&
python manage.py runserver 0.0.0.0:8000"
depends_on:
- db
nginx:
image: nginx:1.19.3
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- backend_static:/backend_static
- ./nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- "80:80"
- "8000:80"
depends_on:
- backend

volumes:
postgres:
backend_static:
Loading
Loading