
    t"j                         d Z ddlmZ ddl ddl ddlZddlZddl ddl	Z	ddl
ZddZd Z G d d      Z ed	d
d      	 	 	 	 	 dd       ZdddddZd Zd Zd Zd Zd Zd Zy)u  
Модуль экспорта данных в различные форматы файлов (CSV, XML, XLSX).

Основная задача - выгрузка данных из моделей CMF в файлы с поддержкой различных форматов.
    )defaultdict)*Nc                    ddl m}m} t        j                  j	                         }	|	j                  d      }
dt        j                  j                   d|
 d| }t        j                  t        j                  |      }|j                         5  |j                          ddd        |t        | |j                  |||||d|	       |j                  S # 1 sw Y   7xY w)
u  
    Публичная функция для запуска отложенной задачи экспорта данных.
    
    Создает attachment для результата и запускает фоновую задачу export2file_task.
    
    Args:
        class_name: Имя класса модели CMF для экспорта
        field_names: Список полей для экспорта (если None - все видимые поля)
        bql: BQL фильтр для выборки данных
        format_file: Формат файла ('csv', 'xml', 'xlsx')
        include_archived: Включать ли архивные записи
        order_by: Поле для сортировки
        **kwargs: Дополнительные параметры (roadmap_ordered_ids, roadmap_level_of и т.д.)
        
    Returns:
        URL созданного attachment с результатом
    r   )schedule_deferred_jobcmfutilz%Y%m%d%H%M%Szexport..)parentnameN)
class_nameres_attachment_idfield_namesbqlformat_fileinclude_archivedorder_by)kwargs)cmf.includer   r   datetimenowstrftimegcurrent_useridmodelsCmfAttachmentdisable_aclsaveexport2file_taskurl)r   r   r   r   r   r   r   r   r   r   formatted_timeexport_filename
attachments                ./cmf/util/cmf_export.pyexport2filer$      s    $ ; 




!C \\.1N   1 12!N3C1[MRO%%Q^^/%RJ				 **ISCN;>CNHX@H4> 7=4> ? >> s   CC c                     | syt         j                  j                  |       }|j                         j	                         S )u  
    Удаляет HTML теги из строки и возвращает чистый текст.
    
    Использует lxml для быстрого парсинга HTML. Если парсинг не удался,
    возвращает исходную строку как есть.
    
    Args:
        html_content: Строка с HTML разметкой
        
    Returns:
        Строка без HTML тегов
     )lxmlhtml
fromstringtext_contentstrip)html_contentdocs     r#   _strip_htmlr.   @   s6     
))

|
,C##%%    c                   H    e Zd ZdZd ZdefdZd ZddZd Z	d	 Z
d
efdZy)ExportDataManageruc  
    Менеджер для управления процессом экспорта данных.
    
    Инкапсулирует логику работы с данными: пагинация, проверка прав доступа,
    конвертация значений полей, получение метаданных полей.
    c                 d    || _         || _        || _        || _        || _        ddlm} || _        y)u  
        Инициализация менеджера экспорта.
        
        Args:
            cls: Класс модели CMF для экспорта
            bql: BQL фильтр для выборки данных
            include_archived: Включать ли архивные записи
            order_by: Поле для сортировки
            forced_captions: Словарь принудительных заголовков полей
        r   r   N)clsr   r   r   forced_captionsr   r   )selfr4   r   r   r   r5   r   s          r#   __init__zExportDataManager.__init__[   s3      0 .'r/   returnc                 n   	 | j                   j                  |j                        }|j                  }t	        |t
        j                  j                        xr |j                  }t	        |t
        j                  j                        xr |j                  }t	        |t
        j                  j                        xr |j                  }t	        |t
        j                  j                        xr |j                  }t        j                  j                  ||j                  ||j                  ||d|d	      S )uC  
        Проверяет права доступа к объекту для чтения.
        
        Args:
            obj: Объект модели CMF для проверки
            
        Returns:
            True если доступ разрешен, False в противном случае
        readF)	initial_acl_keyobject_modelobject_owner_id	object_idobject_parent_idobject_dictaccess_levelperm_security_level_allowed_idsraise_error)r   get_model_by_namer   __dict__
issubclasscmfr   	CmfEntityperm_effective_acl_idCmfModelcmf_owner_id	parent_id%perm_security_level_allowed_ids_cacheCmfAccessListcheck_accessr   )r6   objmodelobj_dictr;   obj_owner_idobj_parent_idrM   s           r#   check_access_objz"ExportDataManager.check_access_objn   s    	 ..s~~><<$UCJJ,@,@A_cF_F_!%)<)<=R#BRBR"5#***=*=>P3==0:5#**BVBV0W  1F\_  ]F  ]F-##00+#..Zfff}(,Q_d	 1 
 	
r/   c              #     K   ddl m} d}d}d|vrdg|z   }g d}g }t        | j                  |      r|ddgz  }	 | j                  j	                  | j
                  |||z   g||z   |z   | j                  | j                        }|sy
||z  }|D ]T  }| j                  |      st        | j                  d	      r| j                  j                  |      }	n|g}	|	D ]  }
|
  V w)uP  
        Генератор для постраничной загрузки данных с проверкой прав доступа.
        
        Загружает данные пакетами по 50000 записей, фильтрует по правам доступа
        и возвращает только разрешённые записи.
        
        Args:
            fields: Список полей для экспорта. Если '--' отсутствует, добавляется в начало.
            
        Yields:
            Записи, к которым пользователь имеет доступ на чтение.
            
        Note:
            Для проверки доступа автоматически подключаются служебные поля:
            - cmf_owner_id, cmf_author_id — владелец и автор
            - access_author, access_responsible — права автора и ответственного
            - perm_security_level_id — уровень безопасности
            - responsible_id, spectators, executors — исполнители и наблюдатели
            - project_id, parent_id — иерархия
            - perm_effective_acl_id, perm_security_level_allowed_ids_cache — кэш ACL
        r   CmfActiveEntityiP  --)rK   access_authorcmf_author_idperm_security_level_idaccess_responsibleresponsible_id
spectators	executors
project_idrL   rI   rM   
logic_typeactivity)filterslicefieldsr   r   export_hookN)common.models.cmf_active_entityrX   rF   r4   slistr   r   r   rU   hasattrrg   )r6   rf   rX   startstepsecurity_fieldspreload_fieldsdatarowrowssub_rows              r#   paginate_datazExportDataManager.paginate_data   s    , 	DvVf_F

 dhh0 |Z88N88>>$)54<#8$*_$<~$M.2.C.C&*mm	 " 5D
 TME ",,S1488]388//4D5D# "G!M"" s   C$C&Nc                    |yt        |d      rt        |j                        S t        |d      rt        |j                        S t	        |t
              r.dj                  |D cg c]  }| j                  |       c}      S |r^t        |t        j                  j                        r:||n|j                  }|t        |      n|}|j                  |t        |            S |rXt        |t        j                  j                        r4||n|j                  }|j                  |t        |      |      S ||      S |rt        |      S dS c c}w )u  
        Конвертирует значение поля в строку для экспорта.
        
        Обрабатывает различные типы данных: None, объекты с атрибутами name/id,
        списки, поля CmfChoiceInt и CmfChoice.
        
        Args:
            val: Значение поля
            field_obj: Объект поля (опционально)
            choices_override: Альтернативный словарь choices из ui_form_custom,
                              со строковыми ключами (CmfJson)
            
        Returns:
            Строковое представление значения
        r&   r
   r   ,)rj   strr
   r   
isinstancelistjoinconvert_valrF   rG   rf   CmfChoiceIntchoicesget	CmfChoice)r6   val	field_objchoices_overriderp   r|   
lookup_keys          r#   rz   zExportDataManager.convert_val   s      ;S&!sxx= S$svv;T"88cBsT--c2BCC:i1H1HI*:*F&IL]L]G%5%ASsJ;;z3s844:i1E1EF*:*F&IL]L]G;;+;+Gs3xRUVVSRUVV"3s8** Cs   %Ec                     t        ||      rt        ||      S |}|j                  d      D ]  }| yt        ||d      } |S )u  
        Получает поле объекта. Работает с вложенными полями через точку.
        
        Например: get_included_attr(doc, 'person.second_name')
        
        Args:
            obj: Объект модели
            field_str: Имя поля (может содержать точку для вложенных полей)
            
        Returns:
            Значение поля или None
        r   N)rj   getattrsplit)r6   rP   	field_strcurfields        r#   get_included_attrz#ExportDataManager.get_included_attr   sU     3	"3	**__S) 	,E{#ud+C	, 
r/   c                    |j                  d      }|}t        |      D ]  \  }}t        ||d      }|s't        |d      r|j                  j                  |      }|t        |      dz
  k(  s|s|c S t        |d      r)t        t              j                  |j                        }t        |d      r8|j                  r,t        t              j                  |j                  d         } y y)un  
        Рекурсивно получает объект поля, учитывая вложенность через точку.
        
        Args:
            class_obj: Класс модели
            field_name: Имя поля с учетом вложенности
            
        Returns:
            Объект поля или None
        r   Nrf      rQ   r   r   )
r   	enumerater   rj   rf   r}   lenvarsr   rQ   )r6   	class_obj
field_namepartscurrent_objipartr   s           r#   get_field_objzExportDataManager.get_field_obj  s       % ' 	GAtT48Ih!?'..2248	CJN")  y'*"6l..y?H-)2B2B"6l..y/?/?/BC	 r/   fields_namec                 T      fdi }|D ]  }  j                   |      ||<    |S )u  
        Получает заголовки (caption) для списка полей.
        
        Учитывает forced_captions, вложенные поля и связанные сущности.
        
        Args:
            fields_name: Список имен полей
            
        Returns:
            Словарь {имя_поля: заголовок}
        c           
         |
j                   v r
j                   |   S |j                  d      }| j                  j                  |d         }ddi}|sY| j                  |j                         v r=t        t              || j                        }|j                  j                  |d         }|}t        t        t                    }t        j                  j                  |d<   |r|j                  }t        |      dkD  rft        |d      r|j                  r|j                  d   }nt        |d      r|j                  }n|S | d 	||   dj                  |dd               }|S )	Nr   r   CmfTestplanTestcaseCmfTestcaserH   r   r   rQ   )r5   r   rf   r}   r   keysr   r   copyrG   rH   captionr   rj   rQ   ry   )r   r   pural_namesr   linked_entitylinked_class_objres
models_cls
model_nameget_captionr6   s            r#   r   z3ExportDataManager.get_captions.<locals>.get_caption-  sQ   T111++J77$**3/K$$((Q8E%}M Y11]5G5G5II#'<i>R>R0S#T (//33KNCCd6l+J&)jj&:&:J{#mm{#a'uh/ELL%*\\!_
 0%*[[
"
 E;z*/EsxxP[\]\^P_G`#a"bcCJr/   )r4   )r6   r   r   r   r   s   `   @r#   get_captionszExportDataManager.get_captions!  s9    	6 % 	@J)$((J?C
O	@
r/   )NN)__name__
__module____qualname____doc__r7   boolrU   rs   rz   r   r   rx   r    r/   r#   r1   r1   S   s;    &
t 
B9"v!+F,<* *r/   r1   u'   Экспорт данных в файл   T)descriptionpriorityshow_bg_progressbarc                    ddl m | dk(  rd} ddddd	}t        t              |    t        j                  j                  |d
g      }	t        ||||      s?j                  j                         D 
cg c]  }
|
j                  s|
j                   c}
t               }r|j                  d       t               D ]  }r|dk(  rj                  g d       nj|dk(  rd}d|d<   n]j                  j                  |      rBt        j                  j                  |      t        j                  j                         r|dz  }|j                  |        |j#                        D 
ci c]  }
|
j%                  |
       c}
i fdfdfd}fd}fd}ddlm} t+        j,                         5 }	 | d| }|dk(  r	 ||       n*|dk(  r	 ||       n|dk(  r	 ||       nt/        d|       t1        |d      5 }|j3                         5  |	j5                  |dd        d!d!d!       d!d!d!       d!d!d!       y!c c}
w c c}
w # 1 sw Y   $xY w# 1 sw Y   (xY w# t6        $ r#}t8        j;                  d"| d#| d$%        d!}~ww xY w# 1 sw Y   y!xY w)&zN
    https://bcrm.carbonsoft.ru/project/Document/DOC-007693#spec-007668-b
    r   rW   
CmfRoadmapCmfTasku   Индикаторыu   №u   Предшественникиu   Последователи)custom_column_indicatorscustom_column_sequence_numbercustom_column_predecessorscustom_column_followers	file_type)r   rf   r   r   )z op_gantt_task.actual_finish_datezop_gantt_task.sched_finish_datez"op_gantt_task.constrain_start_typez"op_gantt_task.constrain_start_datez#op_gantt_task.constrain_finish_typez#op_gantt_task.constrain_finish_datedeadlinetimetracker_historyzop_gantt_task.actual_workuG   Журнал работ.Фактические трудозатратыz.namec           	      f   t              syt        | dd      }|syt        |      t        t        | dd            t        t        | dd            |f}|v r|   S j                  |       }d}|r8|j	                  di       j	                  |      r|d   |   j	                  d      }||<   |S )uh   Возвращает choices для поля строки с учётом ui_form_custom проекта.Nra   logic_type_idactivity_id	ui_fieldsr|   )rF   r   rv   _build_ui_formr}   )	rp   r   ra   	cache_keyui_form_customresultrX   _row_choices_cacher4   s	         r#   get_row_field_choicesz/export2file_task.<locals>.get_row_field_choices  s    #/S,5

O_d34]D12	
	 **%i00++C0n00bAEEjQ#K0<@@KF(.9%r/   c                     |rGt        |t        j                  j                  t        j                  j                  f      r	 | |      S y)u_   Возвращает choices_override для choice-полей; None для остальных.N)rF   rG   rf   r{   r~   )rp   r   r   r   s      r#   get_choices_overridez.export2file_task.<locals>.get_choices_override  s8    I

0G0GI]I]/^_(j99r/   c                    ddl }t        | dd      5 }|j                  |dd|j                        }g }t	        j                              D ]  \  }}g }D ]  }|dk(  rT|j                  j                  |             |j                  d	      r#|j                  j                  |       d
       j                  ||      }	j                  |      }
 |||
      }j                  |	|
|      }|j                  |       |j                  d	      s|j                  t        |              |dk(  r|j                  |       |j                  |        	 ddd       y# 1 sw Y   yxY w)u   
        Записывает данные в файл формата CSV.
        
        Args:
            file_path: Путь к файлу для записи
        r   Nzw+r&   )newline;")	delimiter	quotecharquotingtext    без html)csvopenwriterQUOTE_MINIMALr   rs   appendr}   endswithr   rz   r.   writerow)	file_pathr   csvfiler   csv_headr   rp   csv_rowr   field_valuer   r   val_strcaptionsdata_managerr   field_objects_cacher   s                r#   
_write_csvz$export2file_task.<locals>._write_csv  sZ    	)T2. 	)'ZZ3#sO`O`ZaFH#L$>$>{$KL )3( =EAv U(;< >>&1$OOx||E/B.C<,PQ"."@"@e"LK 3 7 7 >I';C	'R$*66{IO_`GNN7+ ~~f-{7';<=" 6OOH-(+)	) 	) 	)s   DE*AE**E3c                 
   ddl m} ddlm} |j	                  d      }|j                  dd       |j	                  d      }|j                  |       |j	                  d      }d	|_        |j                  |       |j	                  d
      }dt               v rt        j                  nd|_        |j                  |       |j	                  d      }d|_        |j                  |       |j	                  d      }d|_        |j                  |       |j	                  d      }	|	j                  dd       |j                  |	       |j	                  d      }
|j                  |
       d}t        j                              D ]  \  }}|j	                  d      }t               }|j                  |       D ]G  }|j                  d      }j                  j!                  |d         }|d   |vr1|j	                  |d         ||d   <   |j                  ||d             |j!                  |d         }t#        ||d   d      }|rt%        |t&        j                  j(                        r|st+        t,              |j.                     j                  D ]K  }|dk(  r|j0                  |_        t3        ||      s'|j                  |t5        t#        ||                   M |j                  d|j6                         J|rt%        |t&        j                  j8                  t&        j                  j:                  f      r|s|j                  d|j6                         |D ]Z  }|j	                  d      }|j                  dt5        |j<                               |j0                  |_        |j                  |       \ |rt%        |t&        j                  j>                  t&        j                  j@                  f      r|O |||      }t%        |t&        j                  j>                        r7||n|jB                  }|t5        |      n|}|j!                  |d      |_        n4||n|jB                  }|j!                  |t5        |      n|d      |_        |j                  d|j6                         t5        jE                  ||            |_        |j                  dj!                  ||             J  |	j                  dt5        |             |	j                  dt5        |              ||       jG                  |jI                  |             y)u   
        Записывает данные в файл формата XML (RSS 0.92).
        
        Args:
            file_path: Путь к файлу для записи
        r   )ElementTree)Pathrssversionz0.92channeltitleEvaTeamlinkrequestr&   r   u+   XML представление данныхlanguagezru-rutaskrk   0
build_infoitemr   Nr
   r   valuer   endtotal)%	xml.etreer   pathlibr   Elementsetr   r   globalsr   r   r   rs   dictr   rf   r}   r   rF   rG   CmfRelationBaser   r   r   r
   rj   rv   r   
CmfM2MBaseCmfBackrefBaser   r{   r~   r|   r   write_bytestostring)r   ETr   r   r   r   r   r   r   r   r   r   rp   r   elementsr   r   r   
field_infor   keyvr   r   r|   r   r   r4   r   r   r   s                             r#   
_write_xmlz$export2file_task.<locals>._write_xml  sT    	0 jj	6"**Y'

7

7#
uzz&!#,	#9GKKr	tjj/H{#::j)x zz&!#tZZ-
z" : :; GH ,	TFAs::f%DvHNN4 ) (T
(..s3

{1~6"1~1/1zz+a./IH[^,KKQ 89%\\+a.9
%c;q>4@Zszz/I/IJ"#'<0F0F#G#N#N TC"f}2=2B2B
 (&{C8 *sCS8Q4R ST #y%--@z%#**2G2GIbIb1cd""y%--@!, 5A$&JJw$7E!IIdCI6)*EJ&--e4	5
 z%#**2I2I3::K_K_1`a"* ';CU'S$!%)@)@A6F6R"2X]XeXe9I9US%5[f
*1++j"*E
6F6R"2X]XeXe*1++JZJfc+6Flwy{*|
NN9emm<&),*H*Hj*Y&ZJONN9hll:z.RSQ(T	,	T\ 	A#a&!Y##BKK$45r/   c                 `   ddl m} |j                         }|j                  d_        g d fd	}rt               t               dv sdv rt               \  !j                  dg       }!j                  d	t                     !j                  d
t                     j                  z         D ci c]  }t        |j                        | }}!j                  d      xs g }|rg }g }	|D ]  }
|
j                  d      dk(  rH|
j                  dd      }|	D cg c]  }|j                  dd      |k  r| }	}|	j                  |
       _t        |
j                  d            }|j                  |      s|j                  |	       g }	|j                  i |
d|i        i t        |d      D ]5  \  }}
|
j                  d      }|
j                  d      dk(  s,|vs1||<   7 t        |      D ]  \  }}
|
j                  d      dk(  rDD ci c]  }|d }}dv r|dz   |d<   dv r|
j                  d      xs d|d<    |d||       ^|
j                  d      }|j                  |d      }|s |||||dz           nhd}t        |d      D ]*  \  }}|j                  |d      }|s |||||       |dz  }, n+t        j                              D ]  \  }} |||        |j                  |        yc c}w c c}w c c}w )u  
        Записывает данные в файл формата XLSX.
        
        Поддерживает группировку строк (outlineLevel) для roadmap.
        
        Args:
            file_path: Путь к файлу для записи
        r   )workbooku   ВыгрузкаNc           	         |xs i }g }D ]  }|dk(  rGj                  j                  |             |dv r#j                  j                  |       d       ||v r6||   }|j                  |       |dv so|j                  t        |             r|dk(  r|j                  |dz          r1|dk(  r,|j                  t        j                  |g                    ىr2|dk(  r-|j                  t        j                  |g                    r!|dk(  r|j                  t	        |              0j                  | |      }j                  |      }	 | ||	      }
j                  ||	|
      }|j                  |       |dv s|j                  t        |              |dk(  rj                         j                  |       r|r|rj                  t        ||f      d      }j                  }j                  |   }t        |d	      |_        |dkD  |_        j                  |d
      }|j                  d|dz  z   |j                   |_        yyyyy)u  Пишет одну XLSX-строку: обычную задачу или synthetic group-header.

            `override_values` нужен для строк группировки: у них нет CMF-объекта задачи,
            поэтому значения колонок («№», «Наименование», пустые id/code) передаются явно.
            r   )r   result_textr   r   r   r   r   r      )rp   columnN    )r   r}   r.   _format_relations_indicator_textr   rz   rv   max_rowrow_dimensionsminoutlineLevelhiddencellr   )row_objrow_idxtidseqoverride_valuesr   r   r   r   r   r   depthxl_rowrd
first_cellr   r   r   r   r   followers_offrom_roadmapr   predecessors_ofroadmap_level_ofroadmap_sequence_map
sheet_xlsxs                  r#   	write_rowz8export2file_task.<locals>._write_xlsx.<locals>.write_row0  sa    .3OG$ =a<OOHLL$78 77 8<<+>*?|(LMO+-e4GNN7+ 77{7';<!e/N&NNN7Q;/!e/K&KNN#4_5H5Hb5QSg#hi!e/H&HNN#4\5E5Ec25NPd#ef!e/I&INN?7#;<"."@"@%"PK 3 7 7 >I';GUI'V$*66{IO_`GNN7+ 77{7';<7=: !|!!(+g&(,,S#s_a@#++..v6"%eQ-!AI	'___B
##/*-*;)<Z=M=M<N'OJ$ 0 ),|r/   r   r   roadmap_ordered_idsr  r   roadmap_group_rowstypegroupr  r   r   )rk   r   r&   r   r
   u   Не назначено)r  )NNN)openpyxlr  Workbookactiver   r   $_prepare_predecessors_followers_mapsr}   rs   rv   r   r   extendr   r   )"r   r  	book_xlsxr"  r#  rP   	id_to_objr$  visible_export_rowspending_group_rows
export_rowr  rp   task_idsequence_numberr   r   r  r  r  r   r  r  r  r   r!  additional_fieldsr   r   r   r   r  r   r   s"                       @@@@@@r#   _write_xlsxz%export2file_task.<locals>._write_xlsx   s    	&%%'	%%
-
1	P 1	P 1	Pf 6L"fO+{:>W[f>f0T0V-o"(**-BB"G%zz*<dfE#)::.Ddf#M 5A5O5OP[^oPo5pqcSVVc)qIq!',@!A!GR! ')#%'""4 RJ!~~f-8 *w :+=.$'"www2U:  .* . +11*= !*.."67G }}W-+223EF-/*+223Pj3P$3PQR  (*$ 4==PXY3Z H/OZ(nnT2G!~~f-7GK_<_8G,W5H
 &//B%C 2MAz!~~f-8 CN*N5"9*N*N:kIOPSTuO,KL![06@nnV6L6iPiOF3!$?K $...C#--T2C c1c1q51!2$  )*=Q G HC#--T2C c1c3/FA $L$>$>{$KL "3#q!" 	y!y r.2 +Os   L!6L&0
L+r3   z/export.r   xmlxlsxu0   Формат не поддерживается: rbF)backupmake_previewNzError during export to z: ERROR)level)rh   rX   r   r   r   r}   r1   rf   valuesvisibler   rx   r   r+  rF   rG   
CmfRelBaser   r   r   r   tempfileTemporaryDirectory
ValueErrorr   r   upload_stream_file	Exceptionr   debug)r   r   r   r   r   r   r   r   r5   r"   r   
new_fieldsr   r   r  r4  r   tmpdirr   ferX   r   r3  r   r4   r   r   r  r   r   s     `    `             @@@@@@@@@@r#   r   r   M  s    @-L
$:).&F#?	O v,z
"C%%))->})UJ$S#/??[L58ZZ5F5F5HZEEMMu''Z J9:! &
J*DD$$ &2 3 004J <EO78ZZ^^J'Jszz~~j7QSVS]S]ShSh,i'!J*%& K((5H WbbU5,"<"<S%"HHb , )  )DW6 W6rI" I"T $		$	$	& &	!((;-8Ie#9%%9%&I& #ST_S`!abbi& W!((* W11!EPU1VWW 	 [. cn	W WW W
  	GG-k]"QC@GP	% sm   I1I1I6%K'AJ2JI;J J;J JJ	J	J?J::J??KKu   ОНu   ООu   ННu   НО)zsystem.finish:startzsystem.finish:finishzsystem.start:startzsystem.start:finishc                     t        t              } t        t              }g d}ddt        t        j                               g}t        j
                  j                  ||      D ]  }t        j                  |j                  j                  d      }| |j                     j                  |j                  |f       ||j                     j                  |j                  |f        | |fS )u  
    Подготавливает карты связей предшественников и последователей для roadmap.

    Загружает все 4 типа связей (ОН/ОО/НН/НО) и строит словари:
    - followers_of: {id_задачи: [(id_последователя, code), ...]}
    - predecessors_of: {id_задачи: [(id_предшественника, code), ...]}

    Returns:
        Кортеж (followers_of, predecessors_of)
    )rY   out_link_id
in_link_idrelation_type.coderL  IN)rf   rd   r&   )r   rx   RELATION_CODE_LABELr   r   CmfRelationOptionri   r}   relation_typecoderJ  r   rK  )r  r  _fields_filterrelrQ  s         r#   r*  r*    s     t$L!$'OGG#T40C0H0H0J+KLG''--WW-M H"&&s'8'8'='=rBS__%,,cnnd-CD'../FGH ((r/   c                     g }| D ]J  \  }}|j                  |      }||j                  t        |            }|6|j                  | |        L dj                  |      S )uQ   
    Формирует строку '5ОО; 7ОН' из списка пар
    z; )r}   rv   r   ry   )pairssequence_mapr   	linked_idrQ  r  s         r#   r  r    so     E  )	4y);""3y>2C?LLC5() 99Ur/   c                 2    dddd}|j                  | |       S )u@  
    Получить текстовое название для типа ограничения начала задачи.
    
    Args:
        code: Код типа ограничения ('0-const', '3-after', '4-before')
        
    Returns:
        Текстовое название ограничения
    u   Фиксированная   Не раньше   Не позже0-constz3-afterz4-beforer}   )rQ  start_restriction_names     r#   _get_start_restriction_labler`    s*     4*)
 "%%dD11r/   c                 2    dddd}|j                  | |       S )uF  
    Получить текстовое название для типа ограничения окончания задачи.
    
    Args:
        code: Код типа ограничения ('0-const', '1-after', '2-before')
        
    Returns:
        Текстовое название ограничения
    u   ФиксированныйrZ  r[  r]  z1-afterz2-beforer^  )rQ  finish_restriction_lables     r#   _get_finish_restriction_lablerd    s*     4*) 
 $''d33r/   c                    t        | t        j                        r| j                  d      S t        | t        j                        rDt        j                  | j                  | j
                  | j                        j                  d      S y)u   
    Форматирует дату в строку dd.mm.yyyy (как на фронтенде).
    
    Args:
        d: Объект datetime или date
        
    Returns:
        Отформатированная строка даты
    z%d.%m.%Yr&   )rw   r   r   dateyearmonthday)ds    r#   _format_daterk    s`     !X&&'zz*%%!X]]#  !%%8AA*MMr/   c                 V   t        | dd      }|syt        |dd      }|rdt        |       dS g }t        |dd      }|dv rCt        |      }d	| d
}t        |dd      }|r|dt        |       dz  }|j                  |       t        |dd      }|dv rCt	        |      }d	| d}t        |dd      }	|	r|dt        |	       dz  }|j                  |       t        | dd      }
t        |dd      }|r%|
r#||
kD  r|j                  dt        |
       d       dj                  |      S )u  
    Формирует текстовое описание индикатора состояния задачи roadmap.
    
    Включает информацию о завершении задачи, ограничениях по датам начала и окончания,
    а также предупреждения о нарушении дедлайна.
    
    Args:
        instance: Объект задачи (CmfTask)
        
    Returns:
        Текстовое описание индикатора
    op_gantt_taskNr&   actual_finish_dateu   Была завершена "r   constrain_start_typer\  u/   Установлено ограничение "u    " для "Дата начала"constrain_start_dateu    на "constrain_finish_typerb  u&   " для "Дата окончания"constrain_finish_dater   sched_finish_dateuS   Указана дата окончания позже крайнего срока "
)r   rk  r`  r   rd  ry   )instance
gantt_taskactual_finishlines	cst_valuelabellinecsd	cft_valuecfdr   sched_finishs               r#   r  r  '  s    ?D9J J(<dCM.|M/J.K1MME 
$:DAI66,Y7@Gghj"8$?gl3/022DT 
$;TBI55-i8@Gmnj"94@gl3/022DT xT2H:':DAL\H%<jkw  yA  lB  kC  CD  E  	F99Ur/   )NNNr   FN)NNr   FN)r   collectionsr   r   
cmf.configr   
cmf.fieldsrG   r   r?  	lxml.htmlr'   r$   r.   r1   cmf_deferred_jobr   rN  r*  r  r`  rd  rk  r  r   r/   r#   <module>r     s    $       )Z&&x xt GRSimn "&!&&+"q oqr "" !	 ),2$4$"3r/   