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
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ updates:
pull-request-branch-name:
separator: "-"

- package-ecosystem: "pip"
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "weekly"
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/lint-and-test-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,6 @@ jobs:
steps:
- name: Check the code formatting
run: make format-check
- name: Run the linter
run: make lint
- name: Check that migrations are up-to-date
run: make migrations-check
- name: Check that translations are up-to-date
Expand Down
10 changes: 8 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"python.formatting.provider": "black",
"python.linting.flake8Enabled": true
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.ruff": "explicit",
"source.organizeImports.ruff": "explicit"
}
}
}
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
FROM python:3.13-slim AS builder

ARG EXPORT_FLAG="--with dev"
ARG EXPORT_FLAG="--all-groups"

RUN pip install --upgrade pip poetry poetry-plugin-export
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

COPY pyproject.toml poetry.toml poetry.lock ./
COPY pyproject.toml uv.lock ./

RUN poetry export -f requirements.txt $EXPORT_FLAG --without-hashes --output /tmp/requirements.txt
RUN uv export ${EXPORT_FLAG:+$EXPORT_FLAG} --no-hashes --no-emit-project --output-file /tmp/requirements.txt


FROM python:3.13-slim
Expand Down
20 changes: 4 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ fullstack:

.PHONY: format
format:
isort .
black .
ruff check --fix .
ruff format .

.PHONY: format-check
format-check:
isort . -c
black --check .
ruff check .
ruff format --check .

DJANGO_CHECK_FAIL_LEVEL ?= WARNING
.PHONY: check
Expand Down Expand Up @@ -86,17 +86,6 @@ start:
start-uvicorn:
uvicorn projects.asgi:application --workers 1 --host 0.0.0.0

.PHONY: bandit
bandit:
bandit -c pyproject.toml -r apps/ projects/

.PHONY: flake8
flake8:
flake8 apps projects services

.PHONY: lint
lint: flake8 bandit

.PHONY: test
test:
coverage run
Expand Down Expand Up @@ -133,6 +122,5 @@ rebuild-index:
.PHONY: precommit
precommit:
${MAKE} format
${MAKE} lint
${MAKE} locales
${MAKE} migrations
21 changes: 9 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

## Requirements
- Docker Compose V2
- uv

## Usage

Expand All @@ -17,6 +18,11 @@ git clone --recurse-submodules git@github.com:CyberCRI/projects-backend.git
cd projects-backend
```

### Install dependencies
```bash
uv sync
```

### Set up your environment variables

if you want to set your environnement variables (Mostly third-parties secrets):
Expand Down Expand Up @@ -114,18 +120,9 @@ You can check locally that the CI will validate your pull request by running the
make format
```

This will automatically update your files

2. Respect lint rules:

```bash
# inside the container
make lint
```

This will return errors that you need to fix manually. If there are some, fix them then repeat step 1.

3. Keep translations up to date:
2. Keep translations up to date:

```bash
# inside the container
Expand All @@ -136,14 +133,14 @@ This will detect changes in translated messages. Even if you didn't add, remove

If there are new messages, be sure to add the translation after running this command.

4. Create migrations if needed:
3. Create migrations if needed:

```bash
# inside the container
python manage.py makemigrations
```

5. Be sure that all tests pass
4. Be sure that all tests pass

```bash
# inside the container
Expand Down
4 changes: 1 addition & 3 deletions apps/accounts/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ def keycloak_account_link(self, obj):
"admin:keycloak_keycloakaccount_change",
args=(obj.keycloak_account.pk,),
)
return mark_safe(
f'<a href="{admin_page}">{obj.keycloak_account}</a>'
) # nosec
return mark_safe(f'<a href="{admin_page}">{obj.keycloak_account}</a>') # nosec
return None

keycloak_account_link.short_description = "Keycloak account"
Expand Down
2 changes: 1 addition & 1 deletion apps/accounts/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ class AccountConfig(AppConfig):

def ready(self):
"""Register signals once the apps are loaded."""
import apps.accounts.signals # noqa
import apps.accounts.signals # noqa: F401
2 changes: 1 addition & 1 deletion apps/accounts/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

class PeopleGroupFilter(filters.FilterSet):
organizations = MultiValueCharFilter(method="filter_organizations")
type = MultiValueCharFilter(field_name="type", lookup_expr="in") # noqa
type = MultiValueCharFilter(field_name="type", lookup_expr="in")
is_root = filters.BooleanFilter(field_name="is_root")

def filter_organizations(self, queryset, name, value):
Expand Down
22 changes: 11 additions & 11 deletions apps/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,6 @@ class PeopleGroup(
The visibility setting of the group.
"""

objects = MultipleIdsQuerySet.as_manager()

auto_translated_fields: list[str] = [
"name",
"html:description",
Expand Down Expand Up @@ -167,6 +165,17 @@ class PublicationStatus(models.TextChoices):

tags = models.ManyToManyField("skills.Tag", related_name="people_groups")

objects = MultipleIdsQuerySet.as_manager()

class Meta:
constraints = [
UniqueConstraint(
name="unique_root_group_per_organization",
fields=["organization"],
condition=Q(is_root=True),
)
]

def __str__(self) -> str:
return str(self.name)

Expand Down Expand Up @@ -272,15 +281,6 @@ def set_role_groups_members(self):
for group in project.groups.filter(people_groups=self):
project.set_role_group_members(group)

class Meta:
constraints = [
UniqueConstraint(
name="unique_root_group_per_organization",
fields=["organization"],
condition=Q(is_root=True),
)
]


class ProjectUser(
HasAutoTranslatedFields,
Expand Down
2 changes: 1 addition & 1 deletion apps/accounts/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1016,7 +1016,7 @@ def to_internal_value(self, data):
organization_groups = Group.objects.filter(
organizations__isnull=False, name__in=groups_to_add
)
if organization_groups.exists() and "language" not in data.keys():
if organization_groups.exists() and "language" not in data:
group = organization_groups.first()
organization = group.organizations.first()
data["language"] = organization.language
Expand Down
16 changes: 8 additions & 8 deletions apps/accounts/tests/views/test_user_publication_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ def test_view_project_members(self, role, expected_users):
{user["id"] for user in content["team"]["members"]},
{
self.users[user_type].id if user_type in expected_users else None
for user_type in self.users.keys()
for user_type in self.users
},
)

Expand Down Expand Up @@ -186,7 +186,7 @@ def test_view_people_group_members(self, role, expected_users):
{user["id"] for user in content},
{
self.users[user_type].id if user_type in expected_users else None
for user_type in self.users.keys()
for user_type in self.users
},
)

Expand Down Expand Up @@ -217,7 +217,7 @@ def test_view_users_in_comments(self, role, expected_users):
if user_type in expected_users
else (None, self.comments[user_type].id)
)
for user_type in self.users.keys()
for user_type in self.users
},
)

Expand Down Expand Up @@ -248,7 +248,7 @@ def test_view_users_in_follows(self, role, expected_users):
if user_type in expected_users
else (None, self.follows[user_type].id)
)
for user_type in self.users.keys()
for user_type in self.users
},
)

Expand Down Expand Up @@ -279,7 +279,7 @@ def test_view_users_in_reviews(self, role, expected_users):
if user_type in expected_users
else (None, self.reviews[user_type].id)
)
for user_type in self.users.keys()
for user_type in self.users
},
)

Expand Down Expand Up @@ -312,7 +312,7 @@ def test_view_users_in_invitations(self, role, expected_users):
if user_type in expected_users
else (None, self.invitations[user_type].id)
)
for user_type in self.users.keys()
for user_type in self.users
},
)

Expand All @@ -337,7 +337,7 @@ def test_view_users_in_notifications(self, role, expected_users):
project=self.project,
organization=organization,
)
for user_type in self.users.keys()
for user_type in self.users
}
response = self.client.get(
reverse("Notification-list", args=(organization.code,))
Expand All @@ -356,7 +356,7 @@ def test_view_users_in_notifications(self, role, expected_users):
if user_type in expected_users
else (None, notifications[user_type].id)
)
for user_type in self.users.keys()
for user_type in self.users
},
)

Expand Down
2 changes: 1 addition & 1 deletion apps/accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ def refresh_keycloak_actions_link(self, request, *args, **kwargs):
template_path = "execute_actions_email_not_sent.html"
with translation.override(user.language):
return render(request, f"authentication/{template_path}")
except Exception as e: # noqa: PIE786
except Exception as e:
with translation.override(user.language):
return render(
request,
Expand Down
6 changes: 3 additions & 3 deletions apps/announcements/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ class AnnouncementStatus(models.TextChoices):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def __str__(self):
return str(self.title)

def get_related_organizations(self) -> list["Organization"]:
"""Return the organizations related to this model."""
return self.project.get_related_organizations()

def get_related_project(self) -> Optional["Project"]:
"""Return the project related to this model."""
return self.project

def __str__(self):
return str(self.title)
5 changes: 1 addition & 4 deletions apps/commons/queryset.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ def build_identifiers_query(self, identifiers: tuple[str | int]) -> models.Q:
final_query = models.Q()
for field, values in query.items():
field_cls = self._get_related_field(self.model, field)
if isinstance(field_cls, ArrayField):
lookup = "__contains"
else:
lookup = "__in"
lookup = "__contains" if isinstance(field_cls, ArrayField) else "__in"

final_query |= models.Q(**{f"{field}{lookup}": list(values)})

Expand Down
2 changes: 1 addition & 1 deletion apps/commons/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class LazySerializer(serializers.Serializer):
are passed down to the serializer.
"""

def __init__(self, ref, *args, **kwargs): # noqa
def __init__(self, ref, *args, **kwargs):
self._args = args
self._kwargs = kwargs
self._reference_as_string = ref
Expand Down
2 changes: 1 addition & 1 deletion apps/commons/swagger.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class CustomAutoSchema(AutoSchema):

def get_operation(self, *args, **kwargs):
operation = super().get_operation(*args, **kwargs)
if "summary" not in operation:
if operation and "summary" not in operation:
title = operation.get("description")
if title is not None and len(title) > 150:
title = title[:148] + "..."
Expand Down
11 changes: 4 additions & 7 deletions apps/commons/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,9 @@ def process_text(
The processed text and the images to link to the instance.
"""

assert all(
(instance, upload_to, view)
), "instance, upload_to and view parameters are required."
assert all((instance, upload_to, view)), (
"instance, upload_to and view parameters are required."
)

soup = BeautifulSoupProjects(text)

Expand Down Expand Up @@ -273,10 +273,7 @@ def process_unlinked_images(
List[Image]
The images to link to the instance.
"""
if isinstance(text, str):
soup = BeautifulSoupProjects(text)
else:
soup = text
soup = BeautifulSoupProjects(text) if isinstance(text, str) else text

images_ids = []
for image_tag in soup.find_all("img"):
Expand Down
Loading
Loading