refactor(employees): remove permissions.py shim, fix relative imports

- Replaced ..permissions with ...permissions in all 7 view modules
- Deleted backend/api/v1/employees/permissions.py (duplicate re-export)
- Views now import directly from api.v1.permissions (3 dots up)

manage.py check: System check identified no issues
This commit is contained in:
igor_nadein 2026-02-16 16:21:45 +03:00
parent 8efcabf530
commit eaedea1403
19 changed files with 9 additions and 317 deletions

View File

@ -1,20 +0,0 @@
# flake8: noqa: F401
"""Реэкспорт permissions из api.v1.permissions для обратной совместимости.
Модуль views/ стал пакетом, и относительный импорт ``..permissions``
из его подмодулей резолвится сюда (api.v1.employees.permissions).
"""
from api.v1.permissions import (
AdminOrActionOrModelPerms,
AdminOrDeptAllowed,
IsSelfOrStaff,
has_dept_perm,
)
__all__ = [
"AdminOrActionOrModelPerms",
"AdminOrDeptAllowed",
"IsSelfOrStaff",
"has_dept_perm",
]

View File

@ -23,7 +23,7 @@ from rest_framework import filters, viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from ..permissions import AdminOrActionOrModelPerms
from ...permissions import AdminOrActionOrModelPerms
from ..serializers import EmployeeActionSerializer
from ._helpers import HistoryActionMixin, _is_ldap_enabled

View File

@ -49,7 +49,7 @@ from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from ..permissions import (
from ...permissions import (
AdminOrActionOrModelPerms,
AdminOrDeptAllowed,
has_dept_perm,

View File

@ -42,7 +42,7 @@ from rest_framework.exceptions import ValidationError
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from ..permissions import AdminOrActionOrModelPerms, IsSelfOrStaff
from ...permissions import AdminOrActionOrModelPerms, IsSelfOrStaff
from ..serializers import (
EmployeeListSerializer,
EmployeeSerializer,

View File

@ -16,7 +16,7 @@ from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from ..permissions import AdminOrActionOrModelPerms
from ...permissions import AdminOrActionOrModelPerms
from ..serializers import GroupSerializer
from ._helpers import Employee, _is_ldap_enabled

View File

@ -10,7 +10,7 @@ from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from ..permissions import AdminOrActionOrModelPerms
from ...permissions import AdminOrActionOrModelPerms
from ..serializers import PositionSerializer
from ._helpers import HistoryActionMixin, _is_ldap_enabled, _ldap_try

View File

@ -22,7 +22,7 @@ from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from ..permissions import AdminOrDeptAllowed
from ...permissions import AdminOrDeptAllowed
from ..serializers import DepartmentRoleSerializer
from ._helpers import Employee, _is_ldap_enabled

View File

@ -8,7 +8,7 @@ from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from ..permissions import AdminOrActionOrModelPerms
from ...permissions import AdminOrActionOrModelPerms
from ..serializers import SkillSerializer
from ._helpers import Employee

View File

@ -41,7 +41,7 @@ INSTALLED_APPS = [
# Основные приложения
"employees.apps.EmployeesConfig",
"api.apps.ApiConfig",
"hikcentral.apps.HikcentralConfig",
"calendar_app.apps.CalendarAppConfig",
"documents.apps.DocumentsConfig",
"requests_app.apps.RequestsAppConfig",
@ -100,15 +100,7 @@ USE_SQLITE = os.getenv("USE_SQLITE", "false").lower() == "true"
DATABASES = {
"default": {},
"hikcentral": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "cms",
"USER": "postgres",
"PASSWORD": None,
"HOST": "127.0.0.1",
"PORT": "5432",
"OPTIONS": {"options": "-c search_path=platform"},
},
}
if USE_SQLITE:

View File

@ -1 +0,0 @@
default_app_config = 'hikcentral.apps.HikcentralConfig'

View File

@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.

View File

@ -1,10 +0,0 @@
# hikcentral/apps.py
from django.apps import AppConfig
class HikcentralConfig(AppConfig):
name = 'hikcentral'
def ready(self):
import hikcentral.signals

View File

@ -1,65 +0,0 @@
# Generated by Django 5.2.4 on 2025-07-24 09:24
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='HikPerson',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('person_group_id', models.IntegerField(blank=True, null=True)),
('person_type', models.IntegerField(blank=True, null=True)),
('person_code', models.CharField(max_length=16, unique=True)),
('given_name', models.CharField(blank=True, max_length=255, null=True)),
('family_name', models.CharField(blank=True, max_length=255, null=True)),
('full_name', models.CharField(blank=True, max_length=255, null=True)),
('gender', models.IntegerField(blank=True, null=True)),
('email', models.EmailField(blank=True, max_length=254, null=True)),
('phone_number', models.CharField(blank=True, max_length=32, null=True)),
('photo_url', models.CharField(blank=True, max_length=512, null=True)),
('photo_index', models.IntegerField(blank=True, null=True)),
('small_photo_url', models.CharField(blank=True, max_length=512, null=True)),
('start_valid_date', models.DateTimeField(blank=True, null=True)),
('end_valid_date', models.DateTimeField(blank=True, null=True)),
('remark', models.TextField(blank=True, null=True)),
('update_time', models.DateTimeField(blank=True, null=True)),
('create_time', models.DateTimeField(blank=True, null=True)),
('is_deleted', models.IntegerField(default=0)),
('object_guid', models.CharField(blank=True, max_length=64, null=True)),
('person_from', models.IntegerField(blank=True, null=True)),
('rdn', models.CharField(blank=True, max_length=255, null=True)),
('dn', models.CharField(blank=True, max_length=255, null=True)),
('usn_changed', models.BigIntegerField(blank=True, null=True)),
('start_time_differ', models.IntegerField(blank=True, null=True)),
('end_time_differ', models.IntegerField(blank=True, null=True)),
],
options={
'db_table': 'person',
'managed': False,
},
),
migrations.CreateModel(
name='HikPersonHeadPic',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('standard_head_portrait', models.TextField(blank=True, null=True)),
('thumbnail_head_portrait', models.TextField(blank=True, null=True)),
('deepmode_head_portrait', models.TextField(blank=True, null=True)),
('identi_photo', models.TextField(blank=True, null=True)),
('create_time', models.DateTimeField(blank=True, null=True)),
('update_time', models.DateTimeField(blank=True, null=True)),
],
options={
'db_table': 'person_head_pic',
'managed': False,
},
),
]

View File

@ -1,68 +0,0 @@
# hikcentral/models.py
from django.db import models
class HikPerson(models.Model):
id = models.AutoField(primary_key=True)
person_group_id = models.IntegerField(blank=True, null=True)
person_type = models.IntegerField(blank=True, null=True)
# важно! Уникальный ID от 1 до 16 символов (цифры/буквы)
person_code = models.CharField(max_length=16, unique=True)
given_name = models.CharField(max_length=255, blank=True, null=True)
family_name = models.CharField(max_length=255, blank=True, null=True)
full_name = models.CharField(max_length=255, blank=True, null=True)
gender = models.IntegerField(blank=True, null=True)
email = models.EmailField(blank=True, null=True)
phone_number = models.CharField(max_length=32, blank=True, null=True)
photo_url = models.CharField(
max_length=512, blank=True, null=True, default="")
photo_index = models.IntegerField(blank=True, null=True)
small_photo_url = models.CharField(max_length=512, blank=True, null=True)
start_valid_date = models.DateTimeField(blank=True, null=True)
end_valid_date = models.DateTimeField(blank=True, null=True)
remark = models.TextField(blank=True, null=True)
update_time = models.DateTimeField(blank=True, null=True)
create_time = models.DateTimeField(blank=True, null=True)
is_deleted = models.IntegerField(default=0)
object_guid = models.CharField(max_length=64, blank=True, null=True)
person_from = models.IntegerField(blank=True, null=True)
rdn = models.CharField(max_length=255, blank=True, null=True)
dn = models.CharField(max_length=255, blank=True, null=True)
usn_changed = models.BigIntegerField(blank=True, null=True)
start_time_differ = models.IntegerField(blank=True, null=True)
end_time_differ = models.IntegerField(blank=True, null=True)
class Meta:
db_table = 'person'
app_label = 'hikcentral'
managed = False # очень важно! — Django не управляет этой таблицей
def __str__(self):
return self.full_name or self.person_code
class HikPersonHeadPic(models.Model):
id = models.AutoField(primary_key=True)
person = models.ForeignKey(
HikPerson,
db_column='person_id',
on_delete=models.CASCADE,
related_name='head_pics'
)
standard_head_portrait = models.TextField(
blank=True, null=True) # основное фото (base64-строка)
thumbnail_head_portrait = models.TextField(
blank=True, null=True) # миниатюра (base64)
deepmode_head_portrait = models.TextField(blank=True, null=True)
identi_photo = models.TextField(blank=True, null=True)
create_time = models.DateTimeField(blank=True, null=True)
update_time = models.DateTimeField(blank=True, null=True)
class Meta:
db_table = 'person_head_pic'
app_label = 'hikcentral'
managed = False
def __str__(self):
return f"Photo for person_id={self.person_id}"

View File

@ -1,77 +0,0 @@
# # backend/hikcentral/signals.py
# from django.db.models.signals import post_save, post_delete
# from django.dispatch import receiver
# from users.models import Employee
# from .models import HikPerson, HikPersonHeadPic
# from django.utils import timezone
# from .utils import gen_person_code, compress_image_to_base64
# @receiver(post_save, sender=Employee)
# def sync_employee_to_hikcentral(sender, instance, created, **kwargs):
# db = 'hikcentral'
# # Определяем person_code (уникальный)
# person_code = gen_person_code(instance)
# # Проставим даты
# now = timezone.now()
# start_valid = now
# end_valid = now.replace(year=now.year+10)
# # 1. Создаём/обновляем запись в person
# person_values = dict(
# person_group_id=1,
# person_type=0,
# person_code=person_code,
# given_name=instance.first_name,
# family_name=instance.last_name,
# full_name=f"{instance.first_name} {instance.last_name}",
# gender=0, # по аналогии: 0 - не указан, 1 - муж, 2 - жен
# email=instance.email,
# phone_number=str(
# instance.phone_number) if instance.phone_number else "",
# photo_url=instance.avatar.url if instance.avatar else "",
# photo_index=0,
# small_photo_url=instance.avatar.url if instance.avatar else "",
# start_valid_date=start_valid,
# end_valid_date=end_valid,
# remark='',
# update_time=now,
# create_time=now,
# is_deleted=0,
# person_from=0,
# usn_changed=0,
# start_time_differ=10800,
# end_time_differ=10800,
# )
# # Сохраняем сотрудника в HikCentral
# person = HikPerson.objects.using(db).create(**person_values)
# # 2. Картинка (photo/avatar)
# if instance.avatar:
# img_b64 = compress_image_to_base64(instance.avatar)
# HikPersonHeadPic.objects.using(db).create(
# person_id=person.id,
# standard_head_portrait=img_b64,
# thumbnail_head_portrait='',
# deepmode_head_portrait='',
# identi_photo='',
# create_time=now,
# update_time=now,
# )
# @receiver(post_delete, sender=Employee)
# def delete_employee_from_hikcentral(sender, instance, **kwargs):
# db = 'hikcentral'
# # Найдём всех по имени и почте — или храни person_code в Employee
# HikPerson.objects.using(db).filter(
# email=instance.email,
# full_name=f"{instance.first_name} {instance.last_name}",
# ).delete()
# # Не забудь удалить фото
# # person_ids = list(...)
# # HikPersonHeadPic.objects.using(db).filter(person_id__in=person_ids).delete()

View File

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

View File

@ -1,50 +0,0 @@
import base64
from PIL import Image
from io import BytesIO
import random
import string
def gen_person_code(instance):
import re
base = str(instance.phone_number or instance.email or instance.pk)
code = re.sub(r'\W+', '', base)[:16]
# Проверяем уникальность, если не уникален — добавляем символы в конец
from .models import HikPerson
existing = (
HikPerson.objects.using('hikcentral')
.filter(person_code=code)
.exists()
)
orig_code = code
while existing:
# Укорачиваем до 12 символов и дописываем случайные 4 символа
code = (
orig_code[:12] + ''.join(random.choices(string.ascii_letters + string.digits, k=4)))[:16]
existing = (
HikPerson.objects.using('hikcentral')
.filter(person_code=code)
.exists()
)
return code
def compress_image_to_base64(image_field, target_size_kb=200):
"""Сжимает изображение до размера (примерно) target_size_kb и возвращает base64-строку."""
if not image_field:
return ''
image = Image.open(image_field)
output = BytesIO()
quality = 90
# Сжимать до ~200КБ
while True:
output.seek(0)
image.save(output, format='JPEG', quality=quality)
size_kb = output.tell() / 1024
if size_kb <= target_size_kb or quality <= 20:
break
quality -= 10
output.seek(0)
img_b64 = base64.b64encode(output.read()).decode('utf-8')
return img_b64

View File

@ -1,3 +0,0 @@
from django.shortcuts import render
# Create your views here.