Compare commits
1 Commits
2a35a9ce78
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
816c9f1cda |
BIN
__pycache__/models.cpython-312.pyc
Normal file
BIN
__pycache__/models.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/storage.cpython-312.pyc
Normal file
BIN
__pycache__/storage.cpython-312.pyc
Normal file
Binary file not shown.
141
calendar_cli.py
Normal file
141
calendar_cli.py
Normal file
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Personal Calendar - CLI приложение для управления событиями
|
||||
"""
|
||||
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from models import Event
|
||||
from storage import load_events, add_event, remove_event, get_events_by_date
|
||||
|
||||
try:
|
||||
from prettytable import PrettyTable
|
||||
except ImportError:
|
||||
print("Ошибка: установите prettytable: pip install prettytable")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def show_menu():
|
||||
"""Показывает главное меню"""
|
||||
print("\n" + "=" * 40)
|
||||
print(" 📅 PERSONAL CALENDAR")
|
||||
print("=" * 40)
|
||||
print("1. Добавить событие")
|
||||
print("2. Показать все события")
|
||||
print("3. Показать события на дату")
|
||||
print("4. Удалить событие")
|
||||
print("5. Выйти")
|
||||
print("=" * 40)
|
||||
|
||||
|
||||
def add_event_interactive():
|
||||
"""Интерактивное добавление события"""
|
||||
print("\n--- Добавление события ---")
|
||||
title = input("Название: ").strip()
|
||||
if not title:
|
||||
print("Ошибка: название не может быть пустым")
|
||||
return
|
||||
|
||||
date = input("Дата (ГГГГ-ММ-ДД): ").strip()
|
||||
try:
|
||||
datetime.strptime(date, "%Y-%m-%d")
|
||||
except ValueError:
|
||||
print("Ошибка: неверный формат даты")
|
||||
return
|
||||
|
||||
time = input("Время (ЧЧ:ММ) - необязательно: ").strip()
|
||||
description = input("Описание: ").strip()
|
||||
|
||||
event = Event(title, date, time, description)
|
||||
add_event(event)
|
||||
print(f"✅ Событие добавлено: {event}")
|
||||
|
||||
|
||||
def show_all_events():
|
||||
"""Показывает все события в виде таблицы"""
|
||||
events = load_events()
|
||||
|
||||
if not events:
|
||||
print("\n📭 Нет сохранённых событий")
|
||||
return
|
||||
|
||||
table = PrettyTable()
|
||||
table.field_names = ["№", "Дата", "Время", "Название", "Описание"]
|
||||
table.align = "l"
|
||||
|
||||
for i, event in enumerate(events, 1):
|
||||
table.add_row([
|
||||
i,
|
||||
event.date,
|
||||
event.time if event.time else "-",
|
||||
event.title[:30],
|
||||
event.description[:40] if event.description else "-"
|
||||
])
|
||||
|
||||
print("\n" + str(table))
|
||||
|
||||
|
||||
def show_events_by_date():
|
||||
"""Показывает события на конкретную дату"""
|
||||
date = input("\nВведите дату (ГГГГ-ММ-ДД): ").strip()
|
||||
|
||||
try:
|
||||
datetime.strptime(date, "%Y-%m-%d")
|
||||
except ValueError:
|
||||
print("Ошибка: неверный формат даты")
|
||||
return
|
||||
|
||||
events = get_events_by_date(date)
|
||||
|
||||
if not events:
|
||||
print(f"\n📭 Нет событий на {date}")
|
||||
return
|
||||
|
||||
print(f"\n📅 События на {date}:")
|
||||
for i, event in enumerate(events, 1):
|
||||
print(f" {i}. {event}")
|
||||
|
||||
|
||||
def remove_event_interactive():
|
||||
"""Интерактивное удаление события"""
|
||||
show_all_events()
|
||||
events = load_events()
|
||||
|
||||
if not events:
|
||||
return
|
||||
|
||||
try:
|
||||
index = int(input("\nВведите номер события для удаления: ")) - 1
|
||||
if remove_event(index):
|
||||
print("✅ Событие удалено")
|
||||
else:
|
||||
print("❌ Неверный номер")
|
||||
except ValueError:
|
||||
print("Ошибка: введите число")
|
||||
|
||||
|
||||
def main():
|
||||
"""Основная функция"""
|
||||
print("Добро пожаловать в Personal Calendar!")
|
||||
|
||||
while True:
|
||||
show_menu()
|
||||
choice = input("\nВыберите действие (1-5): ").strip()
|
||||
|
||||
if choice == "1":
|
||||
add_event_interactive()
|
||||
elif choice == "2":
|
||||
show_all_events()
|
||||
elif choice == "3":
|
||||
show_events_by_date()
|
||||
elif choice == "4":
|
||||
remove_event_interactive()
|
||||
elif choice == "5":
|
||||
print("\n👋 До свидания!")
|
||||
break
|
||||
else:
|
||||
print("❌ Неверный выбор. Попробуйте снова.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
8
calendar_data.json
Normal file
8
calendar_data.json
Normal file
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"title": "Встреча",
|
||||
"date": "2026-05-20",
|
||||
"time": "15:00",
|
||||
"description": "Созвон с командой"
|
||||
}
|
||||
]
|
||||
35
models.py
Normal file
35
models.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
class Event:
|
||||
"""Класс события календаря"""
|
||||
|
||||
def __init__(self, title: str, date: str, time: str = "", description: str = ""):
|
||||
self.title = title
|
||||
self.date = date # формат YYYY-MM-DD
|
||||
self.time = time # формат HH:MM
|
||||
self.description = description
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Преобразует событие в словарь для JSON"""
|
||||
return {
|
||||
"title": self.title,
|
||||
"date": self.date,
|
||||
"time": self.time,
|
||||
"description": self.description
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: dict) -> 'Event':
|
||||
"""Создаёт событие из словаря"""
|
||||
return cls(
|
||||
title=data["title"],
|
||||
date=data["date"],
|
||||
time=data.get("time", ""),
|
||||
description=data.get("description", "")
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
time_str = f" в {self.time}" if self.time else ""
|
||||
return f"{self.date}{time_str}: {self.title} - {self.description}"
|
||||
44
storage.py
Normal file
44
storage.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import json
|
||||
import os
|
||||
from typing import List
|
||||
from models import Event
|
||||
|
||||
DATA_FILE = "calendar_data.json"
|
||||
|
||||
def load_events() -> List[Event]:
|
||||
"""Загружает события из JSON файла"""
|
||||
if not os.path.exists(DATA_FILE):
|
||||
return []
|
||||
|
||||
try:
|
||||
with open(DATA_FILE, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
return [Event.from_dict(item) for item in data]
|
||||
except (json.JSONDecodeError, FileNotFoundError):
|
||||
return []
|
||||
|
||||
def save_events(events: List[Event]) -> None:
|
||||
"""Сохраняет события в JSON файл"""
|
||||
with open(DATA_FILE, 'w', encoding='utf-8') as f:
|
||||
data = [event.to_dict() for event in events]
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
def add_event(event: Event) -> None:
|
||||
"""Добавляет новое событие"""
|
||||
events = load_events()
|
||||
events.append(event)
|
||||
save_events(events)
|
||||
|
||||
def remove_event(index: int) -> bool:
|
||||
"""Удаляет событие по индексу (начиная с 0)"""
|
||||
events = load_events()
|
||||
if 0 <= index < len(events):
|
||||
del events[index]
|
||||
save_events(events)
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_events_by_date(date: str) -> List[Event]:
|
||||
"""Возвращает события на конкретную дату"""
|
||||
events = load_events()
|
||||
return [e for e in events if e.date == date]
|
||||
Reference in New Issue
Block a user