This commit is contained in:
Ilya Mukhortov 2025-03-09 10:28:30 +10:00
parent f8517d0677
commit 7a7f4f82cb
8 changed files with 68 additions and 79 deletions

View File

@ -8,7 +8,7 @@ RUN apt-get install -y \
nginx \
&& rm -rf /var/lib/apt/lists/*
RUN pip install pip==25.0
RUN pip install pip==25.0.1
WORKDIR /app

View File

@ -1,17 +1,11 @@
from django.contrib.auth.models import User
from django import forms
from django.contrib import admin
from django.core.checks import messages
from django.utils import timezone
from django.utils.safestring import mark_safe
from django.conf import settings
from constance import config
from ace_editor import AceJSONWidget
from rangefilter.filters import DateRangeFilterBuilder
from .models import *
from .admin_forms import *
from .admin_filters import *
from .tasks import send_call_request_task, update_call_requests_task
@ -101,19 +95,11 @@ class CallRequestAdmin(admin.ModelAdmin):
@admin.action(description='Отправить выбранные записи на обзвон')
def send_call_request(self, request, queryset):
if config.CALL_TIME_START <= timezone.now().time() <= config.CALL_TIME_END:
ids = [item.id for item in queryset.filter(request_status=CallRequest.RequestStatus.NOT_SENT)]
if len(ids) > 0:
send_call_request_task.apply_async(args=(ids,))
ids = [item.id for item in queryset.filter(request_status=CallRequest.RequestStatus.NOT_SENT)]
if len(ids) > 0:
send_call_request_task.apply_async(args=(ids,))
self.message_user(request, message=f'Записей отправлено на обзвон: {len(ids)}')
else:
self.message_user(
request,
message=f'Время для обзвона регламентируется с '
f'{settings.CALL_REQUEST_TIME_START} по {settings.CALL_REQUEST_TIME_END}',
level=messages.ERROR
)
self.message_user(request, message=f'Записей отправлено на обзвон: {len(ids)}')
@admin.action(description='Удалить выбранные записи из обзвона')
def delete_call_request(self, request, queryset):

View File

@ -4,31 +4,26 @@ import json
import requests
from django.utils import timezone
from django.core.cache import cache
from constance import config
from appa.models import CallRequest, RequestLog
from . import call_status
def auth_request():
r = requests.post(
config.ENTRYPOINT_AUTH,
data=dict(
username=config.USERNAME,
password=config.PASSWORD,
)
)
if r.status_code == 200:
config.ACCESS_TOKEN = r.json()['accessToken']
config.REFRESH_TOKEN = r.json()['refreshToken']
config.ACCESS_TOKEN_EXPIRED = datetime.datetime.now() + datetime.timedelta(minutes=5)
def get_token():
if not config.ACCESS_TOKEN or config.ACCESS_TOKEN_EXPIRED <= timezone.now():
auth_request()
token = cache.get('ACCESS_TOKEN')
if token is None:
r = requests.post(
config.ENTRYPOINT_AUTH,
data=dict(username=config.USERNAME, password=config.PASSWORD)
)
if r.status_code == 200:
token = r.json()['accessToken']
cache.set('ACCESS_TOKEN', token, timeout=300)
cache.set('REFRESH_TOKEN', r.json()['refreshToken'], timeout=300)
return config.ACCESS_TOKEN
return token
def delete_call_request(call_request: CallRequest, logging=True):
@ -73,6 +68,9 @@ def get_record(call_request: CallRequest, logging=True):
if result['confirmed']:
call_request.status = call_request.Status.APPROVED
elif result['call_result_code'] == call_status.STATUS_4:
call_request.status = call_request.Status.TRANSFER
elif result['call_result_code'] == call_status.STATUS_5:
call_request.status = call_request.Status.CALLBACK

View File

@ -1,4 +1,3 @@
import datetime
import requests
from constance import config
@ -43,10 +42,10 @@ def get_bookings(date):
def cancel_booking(booking_id):
r = requests.post(
f'{config.MEDICINE_HOST}api/1/booking/{booking_id}/failed/',
requests.delete(
f'{config.MEDICINE_HOST}api/1/booking/{booking_id}/',
data={
'failed_reason': 'CANCELED'
'comment': 'Пациент отменил запись по телефону'
},
headers={
'Authorization': f'Token {config.MEDICINE_TOKEN}'

View File

@ -63,6 +63,7 @@ class CallRequest(models.Model):
CANCELED = 'CANCELED', 'Прием отменен'
CALLBACK = 'CALLBACK', 'Перезвонить'
WITHOUT_ANSWER = 'WITHOUT_ANSWER', 'Не дозвонились'
TRANSFER = 'TRANSFER', 'Просьба перенести запись'
OTHER = 'OTHER', 'Другая ошибка'
STATUS_COLOR = {
@ -71,6 +72,7 @@ class CallRequest(models.Model):
Status.CANCELED: '#CD4246',
Status.CALLBACK: '#935610',
Status.WITHOUT_ANSWER: '#866103',
Status.TRANSFER: '#B83211',
Status.OTHER: '#5A701A'
}
@ -151,11 +153,6 @@ class CallRequest(models.Model):
class RequestLog(models.Model):
class Meta:
verbose_name = 'Запрос'
verbose_name_plural = 'История запросов'
ordering = ('-created',)
request_url = models.CharField(max_length=500, verbose_name='Адрес запроса')
request_body = models.TextField(null=True, blank=True, verbose_name='Запрос')
request_method = models.CharField(max_length=100, null=True, verbose_name='Тип запроса')
@ -165,6 +162,11 @@ class RequestLog(models.Model):
created = models.DateTimeField(auto_now_add=True, editable=False, db_index=True, verbose_name='Дата запроса')
class Meta:
verbose_name = 'Запрос'
verbose_name_plural = 'История запросов'
ordering = ('-created',)
def __str__(self):
return self.request_url

View File

@ -152,27 +152,9 @@ CONSTANCE_CONFIG = {
'ENTRYPOINT_AUTH': ('https://amniss-ai.ru/api/auth', 'Адрес для авторизации', 'char'),
'ENTRYPOINT_DELETE': ('https://amniss-ai.ru/api/v.1/records/delete/%s', 'Адрес для удаления', 'char'),
'ENTRYPOINT_RECORD': ('https://amniss-ai.ru/api/v.1/records/%s', 'Адрес записи', 'char'),
'SCENARIO_ID': ('', 'ID сценария', 'char'),
'USERNAME': ('', 'Имя пользователя', 'char'),
'PASSWORD': ('', 'Пароль', 'char'),
'IS_ENABLED': (False, 'Активен', bool),
'ACCESS_TOKEN': ('', 'Access token', 'char'),
'REFRESH_TOKEN': ('', 'Refresh token', 'char'),
'ACCESS_TOKEN_EXPIRED': (
datetime.datetime(2025, 1, 1, 0, 0),
'Access token expired',
datetime.datetime
),
'CALL_TIME_START': (
datetime.time(9, 30),
'Время старта обзвона',
datetime.time
),
'CALL_TIME_END': (
datetime.time(18, 30),
'Время окончания обзвона',
datetime.time
),
'MEDICINE_TOKEN': ('', 'Токен', 'char'),
'MEDICINE_HOST': ('http://medicine-app/', 'Хост', 'char'),
@ -181,18 +163,12 @@ CONSTANCE_CONFIG = {
CONSTANCE_CONFIG_FIELDSETS = (
('API', (
'IS_ENABLED',
'CALL_TIME_START',
'CALL_TIME_END',
'SCENARIO_ID',
'USERNAME',
'PASSWORD',
'ENTRYPOINT_AUTH',
'ENTRYPOINT_ADD_RECORDS',
'ENTRYPOINT_DELETE',
'ENTRYPOINT_RECORD',
'ACCESS_TOKEN',
'REFRESH_TOKEN',
'ACCESS_TOKEN_EXPIRED',
)),
('МИС', (
'MEDICINE_HOST',
@ -216,12 +192,17 @@ CELERYBEAT_SCHEDULE = {
},
'check_call_requests_task': {
'task': 'appa.tasks.check_call_requests_task',
'schedule': crontab(minute='*/5'),
'schedule': crontab(minute='*/1'),
'options': {'queue': CELERY_DEFAULT_QUEUE}
},
'send_daily_call_request_task': {
'task': 'appa.tasks.send_daily_call_request_task',
'schedule': crontab(hour='10', minute='0'),
#'send_daily_call_request_task': {
# 'task': 'appa.tasks.send_daily_call_request_task',
# 'schedule': crontab(hour='10', minute='0'),
# 'options': {'queue': CELERY_DEFAULT_QUEUE}
#},
'cleanup_task': {
'task': 'appa.tasks.cleanup_task',
'schedule': crontab(hour='0', minute='0'),
'options': {'queue': CELERY_DEFAULT_QUEUE}
},
}
@ -229,13 +210,21 @@ CELERYBEAT_SCHEDULE = {
DATE_FORMAT = 'd.m.Y'
DATETIME_FORMAT = 'd.m.Y H:i'
CALL_REQUEST_TIME_START = datetime.time(9, 0)
CALL_REQUEST_TIME_END = datetime.time(21, 0)
DATABASE_ROUTERS = [
'appa.medicine.router.MedicineRouter',
]
REDIS_CACHE_DB = env.str('REDIS_CELERY_DB', '8')
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_CACHE_DB}",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
if DEBUG:
AUTH_PASSWORD_VALIDATORS = []
SESSION_COOKIE_NAME = 'dev-medicine-call'

View File

@ -1,7 +1,8 @@
import pytz
import datetime
from constance import config
from django.contrib.sessions.models import Session
from django.utils import timezone
from appa.celery import app
from appa.models import *
from appa.call_api import api as call_api
@ -21,7 +22,10 @@ def send_call_request_task(self, ids=None):
)
for call_request in call_requests:
call_api.add_call_request(call_request)
try:
call_api.add_call_request(call_request)
except Exception as e:
print(e)
@app.task(bind=True, acks_late=True)
@ -60,6 +64,9 @@ def update_call_requests_task(self):
if lpu is None or speciality is None:
continue
if not booking['patient']['phone_mobile']:
continue
CallRequest.objects.update_or_create(
defaults=dict(
date=datetime.datetime.strptime(booking['date_start'].split('T')[0], '%Y-%m-%d').date(),
@ -83,6 +90,13 @@ def update_call_requests_task(self):
def check_call_requests_task(self):
for call_request in CallRequest.objects.filter(
request_status=CallRequest.RequestStatus.SENT,
date=datetime.date.today()
date__in=[datetime.date.today(), datetime.date.today() + datetime.timedelta(days=1)]
):
call_api.get_record(call_request)
@app.task(bind=True, acks_late=True)
def cleanup_task(self):
tomorrow = timezone.now() + datetime.timedelta(days=1)
RequestLog.objects.filter(created__lte=datetime.datetime.now() - datetime.timedelta(days=3)).delete()
Session.objects.filter(expire_date__lte=tomorrow).delete()

View File

@ -16,6 +16,7 @@ django-constance==3.1.0
django-environ==0.11.2
django-ninja==1.2.0
django-picklefield==3.2
django-redis==5.4.0
idna==3.8
kombu==5.4.0
prompt_toolkit==3.0.47