Skip to content
Open
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
7 changes: 1 addition & 6 deletions .flake8
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
[flake8]
max-line-length = 120

ignore =
VNE002
VNE003
W503

exclude =
./.cache,
./.git,
Expand All @@ -20,4 +15,4 @@ exclude =
./.vscode,
*migrations*,

enable-extensions = print, VNE
enable-extensions = print, VNE, B, A, C4, BLK
47 changes: 47 additions & 0 deletions .github/workflows/new_deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: 'new deploy prod server'

on:
workflow_dispatch:

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: run on server
uses: garygrossgarten/github-action-ssh@release
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
password: ${{ secrets.SERVER_PASSWORD }}
command: |
cd /home/app/new_procollab_deploy &&
git pull origin master &&

rm -f .env &&
touch .env &&

echo "DJANGO_SECRET_KEY=${{ secrets.DJANGO_SECRET_KEY }}" >> .env &&

echo "DATABASE_NAME=${{ secrets.DATABASE_NAME }}" >> .env &&
echo "DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }}" >> .env &&
echo "DATABASE_USER=${{ secrets.DATABASE_USER }}" >> .env &&
echo "DATABASE_HOST=${{ secrets.DATABASE_HOST }}" >> .env &&
echo "DATABASE_PORT=${{ secrets.DATABASE_PORT }}" >> .env &&

echo "EMAIL_USER=${{ secrets.EMAIL_USER }}" >> .env &&
echo "EMAIL_PASSWORD=${{ secrets.EMAIL_PASSWORD }}" >> .env &&
echo "EMAIL_HOST=${{ secrets.EMAIL_HOST }}" >> .env &&
echo "EMAIL_PORT=${{ secrets.EMAIL_PORT }}" >> .env &&
echo "SELECTEL_ACCOUNT_ID=${{ secrets.SELECTEL_ACCOUNT_ID }}" >> .env &&
echo "SELECTEL_CONTAINER_NAME=${{ secrets.SELECTEL_CONTAINER_NAME }}" >> .env &&
echo "SELECTEL_CONTAINER_PASSWORD=${{ secrets.SELECTEL_CONTAINER_PASSWORD }}" >> .env &&
echo "SELECTEL_CONTAINER_USERNAME=${{ secrets.SELECTEL_CONTAINER_USERNAME }}" >> .env &&

echo "CLICKUP_API_TOKEN=${{ secrets.CLICKUP_API_TOKEN }}" >> .env &&
echo "CLICKUP_SPACE_ID=${{ secrets.CLICKUP_SPACE_ID }}" >> .env &&

echo "SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env &&

echo "UNISENDER_GO_API_KEY=${{ secrets.UNISENDER_GO_API_KEY }}" >> .env &&

docker compose -f docker-compose.prod-ci.yml -p prod up -d --build
10 changes: 9 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ repos:
exclude: ^.*\b(migrations)\b.*$
language_version: python3
- repo: https://github.com/pycqa/flake8
rev: 5.0.4
rev: 6.1.0
hooks:
- id: flake8
additional_dependencies:
- flake8-print
- flake8-variables-names
- flake8-bugbear
- flake8-builtins
- flake8-comprehensions
- flake8-broken-line
- flake8-mutable
6 changes: 3 additions & 3 deletions chats/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class ProjectChat(BaseChat):
created_at: A DateTimeField indicating date of creation.
"""

id = models.PositiveIntegerField(primary_key=True, unique=True)
id = models.PositiveIntegerField(primary_key=True, unique=True) # noqa A003 VNE003
project = models.ForeignKey(
Project, on_delete=models.CASCADE, related_name="project_chats"
)
Expand Down Expand Up @@ -126,7 +126,7 @@ class DirectChat(BaseChat):
get_users: returns list of users, who are in chat
"""

id = models.CharField(primary_key=True, max_length=64)
id = models.CharField(primary_key=True, max_length=64) # noqa A003 VNE003
users = models.ManyToManyField(User, related_name="direct_chats")

def get_users(self):
Expand Down Expand Up @@ -287,7 +287,7 @@ class Meta:


class FileToMessage(models.Model):
file = models.OneToOneField(
file = models.OneToOneField( # noqa VNE002
UserFile,
on_delete=models.CASCADE,
related_name="file_to_message",
Expand Down
4 changes: 2 additions & 2 deletions chats/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,12 @@ async def create_file_to_message(

async def match_files_and_messages(file_urls, messages):
for url in file_urls:
file = await sync_to_async(UserFile.objects.get)(pk=url)
user_file = await sync_to_async(UserFile.objects.get)(pk=url)
# implicitly matches a file and a message
await create_file_to_message(
direct_message=messages["direct_message"],
project_message=messages["project_message"],
file=file,
file=user_file,
)


Expand Down
2 changes: 1 addition & 1 deletion chats/websockets_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ class EventGroupType(str, Enum):

@dataclass(frozen=True)
class Event:
type: EventType
type: EventType # noqa: A003, VNE003
content: dict
2 changes: 1 addition & 1 deletion core/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class Meta:


class SkillToObjectSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source="skill.id")
id = serializers.IntegerField(source="skill.id") # noqa A003 VNE003
name = serializers.CharField(source="skill.name")
category = SkillCategorySerializer(source="skill.category")

Expand Down
9 changes: 6 additions & 3 deletions core/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def get_links(obj):

class Base64ImageEncoder:
"""Encode image to base64."""

BASE_QUALITY: int = 85

def get_encoded_base64_from_url(self, url: str, max_width: int = 300) -> str:
Expand All @@ -146,10 +147,12 @@ def get_encoded_base64_from_url(self, url: str, max_width: int = 300) -> str:
def get_encoded_base64_from_local_path(self, image_path: str) -> str:
"""Returns the full prepared base64 string pron url path."""
with open(image_path, "rb") as image_file:
base64_image = base64.b64encode(image_file.read()).decode('utf-8')
base64_image = base64.b64encode(image_file.read()).decode("utf-8")
return self._base64_full_string(base64_image, image_path.split(".")[-1])

def _base64_full_string(self, base_64_string: str, image_extension: str = "jpeg") -> str:
def _base64_full_string(
self, base_64_string: str, image_extension: str = "jpeg"
) -> str:
return f"data:image/{image_extension};base64,{base_64_string}"

def _get_compressed_image(self, image_data, max_width: int) -> str:
Expand All @@ -158,5 +161,5 @@ def _get_compressed_image(self, image_data, max_width: int) -> str:
image.thumbnail((max_width, max_width))
buffer = BytesIO()
image.save(buffer, format="JPEG", quality=self.BASE_QUALITY)
base64_image = base64.b64encode(buffer.getvalue()).decode('utf-8')
base64_image = base64.b64encode(buffer.getvalue()).decode("utf-8")
return base64_image
17 changes: 16 additions & 1 deletion docker-compose.prod-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,31 @@ version: '3.9'

services:
web:
build:
context: .
dockerfile: ./Dockerfile
image: ghcr.io/procollab-github/api:latest
restart: unless-stopped
volumes:
- log:/procollab/log
- ./log:/procollab/log
env_file:
- .env
environment:
HOST: 0.0.0.0
expose:
- 8000

# web:
# image: ghcr.io/procollab-github/api:latest
# restart: unless-stopped
# volumes:
# - log:/procollab/log
# env_file:
# - .env
# environment:
# HOST: 0.0.0.0
# expose:
# - 8000
grafana:
image: grafana/grafana:latest
restart: unless-stopped
Expand Down
8 changes: 3 additions & 5 deletions feed/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,9 @@ def _get_filter_data(self) -> list[str]:

def _get_excluded_projects_ids(self) -> list[int]:
"""IDs for exclude projects which in Partner Program."""
excluded_projects = (
PartnerProgramUserProfile.objects
.values_list("project_id", flat=True)
.exclude(project_id__isnull=True)
)
excluded_projects = PartnerProgramUserProfile.objects.values_list(
"project_id", flat=True
).exclude(project_id__isnull=True)
return excluded_projects

def get_queryset(self) -> QuerySet[News]:
Expand Down
5 changes: 3 additions & 2 deletions files/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


class UserFileForm(ModelForm):
file = FileField(required=True)
file = FileField(required=True) # noqa VNE002

class Meta:
model = UserFile
Expand Down Expand Up @@ -51,7 +51,8 @@ def filename(self, obj):

@admin.display(empty_value="Empty link")
def short_link(self, obj):
return reprlib.repr(obj.link.lstrip("https://")).strip("'")
link = obj.link.removeprefix("https://")
return reprlib.repr(link).removesuffix("'").removeprefix("'")

def get_fieldsets(self, request, obj=None):
fieldsets = super().get_fieldsets(request, obj)
Expand Down
65 changes: 34 additions & 31 deletions files/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,36 @@

class File:
def __init__(
self, file: TemporaryUploadedFile | InMemoryUploadedFile, quality: int = 70
self,
file_obj: TemporaryUploadedFile | InMemoryUploadedFile,
quality: int = 70,
):
self.size = file.size
self.name = File._get_name(file)
self.extension = File._get_extension(file)
self.buffer = file.open(mode="rb")
self.content_type = file.content_type
self.size = file_obj.size
self.name = File._get_name(file_obj)
self.extension = File._get_extension(file_obj)
self.buffer = file_obj.open(mode="rb")
self.content_type = file_obj.content_type

# we can compress given type of image
if self.content_type in SUPPORTED_IMAGES_TYPES:
webp_image = convert_image_to_webp(file, quality)
webp_image = convert_image_to_webp(file_obj, quality)
self.buffer = webp_image.buffer()
self.size = webp_image.size
self.content_type = "image/webp"
self.extension = "webp"

@staticmethod
def _get_name(file) -> str:
name_parts = file.name.split(".")
if len(name_parts) == 1:
return name_parts[0]
return ".".join(name_parts[:-1])
def _get_name(file_obj) -> str:
file_name_parts = file_obj.name.split(".")
if len(file_name_parts) == 1:
return file_name_parts[0]
return ".".join(file_name_parts[:-1])

@staticmethod
def _get_extension(file) -> str:
if len(file.name.split(".")) > 1:
return file.name.split(".")[-1]
def _get_extension(file_obj) -> str:
file_name_parts = file_obj.name.split(".")
if len(file_name_parts) > 1:
return file_name_parts[-1]
return ""


Expand All @@ -54,7 +57,7 @@ def delete(self, url: str) -> Response:
pass

@abstractmethod
def upload(self, file: File, user: User) -> FileInfo:
def upload(self, file_obj: File, user: User) -> FileInfo:
pass


Expand All @@ -63,32 +66,32 @@ def delete(self, url: str) -> Response:
token = self._get_auth_token()
return requests.delete(url, headers={"X-Auth-Token": token})

def upload(self, file: File, user: User) -> FileInfo:
url = self._upload(file, user)
def upload(self, file_obj: File, user: User) -> FileInfo:
url = self._upload(file_obj, user)
return FileInfo(
url=url,
name=file.name,
extension=file.extension,
mime_type=file.content_type,
size=file.size,
name=file_obj.name,
extension=file_obj.extension,
mime_type=file_obj.content_type,
size=file_obj.size,
)

def _upload(self, file: File, user: User) -> str:
def _upload(self, file_obj: File, user: User) -> str:
token = self._get_auth_token()
url = self._generate_url(file, user)
url = self._generate_url(file_obj, user)

requests.put(
url,
headers={
"X-Auth-Token": token,
"Content-Type": file.content_type,
"Content-Type": file_obj.content_type,
},
data=file.buffer,
data=file_obj.buffer,
)

return url

def _generate_url(self, file: File, user: User) -> str:
def _generate_url(self, file_obj: File, user: User) -> str:
"""
Generates url for selcdn
Returns:
Expand All @@ -97,9 +100,9 @@ def _generate_url(self, file: File, user: User) -> str:
return (
f"{SELECTEL_SWIFT_URL}"
f"{abs(hash(user.email))}"
f"/{abs(hash(file.name))}"
f"/{abs(hash(file_obj.name))}"
f"_{abs(hash(time.time()))}"
f".{file.extension}"
f".{file_obj.extension}"
)

@staticmethod
Expand Down Expand Up @@ -138,8 +141,8 @@ def delete(self, url: str) -> Response:

def upload(
self,
file: TemporaryUploadedFile | InMemoryUploadedFile,
file_obj: TemporaryUploadedFile | InMemoryUploadedFile,
user: User,
quality: int = 70,
) -> FileInfo:
return self.storage.upload(File(file, quality), user)
return self.storage.upload(File(file_obj, quality), user)
File renamed without changes.
2 changes: 1 addition & 1 deletion mailing/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from django.core.mail import EmailMultiAlternatives
from django.template import Context, Template

from .typing import MailDataDict, EmailDataToPrepare
from .definitions import MailDataDict, EmailDataToPrepare

User = get_user_model()

Expand Down
2 changes: 1 addition & 1 deletion metrics/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class MetricsView(APIView):

permission_classes = [permissions.IsAdminUser]

def get(self, request, format=None):
def get(self, request):
data = {}

models = [User, Expert, Investor, Member, Mentor, Project, Vacancy]
Expand Down
1 change: 1 addition & 0 deletions news/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.db import models
from django.db.models.query import QuerySet
import typing

if typing.TYPE_CHECKING:
from news.models import News

Expand Down
Loading
Loading