dajanga2 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dajanga2-0.1.0/Dajanga2/__init__.py +2 -0
- dajanga2-0.1.0/Dajanga2/cli.py +46 -0
- dajanga2-0.1.0/Dajanga2/template/ggggg/manage.py +3 -0
- dajanga2-0.1.0/MANIFEST.in +3 -0
- dajanga2-0.1.0/PKG-INFO +34 -0
- dajanga2-0.1.0/README.md +19 -0
- dajanga2-0.1.0/dajanga2.egg-info/PKG-INFO +34 -0
- dajanga2-0.1.0/dajanga2.egg-info/SOURCES.txt +31 -0
- dajanga2-0.1.0/dajanga2.egg-info/dependency_links.txt +1 -0
- dajanga2-0.1.0/dajanga2.egg-info/entry_points.txt +2 -0
- dajanga2-0.1.0/dajanga2.egg-info/top_level.txt +1 -0
- dajanga2-0.1.0/korochki_starter/template/korochki/asgi.py +6 -0
- dajanga2-0.1.0/korochki_starter/template/korochki/settings.py +64 -0
- dajanga2-0.1.0/korochki_starter/template/korochki/urls.py +7 -0
- dajanga2-0.1.0/korochki_starter/template/korochki/wsgi.py +6 -0
- dajanga2-0.1.0/korochki_starter/template/main/admin.py +13 -0
- dajanga2-0.1.0/korochki_starter/template/main/apps.py +6 -0
- dajanga2-0.1.0/korochki_starter/template/main/forms.py +65 -0
- dajanga2-0.1.0/korochki_starter/template/main/models.py +39 -0
- dajanga2-0.1.0/korochki_starter/template/main/static/main/style.css +9 -0
- dajanga2-0.1.0/korochki_starter/template/main/templates/main/admin_login.html +11 -0
- dajanga2-0.1.0/korochki_starter/template/main/templates/main/admin_panel.html +36 -0
- dajanga2-0.1.0/korochki_starter/template/main/templates/main/applications.html +17 -0
- dajanga2-0.1.0/korochki_starter/template/main/templates/main/base.html +32 -0
- dajanga2-0.1.0/korochki_starter/template/main/templates/main/create_application.html +10 -0
- dajanga2-0.1.0/korochki_starter/template/main/templates/main/index.html +6 -0
- dajanga2-0.1.0/korochki_starter/template/main/templates/main/login.html +13 -0
- dajanga2-0.1.0/korochki_starter/template/main/templates/main/register.html +10 -0
- dajanga2-0.1.0/korochki_starter/template/main/urls.py +14 -0
- dajanga2-0.1.0/korochki_starter/template/main/views.py +101 -0
- dajanga2-0.1.0/korochki_starter/template/manage.py +21 -0
- dajanga2-0.1.0/pyproject.toml +30 -0
- dajanga2-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""CLI that copies the bundled `ggggg` folder to the current working directory.
|
|
2
|
+
|
|
3
|
+
Usage: dajanga-start
|
|
4
|
+
|
|
5
|
+
The command will copy the packaged `template/ggggg` directory into the
|
|
6
|
+
current working directory. If a folder with the same name already exists,
|
|
7
|
+
the command will abort.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import shutil
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
TEMPLATE_NAME = "ggggg"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main(argv: list[str] | None = None) -> int:
|
|
20
|
+
"""Entry point for the CLI. Copies template into cwd/TEMPLATE_NAME."""
|
|
21
|
+
argv = list(argv) if argv is not None else []
|
|
22
|
+
|
|
23
|
+
dest = Path.cwd() / TEMPLATE_NAME
|
|
24
|
+
if dest.exists():
|
|
25
|
+
print(f"Error: destination '{dest}' already exists. Aborting.")
|
|
26
|
+
return 2
|
|
27
|
+
|
|
28
|
+
# locate package template directory
|
|
29
|
+
this_dir = Path(__file__).resolve().parent
|
|
30
|
+
template_dir = this_dir / "template" / TEMPLATE_NAME
|
|
31
|
+
if not template_dir.exists():
|
|
32
|
+
print("Error: packaged template not found inside the package.")
|
|
33
|
+
return 3
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
print(f"Copying template '{TEMPLATE_NAME}' to: {dest}")
|
|
37
|
+
shutil.copytree(template_dir, dest)
|
|
38
|
+
print("Copy finished successfully.")
|
|
39
|
+
return 0
|
|
40
|
+
except Exception as exc:
|
|
41
|
+
print(f"Error while copying template: {exc}")
|
|
42
|
+
return 1
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
raise SystemExit(main())
|
dajanga2-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: dajanga2
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI tool to copy bundled ggggg folder into current directory
|
|
5
|
+
Author: Auto Generated
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: django,template,starter,cli
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
korochki-starter
|
|
17
|
+
=================
|
|
18
|
+
|
|
19
|
+
Утилита CLI для разворачивания шаблона Django-проекта из пакета.
|
|
20
|
+
|
|
21
|
+
Сборка и установка локально:
|
|
22
|
+
|
|
23
|
+
```powershell
|
|
24
|
+
python -m build
|
|
25
|
+
pip install dist\korochki_starter-0.1.0-py3-none-any.whl
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
После установки доступна команда:
|
|
29
|
+
|
|
30
|
+
```powershell
|
|
31
|
+
korochki-start myproject
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Это создаст папку `myproject/` с шаблоном и выполнит замену всех вхождений `korochki` на `myproject`.
|
dajanga2-0.1.0/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
korochki-starter
|
|
2
|
+
=================
|
|
3
|
+
|
|
4
|
+
Утилита CLI для разворачивания шаблона Django-проекта из пакета.
|
|
5
|
+
|
|
6
|
+
Сборка и установка локально:
|
|
7
|
+
|
|
8
|
+
```powershell
|
|
9
|
+
python -m build
|
|
10
|
+
pip install dist\korochki_starter-0.1.0-py3-none-any.whl
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
После установки доступна команда:
|
|
14
|
+
|
|
15
|
+
```powershell
|
|
16
|
+
korochki-start myproject
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Это создаст папку `myproject/` с шаблоном и выполнит замену всех вхождений `korochki` на `myproject`.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: dajanga2
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI tool to copy bundled ggggg folder into current directory
|
|
5
|
+
Author: Auto Generated
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: django,template,starter,cli
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
korochki-starter
|
|
17
|
+
=================
|
|
18
|
+
|
|
19
|
+
Утилита CLI для разворачивания шаблона Django-проекта из пакета.
|
|
20
|
+
|
|
21
|
+
Сборка и установка локально:
|
|
22
|
+
|
|
23
|
+
```powershell
|
|
24
|
+
python -m build
|
|
25
|
+
pip install dist\korochki_starter-0.1.0-py3-none-any.whl
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
После установки доступна команда:
|
|
29
|
+
|
|
30
|
+
```powershell
|
|
31
|
+
korochki-start myproject
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Это создаст папку `myproject/` с шаблоном и выполнит замену всех вхождений `korochki` на `myproject`.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
MANIFEST.in
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
Dajanga2/__init__.py
|
|
5
|
+
Dajanga2/cli.py
|
|
6
|
+
Dajanga2/template/ggggg/manage.py
|
|
7
|
+
dajanga2.egg-info/PKG-INFO
|
|
8
|
+
dajanga2.egg-info/SOURCES.txt
|
|
9
|
+
dajanga2.egg-info/dependency_links.txt
|
|
10
|
+
dajanga2.egg-info/entry_points.txt
|
|
11
|
+
dajanga2.egg-info/top_level.txt
|
|
12
|
+
korochki_starter/template/manage.py
|
|
13
|
+
korochki_starter/template/korochki/asgi.py
|
|
14
|
+
korochki_starter/template/korochki/settings.py
|
|
15
|
+
korochki_starter/template/korochki/urls.py
|
|
16
|
+
korochki_starter/template/korochki/wsgi.py
|
|
17
|
+
korochki_starter/template/main/admin.py
|
|
18
|
+
korochki_starter/template/main/apps.py
|
|
19
|
+
korochki_starter/template/main/forms.py
|
|
20
|
+
korochki_starter/template/main/models.py
|
|
21
|
+
korochki_starter/template/main/urls.py
|
|
22
|
+
korochki_starter/template/main/views.py
|
|
23
|
+
korochki_starter/template/main/static/main/style.css
|
|
24
|
+
korochki_starter/template/main/templates/main/admin_login.html
|
|
25
|
+
korochki_starter/template/main/templates/main/admin_panel.html
|
|
26
|
+
korochki_starter/template/main/templates/main/applications.html
|
|
27
|
+
korochki_starter/template/main/templates/main/base.html
|
|
28
|
+
korochki_starter/template/main/templates/main/create_application.html
|
|
29
|
+
korochki_starter/template/main/templates/main/index.html
|
|
30
|
+
korochki_starter/template/main/templates/main/login.html
|
|
31
|
+
korochki_starter/template/main/templates/main/register.html
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Dajanga2
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
# Basic settings copied from example template
|
|
4
|
+
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
5
|
+
|
|
6
|
+
SECRET_KEY = "replace-me"
|
|
7
|
+
DEBUG = True
|
|
8
|
+
ALLOWED_HOSTS = []
|
|
9
|
+
|
|
10
|
+
INSTALLED_APPS = [
|
|
11
|
+
"django.contrib.admin",
|
|
12
|
+
"django.contrib.auth",
|
|
13
|
+
"django.contrib.contenttypes",
|
|
14
|
+
"django.contrib.sessions",
|
|
15
|
+
"django.contrib.messages",
|
|
16
|
+
"django.contrib.staticfiles",
|
|
17
|
+
"main",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
MIDDLEWARE = [
|
|
21
|
+
"django.middleware.security.SecurityMiddleware",
|
|
22
|
+
"django.contrib.sessions.middleware.SessionMiddleware",
|
|
23
|
+
"django.middleware.common.CommonMiddleware",
|
|
24
|
+
"django.middleware.csrf.CsrfViewMiddleware",
|
|
25
|
+
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
|
26
|
+
"django.contrib.messages.middleware.MessageMiddleware",
|
|
27
|
+
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
ROOT_URLCONF = "korochki.urls"
|
|
31
|
+
|
|
32
|
+
TEMPLATES = [
|
|
33
|
+
{
|
|
34
|
+
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
|
35
|
+
"DIRS": [],
|
|
36
|
+
"APP_DIRS": True,
|
|
37
|
+
"OPTIONS": {
|
|
38
|
+
"context_processors": [
|
|
39
|
+
"django.template.context_processors.debug",
|
|
40
|
+
"django.template.context_processors.request",
|
|
41
|
+
"django.contrib.auth.context_processors.auth",
|
|
42
|
+
"django.contrib.messages.context_processors.messages",
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
WSGI_APPLICATION = "korochki.wsgi.application"
|
|
49
|
+
|
|
50
|
+
DATABASES = {
|
|
51
|
+
"default": {
|
|
52
|
+
"ENGINE": "django.db.backends.sqlite3",
|
|
53
|
+
"NAME": BASE_DIR / "db.sqlite3",
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
LANGUAGE_CODE = "ru-ru"
|
|
58
|
+
TIME_ZONE = "UTC"
|
|
59
|
+
USE_I18N = True
|
|
60
|
+
USE_TZ = True
|
|
61
|
+
|
|
62
|
+
STATIC_URL = "static/"
|
|
63
|
+
|
|
64
|
+
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
from .models import Profile, Application
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@admin.register(Profile)
|
|
6
|
+
class ProfileAdmin(admin.ModelAdmin):
|
|
7
|
+
list_display = ("user", "full_name", "phone")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@admin.register(Application)
|
|
11
|
+
class ApplicationAdmin(admin.ModelAdmin):
|
|
12
|
+
list_display = ("user", "course_name", "start_date", "payment_method", "status", "created_at")
|
|
13
|
+
list_filter = ("status", "payment_method")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from django import forms
|
|
2
|
+
from django.contrib.auth.models import User
|
|
3
|
+
from .models import Application
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RegistrationForm(forms.Form):
|
|
8
|
+
username = forms.CharField(label="Логин", max_length=150)
|
|
9
|
+
password1 = forms.CharField(label="Пароль", widget=forms.PasswordInput)
|
|
10
|
+
password2 = forms.CharField(label="Повтор пароля", widget=forms.PasswordInput)
|
|
11
|
+
full_name = forms.CharField(label="ФИО", max_length=200)
|
|
12
|
+
phone = forms.CharField(label="Телефон", max_length=20)
|
|
13
|
+
email = forms.EmailField(label="Email")
|
|
14
|
+
|
|
15
|
+
def clean_username(self):
|
|
16
|
+
username = self.cleaned_data["username"]
|
|
17
|
+
if not re.match(r"^[A-Za-z0-9]{6,}$", username):
|
|
18
|
+
raise forms.ValidationError("Логин должен содержать латиницу и цифры, не менее 6 символов")
|
|
19
|
+
if User.objects.filter(username=username).exists():
|
|
20
|
+
raise forms.ValidationError("Пользователь с таким логином уже существует")
|
|
21
|
+
return username
|
|
22
|
+
|
|
23
|
+
def clean_password1(self):
|
|
24
|
+
p = self.cleaned_data["password1"]
|
|
25
|
+
if len(p) < 8:
|
|
26
|
+
raise forms.ValidationError("Пароль минимум 8 символов")
|
|
27
|
+
return p
|
|
28
|
+
|
|
29
|
+
def clean(self):
|
|
30
|
+
cleaned = super().clean()
|
|
31
|
+
p1 = cleaned.get("password1")
|
|
32
|
+
p2 = cleaned.get("password2")
|
|
33
|
+
if p1 and p2 and p1 != p2:
|
|
34
|
+
raise forms.ValidationError("Пароли не совпадают")
|
|
35
|
+
full = cleaned.get("full_name")
|
|
36
|
+
if full and not re.match(r"^[А-Яа-яЁё\s]+$", full):
|
|
37
|
+
self.add_error("full_name", "ФИО должно содержать только кириллицу и пробелы")
|
|
38
|
+
phone = cleaned.get("phone")
|
|
39
|
+
if phone and not re.match(r"^8\(\d{3}\)\d{3}-\d{2}-\d{2}$", phone):
|
|
40
|
+
self.add_error("phone", "Телефон в формате 8(XXX)XXX-XX-XX")
|
|
41
|
+
|
|
42
|
+
def save(self):
|
|
43
|
+
data = self.cleaned_data
|
|
44
|
+
user = User.objects.create_user(username=data["username"], email=data["email"])
|
|
45
|
+
user.set_password(data["password1"])
|
|
46
|
+
user.save()
|
|
47
|
+
# create profile
|
|
48
|
+
from .models import Profile
|
|
49
|
+
|
|
50
|
+
Profile.objects.create(user=user, full_name=data["full_name"], phone=data["phone"])
|
|
51
|
+
return user
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class LoginForm(forms.Form):
|
|
55
|
+
username = forms.CharField(label="Логин")
|
|
56
|
+
password = forms.CharField(label="Пароль", widget=forms.PasswordInput)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ApplicationForm(forms.ModelForm):
|
|
60
|
+
class Meta:
|
|
61
|
+
model = Application
|
|
62
|
+
fields = ["course_name", "start_date", "payment_method"]
|
|
63
|
+
widgets = {
|
|
64
|
+
"start_date": forms.DateInput(attrs={"type": "date"}),
|
|
65
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from django.contrib.auth.models import User
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Profile(models.Model):
|
|
6
|
+
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
|
7
|
+
full_name = models.CharField(max_length=200)
|
|
8
|
+
phone = models.CharField(max_length=20)
|
|
9
|
+
|
|
10
|
+
def __str__(self):
|
|
11
|
+
return f"{self.user.username} - {self.full_name}"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Application(models.Model):
|
|
15
|
+
PAYMENT_CHOICES = [
|
|
16
|
+
("cash", "Наличными"),
|
|
17
|
+
("phone", "Перевод по номеру телефона"),
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
STATUS_NEW = "NEW"
|
|
21
|
+
STATUS_IN_PROGRESS = "IN_PROGRESS"
|
|
22
|
+
STATUS_COMPLETED = "COMPLETED"
|
|
23
|
+
|
|
24
|
+
STATUS_CHOICES = [
|
|
25
|
+
(STATUS_NEW, "Новая"),
|
|
26
|
+
(STATUS_IN_PROGRESS, "Идет обучение"),
|
|
27
|
+
(STATUS_COMPLETED, "Обучение завершено"),
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="applications")
|
|
31
|
+
course_name = models.CharField(max_length=200)
|
|
32
|
+
start_date = models.DateField()
|
|
33
|
+
payment_method = models.CharField(max_length=20, choices=PAYMENT_CHOICES)
|
|
34
|
+
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_NEW)
|
|
35
|
+
review = models.TextField(blank=True, null=True)
|
|
36
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
37
|
+
|
|
38
|
+
def __str__(self):
|
|
39
|
+
return f"{self.course_name} ({self.user.username}) - {self.status}"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
body{font-family: Arial, sans-serif; margin:20px; color:#222}
|
|
2
|
+
header{border-bottom:1px solid #ddd; padding-bottom:10px; margin-bottom:20px}
|
|
3
|
+
nav a{margin-right:10px}
|
|
4
|
+
form{max-width:480px}
|
|
5
|
+
input,select,textarea{display:block;margin:6px 0;padding:6px;width:100%;box-sizing:border-box}
|
|
6
|
+
button{padding:8px 12px}
|
|
7
|
+
table{border-collapse:collapse;width:100%}
|
|
8
|
+
table th,table td{border:1px solid #ccc;padding:6px}
|
|
9
|
+
.error{color:#900}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{% extends 'main/base.html' %}
|
|
2
|
+
{% block content %}
|
|
3
|
+
<h2>Вход в панель администратора</h2>
|
|
4
|
+
{% if error %}<div class="error">{{ error }}</div>{% endif %}
|
|
5
|
+
<form method="post">
|
|
6
|
+
{% csrf_token %}
|
|
7
|
+
<p><label>Логин: <input name="username" value="Admin"></label></p>
|
|
8
|
+
<p><label>Пароль: <input type="password" name="password"></label></p>
|
|
9
|
+
<button type="submit">Войти</button>
|
|
10
|
+
</form>
|
|
11
|
+
{% endblock %}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{% extends 'main/base.html' %}
|
|
2
|
+
{% block content %}
|
|
3
|
+
<h2>Панель администратора</h2>
|
|
4
|
+
<p><a href="{% url 'admin_logout' %}">Выйти из панели</a></p>
|
|
5
|
+
{% if applications %}
|
|
6
|
+
<table>
|
|
7
|
+
<thead><tr><th>ID</th><th>Пользователь</th><th>Курс</th><th>Дата</th><th>Оплата</th><th>Статус</th><th>Действия</th></tr></thead>
|
|
8
|
+
<tbody>
|
|
9
|
+
{% for a in applications %}
|
|
10
|
+
<tr>
|
|
11
|
+
<td>{{ a.id }}</td>
|
|
12
|
+
<td>{{ a.user.username }}</td>
|
|
13
|
+
<td>{{ a.course_name }}</td>
|
|
14
|
+
<td>{{ a.start_date }}</td>
|
|
15
|
+
<td>{{ a.get_payment_method_display }}</td>
|
|
16
|
+
<td>{{ a.get_status_display }}</td>
|
|
17
|
+
<td>
|
|
18
|
+
<form method="post" style="display:inline">
|
|
19
|
+
{% csrf_token %}
|
|
20
|
+
<input type="hidden" name="app_id" value="{{ a.id }}">
|
|
21
|
+
<select name="status">
|
|
22
|
+
<option value="NEW" {% if a.status == 'NEW' %}selected{% endif %}>Новая</option>
|
|
23
|
+
<option value="IN_PROGRESS" {% if a.status == 'IN_PROGRESS' %}selected{% endif %}>Идет обучение</option>
|
|
24
|
+
<option value="COMPLETED" {% if a.status == 'COMPLETED' %}selected{% endif %}>Обучение завершено</option>
|
|
25
|
+
</select>
|
|
26
|
+
<button type="submit">Сменить</button>
|
|
27
|
+
</form>
|
|
28
|
+
</td>
|
|
29
|
+
</tr>
|
|
30
|
+
{% endfor %}
|
|
31
|
+
</tbody>
|
|
32
|
+
</table>
|
|
33
|
+
{% else %}
|
|
34
|
+
<p>Заявок нет.</p>
|
|
35
|
+
{% endif %}
|
|
36
|
+
{% endblock %}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{% extends 'main/base.html' %}
|
|
2
|
+
{% block content %}
|
|
3
|
+
<h2>Мои заявки</h2>
|
|
4
|
+
{% if applications %}
|
|
5
|
+
<ul>
|
|
6
|
+
{% for a in applications %}
|
|
7
|
+
<li>
|
|
8
|
+
<strong>{{ a.course_name }}</strong> — {{ a.get_status_display }} — начало {{ a.start_date }}
|
|
9
|
+
<div>Оплата: {{ a.get_payment_method_display }}</div>
|
|
10
|
+
<div>Отзыв: {{ a.review|default:"(пока нет)" }}</div>
|
|
11
|
+
</li>
|
|
12
|
+
{% endfor %}
|
|
13
|
+
</ul>
|
|
14
|
+
{% else %}
|
|
15
|
+
<p>Заявок нет. <a href="{% url 'create_application' %}">Создать заявку</a></p>
|
|
16
|
+
{% endif %}
|
|
17
|
+
{% endblock %}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="ru">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Корочки.есть</title>
|
|
7
|
+
<link rel="stylesheet" href="/static/main/style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<header>
|
|
11
|
+
<h1>Портал «Корочки.есть»</h1>
|
|
12
|
+
<nav>
|
|
13
|
+
<a href="/">Главная</a>
|
|
14
|
+
{% if user.is_authenticated %}
|
|
15
|
+
<a href="{% url 'applications' %}">Мои заявки</a>
|
|
16
|
+
<a href="{% url 'create_application' %}">Новая заявка</a>
|
|
17
|
+
<a href="{% url 'logout' %}">Выход</a>
|
|
18
|
+
{% else %}
|
|
19
|
+
<a href="{% url 'login' %}">Вход</a>
|
|
20
|
+
<a href="{% url 'register' %}">Регистрация</a>
|
|
21
|
+
{% endif %}
|
|
22
|
+
<a href="{% url 'admin_login' %}">Панель администратора</a>
|
|
23
|
+
</nav>
|
|
24
|
+
</header>
|
|
25
|
+
<main>
|
|
26
|
+
{% block content %}{% endblock %}
|
|
27
|
+
</main>
|
|
28
|
+
<footer>
|
|
29
|
+
<p>Учебный проект — минимальный шаблон информационной системы</p>
|
|
30
|
+
</footer>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{% extends 'main/base.html' %}
|
|
2
|
+
{% block content %}
|
|
3
|
+
<h2>Новая заявка</h2>
|
|
4
|
+
<form method="post">
|
|
5
|
+
{% csrf_token %}
|
|
6
|
+
{{ form.as_p }}
|
|
7
|
+
<button type="submit">Отправить</button>
|
|
8
|
+
<p>Способ оплаты: Наличными или Перевод по номеру телефона</p>
|
|
9
|
+
</form>
|
|
10
|
+
{% endblock %}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{% extends 'main/base.html' %}
|
|
2
|
+
{% block content %}
|
|
3
|
+
<h2>Авторизация</h2>
|
|
4
|
+
<form method="post">
|
|
5
|
+
{% csrf_token %}
|
|
6
|
+
{{ form.as_p }}
|
|
7
|
+
<button type="submit">Войти</button>
|
|
8
|
+
{% if form.non_field_errors %}
|
|
9
|
+
<div class="error">{{ form.non_field_errors }}</div>
|
|
10
|
+
{% endif %}
|
|
11
|
+
</form>
|
|
12
|
+
<p>Еще не зарегистрированы? <a href="{% url 'register' %}">Регистрация</a></p>
|
|
13
|
+
{% endblock %}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{% extends 'main/base.html' %}
|
|
2
|
+
{% block content %}
|
|
3
|
+
<h2>Регистрация</h2>
|
|
4
|
+
<form method="post">
|
|
5
|
+
{% csrf_token %}
|
|
6
|
+
{{ form.as_p }}
|
|
7
|
+
<button type="submit">Создать пользователя</button>
|
|
8
|
+
</form>
|
|
9
|
+
<p>Уже зарегистрированы? <a href="{% url 'login' %}">Вход</a></p>
|
|
10
|
+
{% endblock %}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from django.urls import path
|
|
2
|
+
from . import views
|
|
3
|
+
|
|
4
|
+
urlpatterns = [
|
|
5
|
+
path("", views.index, name="index"),
|
|
6
|
+
path("register/", views.register_view, name="register"),
|
|
7
|
+
path("login/", views.login_view, name="login"),
|
|
8
|
+
path("logout/", views.logout_view, name="logout"),
|
|
9
|
+
path("applications/", views.applications_view, name="applications"),
|
|
10
|
+
path("application/new/", views.create_application, name="create_application"),
|
|
11
|
+
path("admin-login/", views.admin_login, name="admin_login"),
|
|
12
|
+
path("admin-panel/", views.admin_panel, name="admin_panel"),
|
|
13
|
+
path("admin-logout/", views.admin_logout, name="admin_logout"),
|
|
14
|
+
]
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from django.shortcuts import render, redirect, get_object_or_404
|
|
2
|
+
from django.contrib.auth import authenticate, login, logout
|
|
3
|
+
from django.contrib.auth.decorators import login_required, user_passes_test
|
|
4
|
+
from .forms import RegistrationForm, LoginForm, ApplicationForm
|
|
5
|
+
from .models import Application
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def index(request):
|
|
9
|
+
return render(request, "main/index.html")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def register_view(request):
|
|
13
|
+
if request.method == "POST":
|
|
14
|
+
form = RegistrationForm(request.POST)
|
|
15
|
+
if form.is_valid():
|
|
16
|
+
form.save()
|
|
17
|
+
return redirect("login")
|
|
18
|
+
else:
|
|
19
|
+
form = RegistrationForm()
|
|
20
|
+
return render(request, "main/register.html", {"form": form})
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def login_view(request):
|
|
24
|
+
if request.method == "POST":
|
|
25
|
+
form = LoginForm(request.POST)
|
|
26
|
+
if form.is_valid():
|
|
27
|
+
user = authenticate(request, username=form.cleaned_data["username"], password=form.cleaned_data["password"])
|
|
28
|
+
if user:
|
|
29
|
+
login(request, user)
|
|
30
|
+
return redirect("applications")
|
|
31
|
+
else:
|
|
32
|
+
form.add_error(None, "Неверный логин или пароль")
|
|
33
|
+
else:
|
|
34
|
+
form = LoginForm()
|
|
35
|
+
return render(request, "main/login.html", {"form": form})
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def logout_view(request):
|
|
39
|
+
logout(request)
|
|
40
|
+
return redirect("index")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@login_required
|
|
44
|
+
def applications_view(request):
|
|
45
|
+
apps = request.user.applications.all()
|
|
46
|
+
return render(request, "main/applications.html", {"applications": apps})
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@login_required
|
|
50
|
+
def create_application(request):
|
|
51
|
+
if request.method == "POST":
|
|
52
|
+
form = ApplicationForm(request.POST)
|
|
53
|
+
if form.is_valid():
|
|
54
|
+
app = form.save(commit=False)
|
|
55
|
+
app.user = request.user
|
|
56
|
+
app.save()
|
|
57
|
+
return redirect("applications")
|
|
58
|
+
else:
|
|
59
|
+
form = ApplicationForm()
|
|
60
|
+
return render(request, "main/create_application.html", {"form": form})
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def is_admin(user):
|
|
64
|
+
return user.is_superuser or (user.username == "Admin")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@user_passes_test(is_admin)
|
|
68
|
+
def admin_panel(request):
|
|
69
|
+
apps = Application.objects.all().order_by("-created_at")
|
|
70
|
+
if request.method == "POST":
|
|
71
|
+
app_id = request.POST.get("app_id")
|
|
72
|
+
status = request.POST.get("status")
|
|
73
|
+
app = get_object_or_404(Application, id=app_id)
|
|
74
|
+
app.status = status
|
|
75
|
+
app.save()
|
|
76
|
+
return render(request, "main/admin_panel.html", {"applications": apps})
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def admin_login(request):
|
|
80
|
+
error = None
|
|
81
|
+
if request.method == "POST":
|
|
82
|
+
login_input = request.POST.get("username")
|
|
83
|
+
password_input = request.POST.get("password")
|
|
84
|
+
if login_input == "Admin" and password_input == "KorokNET":
|
|
85
|
+
from django.contrib.auth.models import User
|
|
86
|
+
|
|
87
|
+
user, created = User.objects.get_or_create(username="Admin", defaults={"is_staff": True, "is_superuser": True})
|
|
88
|
+
if created:
|
|
89
|
+
user.set_password("KorokNET")
|
|
90
|
+
user.save()
|
|
91
|
+
user = authenticate(request, username="Admin", password="KorokNET")
|
|
92
|
+
if user:
|
|
93
|
+
login(request, user)
|
|
94
|
+
return redirect("admin_panel")
|
|
95
|
+
error = "Неверный логин или пароль администратора"
|
|
96
|
+
return render(request, "main/admin_login.html", {"error": error})
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def admin_logout(request):
|
|
100
|
+
logout(request)
|
|
101
|
+
return redirect("index")
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Django's command-line utility for administrative tasks."""
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "korochki.settings")
|
|
9
|
+
try:
|
|
10
|
+
from django.core.management import execute_from_command_line
|
|
11
|
+
except ImportError as exc:
|
|
12
|
+
raise ImportError(
|
|
13
|
+
"Couldn't import Django. Are you sure it's installed and "
|
|
14
|
+
"available on your PYTHONPATH environment variable? Did you "
|
|
15
|
+
"forget to activate a virtual environment?"
|
|
16
|
+
) from exc
|
|
17
|
+
execute_from_command_line(sys.argv)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
main()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel", "build"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dajanga2"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "CLI tool to copy bundled ggggg folder into current directory"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
authors = [ {name = "Auto Generated"} ]
|
|
12
|
+
license = {text = "MIT"}
|
|
13
|
+
classifiers = [
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Topic :: Software Development :: Build Tools",
|
|
19
|
+
]
|
|
20
|
+
keywords = ["django", "template", "starter", "cli"]
|
|
21
|
+
|
|
22
|
+
[project.scripts]
|
|
23
|
+
dajanga-start = "Dajanga2.cli:main"
|
|
24
|
+
|
|
25
|
+
[tool.setuptools]
|
|
26
|
+
include-package-data = true
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.packages.find]
|
|
29
|
+
where = ["."]
|
|
30
|
+
include = ["Dajanga2*"]
|
dajanga2-0.1.0/setup.cfg
ADDED