Найти в Дзене
Адаптация персонала

ИСХОДНЫЙ КОД ОН-ЛАЙН КОНСТРУКТОРА КАРЬЕРЫ

{% load static %} <!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>Моя карьера</title> <link href="{% static 'career/css/style.css' %}" rel="stylesheet"> </head> <body class="body"> <div class="container"> <h1 class="title">МОЯ КАРЬЕРА</h1> <form method="post" class="form-section"> {% csrf_token %} <div> <h3>Я СЕЙЧАС РАБОТАЮ (ВЫБЕРИ ДОЛЖНОСТЬ)</h3> <select name="current_position" required> <option value="">-- Выберите текущую должность --</option> {% for position in positions %} <option value="{{ position.id }}">{{ position.name }} (Уровень {{ position.level }})</option> {% endfor %} </select> </div> <div> <h3>Я ХОЧУ РАБОТАТЬ</h3> <select name="target_position" required> <option value="">-- Выберите целевую должность --</option> {% for position in positions %} <option value="{{ position.id }}">{{ position.name }} (Уровень {{ position.level }})</option> {% endfor %} </select> </div> <button type="submit" class="btn">ПОСТРОИТЬ КАРЬЕРУ</button> </form> {% if target_

{% load static %}

<!DOCTYPE html>

<html lang="ru">

<head>

<meta charset="UTF-8">

<title>Моя карьера</title>

<link href="{% static 'career/css/style.css' %}" rel="stylesheet">

</head>

<body class="body">

<div class="container">

<h1 class="title">МОЯ КАРЬЕРА</h1>

<form method="post" class="form-section">

{% csrf_token %}

<div>

<h3>Я СЕЙЧАС РАБОТАЮ (ВЫБЕРИ ДОЛЖНОСТЬ)</h3>

<select name="current_position" required>

<option value="">-- Выберите текущую должность --</option>

{% for position in positions %}

<option value="{{ position.id }}">{{ position.name }} (Уровень {{ position.level }})</option>

{% endfor %}

</select>

</div>

<div>

<h3>Я ХОЧУ РАБОТАТЬ</h3>

<select name="target_position" required>

<option value="">-- Выберите целевую должность --</option>

{% for position in positions %}

<option value="{{ position.id }}">{{ position.name }} (Уровень {{ position.level }})</option>

{% endfor %}

</select>

</div>

<button type="submit" class="btn">ПОСТРОИТЬ КАРЬЕРУ</button>

</form>

{% if target_position and current_position %}

{% if is_too_far %}

<div class="warning">

<h3>ВНИМАНИЕ!</h3>

<p>

Вы выбрали должность, которая находится на слишком высокой ступеньке

и при этом относится к другой сфере деятельности. Это слишком большой шаг для текущего уровня.

</p>

<p>

Рекомендуем выбрать должность, которая ближе к вашей текущей сфере и уровню,

чтобы постепенно развивать свои навыки.

</p>

</div>

{% else %}

{% if level_difference > 1 %}

<div class="warning">

<h3>ВНИМАНИЕ!</h3>

<p>Вы выбрали сложную должность на {{ level_difference }} ступенек выше.</p>

<p>Её будет освоить не так-то просто. Потребуется:</p>

<ul>

<li>Прочитать много литературы</li>

<li>Освоить различные технические средства работы</li>

<li>Самостоятельное изучение материалов</li>

</ul>

{% endif %}

{% if required_skills %}

<h2>Необходимые навыки:</h2>

<ul>

{% for skill in required_skills %}

<li>{{ skill.name }}</li>

{% endfor %}

</ul>

<form method="post">

{% csrf_token %}

<input type="hidden" name="current_position" value="{{ current_position.id }}">

<input type="hidden" name="target_position" value="{{ target_position.id }}">

<button type="submit" name="download_pdf" class="btn btn_pdf">Скачать PDF</button>

</form>

{% else %}

<p>Навыков нет или они не были загружены.</p>

{% endif %}

</div>

{% endif %}

{% endif %}

</div>

</body>

<script src="{% static 'career/js/main.js' %}"></script>

</html>

from django.contrib import admin

from .models import Position, Skill, PositionGroup

admin.site.site_header = 'Администрирование' # Заголовок в админке

admin.site.site_title = 'Администрирование' # Заголовок вкладки браузера

admin.site.index_title = 'Панель управления' # Заголовок на главной странице админки

class SkillInline(admin.TabularInline):

model = Skill.positions.through

extra = 1

class PositionInline(admin.TabularInline):

model = Position

extra = 1

fields = ('name', 'level')

show_change_link = True

list_filter = ('level',)

ordering = ('name',)

@admin.register(Position)

class PositionAdmin(admin.ModelAdmin):

list_display = ('name', 'level')

list_filter = ('level',)

search_fields = ('name',)

inlines = [SkillInline]

@admin.register(Skill)

class SkillAdmin(admin.ModelAdmin):

list_display = ('name',)

search_fields = ('name',)

filter_horizontal = ('positions',)

@admin.register(PositionGroup)

class PositionGroupAdmin(admin.ModelAdmin):

list_display = ('name',)

search_fields = ('name',)

inlines = [PositionInline]

from django.apps import AppConfig

class CareerConfig(AppConfig):

default_auto_field = 'django.db.models.BigAutoField'

name = 'career'

verbose_name = '📊 Карьера'

from django.db import models

class PositionGroup(models.Model):

name = models.CharField(max_length=255, verbose_name="Название группы")

class Meta:

verbose_name = "Группа должностей"

verbose_name_plural = "Группы должностей"

def __str__(self):

return self.name

class Position(models.Model):

name = models.CharField(max_length=255, verbose_name='Название должности')

level = models.PositiveSmallIntegerField(verbose_name='Уровень должности')

group = models.ForeignKey(PositionGroup, null=True, on_delete=models.CASCADE, related_name="positions", verbose_name="Группа")

class Meta:

verbose_name = 'Должность'

verbose_name_plural = 'Должности'

def __str__(self):

return f"{self.name} (Уровень {self.level})"

class Skill(models.Model):

name = models.CharField(max_length=255, verbose_name='Название навыка')

positions = models.ManyToManyField(Position, related_name='skills', verbose_name='Должности')

class Meta:

verbose_name = 'Умение'

verbose_name_plural = 'Умения'

def __str__(self):

return self.name

from django.urls import path

from . import views

urlpatterns = [

path('', views.career_plan, name='career_plan'),

]

from io import BytesIO

from reportlab.pdfgen import canvas

from reportlab.pdfbase import pdfmetrics

from reportlab.pdfbase.ttfonts import TTFont

from django.http import HttpResponse

from config.settings import FONT_PATH

def generate_pdf_response(skills, position_name):

# Регистрирует шрифт Arial для поддержки кириллицы

pdfmetrics.registerFont(TTFont('Arial', FONT_PATH))

# Создает буфер для PDF

buffer = BytesIO()

# Создает PDF-документ

p = canvas.Canvas(buffer)

# Настройки документа

p.setFont("Arial", 16)

p.setTitle(f"Навыки на должность {position_name}")

# Заголовок

p.drawString(50, 800, f"Необходимые навыки для {position_name}:")

p.setFont("Arial", 12) # Уменьшает размер шрифта для основного текста

# Начальная позиция для текста

y_position = 770

# Список навыков

for i, skill in enumerate(skills, 1):

p.drawString(70, y_position, f"{i}. {skill.name}")

y_position -= 25 # Переходит на следующую строку

# Проверка на конец страницы

if y_position < 50:

p.showPage() # Создает новую страницу

y_position = 800 # Сбрасывает позицию

p.setFont("Arial", 12) # Устанавливает шрифт для новой страницы

# Закрывает PDF

p.showPage()

p.save()

# Получает содержимое PDF

buffer.seek(0)

response = HttpResponse(buffer, content_type='application/pdf')

response['Content-Disposition'] = 'attachment; filename="required_skills.pdf"'

return response

from django.shortcuts import render

from .models import Position

from .utils import generate_pdf_response

def career_plan(request):

positions = Position.objects.all().order_by('level')

context = {'positions': positions} # Базовый контекст

if request.method == 'POST':

current_position_id = request.POST.get('current_position')

target_position_id = request.POST.get('target_position')

current_position = Position.objects.get(id=current_position_id)

target_position = Position.objects.get(id=target_position_id)

level_difference = target_position.level - current_position.level

is_too_far = (

target_position.level > 1 and

current_position.group != target_position.group

)

if 'download_pdf' in request.POST:

return generate_pdf_response(target_position.skills.all(), target_position.name)

# Обновляет контекст

context.update({

'current_position': current_position,

'target_position': target_position,

'level_difference': level_difference,

'is_too_far': is_too_far,

# если нельзя показывать скилы, то не достает их из бд

'required_skills': target_position.skills.all() if not is_too_far else None,

})

return render(request, 'career/plan.html', context)

"""

Django settings for config project.

Generated by 'django-admin startproject' using Django 4.1.5.

For more information on this file, see

https://docs.djangoproject.com/en/4.1/topics/settings/

For the full list of settings and their values, see

https://docs.djangoproject.com/en/4.1/ref/settings/
"""

import os

from pathlib import Path

from dotenv.main import load_dotenv

# Build paths inside the project like this: BASE_DIR / 'subdir'.

BASE_DIR = Path(__file__).resolve().parent.parent

load_dotenv(BASE_DIR / '.env')

# Quick-start development settings - unsuitable for production

# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!

SECRET_KEY = os.getenv('SECRET_KEY')

# SECURITY WARNING: don't run with debug turned on in production!

DEBUG = os.getenv('DEBUG')

FONT_PATH = os.path.join(BASE_DIR, 'fonts', 'arial.ttf')

ALLOWED_HOSTS = ['195.161.62.128', '127.0.0.1', 'localhost']

# Application definition

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'users',

'career.apps.CareerConfig',

]

MIDDLEWARE = [

'django.middleware.security.SecurityMiddleware',

'django.contrib.sessions.middleware.SessionMiddleware',

'django.middleware.common.CommonMiddleware',

'django.middleware.csrf.CsrfViewMiddleware',

'django.contrib.auth.middleware.AuthenticationMiddleware',

'django.contrib.messages.middleware.MessageMiddleware',

'django.middleware.clickjacking.XFrameOptionsMiddleware',

]

ROOT_URLCONF = 'config.urls'

TEMPLATES = [

{

'BACKEND': 'django.template.backends.django.DjangoTemplates',

'DIRS': [],

'APP_DIRS': True,

'OPTIONS': {

'context_processors': [

'django.template.context_processors.debug',

'django.template.context_processors.request',

'django.contrib.auth.context_processors.auth',

'django.contrib.messages.context_processors.messages',

],

},

},

]

WSGI_APPLICATION = 'config.wsgi.application'

# Database

# https://docs.djangoproject.com/en/4.1/ref/settings/#databases

DATABASES = {

'default': {

'ENGINE': 'django.db.backends.sqlite3',

'NAME': BASE_DIR / 'db.sqlite3',

}

}

# Password validation

# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [

{

'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',

},

{

'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',

},

{

'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',

},

{

'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',

},

]

# Internationalization

# https://docs.djangoproject.com/en/4.1/topics/i18n/

LANGUAGE_CODE = 'ru-RU'

TIME_ZONE = 'Europe/Moscow'

USE_I18N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)

# https://docs.djangoproject.com/en/4.1/howto/static-files/

STATIC_URL = 'static/'

# Отвечает за место на диске, откуда необходимо подгружать статику

STATICFILES_DIRS = (

BASE_DIR / 'static',

)

# Default primary key field type

# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

AUTH_USER_MODEL = 'users.User'

MEDIA_URL = '/media/'

MEDIA_ROOT = BASE_DIR / 'media'

"""config URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:

https://docs.djangoproject.com/en/4.1/topics/http/urls/
Examples:

Function views

1. Add an import: from my_app import views

2. Add a URL to urlpatterns: path('', views.home, name='home')

Class-based views

1. Add an import: from other_app.views import Home

2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')

Including another URLconf

1. Import the include() function: from django.urls import include, path

2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))

"""

from django.conf import settings

from django.conf.urls.static import static

from django.contrib import admin

from django.urls import path, include

urlpatterns = [

path('admin/', admin.site.urls),

path('', include('career.urls')),

] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

"""

WSGI config for config project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see

https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')

application = get_wsgi_application()

from django.apps import AppConfig

class UsersConfig(AppConfig):

default_auto_field = 'django.db.models.BigAutoField'

name = 'users'

from django.contrib.auth.models import AbstractUser

from django.db import models

class User(AbstractUser):

username = None

email = models.EmailField(unique=True, verbose_name='email')

USERNAME_FIELD = "email"

REQUIRED_FIELDS = []

def __str__(self):

return self.email

#!/usr/bin/env python

"""Django's command-line utility for administrative tasks."""

import os

import sys

def main():

"""Run administrative tasks."""

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')

try:

from django.core.management import execute_from_command_line

except ImportError as exc:

raise ImportError(

"Couldn't import Django. Are you sure it's installed and "

"available on your PYTHONPATH environment variable? Did you "

"forget to activate a virtual environment?"

) from exc

execute_from_command_line(sys.argv)

if __name__ == '__main__':

main()

asgiref==3.8.1

attrs==25.1.0

chardet==5.2.0

Django==5.1.6

djangorestframework==3.15.2

dotenv-python==0.0.1

drf-spectacular==0.28.0

inflection==0.5.1

jsonschema==4.23.0

jsonschema-specifications==2024.10.1

pillow==11.1.0

psycopg2==2.9.10

python-dotenv==1.0.1

PyYAML==6.0.2

referencing==0.36.2

reportlab==4.3.1

rpds-py==0.22.3

sqlparse==0.5.3

typing_extensions==4.12.2

tzdata==2025.1

uritemplate==4.1.1