from dataclasses import dataclass, field
from datetime import date, datetime
from enum import Enum
from typing import List, Optional
class Level( Enum) : JUNIOR= "начинающий" ; MIDDLE= "средний" ; SENIOR= "продвинутый"
class Role( Enum) : DEVELOPER= "разработчик" ; TESTER= "тестировщик" ; ANALYST= "аналитик" ; MANAGER= "менеджер"
class Complexity( Enum) : LOW= "низкая" ; MEDIUM= "средняя" ; HIGH= "высокая"
class Priority( Enum) : LOW= "низкий" ; MEDIUM= "средний" ; HIGH= "высокий"
@ dataclass
class Employee:
fio: str ; role: Role; level: Level
def __str__ ( self ) : return f"Сотрудник: {self.fio} | роль: {self.role.value} | категория: {self.level.value}"
@ dataclass
class Task:
description: str ; start: date; finish: date; complexity: Complexity; priority: Priority
assignee: Optional[ Employee] = None ; project_name: Optional[ str ] = None
def __post_init__( self ) :
if self .finish < self .start : raise ValueError ( "Дата окончания задачи не может быть раньше даты начала." )
def __str__ ( self ) :
who = self .assignee .fio if self .assignee else "не назначен"
return ( f"Задача: {self.description} ({self.start} → {self.finish}) | "
f"сложность: {self.complexity.value} | приоритет: {self.priority.value} | исполнитель: {who}" )
@ dataclass
class Project:
name: str ; start: date; finish: date; tasks: List[ Task] = field( default_factory= list )
def add_task( self , task: Task) :
if task.start < self .start or task.finish > self .finish :
raise ValueError ( f"Сроки задачи {task.start}–{task.finish} вне диапазона проекта {self.start}–{self.finish}." )
task.project_name = self .name ; self .tasks .append ( task)
def tasks_in_execution_order( self ) :
pr = { Priority.HIGH :0 , Priority.MEDIUM :1 , Priority.LOW :2 }
return sorted ( self .tasks , key= lambda t:( t.start , pr[ t.priority ] ) )
def __str__ ( self ) : return f"Проект: {self.name} ({self.start} → {self.finish}) | задач: {len(self.tasks)}"
d = lambda s: datetime .strptime ( s, "%Y-%m-%d" ) .date ( )
print ( "=== 1) Создание проекта ===" )
project = Project( "CRM-платформа" , d( "2025-01-10" ) , d( "2025-03-31" ) )
print ( project, "\n " )
print ( "=== 2) Добавление сотрудников ===" )
e1 = Employee( "Иванов И.И." , Role.DEVELOPER , Level.MIDDLE )
e2 = Employee( "Петров П.П." , Role.TESTER , Level.JUNIOR )
print ( e1) ; print ( e2, "\n " )
print ( "=== 3) Добавление задач ===" )
t1 = Task( "API авторизации" , d( "2025-01-15" ) , d( "2025-01-31" ) , Complexity.MEDIUM , Priority.HIGH , e1)
t2 = Task( "Тесты для авторизации" , d( "2025-02-01" ) , d( "2025-02-07" ) , Complexity.LOW , Priority.MEDIUM , e2)
project.add_task ( t1) ; project.add_task ( t2)
print ( t1) ; print ( t2, "\n " )
print ( "=== 4) Вывод задач в порядке выполнения ===" )
for t in project.tasks_in_execution_order ( ) : print ( " -" , t)
print ( )
print ( "=== 5) Негативный сценарий ===" )
try :
bad = Task( "Отчетность" , d( "2025-04-01" ) , d( "2025-04-05" ) , Complexity.LOW , Priority.LOW , e2)
project.add_task ( bad)
except ValueError as ex:
print ( "ОЖИДАЕМАЯ ОШИБКА:" , ex)
ZnJvbSBkYXRhY2xhc3NlcyBpbXBvcnQgZGF0YWNsYXNzLCBmaWVsZApmcm9tIGRhdGV0aW1lIGltcG9ydCBkYXRlLCBkYXRldGltZQpmcm9tIGVudW0gaW1wb3J0IEVudW0KZnJvbSB0eXBpbmcgaW1wb3J0IExpc3QsIE9wdGlvbmFsCgpjbGFzcyBMZXZlbChFbnVtKTogSlVOSU9SPSLQvdCw0YfQuNC90LDRjtGJ0LjQuSI7IE1JRERMRT0i0YHRgNC10LTQvdC40LkiOyBTRU5JT1I9ItC/0YDQvtC00LLQuNC90YPRgtGL0LkiCmNsYXNzIFJvbGUoRW51bSk6IERFVkVMT1BFUj0i0YDQsNC30YDQsNCx0L7RgtGH0LjQuiI7IFRFU1RFUj0i0YLQtdGB0YLQuNGA0L7QstGJ0LjQuiI7IEFOQUxZU1Q9ItCw0L3QsNC70LjRgtC40LoiOyBNQU5BR0VSPSLQvNC10L3QtdC00LbQtdGAIgpjbGFzcyBDb21wbGV4aXR5KEVudW0pOiBMT1c9ItC90LjQt9C60LDRjyI7IE1FRElVTT0i0YHRgNC10LTQvdGP0Y8iOyBISUdIPSLQstGL0YHQvtC60LDRjyIKY2xhc3MgUHJpb3JpdHkoRW51bSk6IExPVz0i0L3QuNC30LrQuNC5IjsgTUVESVVNPSLRgdGA0LXQtNC90LjQuSI7IEhJR0g9ItCy0YvRgdC+0LrQuNC5IgoKQGRhdGFjbGFzcwpjbGFzcyBFbXBsb3llZToKICAgIGZpbzogc3RyOyByb2xlOiBSb2xlOyBsZXZlbDogTGV2ZWwKICAgIGRlZiBfX3N0cl9fKHNlbGYpOiByZXR1cm4gZiLQodC+0YLRgNGD0LTQvdC40Lo6IHtzZWxmLmZpb30gfCDRgNC+0LvRjDoge3NlbGYucm9sZS52YWx1ZX0gfCDQutCw0YLQtdCz0L7RgNC40Y86IHtzZWxmLmxldmVsLnZhbHVlfSIKCkBkYXRhY2xhc3MKY2xhc3MgVGFzazoKICAgIGRlc2NyaXB0aW9uOiBzdHI7IHN0YXJ0OiBkYXRlOyBmaW5pc2g6IGRhdGU7IGNvbXBsZXhpdHk6IENvbXBsZXhpdHk7IHByaW9yaXR5OiBQcmlvcml0eQogICAgYXNzaWduZWU6IE9wdGlvbmFsW0VtcGxveWVlXT1Ob25lOyBwcm9qZWN0X25hbWU6IE9wdGlvbmFsW3N0cl09Tm9uZQogICAgZGVmIF9fcG9zdF9pbml0X18oc2VsZik6CiAgICAgICAgaWYgc2VsZi5maW5pc2ggPCBzZWxmLnN0YXJ0OiByYWlzZSBWYWx1ZUVycm9yKCLQlNCw0YLQsCDQvtC60L7QvdGH0LDQvdC40Y8g0LfQsNC00LDRh9C4INC90LUg0LzQvtC20LXRgiDQsdGL0YLRjCDRgNCw0L3RjNGI0LUg0LTQsNGC0Ysg0L3QsNGH0LDQu9CwLiIpCiAgICBkZWYgX19zdHJfXyhzZWxmKToKICAgICAgICB3aG8gPSBzZWxmLmFzc2lnbmVlLmZpbyBpZiBzZWxmLmFzc2lnbmVlIGVsc2UgItC90LUg0L3QsNC30L3QsNGH0LXQvSIKICAgICAgICByZXR1cm4gKGYi0JfQsNC00LDRh9CwOiB7c2VsZi5kZXNjcmlwdGlvbn0gKHtzZWxmLnN0YXJ0fSDihpIge3NlbGYuZmluaXNofSkgfCAiCiAgICAgICAgICAgICAgICBmItGB0LvQvtC20L3QvtGB0YLRjDoge3NlbGYuY29tcGxleGl0eS52YWx1ZX0gfCDQv9GA0LjQvtGA0LjRgtC10YI6IHtzZWxmLnByaW9yaXR5LnZhbHVlfSB8INC40YHQv9C+0LvQvdC40YLQtdC70Yw6IHt3aG99IikKCkBkYXRhY2xhc3MKY2xhc3MgUHJvamVjdDoKICAgIG5hbWU6IHN0cjsgc3RhcnQ6IGRhdGU7IGZpbmlzaDogZGF0ZTsgdGFza3M6IExpc3RbVGFza109ZmllbGQoZGVmYXVsdF9mYWN0b3J5PWxpc3QpCiAgICBkZWYgYWRkX3Rhc2soc2VsZiwgdGFzazogVGFzayk6CiAgICAgICAgaWYgdGFzay5zdGFydCA8IHNlbGYuc3RhcnQgb3IgdGFzay5maW5pc2ggPiBzZWxmLmZpbmlzaDoKICAgICAgICAgICAgcmFpc2UgVmFsdWVFcnJvcihmItCh0YDQvtC60Lgg0LfQsNC00LDRh9C4IHt0YXNrLnN0YXJ0feKAk3t0YXNrLmZpbmlzaH0g0LLQvdC1INC00LjQsNC/0LDQt9C+0L3QsCDQv9GA0L7QtdC60YLQsCB7c2VsZi5zdGFydH3igJN7c2VsZi5maW5pc2h9LiIpCiAgICAgICAgdGFzay5wcm9qZWN0X25hbWU9c2VsZi5uYW1lOyBzZWxmLnRhc2tzLmFwcGVuZCh0YXNrKQogICAgZGVmIHRhc2tzX2luX2V4ZWN1dGlvbl9vcmRlcihzZWxmKToKICAgICAgICBwciA9IHtQcmlvcml0eS5ISUdIOjAsIFByaW9yaXR5Lk1FRElVTToxLCBQcmlvcml0eS5MT1c6Mn0KICAgICAgICByZXR1cm4gc29ydGVkKHNlbGYudGFza3MsIGtleT1sYW1iZGEgdDoodC5zdGFydCwgcHJbdC5wcmlvcml0eV0pKQogICAgZGVmIF9fc3RyX18oc2VsZik6IHJldHVybiBmItCf0YDQvtC10LrRgjoge3NlbGYubmFtZX0gKHtzZWxmLnN0YXJ0fSDihpIge3NlbGYuZmluaXNofSkgfCDQt9Cw0LTQsNGHOiB7bGVuKHNlbGYudGFza3MpfSIKCmQgPSBsYW1iZGEgczogZGF0ZXRpbWUuc3RycHRpbWUocywiJVktJW0tJWQiKS5kYXRlKCkKCnByaW50KCI9PT0gMSkg0KHQvtC30LTQsNC90LjQtSDQv9GA0L7QtdC60YLQsCA9PT0iKQpwcm9qZWN0ID0gUHJvamVjdCgiQ1JNLdC/0LvQsNGC0YTQvtGA0LzQsCIsIGQoIjIwMjUtMDEtMTAiKSwgZCgiMjAyNS0wMy0zMSIpKQpwcmludChwcm9qZWN0LCAiXG4iKQoKcHJpbnQoIj09PSAyKSDQlNC+0LHQsNCy0LvQtdC90LjQtSDRgdC+0YLRgNGD0LTQvdC40LrQvtCyID09PSIpCmUxID0gRW1wbG95ZWUoItCY0LLQsNC90L7QsiDQmC7QmC4iLCBSb2xlLkRFVkVMT1BFUiwgTGV2ZWwuTUlERExFKQplMiA9IEVtcGxveWVlKCLQn9C10YLRgNC+0LIg0J8u0J8uIiwgUm9sZS5URVNURVIsIExldmVsLkpVTklPUikKcHJpbnQoZTEpOyBwcmludChlMiwgIlxuIikKCnByaW50KCI9PT0gMykg0JTQvtCx0LDQstC70LXQvdC40LUg0LfQsNC00LDRhyA9PT0iKQp0MSA9IFRhc2soIkFQSSDQsNCy0YLQvtGA0LjQt9Cw0YbQuNC4IiwgZCgiMjAyNS0wMS0xNSIpLCBkKCIyMDI1LTAxLTMxIiksIENvbXBsZXhpdHkuTUVESVVNLCBQcmlvcml0eS5ISUdILCBlMSkKdDIgPSBUYXNrKCLQotC10YHRgtGLINC00LvRjyDQsNCy0YLQvtGA0LjQt9Cw0YbQuNC4IiwgZCgiMjAyNS0wMi0wMSIpLCBkKCIyMDI1LTAyLTA3IiksIENvbXBsZXhpdHkuTE9XLCBQcmlvcml0eS5NRURJVU0sIGUyKQpwcm9qZWN0LmFkZF90YXNrKHQxKTsgcHJvamVjdC5hZGRfdGFzayh0MikKcHJpbnQodDEpOyBwcmludCh0MiwgIlxuIikKCnByaW50KCI9PT0gNCkg0JLRi9Cy0L7QtCDQt9Cw0LTQsNGHINCyINC/0L7RgNGP0LTQutC1INCy0YvQv9C+0LvQvdC10L3QuNGPID09PSIpCmZvciB0IGluIHByb2plY3QudGFza3NfaW5fZXhlY3V0aW9uX29yZGVyKCk6IHByaW50KCIgLSIsIHQpCnByaW50KCkKCnByaW50KCI9PT0gNSkg0J3QtdCz0LDRgtC40LLQvdGL0Lkg0YHRhtC10L3QsNGA0LjQuSA9PT0iKQp0cnk6CiAgICBiYWQgPSBUYXNrKCLQntGC0YfQtdGC0L3QvtGB0YLRjCIsIGQoIjIwMjUtMDQtMDEiKSwgZCgiMjAyNS0wNC0wNSIpLCBDb21wbGV4aXR5LkxPVywgUHJpb3JpdHkuTE9XLCBlMikKICAgIHByb2plY3QuYWRkX3Rhc2soYmFkKQpleGNlcHQgVmFsdWVFcnJvciBhcyBleDoKICAgIHByaW50KCLQntCW0JjQlNCQ0JXQnNCQ0K8g0J7QqNCY0JHQmtCQOiIsIGV4KQ==