release 2.0
This commit is contained in:
@@ -324,7 +324,7 @@ def api_birthdate():
|
||||
match request.method:
|
||||
case "POST":
|
||||
reqData = request.json
|
||||
userUpdate = reqData["userUpdate"]
|
||||
userUpdate = reqData.get("userUpdate", None)
|
||||
if userUpdate:
|
||||
logger.info("Обновлен пользователь")
|
||||
userId = userUpdate["userId"]
|
||||
@@ -338,7 +338,7 @@ def api_birthdate():
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при обновлении пользователя: {e}")
|
||||
return jsonify({"status": "error"}), 500
|
||||
scheduleSettings = reqData["scheduleSettings"]
|
||||
scheduleSettings = reqData.get("scheduleSettings")
|
||||
if scheduleSettings:
|
||||
logger.info("Обновлены настройки расписания")
|
||||
try:
|
||||
@@ -554,13 +554,13 @@ def login():
|
||||
protection = Protection.query.first()
|
||||
if not protection:
|
||||
protection = Protection()
|
||||
protection.set_password(password)
|
||||
protection.set_password(new_password)
|
||||
protection.generate_token()
|
||||
|
||||
db.session.add(protection)
|
||||
db.session.commit()
|
||||
else:
|
||||
protection.set_password(password)
|
||||
protection.set_password(new_password)
|
||||
db.session.commit()
|
||||
|
||||
return {"status": "ok"}
|
||||
@@ -595,5 +595,4 @@ def check_auth_cookie():
|
||||
|
||||
if __name__ == "__main__":
|
||||
init_app()
|
||||
app.run(debug=True, host="0.0.0.0", port=80)
|
||||
# app.run(host="0.0.0.0", port=80)
|
||||
app.run(host="0.0.0.0", port=80)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
/* Таблица сотрудников */
|
||||
.table-responsive {
|
||||
max-height: 800px;
|
||||
max-height: 950px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -77,22 +77,22 @@
|
||||
}
|
||||
|
||||
.status-enabled {
|
||||
background-color: rgba(25, 135, 84, 0.1);
|
||||
background-color: rgba(25, 135, 84, 0.5);
|
||||
color: #198754;
|
||||
}
|
||||
|
||||
.status-disabled {
|
||||
background-color: rgba(108, 117, 125, 0.1);
|
||||
background-color: rgba(108, 117, 125, 0.2);
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.status-data {
|
||||
background-color: rgba(13, 110, 253, 0.1);
|
||||
background-color: rgba(13, 110, 253, 0.5);
|
||||
color: #0d6efd;
|
||||
}
|
||||
|
||||
.status-nodata {
|
||||
background-color: rgba(220, 53, 69, 0.1);
|
||||
background-color: rgba(220, 53, 69, 0.5);
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
|
||||
+56
-7
@@ -7,6 +7,8 @@ let schedulerFormChanged = false;
|
||||
let originalUserData = null;
|
||||
let originalSchedulerData = null;
|
||||
let pendingUserSwitch = null;
|
||||
const monthNames = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'];
|
||||
|
||||
|
||||
// Инициализация при загрузке страницы
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
@@ -18,6 +20,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
// Устанавливаем обработчики событий
|
||||
setupEventListeners();
|
||||
|
||||
});
|
||||
|
||||
// Загрузка списка сотрудников
|
||||
@@ -28,6 +31,8 @@ async function loadUsersList() {
|
||||
|
||||
if (data.status === 'ok') {
|
||||
usersData = data.users;
|
||||
// Определение ближайшего дня рождения
|
||||
findNearestBirthday();
|
||||
renderUsersTable();
|
||||
} else {
|
||||
showAlert('danger', 'Ошибка загрузки списка сотрудников');
|
||||
@@ -39,6 +44,53 @@ async function loadUsersList() {
|
||||
}
|
||||
}
|
||||
|
||||
function findNearestBirthday() {
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
let nextBirthday = null;
|
||||
let nextBirthdayUserData = null;
|
||||
|
||||
for (const user of usersData) {
|
||||
if (!user.enabled || !user.birthdate) continue;
|
||||
|
||||
const birthdate = new Date(user.birthdate);
|
||||
|
||||
// День рождения в текущем году
|
||||
let candidate = new Date(
|
||||
today.getFullYear(),
|
||||
birthdate.getMonth(),
|
||||
birthdate.getDate()
|
||||
);
|
||||
|
||||
// Если уже прошёл — переносим на следующий год
|
||||
if (candidate < today) {
|
||||
candidate.setFullYear(today.getFullYear() + 1);
|
||||
}
|
||||
|
||||
if (!nextBirthday || candidate < nextBirthday) {
|
||||
nextBirthday = candidate;
|
||||
|
||||
const age = candidate.getFullYear() - birthdate.getFullYear();
|
||||
|
||||
nextBirthdayUserData = {
|
||||
date: new Intl.DateTimeFormat('ru-RU', { month: 'long', day: 'numeric' })
|
||||
.format(new Date(user.birthdate)),
|
||||
name: user.name,
|
||||
age
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const nextBirthdayElement = document.getElementById('nextBirthday');
|
||||
if (nextBirthdayUserData) {
|
||||
nextBirthdayElement.textContent = `${nextBirthdayUserData.date} ${nextBirthdayUserData.name} исполнится ${nextBirthdayUserData.age} лет 🎉`;
|
||||
}
|
||||
|
||||
return nextBirthdayUserData;
|
||||
}
|
||||
|
||||
|
||||
// Отображение таблицы сотрудников
|
||||
function renderUsersTable() {
|
||||
const tbody = document.getElementById('usersTableBody');
|
||||
@@ -81,14 +133,11 @@ function renderUsersTable() {
|
||||
const birthdate = new Date(user.birthdate);
|
||||
const now = new Date();
|
||||
const age = now.getFullYear() - birthdate.getFullYear();
|
||||
const month = birthdate.getMonth() + 1;
|
||||
const day = birthdate.getDate();
|
||||
const fullDate = birthdate.toLocaleDateString('ru-RU');
|
||||
|
||||
// Форматируем месяц и день (двузначные)
|
||||
const monthNames = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'];
|
||||
const monthStr = monthNames[month - 1];
|
||||
const dayStr = day.toString().padStart(2, '0');
|
||||
const dateText = new Intl.DateTimeFormat('ru-RU', { month: 'long', day: 'numeric' })
|
||||
.format(new Date(user.birthdate));
|
||||
|
||||
// Определяем пол
|
||||
const sexBadge = user.sex === 'male' ?
|
||||
@@ -121,7 +170,7 @@ function renderUsersTable() {
|
||||
<tr class="${isSelected}" onclick="selectUser(${user.id})" data-user-id="${user.id}">
|
||||
<td>
|
||||
<div class="birthdate-cell">
|
||||
<span class="month-day">${dayStr} ${monthStr}</span>
|
||||
<span class="month-day">${dateText}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@@ -134,7 +183,7 @@ function renderUsersTable() {
|
||||
</td>
|
||||
<td>${sexBadge}</td>
|
||||
<td>${specialtiesHtml}</td>
|
||||
<td class="text-center">${enabledStatus} ${dataStatus}</td>
|
||||
<td class="text-center">${enabledStatus} ${user.enabled ? dataStatus : ''}</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path.startswith('/posts') %}active{% endif %}" href="/posts">
|
||||
<i class="bi bi-file-earmark-text me-2"></i>Посты
|
||||
<i class="bi bi-file-earmark-text me-2"></i>Публикации
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
@@ -204,12 +204,17 @@
|
||||
<!-- Информация о следующем запуске -->
|
||||
{% if data.schedulerStatus.next_run_time %}
|
||||
<div class="alert alert-success">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bi bi-calendar-event fs-5 me-3"></i>
|
||||
<div>
|
||||
<h6 class="alert-heading mb-1">Следующий запуск</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-5">
|
||||
<h6 class="alert-heading mb-1"><i class="bi bi-calendar-event fs-5 me-2"></i>Следующий
|
||||
запуск</h6>
|
||||
<p class="mb-0">{{ data.schedulerStatus.next_run_time }}</p>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<h6 class="alert-heading mb-1"><i class="bi bi-calendar-week fs-5 me-2"></i>Ближайший
|
||||
день рождения</h6>
|
||||
<p class="mb-0"><span id="nextBirthday"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
+20
-11
@@ -172,7 +172,7 @@
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="bi bi-calendar-week text-warning me-2"></i>Планировщик</h5>
|
||||
<h5 class="mb-0"><i class="bi bi-calendar-week text-warning me-2"></i>Планировщик Публикаций</h5>
|
||||
{% if exitData.vkPost and exitData.vkPost.scheduler %}
|
||||
{% if exitData.vkPost.scheduler.scheduler %}
|
||||
<span class="badge bg-success"><i class="bi bi-play-circle me-1"></i>Активен</span>
|
||||
@@ -278,7 +278,7 @@
|
||||
<!-- Статистика и ссылки -->
|
||||
<div class="row g-4 mb-4">
|
||||
<!-- Быстрые действия -->
|
||||
<div class="col-lg-6">
|
||||
<div class="col-lg-7">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">
|
||||
@@ -287,7 +287,7 @@
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<div class="col-md-3">
|
||||
<a href="/medods"
|
||||
class="btn btn-outline-info w-100 py-4 d-flex flex-column align-items-center justify-content-center">
|
||||
<i class="bi bi-plug fs-1 mb-2"></i>
|
||||
@@ -296,33 +296,43 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="col-md-3">
|
||||
<a href="/vk"
|
||||
class="btn btn-outline-success w-100 py-4 d-flex flex-column align-items-center justify-content-center">
|
||||
<i class="bi bi-megaphone fs-1 mb-2"></i>
|
||||
<i class="bi bi-chat-dots fs-1 mb-2"></i>
|
||||
<span class="fw-semibold">VK API</span>
|
||||
<small class="text-muted text-center">Настройки VK</small>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="col-md-3">
|
||||
<a href="/posts"
|
||||
class="btn btn-outline-warning w-100 py-4 d-flex flex-column align-items-center justify-content-center">
|
||||
<i class="bi bi-calendar-week fs-1 mb-2"></i>
|
||||
<i class="bi bi-file-earmark-text fs-1 mb-2"></i>
|
||||
<span class="fw-semibold">Публикации</span>
|
||||
<small class="text-muted text-center">Управление</small>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<a href="/birthdate"
|
||||
class="btn btn-outline-primary w-100 py-4 d-flex flex-column align-items-center justify-content-center">
|
||||
<i class="bi bi-balloon-heart-fill mb-2 fs-1"></i>
|
||||
<span class="fw-semibold">Дни рождения</span>
|
||||
<small class="text-muted text-center">Управление</small>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Последний пост VK -->
|
||||
<div class="col-lg-6">
|
||||
<div class="col-lg-5">
|
||||
<div class="card h-100">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="bi bi-send text-success me-2"></i>Последняя публикация</h5>
|
||||
<h5 class="mb-0"><i class="bi bi-send text-success me-2"></i>Последняя публикация слотов</h5>
|
||||
{% if exitData.vkPost and exitData.vkPost.post_link %}
|
||||
<span class="badge bg-success"><i class="bi bi-check-circle me-1"></i>Опубликовано</span>
|
||||
{% else %}
|
||||
@@ -337,8 +347,7 @@
|
||||
<i class="bi bi-check-circle-fill fs-4"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="alert-heading mb-1">Пост успешно опубликован</h6>
|
||||
<p class="mb-2">Ссылка на публикацию в VK</p>
|
||||
<h6 class="alert-heading mb-1">Пост со списком свободных слотов успешно опубликован</h6>
|
||||
<a href="{{ exitData.vkPost.post_link }}" target="_blank"
|
||||
class="btn btn-sm btn-outline-success">
|
||||
<i class="bi bi-box-arrow-up-right me-1"></i>Открыть в VK
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<!-- Заголовок -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="mb-1"><i class="bi bi-file-earmark-text text-primary me-2"></i>Управление постами</h2>
|
||||
<h2 class="mb-1"><i class="bi bi-file-earmark-text text-primary me-2"></i>Управление публикациями</h2>
|
||||
<p class="text-muted mb-0">Создание и планирование публикаций в VK</p>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success btn-lg px-5" onclick="saveSettings()">
|
||||
|
||||
@@ -87,6 +87,10 @@ def handle_vk_birthdate():
|
||||
vk = vk_session.get_api()
|
||||
|
||||
for user in birthdayUsers:
|
||||
if not user.congratulations or not user.photo_link:
|
||||
logger.error(f"Пользователь {user.short_name} не настроен для публикации")
|
||||
continue
|
||||
try:
|
||||
new_post = vk.wall.post(
|
||||
owner_id=-vkApi.group_id,
|
||||
from_group=1,
|
||||
@@ -102,3 +106,5 @@ def handle_vk_birthdate():
|
||||
user.publish_at = datetime.now()
|
||||
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при публикации поста: {e}")
|
||||
|
||||
Reference in New Issue
Block a user