diff --git a/README.md b/README.md index 17cd5641e4be239f4c3dd597b77f82c5beace004..bf6dd22f54d6ee7792ade1af5da5647796018c2b 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ V produkci je potřeba: | proměnná | popis | | --- | --- | | `ALLOWED_HOSTS` | Seznam domén, skrz které se na server lze připojovat. [Více info](https://docs.djangoproject.com/en/4.1/ref/settings/#allowed-hosts) | +| `SENTRY_DSN` | Pokud je zadáno, pády se reportují do Sentry. | ## Vývoj diff --git a/contracts/admin.py b/contracts/admin.py index 1ea01de6d9c1ec44cfc51df2e4e77a15f0719785..22e0dd8bafbf988fa3ed1fa71a8a037306759157 100644 --- a/contracts/admin.py +++ b/contracts/admin.py @@ -42,7 +42,7 @@ class IndexHiddenModelAdmin(MarkdownxGuardedModelAdmin): def permissions_mixin_factory( change_permission: str, delete_permission: str, - obj_conditional: typing.Callable = lambda request, obj: True + obj_conditional: typing.Callable = lambda request, obj: True, ) -> object: class Mixin: def has_change_permission(self, request, obj=None) -> bool: @@ -95,7 +95,8 @@ ParentContractApprovedPermissionsMixin = permissions_mixin_factory( ParentContractOwnPermissionsMixin = permissions_mixin_factory( "contracts.edit_others", "contracts.delete_others", - obj_conditional=lambda request, obj: get_obj_contract(obj).created_by != request.user, + obj_conditional=lambda request, obj: get_obj_contract(obj).created_by + != request.user, ) @@ -500,7 +501,8 @@ class SigneeSignatureRepresentativeAdmin( permissions_mixin_factory( "contracts.edit_others", "contracts.delete_others", - obj_conditional=lambda request, obj: get_obj_contractee_contract(obj).created_by != request.user, + obj_conditional=lambda request, obj: get_obj_contractee_contract(obj).created_by + != request.user, ), ): pass @@ -511,12 +513,15 @@ class ContracteeSignatureRepresentativeAdmin( permissions_mixin_factory( "contracts.edit_when_approved", "contracts.delete_when_approved", - obj_conditional=lambda request, obj: get_obj_contractee_contract(obj).is_approved, + obj_conditional=lambda request, obj: get_obj_contractee_contract( + obj + ).is_approved, ), permissions_mixin_factory( "contracts.edit_others", "contracts.delete_others", - obj_conditional=lambda request, obj: get_obj_contractee_contract(obj).created_by != request.user, + obj_conditional=lambda request, obj: get_obj_contractee_contract(obj).created_by + != request.user, ), ): pass diff --git a/contracts/management/commands/import_old_contracts.py b/contracts/management/commands/import_old_contracts.py index d2fafd01efce80a7967a22edb1984e9eb8b7b81a..312f62523f4e3f7efd5d8020d72702f44fe87081 100644 --- a/contracts/management/commands/import_old_contracts.py +++ b/contracts/management/commands/import_old_contracts.py @@ -1,8 +1,8 @@ import io import os import re -import string import shutil +import string from datetime import date, datetime import yaml @@ -588,7 +588,9 @@ class Command(BaseCommand): issue_count += 1 contract.notes += f"Špatně zadaná funkce zástupce smluvní strany: {signing_party['funkce']}\n" issues.append( - self.use_issue("Špatně zadaná funkce zástupce smluvní strany") + self.use_issue( + "Špatně zadaná funkce zástupce smluvní strany" + ) ) if self.verbosity >= 2: @@ -608,7 +610,9 @@ class Command(BaseCommand): issue_count += 1 contract.notes += f"Špatně zadaný jeden ze zástupců smluvní strany: {representative_name}\n" issues.append( - self.use_issue("Špatně zadaný zástupce smluvní strany") + self.use_issue( + "Špatně zadaný zástupce smluvní strany" + ) ) if self.verbosity >= 2: @@ -779,9 +783,7 @@ class Command(BaseCommand): elif value is not None: observed_issues_count += 1 contract.notes += f"Špatně zadaný začátek platnosti: {value}\n" - issues.append( - self.use_issue("Špatně zadaný začátek platnosti") - ) + issues.append(self.use_issue("Špatně zadaný začátek platnosti")) if self.verbosity >= 2: self.stderr.write( @@ -798,9 +800,7 @@ class Command(BaseCommand): ): observed_issues_count += 1 contract.notes += f"Špatně zadaný konec platnosti: {value}\n" - issues.append( - self.use_issue("Špatně zadaný konec platnosti") - ) + issues.append(self.use_issue("Špatně zadaný konec platnosti")) if self.verbosity >= 2: self.stderr.write( @@ -914,9 +914,7 @@ class Command(BaseCommand): contract.notes += ( f"Původní, špatně zadané náklady: {value}\n" ) - issues.append( - self.use_issue("Špatně zadané náklady") - ) + issues.append(self.use_issue("Špatně zadané náklady")) if self.verbosity >= 2: self.stderr.write( @@ -944,9 +942,7 @@ class Command(BaseCommand): ): observed_issues_count += 1 contract.notes += f"Původní, neropoznané náklady: {value}\n" - issues.append( - self.use_issue("Špatně zadané náklady") - ) + issues.append(self.use_issue("Špatně zadané náklady")) if self.verbosity >= 2: self.stderr.write( @@ -987,9 +983,7 @@ class Command(BaseCommand): else: observed_issues_count += 1 contract.notes += f"Špatně zadaná spisovna: {value}\n" - issues.append( - self.use_issue("Špatně zadaná spisovna") - ) + issues.append(self.use_issue("Špatně zadaná spisovna")) if self.verbosity >= 2: self.stderr.write( @@ -1003,9 +997,7 @@ class Command(BaseCommand): contract.notes += ( f"Špatně zadané smluvní strany, nejsou seznam: {value}\n" ) - issues.append( - self.use_issue("Špatně zadaný smluvní strany") - ) + issues.append(self.use_issue("Špatně zadaný smluvní strany")) if self.verbosity >= 2: self.stderr.write( @@ -1124,9 +1116,7 @@ class Command(BaseCommand): contract.notes += ( f"Neexistující soubor: {file_value}.\n" ) - issues.append( - self.use_issue("Neexistující soubor") - ) + issues.append(self.use_issue("Neexistující soubor")) if self.verbosity >= 2: self.stderr.write( diff --git a/contracts/migrations/0059_alter_contract_is_valid.py b/contracts/migrations/0059_alter_contract_is_valid.py index df29dad1e83e8eb129a54642ba1ab5af5a3fbd6b..3d94f034352cc535066f134e4df2a306470ae868 100644 --- a/contracts/migrations/0059_alter_contract_is_valid.py +++ b/contracts/migrations/0059_alter_contract_is_valid.py @@ -4,15 +4,18 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('contracts', '0058_alter_contract_options_alter_contractee_options_and_more'), + ("contracts", "0058_alter_contract_options_alter_contractee_options_and_more"), ] operations = [ migrations.AlterField( - model_name='contract', - name='is_valid', - field=models.BooleanField(default=False, help_text='Právní vztah vyplývající ze smlouvy je aktuálně účinný a platný', verbose_name='Je právně platná'), + model_name="contract", + name="is_valid", + field=models.BooleanField( + default=False, + help_text="Právní vztah vyplývající ze smlouvy je aktuálně účinný a platný", + verbose_name="Je právně platná", + ), ), ] diff --git a/contracts/migrations/0060_alter_contractfile_name.py b/contracts/migrations/0060_alter_contractfile_name.py new file mode 100644 index 0000000000000000000000000000000000000000..ac14b7d0d0ed106ad523edea9eeaab057d361414 --- /dev/null +++ b/contracts/migrations/0060_alter_contractfile_name.py @@ -0,0 +1,20 @@ +# Generated by Django 4.1.4 on 2023-05-03 11:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("contracts", "0059_alter_contract_is_valid"), + ] + + operations = [ + migrations.AlterField( + model_name="contractfile", + name="name", + field=models.CharField( + default="Neznámé jméno", max_length=256, verbose_name="Jméno" + ), + preserve_default=False, + ), + ] diff --git a/contracts/migrations/0061_alter_contract_id_number.py b/contracts/migrations/0061_alter_contract_id_number.py new file mode 100644 index 0000000000000000000000000000000000000000..ae3ac80d17e9fb51fd21ec788eae39343269c8c8 --- /dev/null +++ b/contracts/migrations/0061_alter_contract_id_number.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.4 on 2023-05-23 13:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("contracts", "0060_alter_contractfile_name"), + ] + + operations = [ + migrations.AlterField( + model_name="contract", + name="id_number", + field=models.CharField( + blank=True, + help_text="<strong>Není IČO!</strong> Používá se pro identifikaci smluv s velkými organizacemi. Např. <code>MF-8687/2022/15-3</code>.", + max_length=256, + null=True, + verbose_name="Identifikační číslo", + ), + ), + ] diff --git a/contracts/models.py b/contracts/models.py index 5e9719e79202e770c039ae56195f6491da9d5ba6..e717261023e8ecbae4ecdf699444289b14bf8782 100644 --- a/contracts/models.py +++ b/contracts/models.py @@ -102,7 +102,7 @@ class RepresentativeMixin: raise NotImplementedError def __str__(self) -> str: - result = self.name + result = str(self.name) if self.function is not None: result += f", {self.function}" @@ -255,7 +255,7 @@ class Signee( return super().clean() def __str__(self) -> str: - result = self.name + result = str(self.name) if self.ico_number is not None: result += f" ({self.ico_number})" @@ -356,7 +356,7 @@ class Contractee( return reverse("contracts:view_contractee", args=(self.id,)) def __str__(self) -> str: - result = self.name + result = str(self.name) if self.department is not None: result += f", {self.department}" @@ -511,6 +511,10 @@ class Contract(NameStrMixin, models.Model): blank=True, null=True, verbose_name="Identifikační číslo", + help_text=mark_safe( + "<strong>Není IČO!</strong> Používá se pro identifikaci smluv " + "s velkými organizacemi. Např. <code>MF-8687/2022/15-3</code>." + ), ) types = models.ManyToManyField( @@ -856,8 +860,6 @@ class ContractFileField(models.FileField): class ContractFile(NameStrMixin, models.Model): name = models.CharField( max_length=256, - blank=True, - null=True, verbose_name="Jméno", ) diff --git a/registry/settings/base.py b/registry/settings/base.py index c2f29bd217dd4272ac6a7fdeb5727ef3a17861a3..8755276a36ebacca4e2e513a808b93bda53978f9 100644 --- a/registry/settings/base.py +++ b/registry/settings/base.py @@ -15,6 +15,9 @@ import pathlib import dj_database_url import environ +import sentry_sdk + +from sentry_sdk.integrations.django import DjangoIntegration # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = pathlib.Path(__file__).parents[2] @@ -229,6 +232,17 @@ CLAMD_TCP_SOCKET = env.int("CLAMD_TCP_SOCKET") CLAMD_TCP_ADDR = env.str("CLAMD_TCP_ADDR") +# Sentry + +SENTRY_DSN = env.str("SENTRY_DSN", default="") + +if SENTRY_DSN != "": + sentry_sdk.init( + dsn=SENTRY_DSN, + integrations=[DjangoIntegration()], + send_default_pii=True, + ) + ## App-specific DEFAULT_CONTRACTEE_NAME = env.str("DEFAULT_CONTRACTEE_NAME") diff --git a/registry/templates/admin/index.html b/registry/templates/admin/index.html index 834866cf238c91b8f6a64496c0e198221679e69c..f96c70270891fdaecba7b90c9f114447e32223e3 100644 --- a/registry/templates/admin/index.html +++ b/registry/templates/admin/index.html @@ -34,7 +34,7 @@ {{ block.super }} -<div style="width:100%;float:left;padding-top:0.5rem;margin-top:0.5rem;border-top:1px solid var(--hairline-color)"> +<div style="width:100%;float:left;padding-top:0.5rem;margin-top:0.5rem;border-top:1px solid var(--hairline-color)"> <h2>Tvá oprávnění</h2> <ul> diff --git a/requirements/production.txt b/requirements/production.txt index ce5169e4440b67843ee5d28199ed63e0d8323cfc..efb3dddbc10dc64194f8c49333b06d1378f132b0 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -1,2 +1,3 @@ gunicorn==20.1.0 whitenoise==6.3.0 +sentry-sdk==1.24.0 diff --git a/shared/models.py b/shared/models.py index ee10b8b8e3b8e01efdbdf4e251b94b37c35863a2..b9f1d20b03e5db0dc6ab6b01d473b8ae1fb537d6 100644 --- a/shared/models.py +++ b/shared/models.py @@ -1,5 +1,7 @@ class NameStrMixin: - name = "" + @property + def name(self): + raise NotImplementedError def __str__(self) -> str: - return self.name + return str(self.name) diff --git a/users/models.py b/users/models.py index aa19469e93ee0a716fe8f6a06512c945cdced777..83f21bfc393d3c21aa0790ff0854f06b246fa5e4 100644 --- a/users/models.py +++ b/users/models.py @@ -90,15 +90,12 @@ class User(pirates_models.AbstractUser): def get_all_permissions_ordered(self, obj=None) -> list: if not self.is_superuser: permissions = ( - Permission. - objects. - filter( - models.Q(group__user=self) | - models.Q(user=self) - ). - order_by("content_type__app_label"). - distinct(). - all() + Permission.objects.filter( + models.Q(group__user=self) | models.Q(user=self) + ) + .order_by("content_type__app_label") + .distinct() + .all() ) else: permissions = Permission.objects.order_by("content_type__app_label").all()