pass
This commit is contained in:
parent
f8517d0677
commit
7a7f4f82cb
|
|
@ -8,7 +8,7 @@ RUN apt-get install -y \
|
||||||
nginx \
|
nginx \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN pip install pip==25.0
|
RUN pip install pip==25.0.1
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,11 @@
|
||||||
from django.contrib.auth.models import User
|
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
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.utils.safestring import mark_safe
|
||||||
from django.conf import settings
|
|
||||||
from constance import config
|
|
||||||
from ace_editor import AceJSONWidget
|
from ace_editor import AceJSONWidget
|
||||||
from rangefilter.filters import DateRangeFilterBuilder
|
from rangefilter.filters import DateRangeFilterBuilder
|
||||||
|
|
||||||
from .models import *
|
from .models import *
|
||||||
from .admin_forms import *
|
|
||||||
from .admin_filters import *
|
from .admin_filters import *
|
||||||
from .tasks import send_call_request_task, update_call_requests_task
|
from .tasks import send_call_request_task, update_call_requests_task
|
||||||
|
|
||||||
|
|
@ -101,19 +95,11 @@ class CallRequestAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
@admin.action(description='Отправить выбранные записи на обзвон')
|
@admin.action(description='Отправить выбранные записи на обзвон')
|
||||||
def send_call_request(self, request, queryset):
|
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)]
|
||||||
ids = [item.id for item in queryset.filter(request_status=CallRequest.RequestStatus.NOT_SENT)]
|
if len(ids) > 0:
|
||||||
if len(ids) > 0:
|
send_call_request_task.apply_async(args=(ids,))
|
||||||
send_call_request_task.apply_async(args=(ids,))
|
|
||||||
|
|
||||||
self.message_user(request, message=f'Записей отправлено на обзвон: {len(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
|
|
||||||
)
|
|
||||||
|
|
||||||
@admin.action(description='Удалить выбранные записи из обзвона')
|
@admin.action(description='Удалить выбранные записи из обзвона')
|
||||||
def delete_call_request(self, request, queryset):
|
def delete_call_request(self, request, queryset):
|
||||||
|
|
|
||||||
|
|
@ -4,31 +4,26 @@ import json
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.core.cache import cache
|
||||||
from constance import config
|
from constance import config
|
||||||
|
|
||||||
from appa.models import CallRequest, RequestLog
|
from appa.models import CallRequest, RequestLog
|
||||||
from . import call_status
|
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():
|
def get_token():
|
||||||
if not config.ACCESS_TOKEN or config.ACCESS_TOKEN_EXPIRED <= timezone.now():
|
token = cache.get('ACCESS_TOKEN')
|
||||||
auth_request()
|
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):
|
def delete_call_request(call_request: CallRequest, logging=True):
|
||||||
|
|
@ -73,6 +68,9 @@ def get_record(call_request: CallRequest, logging=True):
|
||||||
if result['confirmed']:
|
if result['confirmed']:
|
||||||
call_request.status = call_request.Status.APPROVED
|
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:
|
elif result['call_result_code'] == call_status.STATUS_5:
|
||||||
call_request.status = call_request.Status.CALLBACK
|
call_request.status = call_request.Status.CALLBACK
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import datetime
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from constance import config
|
from constance import config
|
||||||
|
|
@ -43,10 +42,10 @@ def get_bookings(date):
|
||||||
|
|
||||||
|
|
||||||
def cancel_booking(booking_id):
|
def cancel_booking(booking_id):
|
||||||
r = requests.post(
|
requests.delete(
|
||||||
f'{config.MEDICINE_HOST}api/1/booking/{booking_id}/failed/',
|
f'{config.MEDICINE_HOST}api/1/booking/{booking_id}/',
|
||||||
data={
|
data={
|
||||||
'failed_reason': 'CANCELED'
|
'comment': 'Пациент отменил запись по телефону'
|
||||||
},
|
},
|
||||||
headers={
|
headers={
|
||||||
'Authorization': f'Token {config.MEDICINE_TOKEN}'
|
'Authorization': f'Token {config.MEDICINE_TOKEN}'
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ class CallRequest(models.Model):
|
||||||
CANCELED = 'CANCELED', 'Прием отменен'
|
CANCELED = 'CANCELED', 'Прием отменен'
|
||||||
CALLBACK = 'CALLBACK', 'Перезвонить'
|
CALLBACK = 'CALLBACK', 'Перезвонить'
|
||||||
WITHOUT_ANSWER = 'WITHOUT_ANSWER', 'Не дозвонились'
|
WITHOUT_ANSWER = 'WITHOUT_ANSWER', 'Не дозвонились'
|
||||||
|
TRANSFER = 'TRANSFER', 'Просьба перенести запись'
|
||||||
OTHER = 'OTHER', 'Другая ошибка'
|
OTHER = 'OTHER', 'Другая ошибка'
|
||||||
|
|
||||||
STATUS_COLOR = {
|
STATUS_COLOR = {
|
||||||
|
|
@ -71,6 +72,7 @@ class CallRequest(models.Model):
|
||||||
Status.CANCELED: '#CD4246',
|
Status.CANCELED: '#CD4246',
|
||||||
Status.CALLBACK: '#935610',
|
Status.CALLBACK: '#935610',
|
||||||
Status.WITHOUT_ANSWER: '#866103',
|
Status.WITHOUT_ANSWER: '#866103',
|
||||||
|
Status.TRANSFER: '#B83211',
|
||||||
Status.OTHER: '#5A701A'
|
Status.OTHER: '#5A701A'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,11 +153,6 @@ class CallRequest(models.Model):
|
||||||
|
|
||||||
class RequestLog(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_url = models.CharField(max_length=500, verbose_name='Адрес запроса')
|
||||||
request_body = models.TextField(null=True, blank=True, verbose_name='Запрос')
|
request_body = models.TextField(null=True, blank=True, verbose_name='Запрос')
|
||||||
request_method = models.CharField(max_length=100, null=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='Дата запроса')
|
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):
|
def __str__(self):
|
||||||
return self.request_url
|
return self.request_url
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -152,27 +152,9 @@ CONSTANCE_CONFIG = {
|
||||||
'ENTRYPOINT_AUTH': ('https://amniss-ai.ru/api/auth', 'Адрес для авторизации', 'char'),
|
'ENTRYPOINT_AUTH': ('https://amniss-ai.ru/api/auth', 'Адрес для авторизации', 'char'),
|
||||||
'ENTRYPOINT_DELETE': ('https://amniss-ai.ru/api/v.1/records/delete/%s', 'Адрес для удаления', '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'),
|
'ENTRYPOINT_RECORD': ('https://amniss-ai.ru/api/v.1/records/%s', 'Адрес записи', 'char'),
|
||||||
'SCENARIO_ID': ('', 'ID сценария', 'char'),
|
|
||||||
'USERNAME': ('', 'Имя пользователя', 'char'),
|
'USERNAME': ('', 'Имя пользователя', 'char'),
|
||||||
'PASSWORD': ('', 'Пароль', 'char'),
|
'PASSWORD': ('', 'Пароль', 'char'),
|
||||||
'IS_ENABLED': (False, 'Активен', bool),
|
'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_TOKEN': ('', 'Токен', 'char'),
|
||||||
'MEDICINE_HOST': ('http://medicine-app/', 'Хост', 'char'),
|
'MEDICINE_HOST': ('http://medicine-app/', 'Хост', 'char'),
|
||||||
|
|
@ -181,18 +163,12 @@ CONSTANCE_CONFIG = {
|
||||||
CONSTANCE_CONFIG_FIELDSETS = (
|
CONSTANCE_CONFIG_FIELDSETS = (
|
||||||
('API', (
|
('API', (
|
||||||
'IS_ENABLED',
|
'IS_ENABLED',
|
||||||
'CALL_TIME_START',
|
|
||||||
'CALL_TIME_END',
|
|
||||||
'SCENARIO_ID',
|
|
||||||
'USERNAME',
|
'USERNAME',
|
||||||
'PASSWORD',
|
'PASSWORD',
|
||||||
'ENTRYPOINT_AUTH',
|
'ENTRYPOINT_AUTH',
|
||||||
'ENTRYPOINT_ADD_RECORDS',
|
'ENTRYPOINT_ADD_RECORDS',
|
||||||
'ENTRYPOINT_DELETE',
|
'ENTRYPOINT_DELETE',
|
||||||
'ENTRYPOINT_RECORD',
|
'ENTRYPOINT_RECORD',
|
||||||
'ACCESS_TOKEN',
|
|
||||||
'REFRESH_TOKEN',
|
|
||||||
'ACCESS_TOKEN_EXPIRED',
|
|
||||||
)),
|
)),
|
||||||
('МИС', (
|
('МИС', (
|
||||||
'MEDICINE_HOST',
|
'MEDICINE_HOST',
|
||||||
|
|
@ -216,12 +192,17 @@ CELERYBEAT_SCHEDULE = {
|
||||||
},
|
},
|
||||||
'check_call_requests_task': {
|
'check_call_requests_task': {
|
||||||
'task': 'appa.tasks.check_call_requests_task',
|
'task': 'appa.tasks.check_call_requests_task',
|
||||||
'schedule': crontab(minute='*/5'),
|
'schedule': crontab(minute='*/1'),
|
||||||
'options': {'queue': CELERY_DEFAULT_QUEUE}
|
'options': {'queue': CELERY_DEFAULT_QUEUE}
|
||||||
},
|
},
|
||||||
'send_daily_call_request_task': {
|
#'send_daily_call_request_task': {
|
||||||
'task': 'appa.tasks.send_daily_call_request_task',
|
# 'task': 'appa.tasks.send_daily_call_request_task',
|
||||||
'schedule': crontab(hour='10', minute='0'),
|
# '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}
|
'options': {'queue': CELERY_DEFAULT_QUEUE}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -229,13 +210,21 @@ CELERYBEAT_SCHEDULE = {
|
||||||
DATE_FORMAT = 'd.m.Y'
|
DATE_FORMAT = 'd.m.Y'
|
||||||
DATETIME_FORMAT = 'd.m.Y H:i'
|
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 = [
|
DATABASE_ROUTERS = [
|
||||||
'appa.medicine.router.MedicineRouter',
|
'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:
|
if DEBUG:
|
||||||
AUTH_PASSWORD_VALIDATORS = []
|
AUTH_PASSWORD_VALIDATORS = []
|
||||||
SESSION_COOKIE_NAME = 'dev-medicine-call'
|
SESSION_COOKIE_NAME = 'dev-medicine-call'
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import pytz
|
import pytz
|
||||||
import datetime
|
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.celery import app
|
||||||
from appa.models import *
|
from appa.models import *
|
||||||
from appa.call_api import api as call_api
|
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:
|
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)
|
@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:
|
if lpu is None or speciality is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if not booking['patient']['phone_mobile']:
|
||||||
|
continue
|
||||||
|
|
||||||
CallRequest.objects.update_or_create(
|
CallRequest.objects.update_or_create(
|
||||||
defaults=dict(
|
defaults=dict(
|
||||||
date=datetime.datetime.strptime(booking['date_start'].split('T')[0], '%Y-%m-%d').date(),
|
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):
|
def check_call_requests_task(self):
|
||||||
for call_request in CallRequest.objects.filter(
|
for call_request in CallRequest.objects.filter(
|
||||||
request_status=CallRequest.RequestStatus.SENT,
|
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)
|
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()
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ django-constance==3.1.0
|
||||||
django-environ==0.11.2
|
django-environ==0.11.2
|
||||||
django-ninja==1.2.0
|
django-ninja==1.2.0
|
||||||
django-picklefield==3.2
|
django-picklefield==3.2
|
||||||
|
django-redis==5.4.0
|
||||||
idna==3.8
|
idna==3.8
|
||||||
kombu==5.4.0
|
kombu==5.4.0
|
||||||
prompt_toolkit==3.0.47
|
prompt_toolkit==3.0.47
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue