diff --git a/.github/workflows/backend-linters.yaml b/.github/workflows/backend-linters.yaml
index d8bb35d69..31d0a1dd4 100644
--- a/.github/workflows/backend-linters.yaml
+++ b/.github/workflows/backend-linters.yaml
@@ -30,6 +30,10 @@ jobs:
working-directory: ${{env.working-directory}}
run: |
python -m pip install ruff
- - name: ruff
+ - name: Run ruff format check
working-directory: ${{env.working-directory}}
- run: ruff check .
+ run: ruff format --check .
+ # NOTE: The following will be uncommented once the codebase is cleaned up
+ # - name: ruff
+ # working-directory: ${{env.working-directory}}
+ # run: ruff check .
diff --git a/.github/workflows/frontend-linters.yaml b/.github/workflows/frontend-linters.yaml
index 69adc89c9..a7128dda3 100644
--- a/.github/workflows/frontend-linters.yaml
+++ b/.github/workflows/frontend-linters.yaml
@@ -26,18 +26,25 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- - name: Install latest npm
+ - name: Install prettier
working-directory: ${{env.working-directory}}
- run: |
- npm install -g npm &&
- npm --version &&
- npm list -g --depth 0
- - name: Install dependencies
+ run: npm install --save-dev prettier
+ - name: Run prettier
working-directory: ${{env.working-directory}}
- run: npm ci
- - name: Run prettier check & eslint
- working-directory: ${{env.working-directory}}
- run: npm run lint
- - name: Run svelte-check
- working-directory: ${{env.working-directory}}
- run: npm run check
+ run: npx prettier --check .
+ # NOTE: The following will be uncommented once the codebase is cleaned up
+ # - name: Install latest npm
+ # working-directory: ${{env.working-directory}}
+ # run: |
+ # npm install -g npm &&
+ # npm --version &&
+ # npm list -g --depth 0
+ # - name: Install dependencies
+ # working-directory: ${{env.working-directory}}
+ # run: npm ci
+ # - name: Run prettier check & eslint
+ # working-directory: ${{env.working-directory}}
+ # run: npm run lint
+ # - name: Run svelte-check
+ # working-directory: ${{env.working-directory}}
+ # run: npm run check
diff --git a/backend/app_tests/api/test_api_compliance_assessments.py b/backend/app_tests/api/test_api_compliance_assessments.py
index 0ff2003d7..14dc7529c 100644
--- a/backend/app_tests/api/test_api_compliance_assessments.py
+++ b/backend/app_tests/api/test_api_compliance_assessments.py
@@ -118,7 +118,10 @@ def test_get_compliance_assessments(self, test):
"framework": Framework.objects.all()[0],
},
{
- "project": {"id": str(project.id), "str": project.folder.name + "/" + project.name},
+ "project": {
+ "id": str(project.id),
+ "str": project.folder.name + "/" + project.name,
+ },
"framework": {
"id": str(Framework.objects.all()[0].id),
"str": str(Framework.objects.all()[0]),
@@ -146,7 +149,10 @@ def test_create_compliance_assessments(self, test):
"framework": str(Framework.objects.all()[0].id),
},
{
- "project": {"id": str(project.id), "str": project.folder.name + "/" + project.name},
+ "project": {
+ "id": str(project.id),
+ "str": project.folder.name + "/" + project.name,
+ },
"framework": {
"id": str(Framework.objects.all()[0].id),
"str": str(Framework.objects.all()[0]),
@@ -186,7 +192,10 @@ def test_update_compliance_assessments(self, test):
"framework": str(Framework.objects.all()[1].id),
},
{
- "project": {"id": str(project.id), "str": project.folder.name + "/" + project.name},
+ "project": {
+ "id": str(project.id),
+ "str": project.folder.name + "/" + project.name,
+ },
"framework": {
"id": str(Framework.objects.all()[0].id),
"str": str(Framework.objects.all()[0]),
diff --git a/backend/app_tests/api/test_api_libraries.py b/backend/app_tests/api/test_api_libraries.py
index 9408b43c3..f6ec9846c 100644
--- a/backend/app_tests/api/test_api_libraries.py
+++ b/backend/app_tests/api/test_api_libraries.py
@@ -67,19 +67,23 @@ def test_import_frameworks(self, test):
Framework.objects.all().count() == 0
), "libraries are already imported in the database"
EndpointTestsQueries.Auth.get_object(
- test.client, "Frameworks", user_group=test.user_group,
+ test.client,
+ "Frameworks",
+ user_group=test.user_group,
)
EndpointTestsQueries.Auth.import_object(
test.client, "Framework", user_group=test.user_group
)
- assert (
- Framework.objects.all().count() == (1 if not EndpointTestsUtils.expected_request_response(
- "add", "library", str(test.folder), test.user_group
- )[0] else 0)
+ assert Framework.objects.all().count() == (
+ 1
+ if not EndpointTestsUtils.expected_request_response(
+ "add", "library", str(test.folder), test.user_group
+ )[0]
+ else 0
), "Frameworks are not correctly imported in the database"
-
+
# Uses the API endpoint to assert that the library was properly imported
EndpointTestsQueries.Auth.get_object(
test.client,
@@ -93,8 +97,8 @@ def test_import_frameworks(self, test):
base_count=1,
user_group=test.user_group,
fails=EndpointTestsUtils.expected_request_response(
- "add", "library", str(test.folder), test.user_group
- )[0]
+ "add", "library", str(test.folder), test.user_group
+ )[0],
)
def test_delete_frameworks(self, test):
@@ -133,10 +137,12 @@ def test_import_risk_matrix(self, test):
test.client, "Risk matrix", user_group=test.user_group
)
- assert (
- RiskMatrix.objects.all().count() == (1 if not EndpointTestsUtils.expected_request_response(
- "add", "library", str(test.folder), test.user_group
- )[0] else 0)
+ assert RiskMatrix.objects.all().count() == (
+ 1
+ if not EndpointTestsUtils.expected_request_response(
+ "add", "library", str(test.folder), test.user_group
+ )[0]
+ else 0
), "Risk matrices are not correctly imported in the database"
# Uses the API endpoint to assert that the library was properly imported
@@ -153,8 +159,8 @@ def test_import_risk_matrix(self, test):
base_count=1,
user_group=test.user_group,
fails=EndpointTestsUtils.expected_request_response(
- "add", "library", str(test.folder), test.user_group
- )[0]
+ "add", "library", str(test.folder), test.user_group
+ )[0],
)
def test_delete_matrix(self, test):
@@ -163,14 +169,13 @@ def test_delete_matrix(self, test):
EndpointTestsQueries.Auth.import_object(test.admin_client, "Risk matrix")
EndpointTestsQueries.Auth.delete_object(
test.client,
- "Risk matrices",
- RiskMatrix,
+ "Risk matrices",
+ RiskMatrix,
user_group=test.user_group,
scope="Global",
- **({
- "fails": True,
- "expected_status": status.HTTP_403_FORBIDDEN
- }
- if test.user_group == "BI-UG-DMA" else {} # Domain Manager can't delete Global risk matrices (i.e. imported matrices)
- )
+ **(
+ {"fails": True, "expected_status": status.HTTP_403_FORBIDDEN}
+ if test.user_group == "BI-UG-DMA"
+ else {} # Domain Manager can't delete Global risk matrices (i.e. imported matrices)
+ ),
)
diff --git a/backend/app_tests/api/test_api_projects.py b/backend/app_tests/api/test_api_projects.py
index 5865602e8..2172b14d6 100644
--- a/backend/app_tests/api/test_api_projects.py
+++ b/backend/app_tests/api/test_api_projects.py
@@ -99,7 +99,7 @@ def test_get_projects(self, test):
"lc_status": PROJECT_STATUS[1],
},
user_group=test.user_group,
- scope=str(test.folder)
+ scope=str(test.folder),
)
def test_create_projects(self, test):
@@ -121,7 +121,7 @@ def test_create_projects(self, test):
"lc_status": PROJECT_STATUS[1],
},
user_group=test.user_group,
- scope=str(test.folder)
+ scope=str(test.folder),
)
def test_update_projects(self, test):
diff --git a/backend/app_tests/api/test_api_risk_assessments.py b/backend/app_tests/api/test_api_risk_assessments.py
index 22ad2431a..3f4e4d7fa 100644
--- a/backend/app_tests/api/test_api_risk_assessments.py
+++ b/backend/app_tests/api/test_api_risk_assessments.py
@@ -121,7 +121,10 @@ def test_get_risk_assessments(self, test):
"risk_matrix": risk_matrix,
},
{
- "project": {"id": str(project.id), "str": project.folder.name + "/" + project.name},
+ "project": {
+ "id": str(project.id),
+ "str": project.folder.name + "/" + project.name,
+ },
"risk_matrix": {"id": str(risk_matrix.id), "str": str(risk_matrix)},
},
user_group=test.user_group,
@@ -147,7 +150,10 @@ def test_create_risk_assessments(self, test):
"risk_matrix": str(risk_matrix.id),
},
{
- "project": {"id": str(project.id), "str": project.folder.name + "/" + project.name},
+ "project": {
+ "id": str(project.id),
+ "str": project.folder.name + "/" + project.name,
+ },
"risk_matrix": {"id": str(risk_matrix.id), "str": str(risk_matrix)},
},
user_group=test.user_group,
@@ -185,7 +191,10 @@ def test_update_risk_assessments(self, test):
"risk_matrix": str(risk_matrix2.id),
},
{
- "project": {"id": str(project.id), "str": project.folder.name + "/" + project.name},
+ "project": {
+ "id": str(project.id),
+ "str": project.folder.name + "/" + project.name,
+ },
"risk_matrix": {"id": str(risk_matrix.id), "str": str(risk_matrix)},
},
user_group=test.user_group,
diff --git a/backend/app_tests/api/test_api_users.py b/backend/app_tests/api/test_api_users.py
index c19ddc6d0..a20d13175 100644
--- a/backend/app_tests/api/test_api_users.py
+++ b/backend/app_tests/api/test_api_users.py
@@ -86,7 +86,7 @@ def test_get_users(self, test):
base_count=2,
item_search_field="email",
user_group=test.user_group,
- scope="Global"
+ scope="Global",
)
def test_create_users(self, test):
@@ -100,7 +100,7 @@ def test_create_users(self, test):
base_count=2,
item_search_field="email",
user_group=test.user_group,
- scope="Global"
+ scope="Global",
)
def test_update_users(self, test):
@@ -117,7 +117,7 @@ def test_update_users(self, test):
"last_name": "new" + USER_NAME,
},
user_group=test.user_group,
- scope="Global"
+ scope="Global",
)
def test_delete_users(self, test):
@@ -129,7 +129,7 @@ def test_delete_users(self, test):
User,
{"email": USER_EMAIL, "first_name": USER_FIRSTNAME, "last_name": USER_NAME},
user_group=test.user_group,
- scope="Global"
+ scope="Global",
)
def test_uniqueness_emails(self, test):
diff --git a/backend/core/apps.py b/backend/core/apps.py
index 9d49751c9..5106d7134 100644
--- a/backend/core/apps.py
+++ b/backend/core/apps.py
@@ -251,9 +251,7 @@ def startup(**kwargs):
print("startup handler: initialize database")
- reader_permissions = Permission.objects.filter(
- codename__in=READER_PERMISSIONS_LIST
- )
+ reader_permissions = Permission.objects.filter(codename__in=READER_PERMISSIONS_LIST)
approver_permissions = Permission.objects.filter(
codename__in=APPROVER_PERMISSIONS_LIST
diff --git a/backend/core/base_models.py b/backend/core/base_models.py
index 4f11da487..bb978fd70 100644
--- a/backend/core/base_models.py
+++ b/backend/core/base_models.py
@@ -92,7 +92,9 @@ def clean(self) -> None:
if not self.is_unique_in_scope(scope=scope, fields_to_check=_fields_to_check):
for field in _fields_to_check:
if not self.is_unique_in_scope(scope=scope, fields_to_check=[field]):
- field_errors[field] = f"{getattr(self, field)} is already used in this scope. Please choose another value."
+ field_errors[
+ field
+ ] = f"{getattr(self, field)} is already used in this scope. Please choose another value."
super().clean()
if field_errors:
raise ValidationError(field_errors)
diff --git a/backend/core/helpers.py b/backend/core/helpers.py
index 34a63f808..1f238efa1 100644
--- a/backend/core/helpers.py
+++ b/backend/core/helpers.py
@@ -230,7 +230,11 @@ def get_sorted_requirement_nodes_rec(
for requirement_node in requirement_nodes
if requirement_node.parent_urn == node.urn
]
- req_as = requirement_assessment_from_requirement_id[str(node.id)] if requirements_assessed else None
+ req_as = (
+ requirement_assessment_from_requirement_id[str(node.id)]
+ if requirements_assessed
+ else None
+ )
result[str(node.id)] = {
"urn": node.urn,
"parent_urn": node.parent_urn,
@@ -238,8 +242,12 @@ def get_sorted_requirement_nodes_rec(
"name": node.name,
"ra_id": str(req_as.id) if requirements_assessed else None,
"status": req_as.status if requirements_assessed else None,
- "status_display": req_as.get_status_display() if requirements_assessed else None,
- "status_i18n": camel_case(req_as.status) if requirements_assessed else None,
+ "status_display": req_as.get_status_display()
+ if requirements_assessed
+ else None,
+ "status_i18n": camel_case(req_as.status)
+ if requirements_assessed
+ else None,
"node_content": node.display_long,
"style": "node",
"assessable": node.assessable,
@@ -494,7 +502,7 @@ def assessment_per_status(user: User, model: RiskAssessment | ComplianceAssessme
v = {"value": count, "itemStyle": {"color": color_map[st[0]]}}
values.append(v)
labels.append(st[1])
- #add undefined as the first element in the labels to balance the values
+ # add undefined as the first element in the labels to balance the values
labels.insert(0, "undefined")
local_lables = [camel_case(str(label)) for label in labels]
return {"localLables": local_lables, "labels": labels, "values": values}
diff --git a/backend/core/migrations/0007_alter_requirementlevel_framework_and_more.py b/backend/core/migrations/0007_alter_requirementlevel_framework_and_more.py
index beb35d513..cd15ea776 100644
--- a/backend/core/migrations/0007_alter_requirementlevel_framework_and_more.py
+++ b/backend/core/migrations/0007_alter_requirementlevel_framework_and_more.py
@@ -5,20 +5,33 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('core', '0006_remove_securitymeasure_security_function_and_more'),
+ ("core", "0006_remove_securitymeasure_security_function_and_more"),
]
operations = [
migrations.AlterField(
- model_name='requirementlevel',
- name='framework',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='requirement_levels', to='core.framework', verbose_name='Framework'),
+ model_name="requirementlevel",
+ name="framework",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="requirement_levels",
+ to="core.framework",
+ verbose_name="Framework",
+ ),
),
migrations.AlterField(
- model_name='requirementnode',
- name='framework',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='requirement_nodes', to='core.framework', verbose_name='Framework'),
+ model_name="requirementnode",
+ name="framework",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="requirement_nodes",
+ to="core.framework",
+ verbose_name="Framework",
+ ),
),
]
diff --git a/backend/core/migrations/0008_alter_complianceassessment_status_and_more.py b/backend/core/migrations/0008_alter_complianceassessment_status_and_more.py
index 7d089b187..cb4557e30 100644
--- a/backend/core/migrations/0008_alter_complianceassessment_status_and_more.py
+++ b/backend/core/migrations/0008_alter_complianceassessment_status_and_more.py
@@ -4,20 +4,45 @@
class Migration(migrations.Migration):
-
dependencies = [
- ('core', '0007_alter_requirementlevel_framework_and_more'),
+ ("core", "0007_alter_requirementlevel_framework_and_more"),
]
operations = [
migrations.AlterField(
- model_name='complianceassessment',
- name='status',
- field=models.CharField(blank=True, choices=[('planned', 'Planned'), ('in_progress', 'In progress'), ('in_review', 'In review'), ('done', 'Done'), ('deprecated', 'Deprecated')], default='planned', max_length=100, null=True, verbose_name='Status'),
+ model_name="complianceassessment",
+ name="status",
+ field=models.CharField(
+ blank=True,
+ choices=[
+ ("planned", "Planned"),
+ ("in_progress", "In progress"),
+ ("in_review", "In review"),
+ ("done", "Done"),
+ ("deprecated", "Deprecated"),
+ ],
+ default="planned",
+ max_length=100,
+ null=True,
+ verbose_name="Status",
+ ),
),
migrations.AlterField(
- model_name='riskassessment',
- name='status',
- field=models.CharField(blank=True, choices=[('planned', 'Planned'), ('in_progress', 'In progress'), ('in_review', 'In review'), ('done', 'Done'), ('deprecated', 'Deprecated')], default='planned', max_length=100, null=True, verbose_name='Status'),
+ model_name="riskassessment",
+ name="status",
+ field=models.CharField(
+ blank=True,
+ choices=[
+ ("planned", "Planned"),
+ ("in_progress", "In progress"),
+ ("in_review", "In review"),
+ ("done", "Done"),
+ ("deprecated", "Deprecated"),
+ ],
+ default="planned",
+ max_length=100,
+ null=True,
+ verbose_name="Status",
+ ),
),
]
diff --git a/backend/core/models.py b/backend/core/models.py
index 04762a1ad..66a1a0fab 100644
--- a/backend/core/models.py
+++ b/backend/core/models.py
@@ -487,7 +487,7 @@ def overall_compliance(self):
return round(count * 100 / total)
def __str__(self):
- return self.folder.name + '/' + self.name
+ return self.folder.name + "/" + self.name
class Asset(NameDescriptionMixin, FolderMixin, PublishInRootFolderMixin):
@@ -1236,11 +1236,7 @@ def get_strength_of_knowledge(self):
return self.DEFAULT_SOK_OPTIONS[self.strength_of_knowledge]
def __str__(self):
- return (
- str(self.parent_project())
- + _(": ")
- + str(self.name)
- )
+ return str(self.parent_project()) + _(": ") + str(self.name)
@property
def rid(self):
diff --git a/backend/core/permissions.py b/backend/core/permissions.py
index f6c38b4ee..05f8bb682 100644
--- a/backend/core/permissions.py
+++ b/backend/core/permissions.py
@@ -9,7 +9,8 @@
class RBACPermissions(permissions.DjangoObjectPermissions):
- """ this is the DRF custom permission model enforcing our RBAC logic """
+ """this is the DRF custom permission model enforcing our RBAC logic"""
+
perms_map = {
"GET": ["%(app_label)s.view_%(model_name)s"],
"OPTIONS": [],
diff --git a/backend/core/views.py b/backend/core/views.py
index da1613d8b..b89f111a0 100644
--- a/backend/core/views.py
+++ b/backend/core/views.py
@@ -837,24 +837,31 @@ def get_queryset(self):
def update(self, request: Request, *args, **kwargs) -> Response:
user = self.get_object()
- if user.is_admin() :
+ if user.is_admin():
number_of_admin_users = User.get_admin_users().count()
admin_group = UserGroup.objects.get(name="BI-UG-ADM")
- if number_of_admin_users == 1 :
+ if number_of_admin_users == 1:
new_user_groups = set(request.data["user_groups"])
- if str(admin_group.pk) not in new_user_groups :
- return Response({"error":"attemptToRemoveOnlyAdminUserGroup"},status=HTTP_403_FORBIDDEN)
+ if str(admin_group.pk) not in new_user_groups:
+ return Response(
+ {"error": "attemptToRemoveOnlyAdminUserGroup"},
+ status=HTTP_403_FORBIDDEN,
+ )
return super().update(request, *args, **kwargs)
def destroy(self, request, *args, **kwargs):
user = self.get_object()
- if user.is_admin() :
+ if user.is_admin():
number_of_admin_users = User.get_admin_users().count()
- if number_of_admin_users == 1 :
- return Response({"error":"attemptToDeleteOnlyAdminAccountError"},status=HTTP_403_FORBIDDEN)
+ if number_of_admin_users == 1:
+ return Response(
+ {"error": "attemptToDeleteOnlyAdminAccountError"},
+ status=HTTP_403_FORBIDDEN,
+ )
+
+ return super().destroy(request, *args, **kwargs)
- return super().destroy(request,*args,**kwargs)
class UserGroupViewSet(BaseModelViewSet):
"""
diff --git a/backend/iam/migrations/0002_purge_validator.py b/backend/iam/migrations/0002_purge_validator.py
index 3778b690b..00c772228 100644
--- a/backend/iam/migrations/0002_purge_validator.py
+++ b/backend/iam/migrations/0002_purge_validator.py
@@ -2,27 +2,27 @@
from django.db import migrations
+
def rename_validator(apps, schema_editor):
"""
rename the validator role since it has been replaced by approver.
"""
UserGroup = apps.get_model("iam", "UserGroup")
for ug in UserGroup.objects.filter(name="BI-UG-GVA"):
- ug.name="BI-UG-GAP"
+ ug.name = "BI-UG-GAP"
ug.save()
for ug in UserGroup.objects.filter(name="BI-UG-VAL"):
- ug.name="BI-UG-APP"
+ ug.name = "BI-UG-APP"
ug.save()
Role = apps.get_model("iam", "Role")
for role in Role.objects.filter(name="BI-RL-VAL"):
- role.name="BI-RL-APP"
+ role.name = "BI-RL-APP"
role.save()
class Migration(migrations.Migration):
-
dependencies = [
- ('iam', '0001_initial'),
+ ("iam", "0001_initial"),
]
operations = [
diff --git a/backend/iam/models.py b/backend/iam/models.py
index 5cf7c5b0d..1a701ac28 100644
--- a/backend/iam/models.py
+++ b/backend/iam/models.py
@@ -236,6 +236,7 @@ def get_user_groups(user):
user_group_list.append(user_group)
return user_group_list
+
class UserManager(BaseUserManager):
use_in_migrations = True
@@ -290,6 +291,7 @@ def create_superuser(self, email, password=None, **extra_fields):
UserGroup.objects.get(name="BI-UG-ADM").user_set.add(superuser)
return superuser
+
class User(AbstractBaseUser, AbstractBaseModel, FolderMixin):
"""a user is a principal corresponding to a human"""
@@ -470,12 +472,13 @@ def set_username(self, username):
self.email = username
@staticmethod
- def get_admin_users() -> List[Self] :
+ def get_admin_users() -> List[Self]:
return User.objects.filter(user_groups__name="BI-UG-ADM")
- def is_admin(self) -> bool :
+ def is_admin(self) -> bool:
return self.user_groups.filter(name="BI-UG-ADM").exists()
+
class Role(NameDescriptionMixin, FolderMixin):
"""A role is a list of permissions"""
diff --git a/backend/library/utils.py b/backend/library/utils.py
index b06b74878..34d622514 100644
--- a/backend/library/utils.py
+++ b/backend/library/utils.py
@@ -54,8 +54,10 @@ def get_available_library_files():
files.append(f)
return files
+
AVAILABLE_LIBRARIES = {}
+
def get_available_libraries():
"""
Returns a list of available libraries
@@ -69,12 +71,12 @@ def get_available_libraries():
for f in files:
fname = path / f
modified_time = os.path.getmtime(fname)
- libs = AVAILABLE_LIBRARIES.get((fname,modified_time))
- if libs is None :
+ libs = AVAILABLE_LIBRARIES.get((fname, modified_time))
+ if libs is None:
with open(fname, "r", encoding="utf-8") as file:
libs = list(yaml.safe_load_all(file))
- AVAILABLE_LIBRARIES[(fname,os.path.getmtime(fname))] = libs
- for _lib in libs :
+ AVAILABLE_LIBRARIES[(fname, os.path.getmtime(fname))] = libs
+ for _lib in libs:
if (lib := Library.objects.filter(urn=_lib["urn"]).first()) is not None:
_lib["id"] = lib.id
_lib["reference_count"] = lib.reference_count
@@ -213,6 +215,7 @@ def import_requirement_node(self, framework_object: Framework):
ReferenceControl.objects.get(urn=reference_control.lower())
)
+
# The couple (URN, locale) is unique. ===> Check it in the future
class FrameworkImporter:
REQUIRED_FIELDS = {"ref_id", "urn"}
@@ -586,13 +589,11 @@ def import_objects(self, library_object):
risk_matrix.import_risk_matrix(library_object)
@transaction.atomic
- def _import_library(self) :
+ def _import_library(self):
library_object = self.create_or_update_library()
self.import_objects(library_object)
library_object.dependencies.set(
- Library.objects.filter(
- urn__in=self._library_data.get("dependencies", [])
- )
+ Library.objects.filter(urn__in=self._library_data.get("dependencies", []))
)
def import_library(self):
@@ -602,20 +603,21 @@ def import_library(self):
self.check_and_import_dependencies()
- for _ in range(10) :
+ for _ in range(10):
try:
self._import_library()
break
- except OperationalError as e :
- if e.args and e.args[0] == 'database is locked' :
+ except OperationalError as e:
+ if e.args and e.args[0] == "database is locked":
time.sleep(1)
- else :
+ else:
raise e
- except Exception as e :
+ except Exception as e:
# TODO: Switch to proper logging
print(f"Library import exception: {e}")
raise e
+
def import_library_view(library: dict) -> Union[str, None]:
"""
Imports a library
diff --git a/backend/library/views.py b/backend/library/views.py
index f570e267a..b1867a526 100644
--- a/backend/library/views.py
+++ b/backend/library/views.py
@@ -31,6 +31,7 @@
from .serializers import LibrarySerializer, LibraryUploadSerializer
from .utils import get_available_libraries, get_library, import_library_view
+
class LibraryViewSet(BaseModelViewSet):
serializer_class = LibrarySerializer
parser_classes = [FileUploadParser]
@@ -67,7 +68,7 @@ def destroy(self, request, *args, pk, **kwargs):
return Response(data="Library not found.", status=status.HTTP_404_NOT_FOUND)
# "reference_count" is not always defined (is this normal ?)
- if library.get("reference_count",0) != 0 :
+ if library.get("reference_count", 0) != 0:
return Response(
data="Library cannot be deleted because it has references.",
status=status.HTTP_400_BAD_REQUEST,
@@ -153,9 +154,11 @@ def upload_library(self, request):
return HttpResponse(json.dumps({}), status=HTTP_200_OK)
except IntegrityError:
return HttpResponse(
- json.dumps({"error" : "libraryAlreadyImportedError"}), status=HTTP_400_BAD_REQUEST
+ json.dumps({"error": "libraryAlreadyImportedError"}),
+ status=HTTP_400_BAD_REQUEST,
)
- except :
+ except:
return HttpResponse(
- json.dumps({"error": "invalidLibraryFileError"}), status=HTTP_400_BAD_REQUEST
+ json.dumps({"error": "invalidLibraryFileError"}),
+ status=HTTP_400_BAD_REQUEST,
)
diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs
index 7e1bc2634..5a12e9941 100644
--- a/frontend/.eslintrc.cjs
+++ b/frontend/.eslintrc.cjs
@@ -1,12 +1,12 @@
module.exports = {
root: true,
extends: [
- "eslint:recommended",
- "plugin:@typescript-eslint/recommended",
- "plugin:svelte/recommended",
- "prettier",
- "plugin:storybook/recommended"
- ],
+ 'eslint:recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:svelte/recommended',
+ 'prettier',
+ 'plugin:storybook/recommended'
+ ],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
parserOptions: {
diff --git a/frontend/messages/ar.json b/frontend/messages/ar.json
index d74701b5e..0a67ba60c 100644
--- a/frontend/messages/ar.json
+++ b/frontend/messages/ar.json
@@ -2,4 +2,3 @@
"$schema": "https://inlang.com/schema/inlang-message-format",
"type": "نوع"
}
-
diff --git a/frontend/package.json b/frontend/package.json
index 8ff4722ad..6c3722dc1 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,83 +1,83 @@
{
- "name": "frontend",
- "version": "0.0.1",
- "private": true,
- "scripts": {
- "dev": "vite dev",
- "build": "paraglide-js compile --project ./project.inlang && vite build",
- "preview": "vite preview",
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
- "test": "vitest",
- "test:ci": "vitest run",
- "test:ui": "vitest --ui",
- "test:e2e": "ARGS=\"$npm_config_args\" docker compose -f ./tests/docker-compose.e2e-tests.yml up --force-recreate --build -V",
- "coverage": "vitest run --coverage",
- "lint": "prettier --plugin-search-dir . --check . && eslint .",
- "format": "prettier --plugin-search-dir . --write .",
- "postinstall": "paraglide-js compile --project ./project.inlang",
- "storybook": "storybook dev -p 6006",
- "build-storybook": "storybook build"
- },
- "devDependencies": {
- "@inlang/paraglide-js": "1.2.5",
- "@playwright/test": "^1.40.1",
- "@skeletonlabs/skeleton": "^2.3.0",
- "@skeletonlabs/tw-plugin": "^0.2.2",
- "@storybook/addon-essentials": "^7.6.17",
- "@storybook/addon-interactions": "^7.6.17",
- "@storybook/addon-links": "^7.6.17",
- "@storybook/blocks": "^7.6.17",
- "@storybook/svelte": "^7.6.17",
- "@storybook/sveltekit": "^7.6.17",
- "@storybook/test": "^7.6.17",
- "@sveltejs/adapter-auto": "^3.0.0",
- "@sveltejs/adapter-node": "^4.0.1",
- "@sveltejs/kit": "^2.0.0",
- "@sveltejs/vite-plugin-svelte": "^3.0.0",
- "@tailwindcss/forms": "^0.5.3",
- "@tailwindcss/typography": "^0.5.9",
- "@testing-library/jest-dom": "^6.1.4",
- "@testing-library/svelte": "^4.0.4",
- "@types/node": "^20.8.7",
- "@typescript-eslint/eslint-plugin": "^5.62.0",
- "@typescript-eslint/parser": "^5.62.0",
- "@vincjo/datatables": "^1.14.0",
- "@vitest/coverage-v8": "^1.1.1",
- "@vitest/ui": "^1.1.1",
- "autoprefixer": "^10.4.14",
- "eslint": "^8.53.0",
- "eslint-config-prettier": "^8.5.0",
- "eslint-plugin-storybook": "^0.8.0",
- "eslint-plugin-svelte": "^2.35.1",
- "jsdom": "^22.1.0",
- "postcss": "^8.4.23",
- "prettier": "^2.8.0",
- "prettier-plugin-svelte": "^2.10.1",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "storybook": "^7.6.17",
- "svelte": "^4.0.0",
- "svelte-check": "^3.4.3",
- "svelte-typewriter": "^3.2.3",
- "sveltekit-flash-message": "^2.2.1",
- "sveltekit-rate-limiter": "^0.4.1",
- "sveltekit-superforms": "^1.6.0",
- "tailwindcss": "^3.3.2",
- "tslib": "^2.4.1",
- "typescript": "^5.0.0",
- "vite": "^5.0.0",
- "vite-plugin-tailwind-purgecss": "^0.2.0",
- "vitest": "^1.1.1",
- "zod": "^3.22.2"
- },
- "type": "module",
- "dependencies": {
- "@floating-ui/dom": "^1.5.1",
- "@fortawesome/fontawesome-free": "^6.5.1",
- "@inlang/paraglide-js-adapter-vite": "^1.2.14",
- "dotenv": "^16.4.1",
- "echarts": "^5.4.3",
- "svelte-multiselect": "^10.2.0"
- }
+ "name": "frontend",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "dev": "vite dev",
+ "build": "paraglide-js compile --project ./project.inlang && vite build",
+ "preview": "vite preview",
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
+ "test": "vitest",
+ "test:ci": "vitest run",
+ "test:ui": "vitest --ui",
+ "test:e2e": "ARGS=\"$npm_config_args\" docker compose -f ./tests/docker-compose.e2e-tests.yml up --force-recreate --build -V",
+ "coverage": "vitest run --coverage",
+ "lint": "prettier --plugin-search-dir . --check . && eslint .",
+ "format": "prettier --plugin-search-dir . --write .",
+ "postinstall": "paraglide-js compile --project ./project.inlang",
+ "storybook": "storybook dev -p 6006",
+ "build-storybook": "storybook build"
+ },
+ "devDependencies": {
+ "@inlang/paraglide-js": "1.2.5",
+ "@playwright/test": "^1.40.1",
+ "@skeletonlabs/skeleton": "^2.3.0",
+ "@skeletonlabs/tw-plugin": "^0.2.2",
+ "@storybook/addon-essentials": "^7.6.17",
+ "@storybook/addon-interactions": "^7.6.17",
+ "@storybook/addon-links": "^7.6.17",
+ "@storybook/blocks": "^7.6.17",
+ "@storybook/svelte": "^7.6.17",
+ "@storybook/sveltekit": "^7.6.17",
+ "@storybook/test": "^7.6.17",
+ "@sveltejs/adapter-auto": "^3.0.0",
+ "@sveltejs/adapter-node": "^4.0.1",
+ "@sveltejs/kit": "^2.0.0",
+ "@sveltejs/vite-plugin-svelte": "^3.0.0",
+ "@tailwindcss/forms": "^0.5.3",
+ "@tailwindcss/typography": "^0.5.9",
+ "@testing-library/jest-dom": "^6.1.4",
+ "@testing-library/svelte": "^4.0.4",
+ "@types/node": "^20.8.7",
+ "@typescript-eslint/eslint-plugin": "^5.62.0",
+ "@typescript-eslint/parser": "^5.62.0",
+ "@vincjo/datatables": "^1.14.0",
+ "@vitest/coverage-v8": "^1.1.1",
+ "@vitest/ui": "^1.1.1",
+ "autoprefixer": "^10.4.14",
+ "eslint": "^8.53.0",
+ "eslint-config-prettier": "^8.5.0",
+ "eslint-plugin-storybook": "^0.8.0",
+ "eslint-plugin-svelte": "^2.35.1",
+ "jsdom": "^22.1.0",
+ "postcss": "^8.4.23",
+ "prettier": "^2.8.0",
+ "prettier-plugin-svelte": "^2.10.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "storybook": "^7.6.17",
+ "svelte": "^4.0.0",
+ "svelte-check": "^3.4.3",
+ "svelte-typewriter": "^3.2.3",
+ "sveltekit-flash-message": "^2.2.1",
+ "sveltekit-rate-limiter": "^0.4.1",
+ "sveltekit-superforms": "^1.6.0",
+ "tailwindcss": "^3.3.2",
+ "tslib": "^2.4.1",
+ "typescript": "^5.0.0",
+ "vite": "^5.0.0",
+ "vite-plugin-tailwind-purgecss": "^0.2.0",
+ "vitest": "^1.1.1",
+ "zod": "^3.22.2"
+ },
+ "type": "module",
+ "dependencies": {
+ "@floating-ui/dom": "^1.5.1",
+ "@fortawesome/fontawesome-free": "^6.5.1",
+ "@inlang/paraglide-js-adapter-vite": "^1.2.14",
+ "dotenv": "^16.4.1",
+ "echarts": "^5.4.3",
+ "svelte-multiselect": "^10.2.0"
+ }
}
diff --git a/frontend/project.inlang/settings.json b/frontend/project.inlang/settings.json
index 66d4dcafc..d4cf30191 100644
--- a/frontend/project.inlang/settings.json
+++ b/frontend/project.inlang/settings.json
@@ -1,21 +1,17 @@
{
- "$schema": "https://inlang.com/schema/project-settings",
- "sourceLanguageTag": "en",
- "languageTags": [
- "en",
- "ar",
- "fr"
- ],
- "modules": [
- "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js",
- "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-identical-pattern@latest/dist/index.js",
- "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js",
- "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@latest/dist/index.js",
- "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-valid-js-identifier@latest/dist/index.js",
- "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js",
- "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js"
- ],
- "plugin.inlang.messageFormat": {
- "pathPattern": "./messages/{languageTag}.json"
- }
-}
\ No newline at end of file
+ "$schema": "https://inlang.com/schema/project-settings",
+ "sourceLanguageTag": "en",
+ "languageTags": ["en", "ar", "fr"],
+ "modules": [
+ "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js",
+ "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-identical-pattern@latest/dist/index.js",
+ "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js",
+ "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@latest/dist/index.js",
+ "https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-valid-js-identifier@latest/dist/index.js",
+ "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js",
+ "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js"
+ ],
+ "plugin.inlang.messageFormat": {
+ "pathPattern": "./messages/{languageTag}.json"
+ }
+}
diff --git a/frontend/server/index.js b/frontend/server/index.js
index 6b0ff570a..3a99edff6 100644
--- a/frontend/server/index.js
+++ b/frontend/server/index.js
@@ -1,21 +1,21 @@
import { server } from '../build/index.js';
process.on('SIGINT', () => {
- console.log('Got SIGINT. Starting graceful shutdown.');
- shutdownServer();
+ console.log('Got SIGINT. Starting graceful shutdown.');
+ shutdownServer();
});
process.on('SIGTERM', () => {
- console.log('Got SIGTERM. Starting graceful shutdown.');
- shutdownServer();
+ console.log('Got SIGTERM. Starting graceful shutdown.');
+ shutdownServer();
});
function shutdownServer() {
- server.server?.close(() => {
- console.log('Server closed');
- process.exit(0);
- });
- server.server?.closeIdleConnections();
- setInterval(() => server.server?.closeIdleConnections(), 1_000);
- setTimeout(() => server.server?.closeAllConnections(), 20_000);
-}
\ No newline at end of file
+ server.server?.close(() => {
+ console.log('Server closed');
+ process.exit(0);
+ });
+ server.server?.closeIdleConnections();
+ setInterval(() => server.server?.closeIdleConnections(), 1_000);
+ setTimeout(() => server.server?.closeAllConnections(), 20_000);
+}
diff --git a/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte b/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte
index 7a537f57b..277b3dd9c 100644
--- a/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte
+++ b/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte
@@ -38,14 +38,17 @@
}
} else if (t === 'folders') {
t = 'domains';
- }
- else{
+ } else {
t = t.replace(/-/g, ' ');
t = capitalizeSecondWord(t);
}
return {
label: $page.data.label || t,
- href: Object.keys(listViewFields).includes(tokens[0]) && !listViewFields[tokens[0]].breadcrumb_link_disabled ? tokenPath : null
+ href:
+ Object.keys(listViewFields).includes(tokens[0]) &&
+ !listViewFields[tokens[0]].breadcrumb_link_disabled
+ ? tokenPath
+ : null
};
});
diff --git a/frontend/src/lib/components/Forms/AutocompleteSelect.svelte b/frontend/src/lib/components/Forms/AutocompleteSelect.svelte
index 8c100fb09..851ba3470 100644
--- a/frontend/src/lib/components/Forms/AutocompleteSelect.svelte
+++ b/frontend/src/lib/components/Forms/AutocompleteSelect.svelte
@@ -30,7 +30,8 @@
const default_value = nullable ? null : selectedValues[0];
- $: ($value = multiple ? selectedValues : selectedValues[0] ?? default_value), handleSelectChange();
+ $: ($value = multiple ? selectedValues : selectedValues[0] ?? default_value),
+ handleSelectChange();
$: disabled = selected.length && options.length === 1 && $constraints?.required;
@@ -87,12 +88,10 @@
{#if option.suggested}
{option.label}
(suggested)
+ {:else if localItems(languageTag())[toCamelCase(option.label)]}
+ {localItems(languageTag())[toCamelCase(option.label)]}
{:else}
- {#if localItems(languageTag())[toCamelCase(option.label)]}
- {localItems(languageTag())[toCamelCase(option.label)]}
- {:else}
- {option.label}
- {/if}
+ {option.label}
{/if}
{:else}
diff --git a/frontend/src/lib/components/Forms/ModelForm.svelte b/frontend/src/lib/components/Forms/ModelForm.svelte
index bda9e89bd..eaa9aa684 100644
--- a/frontend/src/lib/components/Forms/ModelForm.svelte
+++ b/frontend/src/lib/components/Forms/ModelForm.svelte
@@ -44,7 +44,9 @@
onMount(() => {
if (shape.reference_control) {
- const reference_control_input: HTMLElement | null = document.querySelector(`div.multiselect[role="searchbox"] input`); // The MultiSelect component can't be focused automatically with data-focusindex="0" so we focus manually
+ const reference_control_input: HTMLElement | null = document.querySelector(
+ `div.multiselect[role="searchbox"] input`
+ ); // The MultiSelect component can't be focused automatically with data-focusindex="0" so we focus manually
reference_control_input?.focus();
}
});
@@ -69,7 +71,7 @@
{form}
options={getOptions({
objects: model.foreignKeys['reference_control'],
- extra_fields: [["folder","str"]],
+ extra_fields: [['folder', 'str']],
suggestions: suggestions['reference_control']
})}
field="reference_control"
@@ -97,10 +99,10 @@
/>
{/if}
{#if shape.name}
-