PMS_integrations admin template
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
from django.contrib import admin
|
||||
# Register your models here.
|
||||
from .manager import PluginLoader
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.urls import path
|
||||
from django.utils.html import format_html
|
||||
from django.shortcuts import render
|
||||
from django import forms
|
||||
from django.utils.html import format_html
|
||||
from django.http import HttpResponseRedirect
|
||||
from .manager import PluginLoader
|
||||
from pms_integration.models import PMSConfiguration, PMSIntegrationLog
|
||||
|
||||
from django import forms
|
||||
from pms_integration.utils import get_all_plugins
|
||||
from django.urls import reverse
|
||||
|
||||
class PMSConfigurationForm(forms.ModelForm):
|
||||
class Meta:
|
||||
@@ -16,7 +16,6 @@ class PMSConfigurationForm(forms.ModelForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# Загружаем доступные плагины
|
||||
plugins = PluginLoader.load_plugins()
|
||||
plugin_choices = [(plugin_name, plugin_name) for plugin_name in plugins.keys()]
|
||||
self.fields['plugin_name'] = forms.ChoiceField(choices=plugin_choices, required=False)
|
||||
@@ -24,18 +23,37 @@ class PMSConfigurationForm(forms.ModelForm):
|
||||
@admin.register(PMSConfiguration)
|
||||
class PMSConfigurationAdmin(admin.ModelAdmin):
|
||||
form = PMSConfigurationForm
|
||||
list_display = ('name', 'plugin_name', 'created_at')
|
||||
list_display = ('name', 'plugin_name', 'created_at', 'check_plugins_button')
|
||||
search_fields = ('name', 'plugin_name')
|
||||
ordering = ('-created_at',)
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
# Проверка на наличие плагина
|
||||
plugins = PluginLoader.load_plugins()
|
||||
if obj.plugin_name and obj.plugin_name not in plugins.keys():
|
||||
raise ValueError(f"Выберите корректный плагин. '{obj.plugin_name}' нет среди допустимых значений.")
|
||||
super().save_model(request, obj, form, change)
|
||||
def check_plugins_button(self, obj=None):
|
||||
"""
|
||||
Возвращает кнопку для проверки плагинов.
|
||||
"""
|
||||
url = reverse('admin:check-plugins') # Генерируем URL с помощью reverse
|
||||
return format_html(
|
||||
'<a class="button" href="{}">Проверить плагины</a>',
|
||||
url
|
||||
)
|
||||
check_plugins_button.short_description = "Действия"
|
||||
|
||||
def check_plugins_view(self, request, *args, **kwargs):
|
||||
"""
|
||||
Custom admin view to check and display available plugins.
|
||||
"""
|
||||
plugins = get_all_plugins()
|
||||
return render(request, "pms_integration/admin/check_plugins.html", {"plugins": plugins})
|
||||
|
||||
def get_urls(self):
|
||||
"""
|
||||
Add custom URLs to the admin panel.
|
||||
"""
|
||||
urls = super().get_urls()
|
||||
custom_urls = [
|
||||
path("check-plugins/", self.admin_site.admin_view(self.check_plugins_view), name="check-plugins"),
|
||||
]
|
||||
return custom_urls + urls
|
||||
@admin.register(PMSIntegrationLog)
|
||||
class PMSIntegrationLogAdmin(admin.ModelAdmin):
|
||||
list_display = ('hotel', 'checked_at', 'status', 'message')
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{% extends "admin/base_site.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Проверка доступных плагинов</h1>
|
||||
<p>Ниже перечислены плагины, доступные в системе:</p>
|
||||
<ul>
|
||||
{% for plugin_name in plugins.keys %}
|
||||
<li>
|
||||
<strong>{{ plugin_name }}</strong>: {{ plugins[plugin_name].__doc__ }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p>
|
||||
<a class="button" href="{% url 'admin:index' %}">Вернуться в админку</a>
|
||||
</p>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,104 @@
|
||||
{% extends 'admin/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-xl-10 col-xxl-9">
|
||||
<div class="card shadow">
|
||||
<div class="card-header d-flex flex-wrap justify-content-center align-items-center justify-content-sm-between gap-3">
|
||||
<h5 class="display-6 text-nowrap text-capitalize mb-0">Загруженные плагины PMS систем</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Plugin Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for plugin_name, plugin_info in plugins.items %}
|
||||
<tr>
|
||||
<td>{{ plugin_name }}</td>
|
||||
<td>{{ plugin_info.description|default:"No description available" }}</td>
|
||||
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="3" class="text-center">Нет доступных плагинов.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="{% url 'admin:index' %}" class="btn btn-primary">Вернуться в админку</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="pluginCodeModal" tabindex="-1" aria-labelledby="pluginCodeModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="pluginCodeModalLabel">Подробнее</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h6><strong>Имя класса:</strong> <span id="className"></span></h6>
|
||||
<h6><strong>Модуль:</strong> <span id="module"></span></h6>
|
||||
<hr>
|
||||
<h6><strong>Код:</strong></h6>
|
||||
<pre><code id="pluginCode"></code></pre>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Настройка модального окна
|
||||
const modalElement = document.getElementById('pluginCodeModal');
|
||||
const modal = new bootstrap.Modal(modalElement);
|
||||
|
||||
// Обработка кнопки открытия
|
||||
document.querySelectorAll('.view-plugin').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const pluginName = button.getAttribute('data-plugin-name');
|
||||
const pluginCode = button.getAttribute('data-plugin-code') || 'Code unavailable';
|
||||
const className = button.getAttribute('data-class-name') || 'Class name unavailable';
|
||||
const module = button.getAttribute('data-module') || 'Module unavailable';
|
||||
|
||||
// Установка данных в модальное окно
|
||||
document.getElementById('pluginCodeModalLabel').textContent = `Plugin: ${pluginName}`;
|
||||
document.getElementById('pluginCode').textContent = pluginCode;
|
||||
document.getElementById('className').textContent = className;
|
||||
document.getElementById('module').textContent = module;
|
||||
|
||||
modal.show();
|
||||
});
|
||||
});
|
||||
|
||||
// Убираем размытие после закрытия
|
||||
modalElement.addEventListener('hidden.bs.modal', () => {
|
||||
const backdrop = document.querySelector('.modal-backdrop');
|
||||
if (backdrop) backdrop.remove();
|
||||
document.body.classList.remove('modal-open');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
21
pms_integration/utils.py
Normal file
21
pms_integration/utils.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from pms_integration.admin import PluginLoader
|
||||
|
||||
def get_all_plugins():
|
||||
"""
|
||||
Загружает все плагины и возвращает их данные.
|
||||
:return: Словарь с данными о плагинах.
|
||||
"""
|
||||
plugins_data = {}
|
||||
try:
|
||||
# Загружаем плагины
|
||||
plugins = PluginLoader.load_plugins()
|
||||
|
||||
for plugin_name, plugin_class in plugins.items():
|
||||
plugins_data[plugin_name] = {
|
||||
"class_name": plugin_class.__name__, # Имя класса
|
||||
"description": plugin_class.__doc__, # Описание из DocString
|
||||
"module": plugin_class.__module__, # Модуль, где определён класс
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Ошибка загрузки данных о плагинах: {e}")
|
||||
return plugins_data
|
||||
@@ -62,8 +62,8 @@ ROOT_URLCONF = 'touchh.urls'
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'DIRS': [], # Вы можете оставить пустым или указать глобальные пути к шаблонам
|
||||
'APP_DIRS': True, # Включаем автоматический поиск шаблонов в приложениях
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
|
||||
Reference in New Issue
Block a user