Adding pre-commit

This commit is contained in:
Jakub Kropáček 2024-02-03 21:07:20 +00:00
parent a6bac23b9f
commit 3d7261487f
33 changed files with 301 additions and 306 deletions

18
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,18 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/asottile/reorder-python-imports
rev: v3.12.0
hooks:
- id: reorder-python-imports
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.2.0
hooks:
- id: ruff
args: [ --fix, --exit-non-zero-on-fix ]
- id: ruff-format

View file

@ -7,7 +7,4 @@ from . import models
@admin.register(models.User) @admin.register(models.User)
class UserAdmin(BaseUserAdmin): class UserAdmin(BaseUserAdmin):
fieldsets = ( fieldsets = (*BaseUserAdmin.fieldsets, (_('Subjects'), {'fields': ('subjects',)}))
*BaseUserAdmin.fieldsets,
(_("Subjects"), {"fields": ("subjects",)})
)

View file

@ -2,5 +2,5 @@ from django.apps import AppConfig
class AccountConfig(AppConfig): class AccountConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField" default_auto_field = 'django.db.models.BigAutoField'
name = "accounts" name = 'accounts'

View file

@ -1,5 +1,7 @@
from crispy_forms import helper, layout from crispy_forms import helper
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm from crispy_forms import layout
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.forms import UserCreationForm
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .models import User from .models import User
@ -12,19 +14,19 @@ class LoginForm(AuthenticationForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.helper = helper.FormHelper() self.helper = helper.FormHelper()
self.helper.form_action = "accounts:login" self.helper.form_action = 'accounts:login'
self.helper.form_method = "post" self.helper.form_method = 'post'
self.helper.add_input(layout.Submit('submit', _('Login'))) self.helper.add_input(layout.Submit('submit', _('Login')))
class RegisterForm(UserCreationForm): class RegisterForm(UserCreationForm):
class Meta: class Meta:
model = User model = User
fields = UserCreationForm.Meta.fields + ("first_name", "last_name", "email") fields = UserCreationForm.Meta.fields + ('first_name', 'last_name', 'email')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.helper = helper.FormHelper() self.helper = helper.FormHelper()
self.helper.form_action = "accounts:register" self.helper.form_action = 'accounts:register'
self.helper.form_method = "post" self.helper.form_method = 'post'
self.helper.add_input(layout.Submit('submit', _('Register'))) self.helper.add_input(layout.Submit('submit', _('Register')))

View file

@ -1,131 +1,131 @@
# Generated by Django 5.0 on 2023-12-18 21:42 # Generated by Django 5.0 on 2023-12-18 21:42
import django.contrib.auth.models import django.contrib.auth.models
import django.contrib.auth.validators import django.contrib.auth.validators
import django.utils.timezone import django.utils.timezone
from django.db import migrations, models from django.db import migrations
from django.db import models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("auth", "0012_alter_user_first_name_max_length"), ('auth', '0012_alter_user_first_name_max_length'),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name="User", name='User',
fields=[ fields=[
( (
"id", 'id',
models.BigAutoField( models.BigAutoField(
auto_created=True, auto_created=True,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
verbose_name="ID", verbose_name='ID',
), ),
), ),
("password", models.CharField(max_length=128, verbose_name="password")), ('password', models.CharField(max_length=128, verbose_name='password')),
( (
"last_login", 'last_login',
models.DateTimeField( models.DateTimeField(
blank=True, null=True, verbose_name="last login" blank=True, null=True, verbose_name='last login'
), ),
), ),
( (
"is_superuser", 'is_superuser',
models.BooleanField( models.BooleanField(
default=False, default=False,
help_text="Designates that this user has all permissions without explicitly assigning them.", help_text='Designates that this user has all permissions without explicitly assigning them.',
verbose_name="superuser status", verbose_name='superuser status',
), ),
), ),
( (
"username", 'username',
models.CharField( models.CharField(
error_messages={ error_messages={
"unique": "A user with that username already exists." 'unique': 'A user with that username already exists.'
}, },
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.',
max_length=150, max_length=150,
unique=True, unique=True,
validators=[ validators=[
django.contrib.auth.validators.UnicodeUsernameValidator() django.contrib.auth.validators.UnicodeUsernameValidator()
], ],
verbose_name="username", verbose_name='username',
), ),
), ),
( (
"first_name", 'first_name',
models.CharField( models.CharField(
blank=True, max_length=150, verbose_name="first name" blank=True, max_length=150, verbose_name='first name'
), ),
), ),
( (
"last_name", 'last_name',
models.CharField( models.CharField(
blank=True, max_length=150, verbose_name="last name" blank=True, max_length=150, verbose_name='last name'
), ),
), ),
( (
"email", 'email',
models.EmailField( models.EmailField(
blank=True, max_length=254, verbose_name="email address" blank=True, max_length=254, verbose_name='email address'
), ),
), ),
( (
"is_staff", 'is_staff',
models.BooleanField( models.BooleanField(
default=False, default=False,
help_text="Designates whether the user can log into this admin site.", help_text='Designates whether the user can log into this admin site.',
verbose_name="staff status", verbose_name='staff status',
), ),
), ),
( (
"is_active", 'is_active',
models.BooleanField( models.BooleanField(
default=True, default=True,
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.',
verbose_name="active", verbose_name='active',
), ),
), ),
( (
"date_joined", 'date_joined',
models.DateTimeField( models.DateTimeField(
default=django.utils.timezone.now, verbose_name="date joined" default=django.utils.timezone.now, verbose_name='date joined'
), ),
), ),
( (
"groups", 'groups',
models.ManyToManyField( models.ManyToManyField(
blank=True, blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.',
related_name="user_set", related_name='user_set',
related_query_name="user", related_query_name='user',
to="auth.group", to='auth.group',
verbose_name="groups", verbose_name='groups',
), ),
), ),
( (
"user_permissions", 'user_permissions',
models.ManyToManyField( models.ManyToManyField(
blank=True, blank=True,
help_text="Specific permissions for this user.", help_text='Specific permissions for this user.',
related_name="user_set", related_name='user_set',
related_query_name="user", related_query_name='user',
to="auth.permission", to='auth.permission',
verbose_name="user permissions", verbose_name='user permissions',
), ),
), ),
], ],
options={ options={
"verbose_name": "user", 'verbose_name': 'user',
"verbose_name_plural": "users", 'verbose_name_plural': 'users',
"abstract": False, 'abstract': False,
}, },
managers=[ managers=[
("objects", django.contrib.auth.models.UserManager()), ('objects', django.contrib.auth.models.UserManager()),
], ],
), ),
] ]

View file

@ -1,27 +1,27 @@
# Generated by Django 5.0 on 2023-12-19 11:53 # Generated by Django 5.0 on 2023-12-19 11:53
from django.db import migrations
from django.db import migrations, models from django.db import models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("accounts", "0001_initial"), ('accounts', '0001_initial'),
] ]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name="user", model_name='user',
name="email", name='email',
field=models.EmailField(max_length=254, verbose_name="email address"), field=models.EmailField(max_length=254, verbose_name='email address'),
), ),
migrations.AlterField( migrations.AlterField(
model_name="user", model_name='user',
name="first_name", name='first_name',
field=models.CharField(max_length=150, verbose_name="first name"), field=models.CharField(max_length=150, verbose_name='first name'),
), ),
migrations.AlterField( migrations.AlterField(
model_name="user", model_name='user',
name="last_name", name='last_name',
field=models.CharField(max_length=150, verbose_name="last name"), field=models.CharField(max_length=150, verbose_name='last name'),
), ),
] ]

View file

@ -1,18 +1,18 @@
# Generated by Django 5.0.1 on 2024-01-29 21:06 # Generated by Django 5.0.1 on 2024-01-29 21:06
from django.db import migrations
from django.db import migrations, models from django.db import models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("accounts", "0002_alter_user_email_alter_user_first_name_and_more"), ('accounts', '0002_alter_user_email_alter_user_first_name_and_more'),
("subjects", "0003_alter_subject_options"), ('subjects', '0003_alter_subject_options'),
] ]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name="user", model_name='user',
name="subjects", name='subjects',
field=models.ManyToManyField(to="subjects.subject"), field=models.ManyToManyField(to='subjects.subject'),
), ),
] ]

View file

@ -1,18 +1,18 @@
# Generated by Django 5.0.1 on 2024-01-29 21:19 # Generated by Django 5.0.1 on 2024-01-29 21:19
from django.db import migrations
from django.db import migrations, models from django.db import models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("accounts", "0003_user_subjects"), ('accounts', '0003_user_subjects'),
("subjects", "0003_alter_subject_options"), ('subjects', '0003_alter_subject_options'),
] ]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name="user", model_name='user',
name="subjects", name='subjects',
field=models.ManyToManyField(blank=True, to="subjects.subject"), field=models.ManyToManyField(blank=True, to='subjects.subject'),
), ),
] ]

View file

@ -6,11 +6,11 @@ from subjects.models import Subject
class User(AbstractUser): class User(AbstractUser):
REQUIRED_FIELDS = ["email", "first_name", "last_name"] REQUIRED_FIELDS = ['email', 'first_name', 'last_name']
first_name = models.CharField(_("first name"), max_length=150) first_name = models.CharField(_('first name'), max_length=150)
last_name = models.CharField(_("last name"), max_length=150) last_name = models.CharField(_('last name'), max_length=150)
email = models.EmailField(_("email address")) email = models.EmailField(_('email address'))
subjects = models.ManyToManyField(Subject, blank=True) subjects = models.ManyToManyField(Subject, blank=True)

View file

@ -7,4 +7,4 @@
{% block content %} {% block content %}
{% crispy form form.helper %} {% crispy form form.helper %}
{% endblock %} {% endblock %}

View file

@ -1,3 +1 @@
from django.test import TestCase
# Create your tests here. # Create your tests here.

View file

@ -2,12 +2,12 @@ from django.urls import path
from . import views from . import views
app_name = "accounts" app_name = 'accounts'
urlpatterns = [ urlpatterns = [
path("", views.me, name="me"), path('', views.me, name='me'),
path("me/", views.me, name="me_exp"), path('me/', views.me, name='me_exp'),
path("login/", views.auth_login, name="login"), path('login/', views.auth_login, name='login'),
path("logout/", views.auth_logout, name="logout"), path('logout/', views.auth_logout, name='logout'),
path("register/", views.auth_register, name="register"), path('register/', views.auth_register, name='register'),
] ]

View file

@ -1,28 +1,32 @@
from django.conf import settings from django.conf import settings
from django.contrib.auth import login, logout from django.contrib.auth import login
from django.contrib.auth import logout
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest
from django.shortcuts import redirect, render, reverse from django.http import HttpResponse
from django.shortcuts import redirect
from django.shortcuts import render
from django.shortcuts import reverse
from . import forms from . import forms
def auth_login(req: HttpRequest) -> HttpResponse: def auth_login(req: HttpRequest) -> HttpResponse:
if req.method == "POST": if req.method == 'POST':
form = forms.LoginForm(data=req.POST) form = forms.LoginForm(data=req.POST)
if not form.is_valid(): if not form.is_valid():
return render(req, "account/login.html", dict(form=form)) return render(req, 'account/login.html', dict(form=form))
user = form.get_user() user = form.get_user()
login(req, user) login(req, user)
redirect_url = getattr(req.GET, "next", settings.LOGIN_REDIRECT_URL) redirect_url = getattr(req.GET, 'next', settings.LOGIN_REDIRECT_URL)
return redirect(redirect_url) return redirect(redirect_url)
elif req.method == "GET": elif req.method == 'GET':
form = forms.LoginForm() form = forms.LoginForm()
return render(req, "account/login.html", dict(form=form)) return render(req, 'account/login.html', dict(form=form))
return HttpResponse(status=405) return HttpResponse(status=405)
@ -35,20 +39,20 @@ def auth_logout(req: HttpRequest) -> HttpResponse:
def auth_register(req: HttpRequest) -> HttpResponse: def auth_register(req: HttpRequest) -> HttpResponse:
if req.user.is_authenticated: if req.user.is_authenticated:
return redirect(reverse("me")) return redirect(reverse('me'))
if req.method == "POST": if req.method == 'POST':
form = forms.RegisterForm(data=req.POST) form = forms.RegisterForm(data=req.POST)
if not form.is_valid(): if not form.is_valid():
return render(req, "account/register.html", dict(form=form)) return render(req, 'account/register.html', dict(form=form))
user = form.save() user = form.save()
login(req, user) login(req, user)
return redirect(settings.LOGIN_BASE_URL) return redirect(settings.LOGIN_BASE_URL)
elif req.method == "GET": elif req.method == 'GET':
form = forms.RegisterForm() form = forms.RegisterForm()
return render(req, "account/register.html", dict(form=form)) return render(req, 'account/register.html', dict(form=form))
return HttpResponse(status=405) return HttpResponse(status=405)
@ -56,4 +60,4 @@ def auth_register(req: HttpRequest) -> HttpResponse:
@login_required @login_required
def me(req: HttpRequest) -> HttpResponse: def me(req: HttpRequest) -> HttpResponse:
print(req.user.username) print(req.user.username)
return render(req, "account/me.html") return render(req, 'account/me.html')

View file

@ -6,11 +6,10 @@ It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see For more information on this file, see
https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
""" """
import os import os
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "facturio.settings") os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'facturio.settings')
application = get_asgi_application() application = get_asgi_application()

View file

@ -9,8 +9,8 @@ https://docs.djangoproject.com/en/5.0/topics/settings/
For the full list of settings and their values, see For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/ https://docs.djangoproject.com/en/5.0/ref/settings/
""" """
from pathlib import Path from pathlib import Path
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
@ -20,7 +20,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-+l+g3)q1zz2bz7=mz4ys6lhu5uj+=ucj34flm^clo4vb3(wmdp" SECRET_KEY = 'django-insecure-+l+g3)q1zz2bz7=mz4ys6lhu5uj+=ucj34flm^clo4vb3(wmdp'
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
@ -30,56 +30,56 @@ ALLOWED_HOSTS = ['*']
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
"django.contrib.admin", 'django.contrib.admin',
"django.contrib.auth", 'django.contrib.auth',
"django.contrib.contenttypes", 'django.contrib.contenttypes',
"django.contrib.sessions", 'django.contrib.sessions',
"django.contrib.messages", 'django.contrib.messages',
"django.contrib.staticfiles", 'django.contrib.staticfiles',
"crispy_forms", 'crispy_forms',
"crispy_bootstrap5", 'crispy_bootstrap5',
"accounts.apps.AccountConfig", 'accounts.apps.AccountConfig',
"subjects.apps.SubjectsConfig" 'subjects.apps.SubjectsConfig',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware", 'django.middleware.security.SecurityMiddleware',
"django.contrib.sessions.middleware.SessionMiddleware", 'django.contrib.sessions.middleware.SessionMiddleware',
"django.middleware.locale.LocaleMiddleware", 'django.middleware.locale.LocaleMiddleware',
"django.middleware.common.CommonMiddleware", 'django.middleware.common.CommonMiddleware',
"django.middleware.csrf.CsrfViewMiddleware", 'django.middleware.csrf.CsrfViewMiddleware',
"django.contrib.auth.middleware.AuthenticationMiddleware", 'django.contrib.auth.middleware.AuthenticationMiddleware',
"django.contrib.messages.middleware.MessageMiddleware", 'django.contrib.messages.middleware.MessageMiddleware',
"django.middleware.clickjacking.XFrameOptionsMiddleware", 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
ROOT_URLCONF = "facturio.urls" ROOT_URLCONF = 'facturio.urls'
TEMPLATES = [ TEMPLATES = [
{ {
"BACKEND": "django.template.backends.django.DjangoTemplates", 'BACKEND': 'django.template.backends.django.DjangoTemplates',
"DIRS": [BASE_DIR / "templates"], 'DIRS': [BASE_DIR / 'templates'],
"APP_DIRS": True, 'APP_DIRS': True,
"OPTIONS": { 'OPTIONS': {
"context_processors": [ 'context_processors': [
"django.template.context_processors.debug", 'django.template.context_processors.debug',
"django.template.context_processors.request", 'django.template.context_processors.request',
"django.contrib.auth.context_processors.auth", 'django.contrib.auth.context_processors.auth',
"django.contrib.messages.context_processors.messages", 'django.contrib.messages.context_processors.messages',
], ],
}, },
}, },
] ]
WSGI_APPLICATION = "facturio.wsgi.application" WSGI_APPLICATION = 'facturio.wsgi.application'
# Database # Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases # https://docs.djangoproject.com/en/5.0/ref/settings/#databases
DATABASES = { DATABASES = {
"default": { 'default': {
"ENGINE": "django.db.backends.sqlite3", 'ENGINE': 'django.db.backends.sqlite3',
"NAME": BASE_DIR / "db.sqlite3", 'NAME': BASE_DIR / 'db.sqlite3',
} }
} }
@ -88,33 +88,28 @@ DATABASES = {
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
}, },
{ {
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
}, },
{ {
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
}, },
{ {
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
}, },
] ]
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/ # https://docs.djangoproject.com/en/5.0/topics/i18n/
LANGUAGE_CODE = "cs" LANGUAGE_CODE = 'cs'
LANGUAGES = [ LANGUAGES = [('en', _('English')), ('cs', _('Czech'))]
("en", _("English")),
("cs", _("Czech"))
]
LOCALE_PATHS = [ LOCALE_PATHS = [BASE_DIR / 'locale']
BASE_DIR / "locale"
]
TIME_ZONE = "UTC" TIME_ZONE = 'UTC'
USE_I18N = True USE_I18N = True
@ -123,22 +118,22 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/ # https://docs.djangoproject.com/en/5.0/howto/static-files/
STATIC_URL = "static/" STATIC_URL = 'static/'
# Default primary key field type # Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Auth configuration # Auth configuration
LOGIN_BASE_URL = "/accounts" LOGIN_BASE_URL = '/accounts'
LOGIN_URL = f"{LOGIN_BASE_URL}/login" LOGIN_URL = f'{LOGIN_BASE_URL}/login'
LOGOUT_REDIRECT_URL = f"/" LOGOUT_REDIRECT_URL = '/'
LOGIN_REDIRECT_URL = f"{LOGIN_BASE_URL}/me" LOGIN_REDIRECT_URL = f'{LOGIN_BASE_URL}/me'
AUTH_USER_MODEL = "accounts.User" AUTH_USER_MODEL = 'accounts.User'
# Crispy config # Crispy config
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5'
CRISPY_TEMPLATE_PACK = "bootstrap5" CRISPY_TEMPLATE_PACK = 'bootstrap5'

View file

@ -1,18 +1,20 @@
from django.contrib import admin
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render
from django.urls import path, include
from django.conf.urls.i18n import i18n_patterns from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin
from django.http import HttpRequest
from django.http import HttpResponse
from django.shortcuts import render
from django.urls import include
from django.urls import path
def landing_page(req: HttpRequest) -> HttpResponse: def landing_page(req: HttpRequest) -> HttpResponse:
return render(req, "facturio/index.html") return render(req, 'facturio/index.html')
urlpatterns = i18n_patterns( urlpatterns = i18n_patterns(
path("", landing_page, name="main-page"), path('', landing_page, name='main-page'),
path("accounts/", include("accounts.urls")), path('accounts/', include('accounts.urls')),
path("subjects/", include("subjects.urls")), path('subjects/', include('subjects.urls')),
path("admin/", admin.site.urls), path('admin/', admin.site.urls),
prefix_default_language=False prefix_default_language=False,
) )

View file

@ -6,11 +6,10 @@ It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see For more information on this file, see
https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
""" """
import os import os
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "facturio.settings") os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'facturio.settings')
application = get_wsgi_application() application = get_wsgi_application()

View file

@ -6,17 +6,17 @@ import sys
def main(): def main():
"""Run administrative tasks.""" """Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "facturio.settings") os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'facturio.settings')
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:
raise ImportError( raise ImportError(
"Couldn't import Django. Are you sure it's installed and " "Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you " 'available on your PYTHONPATH environment variable? Did you '
"forget to activate a virtual environment?" 'forget to activate a virtual environment?'
) from exc ) from exc
execute_from_command_line(sys.argv) execute_from_command_line(sys.argv)
if __name__ == "__main__": if __name__ == '__main__':
main() main()

View file

@ -17,3 +17,6 @@ ares-util = "^0.3.0"
[build-system] [build-system]
requires = ["poetry-core"] requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.ruff.format]
quote-style = "single"

View file

@ -5,5 +5,4 @@ from . import models
@admin.register(models.Subject) @admin.register(models.Subject)
class SubjectAdmin(admin.ModelAdmin): class SubjectAdmin(admin.ModelAdmin):
list_display = ["id", "name", "city", "city_part", "zip_code"] list_display = ['id', 'name', 'city', 'city_part', 'zip_code']

View file

@ -2,5 +2,5 @@ from django.apps import AppConfig
class SubjectsConfig(AppConfig): class SubjectsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField" default_auto_field = 'django.db.models.BigAutoField'
name = "subjects" name = 'subjects'

View file

@ -1,6 +1,7 @@
from ares_util.ares import validate_czech_company_id from ares_util.ares import validate_czech_company_id
from ares_util.exceptions import InvalidCompanyIDError from ares_util.exceptions import InvalidCompanyIDError
from crispy_forms import helper, layout from crispy_forms import helper
from crispy_forms import layout
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.forms import fields from django.forms import fields
@ -11,36 +12,32 @@ from . import models
class CreateSubjectForm(forms.Form): class CreateSubjectForm(forms.Form):
error_messages = { error_messages = {
"invalid_cin": _("Your provided CIN is not correct."), 'invalid_cin': _('Your provided CIN is not correct.'),
"already_existing": _("Subject with provided CIN already exists.") 'already_existing': _('Subject with provided CIN already exists.'),
} }
cin = fields.CharField( cin = fields.CharField(
label=_("CIN"), label=_('CIN'),
max_length=8, max_length=8,
help_text=_("Enter the subject CIN, rest will be generated automatically") help_text=_('Enter the subject CIN, rest will be generated automatically'),
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.helper = helper.FormHelper() self.helper = helper.FormHelper()
self.helper.form_action = "subjects:create" self.helper.form_action = 'subjects:create'
self.helper.form_method = "post" self.helper.form_method = 'post'
self.helper.add_input(layout.Submit('submit', _('Add'))) self.helper.add_input(layout.Submit('submit', _('Add')))
def clean_cin(self): def clean_cin(self):
cin = self.cleaned_data.get("cin") cin = self.cleaned_data.get('cin')
try: try:
validate_czech_company_id(cin) validate_czech_company_id(cin)
except InvalidCompanyIDError: except InvalidCompanyIDError:
raise ValidationError( raise ValidationError(self.error_messages['invalid_cin'])
self.error_messages["invalid_cin"]
)
try: try:
models.Subject.objects.get(id=cin) models.Subject.objects.get(id=cin)
except models.Subject.DoesNotExist: except models.Subject.DoesNotExist:
return cin return cin
raise ValidationError( raise ValidationError(self.error_messages['already_existing'])
self.error_messages["already_existing"]
)

View file

@ -1,6 +1,6 @@
# Generated by Django 5.0 on 2023-12-20 22:36 # Generated by Django 5.0 on 2023-12-20 22:36
from django.db import migrations
from django.db import migrations, models from django.db import models
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -10,31 +10,31 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name="Subject", name='Subject',
fields=[ fields=[
( (
"id", 'id',
models.CharField( models.CharField(
max_length=8, max_length=8,
primary_key=True, primary_key=True,
serialize=False, serialize=False,
verbose_name="cin", verbose_name='cin',
), ),
), ),
("name", models.CharField(max_length=128, verbose_name="name")), ('name', models.CharField(max_length=128, verbose_name='name')),
( (
"vat_id", 'vat_id',
models.CharField( models.CharField(
blank=True, max_length=12, null=True, verbose_name="vat_id" blank=True, max_length=12, null=True, verbose_name='vat_id'
), ),
), ),
("street", models.CharField(max_length=64, verbose_name="street")), ('street', models.CharField(max_length=64, verbose_name='street')),
("zip_code", models.CharField(max_length=6, verbose_name="zip_code")), ('zip_code', models.CharField(max_length=6, verbose_name='zip_code')),
("city", models.CharField(max_length=64, verbose_name="city")), ('city', models.CharField(max_length=64, verbose_name='city')),
( (
"city_part", 'city_part',
models.CharField( models.CharField(
blank=True, max_length=64, null=True, verbose_name="city_part" blank=True, max_length=64, null=True, verbose_name='city_part'
), ),
), ),
], ],

View file

@ -1,53 +1,53 @@
# Generated by Django 5.0 on 2023-12-20 22:47 # Generated by Django 5.0 on 2023-12-20 22:47
from django.db import migrations
from django.db import migrations, models from django.db import models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("subjects", "0001_initial"), ('subjects', '0001_initial'),
] ]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name="subject", model_name='subject',
name="city", name='city',
field=models.CharField(max_length=64, verbose_name="City"), field=models.CharField(max_length=64, verbose_name='City'),
), ),
migrations.AlterField( migrations.AlterField(
model_name="subject", model_name='subject',
name="city_part", name='city_part',
field=models.CharField( field=models.CharField(
blank=True, max_length=64, null=True, verbose_name="City part" blank=True, max_length=64, null=True, verbose_name='City part'
), ),
), ),
migrations.AlterField( migrations.AlterField(
model_name="subject", model_name='subject',
name="id", name='id',
field=models.CharField( field=models.CharField(
max_length=8, primary_key=True, serialize=False, verbose_name="CIN" max_length=8, primary_key=True, serialize=False, verbose_name='CIN'
), ),
), ),
migrations.AlterField( migrations.AlterField(
model_name="subject", model_name='subject',
name="name", name='name',
field=models.CharField(max_length=128, verbose_name="Name"), field=models.CharField(max_length=128, verbose_name='Name'),
), ),
migrations.AlterField( migrations.AlterField(
model_name="subject", model_name='subject',
name="street", name='street',
field=models.CharField(max_length=64, verbose_name="Street"), field=models.CharField(max_length=64, verbose_name='Street'),
), ),
migrations.AlterField( migrations.AlterField(
model_name="subject", model_name='subject',
name="vat_id", name='vat_id',
field=models.CharField( field=models.CharField(
blank=True, max_length=12, null=True, verbose_name="VAT ID" blank=True, max_length=12, null=True, verbose_name='VAT ID'
), ),
), ),
migrations.AlterField( migrations.AlterField(
model_name="subject", model_name='subject',
name="zip_code", name='zip_code',
field=models.CharField(max_length=6, verbose_name="Zip Code"), field=models.CharField(max_length=6, verbose_name='Zip Code'),
), ),
] ]

View file

@ -1,16 +1,15 @@
# Generated by Django 5.0.1 on 2024-01-29 21:06 # Generated by Django 5.0.1 on 2024-01-29 21:06
from django.db import migrations from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("subjects", "0002_alter_subject_city_alter_subject_city_part_and_more"), ('subjects', '0002_alter_subject_city_alter_subject_city_part_and_more'),
] ]
operations = [ operations = [
migrations.AlterModelOptions( migrations.AlterModelOptions(
name="subject", name='subject',
options={"verbose_name": "Subject", "verbose_name_plural": "Subjects"}, options={'verbose_name': 'Subject', 'verbose_name_plural': 'Subjects'},
), ),
] ]

View file

@ -1,55 +1,37 @@
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
class Subject(models.Model): class Subject(models.Model):
class Meta: class Meta:
verbose_name = _("Subject") verbose_name = _('Subject')
verbose_name_plural = _("Subjects") verbose_name_plural = _('Subjects')
id = models.CharField( id = models.CharField(_('CIN'), max_length=8, primary_key=True)
_("CIN"),
max_length=8,
primary_key=True
)
name = models.CharField( name = models.CharField(_('Name'), max_length=128)
_("Name"),
max_length=128
)
vat_id = models.CharField( vat_id = models.CharField(_('VAT ID'), max_length=12, null=True, blank=True)
_("VAT ID"),
max_length=12,
null=True,
blank=True
)
street = models.CharField( street = models.CharField(
_("Street"), _('Street'),
max_length=64, max_length=64,
) )
zip_code = models.CharField( zip_code = models.CharField(
_("Zip Code"), _('Zip Code'),
max_length=6, max_length=6,
) )
city = models.CharField( city = models.CharField(
_("City"), _('City'),
max_length=64, max_length=64,
) )
city_part = models.CharField( city_part = models.CharField(_('City part'), max_length=64, null=True, blank=True)
_("City part"),
max_length=64,
null=True,
blank=True
)
def __str__(self): def __str__(self):
return self.name return self.name
def get_linked_users(self) -> list[int]: def get_linked_users(self) -> list[int]:
return list(self.user_set.values_list("id", flat=True)) return list(self.user_set.values_list('id', flat=True))

View file

@ -7,4 +7,4 @@
{% block content %} {% block content %}
{% crispy form form.helper %} {% crispy form form.helper %}
{% endblock %} {% endblock %}

View file

@ -1,3 +1 @@
from django.test import TestCase
# Create your tests here. # Create your tests here.

View file

@ -2,11 +2,11 @@ from django.urls import path
from . import views from . import views
app_name = "subjects" app_name = 'subjects'
urlpatterns = [ urlpatterns = [
path("", views.main_page, name="list"), path('', views.main_page, name='list'),
path("new/", views.create_subject, name="create"), path('new/', views.create_subject, name='create'),
path("link/<int:subject_id>", views.link_subject, name="link"), path('link/<int:subject_id>', views.link_subject, name='link'),
path("unlink/<int:subject_id>", views.unlink_subject, name="unlink") path('unlink/<int:subject_id>', views.unlink_subject, name='unlink'),
] ]

View file

@ -1,51 +1,54 @@
from ares_util.ares import call_ares from ares_util.ares import call_ares
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest
from django.shortcuts import render, redirect from django.http import HttpResponse
from django.shortcuts import redirect
from django.shortcuts import render
from django.urls import reverse from django.urls import reverse
from . import models, forms from . import forms
from . import models
def build_address(street: str, zip_code: int | str, city: str, city_part: str) -> str: def build_address(street: str, zip_code: int | str, city: str, city_part: str) -> str:
return f"{street}, {zip_code}, {city} - {city_part}" return f'{street}, {zip_code}, {city} - {city_part}'
@login_required @login_required
def main_page(req: HttpRequest) -> HttpResponse: def main_page(req: HttpRequest) -> HttpResponse:
subjects = models.Subject.objects.all() subjects = models.Subject.objects.all()
return render(req, "subjects/index.html", dict(subjects=subjects)) return render(req, 'subjects/index.html', dict(subjects=subjects))
@login_required @login_required
def create_subject(req: HttpRequest) -> HttpResponse: def create_subject(req: HttpRequest) -> HttpResponse:
if req.method == "POST": if req.method == 'POST':
form = forms.CreateSubjectForm(data=req.POST) form = forms.CreateSubjectForm(data=req.POST)
if not form.is_valid(): if not form.is_valid():
return render(req, "subjects/create.html", dict(form=form)) return render(req, 'subjects/create.html', dict(form=form))
ares_data = call_ares(form.cleaned_data.get("cin")) ares_data = call_ares(form.cleaned_data.get('cin'))
ares_address_data = ares_data["address"] ares_address_data = ares_data['address']
ares_legal_data = ares_data["legal"] ares_legal_data = ares_data['legal']
models.Subject.objects.create( models.Subject.objects.create(
id=ares_legal_data["company_id"], id=ares_legal_data['company_id'],
vat_id=ares_legal_data["company_vat_id"], vat_id=ares_legal_data['company_vat_id'],
name=ares_legal_data["company_name"], name=ares_legal_data['company_name'],
street=ares_address_data["street"], street=ares_address_data['street'],
zip_code=ares_address_data["zip_code"], zip_code=ares_address_data['zip_code'],
city=ares_address_data["city"], city=ares_address_data['city'],
city_part=ares_address_data["city_part"], city_part=ares_address_data['city_part'],
) )
return redirect(reverse("subjects:list")) return redirect(reverse('subjects:list'))
elif req.method == "GET": elif req.method == 'GET':
form = forms.CreateSubjectForm() form = forms.CreateSubjectForm()
return render(req, "subjects/create.html", dict(form=form)) return render(req, 'subjects/create.html', dict(form=form))
return HttpResponse(status=405) return HttpResponse(status=405)
@ -54,11 +57,11 @@ def create_subject(req: HttpRequest) -> HttpResponse:
def link_subject(request: HttpRequest, subject_id: int) -> HttpResponse: def link_subject(request: HttpRequest, subject_id: int) -> HttpResponse:
subject = models.Subject.objects.get(pk=subject_id) subject = models.Subject.objects.get(pk=subject_id)
subject.user_set.add(request.user) subject.user_set.add(request.user)
return redirect(reverse("subjects:list")) return redirect(reverse('subjects:list'))
@login_required @login_required
def unlink_subject(request: HttpRequest, subject_id: int) -> HttpResponse: def unlink_subject(request: HttpRequest, subject_id: int) -> HttpResponse:
subject = models.Subject.objects.get(pk=subject_id) subject = models.Subject.objects.get(pk=subject_id)
subject.user_set.remove(request.user) subject.user_set.remove(request.user)
return redirect(reverse("subjects:list")) return redirect(reverse('subjects:list'))

View file

@ -55,4 +55,4 @@
</div> </div>
</body> </body>
</html> </html>

View file

@ -1,4 +1,4 @@
{% extends "facturio/base.html" %} {% extends "facturio/base.html" %}
{% load i18n %} {% load i18n %}
{% block title %}{% trans "App" %}{% endblock %} {% block title %}{% trans "App" %}{% endblock %}

View file

@ -15,4 +15,4 @@ echo "Compiling messages!"
./manage.py compilemessages >/dev/null ./manage.py compilemessages >/dev/null
echo "Migrating" echo "Migrating"
./manage.py migrate >/dev/null ./manage.py migrate >/dev/null