release 2.0

This commit is contained in:
2025-12-23 03:09:27 +03:00
parent 16c2622d13
commit f99fd8118c
9 changed files with 115 additions and 47 deletions
BIN
View File
Binary file not shown.
+5 -6
View File
@@ -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)
+5 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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>
+9 -4
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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()">
+6
View File
@@ -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}")