@c8y/ngx-components 1021.11.1 → 1021.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/alarms/alarms-date-filter.component.d.ts +34 -0
  2. package/alarms/alarms-date-filter.component.d.ts.map +1 -0
  3. package/alarms/alarms-filter.component.d.ts +2 -1
  4. package/alarms/alarms-filter.component.d.ts.map +1 -1
  5. package/alarms/alarms-list.component.d.ts +2 -17
  6. package/alarms/alarms-list.component.d.ts.map +1 -1
  7. package/alarms/alarms-view.service.d.ts +5 -2
  8. package/alarms/alarms-view.service.d.ts.map +1 -1
  9. package/alarms/alarms.component.d.ts +3 -1
  10. package/alarms/alarms.component.d.ts.map +1 -1
  11. package/alarms/alarms.model.d.ts +47 -1
  12. package/alarms/alarms.model.d.ts.map +1 -1
  13. package/alarms/alarms.module.d.ts +15 -13
  14. package/alarms/alarms.module.d.ts.map +1 -1
  15. package/branding/shared/data/store-branding.service.d.ts +5 -0
  16. package/branding/shared/data/store-branding.service.d.ts.map +1 -1
  17. package/branding/shared/lazy/branding/branding.component.d.ts.map +1 -1
  18. package/context-dashboard/context-dashboard.model.d.ts +1 -0
  19. package/context-dashboard/context-dashboard.model.d.ts.map +1 -1
  20. package/context-dashboard/context-dashboard.service.d.ts +7 -5
  21. package/context-dashboard/context-dashboard.service.d.ts.map +1 -1
  22. package/context-dashboard/dashboard-detail.component.d.ts +4 -7
  23. package/context-dashboard/dashboard-detail.component.d.ts.map +1 -1
  24. package/context-dashboard/dashboard-detail.service.d.ts +4 -4
  25. package/context-dashboard/dashboard-detail.service.d.ts.map +1 -1
  26. package/context-dashboard/dashboard-settings/dashboard-general-settings.component.d.ts +2 -2
  27. package/context-dashboard/dashboard-settings/dashboard-general-settings.component.d.ts.map +1 -1
  28. package/context-dashboard/dashboard-settings/typed-dashboard-settings.component.d.ts +19 -6
  29. package/context-dashboard/dashboard-settings/typed-dashboard-settings.component.d.ts.map +1 -1
  30. package/core/action-bar/action-bar-item.component.d.ts +1 -1
  31. package/core/action-bar/action-bar-item.component.d.ts.map +1 -1
  32. package/core/action-bar/action-bar.module.d.ts +6 -6
  33. package/core/action-bar/action-bar.module.d.ts.map +1 -1
  34. package/core/bootstrap/bootstrap.component.d.ts +3 -1
  35. package/core/bootstrap/bootstrap.component.d.ts.map +1 -1
  36. package/core/common/aggregation/aggregation.model.d.ts +39 -0
  37. package/core/common/aggregation/aggregation.model.d.ts.map +1 -0
  38. package/core/common/aggregation/aggregation.service.d.ts +17 -0
  39. package/core/common/aggregation/aggregation.service.d.ts.map +1 -0
  40. package/core/common/index.d.ts +4 -0
  41. package/core/common/index.d.ts.map +1 -1
  42. package/core/common/inter-app.service.d.ts +48 -0
  43. package/core/common/inter-app.service.d.ts.map +1 -0
  44. package/core/common/interval-based-reload.abstract.d.ts +120 -0
  45. package/core/common/interval-based-reload.abstract.d.ts.map +1 -0
  46. package/core/dashboard/dashboard.module.d.ts +26 -20
  47. package/core/dashboard/dashboard.module.d.ts.map +1 -1
  48. package/core/dashboard/index.d.ts +3 -0
  49. package/core/dashboard/index.d.ts.map +1 -1
  50. package/core/dashboard/widget-auto-refresh-context/auto-refresh-control.component.d.ts +31 -0
  51. package/core/dashboard/widget-auto-refresh-context/auto-refresh-control.component.d.ts.map +1 -0
  52. package/core/dashboard/widget-auto-refresh-context/auto-refresh-select-control.component.d.ts +17 -0
  53. package/core/dashboard/widget-auto-refresh-context/auto-refresh-select-control.component.d.ts.map +1 -0
  54. package/core/dashboard/widget-auto-refresh-context/global-refresh-loading.operator.d.ts +4 -0
  55. package/core/dashboard/widget-auto-refresh-context/global-refresh-loading.operator.d.ts.map +1 -0
  56. package/core/dashboard/widget-auto-refresh-context/index.d.ts +8 -0
  57. package/core/dashboard/widget-auto-refresh-context/index.d.ts.map +1 -0
  58. package/core/dashboard/widget-auto-refresh-context/widget-auto-refresh-context-icon-bar.component.d.ts +7 -0
  59. package/core/dashboard/widget-auto-refresh-context/widget-auto-refresh-context-icon-bar.component.d.ts.map +1 -0
  60. package/core/dashboard/widget-auto-refresh-context/widget-auto-refresh-context.component.d.ts +29 -0
  61. package/core/dashboard/widget-auto-refresh-context/widget-auto-refresh-context.component.d.ts.map +1 -0
  62. package/core/dashboard/widget-auto-refresh-context/widget-auto-refresh-context.model.d.ts +15 -0
  63. package/core/dashboard/widget-auto-refresh-context/widget-auto-refresh-context.model.d.ts.map +1 -0
  64. package/core/dashboard/widget-auto-refresh-context/widget-global-auto-refresh.service.d.ts +169 -0
  65. package/core/dashboard/widget-auto-refresh-context/widget-global-auto-refresh.service.d.ts.map +1 -0
  66. package/core/dashboard/widget-change-event.model.d.ts +1 -1
  67. package/core/dashboard/widget-change-event.model.d.ts.map +1 -1
  68. package/core/dashboard/widgets-dashboard.component.d.ts +6 -3
  69. package/core/dashboard/widgets-dashboard.component.d.ts.map +1 -1
  70. package/core/dashboard/wiget-time-context/aggregation-picker/aggregation-picker.component.d.ts +2 -2
  71. package/core/dashboard/wiget-time-context/aggregation-picker/aggregation-picker.component.d.ts.map +1 -1
  72. package/core/dashboard/wiget-time-context/widget-time-context-helper.service.d.ts +2 -1
  73. package/core/dashboard/wiget-time-context/widget-time-context-helper.service.d.ts.map +1 -1
  74. package/core/dashboard/wiget-time-context/widget-time-context-icon-bar/widget-time-context-icon-bar.component.d.ts +1 -1
  75. package/core/dashboard/wiget-time-context/widget-time-context-icon-bar/widget-time-context-icon-bar.component.d.ts.map +1 -1
  76. package/core/dashboard/wiget-time-context/widget-time-context-query.service.d.ts.map +1 -1
  77. package/core/dashboard/wiget-time-context/widget-time-context.component.d.ts +12 -8
  78. package/core/dashboard/wiget-time-context/widget-time-context.component.d.ts.map +1 -1
  79. package/core/dashboard/wiget-time-context/widget-time-context.model.d.ts +7 -26
  80. package/core/dashboard/wiget-time-context/widget-time-context.model.d.ts.map +1 -1
  81. package/core/date-time-picker/date-time-picker.component.d.ts.map +1 -1
  82. package/core/header/header.module.d.ts +18 -18
  83. package/core/header/header.module.d.ts.map +1 -1
  84. package/core/header/title/title.component.d.ts +1 -1
  85. package/core/header/title/title.component.d.ts.map +1 -1
  86. package/core/i18n/cached-locale-dictionary.service.d.ts +5 -1
  87. package/core/i18n/cached-locale-dictionary.service.d.ts.map +1 -1
  88. package/core/i18n/i18n.module.d.ts +0 -4
  89. package/core/i18n/i18n.module.d.ts.map +1 -1
  90. package/core/i18n/index.d.ts +2 -1
  91. package/core/i18n/index.d.ts.map +1 -1
  92. package/core/i18n/translation-loader.service.d.ts +50 -0
  93. package/core/i18n/translation-loader.service.d.ts.map +1 -0
  94. package/core/i18n/translation-utils.d.ts +14 -0
  95. package/core/i18n/translation-utils.d.ts.map +1 -0
  96. package/core/plugins/plugins-resolve.service.d.ts +7 -10
  97. package/core/plugins/plugins-resolve.service.d.ts.map +1 -1
  98. package/datapoint-selector/datapoint-attributes-form/datapoint-attributes-form-validation.service.d.ts +2 -1
  99. package/datapoint-selector/datapoint-attributes-form/datapoint-attributes-form-validation.service.d.ts.map +1 -1
  100. package/datapoint-selector/datapoint-attributes-form/datapoint-attributes-form.component.d.ts +11 -2
  101. package/datapoint-selector/datapoint-attributes-form/datapoint-attributes-form.component.d.ts.map +1 -1
  102. package/datapoint-selector/datapoint-selection.model.d.ts +6 -0
  103. package/datapoint-selector/datapoint-selection.model.d.ts.map +1 -1
  104. package/datapoints-export-selector/c8y-ngx-components-datapoints-export-selector.d.ts.map +1 -0
  105. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/data-fetching.service.d.ts +117 -0
  106. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/data-fetching.service.d.ts.map +1 -0
  107. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/data-processing.service.d.ts +90 -0
  108. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/data-processing.service.d.ts.map +1 -0
  109. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.component.d.ts +160 -0
  110. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.component.d.ts.map +1 -0
  111. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.service.d.ts +89 -0
  112. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.service.d.ts.map +1 -0
  113. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-preview/datapoints-export-selector-preview.component.d.ts +11 -0
  114. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-preview/datapoints-export-selector-preview.component.d.ts.map +1 -0
  115. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-exports-selector-data-scope/datapoints-exports-selector-data-scope.component.d.ts +27 -0
  116. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-exports-selector-data-scope/datapoints-exports-selector-data-scope.component.d.ts.map +1 -0
  117. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-exports-selector-file-types/datapoints-exports-selector-file-types.component.d.ts +12 -0
  118. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-exports-selector-file-types/datapoints-exports-selector-file-types.component.d.ts.map +1 -0
  119. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-exports-selector-time-range/datapoints-exports-selector-time-range.component.d.ts +22 -0
  120. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-exports-selector-time-range/datapoints-exports-selector-time-range.component.d.ts.map +1 -0
  121. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/generators/csv-generator.d.ts +3 -0
  122. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/generators/csv-generator.d.ts.map +1 -0
  123. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/generators/excel-generator.d.ts +9 -0
  124. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/generators/excel-generator.d.ts.map +1 -0
  125. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/utils.service.d.ts +56 -0
  126. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/utils.service.d.ts.map +1 -0
  127. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-modal.component.d.ts +21 -0
  128. package/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-modal.component.d.ts.map +1 -0
  129. package/datapoints-export-selector/datapoints-export-selector.component.d.ts +14 -0
  130. package/datapoints-export-selector/datapoints-export-selector.component.d.ts.map +1 -0
  131. package/datapoints-export-selector/datapoints-export-selector.model.d.ts +232 -0
  132. package/datapoints-export-selector/datapoints-export-selector.model.d.ts.map +1 -0
  133. package/datapoints-export-selector/index.d.ts +15 -0
  134. package/datapoints-export-selector/index.d.ts.map +1 -0
  135. package/device-list/device-list.module.d.ts +4 -2
  136. package/device-list/device-list.module.d.ts.map +1 -1
  137. package/esm2022/alarm-event-selector/alarm-event-selection-list/alarm-event-selection-list.component.mjs +4 -4
  138. package/esm2022/alarm-event-selector/alarm-event-selector-list-item/alarm-event-selector-list-item.component.mjs +3 -3
  139. package/esm2022/alarms/alarms-date-filter.component.mjs +139 -0
  140. package/esm2022/alarms/alarms-filter.component.mjs +11 -5
  141. package/esm2022/alarms/alarms-list.component.mjs +5 -16
  142. package/esm2022/alarms/alarms-type-filter.component.mjs +3 -3
  143. package/esm2022/alarms/alarms-view.service.mjs +16 -2
  144. package/esm2022/alarms/alarms.component.mjs +12 -6
  145. package/esm2022/alarms/alarms.model.mjs +17 -1
  146. package/esm2022/alarms/alarms.module.mjs +11 -4
  147. package/esm2022/branding/shared/data/store-branding.service.mjs +24 -1
  148. package/esm2022/branding/shared/lazy/branding/branding.component.mjs +3 -1
  149. package/esm2022/context-dashboard/context-dashboard.component.mjs +4 -4
  150. package/esm2022/context-dashboard/context-dashboard.model.mjs +1 -1
  151. package/esm2022/context-dashboard/context-dashboard.service.mjs +47 -21
  152. package/esm2022/context-dashboard/dashboard-detail.component.mjs +8 -16
  153. package/esm2022/context-dashboard/dashboard-detail.service.mjs +13 -6
  154. package/esm2022/context-dashboard/dashboard-settings/dashboard-general-settings.component.mjs +3 -3
  155. package/esm2022/context-dashboard/dashboard-settings/typed-dashboard-settings.component.mjs +32 -11
  156. package/esm2022/core/action-bar/action-bar-item.component.mjs +3 -3
  157. package/esm2022/core/action-bar/action-bar.component.mjs +3 -3
  158. package/esm2022/core/action-bar/action-bar.module.mjs +16 -5
  159. package/esm2022/core/bootstrap/bootstrap.component.mjs +21 -16
  160. package/esm2022/core/common/aggregation/aggregation.model.mjs +46 -0
  161. package/esm2022/core/common/aggregation/aggregation.service.mjs +34 -0
  162. package/esm2022/core/common/humanize-app-name.model.mjs +2 -2
  163. package/esm2022/core/common/humanize-app-name.pipe.mjs +2 -2
  164. package/esm2022/core/common/index.mjs +5 -1
  165. package/esm2022/core/common/inter-app.service.mjs +76 -0
  166. package/esm2022/core/common/interval-based-reload.abstract.mjs +110 -0
  167. package/esm2022/core/countdown-interval/countdown-interval.component.mjs +3 -3
  168. package/esm2022/core/dashboard/dashboard-child.component.mjs +6 -5
  169. package/esm2022/core/dashboard/dashboard.module.mjs +22 -4
  170. package/esm2022/core/dashboard/index.mjs +4 -1
  171. package/esm2022/core/dashboard/widget-auto-refresh-context/auto-refresh-control.component.mjs +101 -0
  172. package/esm2022/core/dashboard/widget-auto-refresh-context/auto-refresh-select-control.component.mjs +50 -0
  173. package/esm2022/core/dashboard/widget-auto-refresh-context/global-refresh-loading.operator.mjs +7 -0
  174. package/esm2022/core/dashboard/widget-auto-refresh-context/index.mjs +8 -0
  175. package/esm2022/core/dashboard/widget-auto-refresh-context/widget-auto-refresh-context-icon-bar.component.mjs +18 -0
  176. package/esm2022/core/dashboard/widget-auto-refresh-context/widget-auto-refresh-context.component.mjs +127 -0
  177. package/esm2022/core/dashboard/widget-auto-refresh-context/widget-auto-refresh-context.model.mjs +5 -0
  178. package/esm2022/core/dashboard/widget-auto-refresh-context/widget-global-auto-refresh.service.mjs +233 -0
  179. package/esm2022/core/dashboard/widget-change-event.model.mjs +1 -1
  180. package/esm2022/core/dashboard/widgets-dashboard.component.mjs +35 -30
  181. package/esm2022/core/dashboard/wiget-time-context/aggregation-picker/aggregation-picker.component.mjs +4 -4
  182. package/esm2022/core/dashboard/wiget-time-context/realtime-control/realtime-control.component.mjs +2 -2
  183. package/esm2022/core/dashboard/wiget-time-context/widget-time-context-helper.service.mjs +3 -2
  184. package/esm2022/core/dashboard/wiget-time-context/widget-time-context-icon-bar/widget-time-context-icon-bar.component.mjs +5 -5
  185. package/esm2022/core/dashboard/wiget-time-context/widget-time-context-query.service.mjs +7 -4
  186. package/esm2022/core/dashboard/wiget-time-context/widget-time-context.component.mjs +37 -36
  187. package/esm2022/core/dashboard/wiget-time-context/widget-time-context.model.mjs +1 -72
  188. package/esm2022/core/date-time-picker/date-time-picker.component.mjs +9 -3
  189. package/esm2022/core/header/header.module.mjs +7 -5
  190. package/esm2022/core/header/title/title.component.mjs +3 -3
  191. package/esm2022/core/i18n/cached-locale-dictionary.service.mjs +1 -1
  192. package/esm2022/core/i18n/i18n.module.mjs +7 -14
  193. package/esm2022/core/i18n/index.mjs +3 -2
  194. package/esm2022/core/i18n/translate.parser.mjs +2 -2
  195. package/esm2022/core/i18n/translation-loader.service.mjs +132 -0
  196. package/esm2022/core/i18n/translation-utils.mjs +24 -0
  197. package/esm2022/core/list-display-switch/list-display-switch.component.mjs +3 -3
  198. package/esm2022/core/plugins/plugins-resolve.service.mjs +12 -37
  199. package/esm2022/datapoint-selector/datapoint-attributes-form/datapoint-attributes-form-validation.service.mjs +13 -6
  200. package/esm2022/datapoint-selector/datapoint-attributes-form/datapoint-attributes-form.component.mjs +22 -5
  201. package/esm2022/datapoint-selector/datapoint-selection-list/datapoint-selection-list.component.mjs +3 -3
  202. package/esm2022/datapoint-selector/datapoint-selection.model.mjs +1 -1
  203. package/esm2022/datapoint-selector/datapoint-selector-list-item/datapoint-selector-list-item.component.mjs +3 -3
  204. package/esm2022/datapoints-export-selector/c8y-ngx-components-datapoints-export-selector.mjs +5 -0
  205. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/data-fetching.service.mjs +407 -0
  206. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/data-processing.service.mjs +207 -0
  207. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.component.mjs +467 -0
  208. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-file-exporter.service.mjs +187 -0
  209. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-export-selector-preview/datapoints-export-selector-preview.component.mjs +27 -0
  210. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-exports-selector-data-scope/datapoints-exports-selector-data-scope.component.mjs +41 -0
  211. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-exports-selector-file-types/datapoints-exports-selector-file-types.component.mjs +23 -0
  212. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/datapoints-exports-selector-time-range/datapoints-exports-selector-time-range.component.mjs +42 -0
  213. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/generators/csv-generator.mjs +120 -0
  214. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/generators/excel-generator.mjs +282 -0
  215. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-file-exporter/utils.service.mjs +76 -0
  216. package/esm2022/datapoints-export-selector/datapoints-export-selector-modal/datapoints-export-selector-modal.component.mjs +46 -0
  217. package/esm2022/datapoints-export-selector/datapoints-export-selector.component.mjs +42 -0
  218. package/esm2022/datapoints-export-selector/datapoints-export-selector.model.mjs +43 -0
  219. package/esm2022/datapoints-export-selector/index.mjs +15 -0
  220. package/esm2022/device-list/add-smart-group.component.mjs +4 -3
  221. package/esm2022/device-list/device-list.module.mjs +22 -4
  222. package/esm2022/interval-picker/c8y-ngx-components-interval-picker.mjs +5 -0
  223. package/esm2022/interval-picker/index.mjs +3 -0
  224. package/esm2022/interval-picker/interval-picker.component.mjs +68 -0
  225. package/esm2022/interval-picker/interval-picker.model.mjs +47 -0
  226. package/esm2022/map/cluster-map.component.mjs +41 -13
  227. package/esm2022/map/map-status.component.mjs +6 -5
  228. package/esm2022/map/map.component.mjs +9 -5
  229. package/esm2022/map/map.model.mjs +1 -1
  230. package/esm2022/translation-editor/c8y-ngx-components-translation-editor.mjs +5 -0
  231. package/esm2022/translation-editor/data/c8y-ngx-components-translation-editor-data.mjs +5 -0
  232. package/esm2022/translation-editor/data/index.mjs +2 -0
  233. package/esm2022/translation-editor/data/translation-store.service.mjs +175 -0
  234. package/esm2022/translation-editor/index.mjs +10 -0
  235. package/esm2022/translation-editor/lazy/add-translation-modal/add-translation-modal.component.mjs +70 -0
  236. package/esm2022/translation-editor/lazy/c8y-ngx-components-translation-editor-lazy.mjs +5 -0
  237. package/esm2022/translation-editor/lazy/index.mjs +2 -0
  238. package/esm2022/translation-editor/lazy/manage-translation-cell-renderer/manage-translation-cell-renderer.component.mjs +53 -0
  239. package/esm2022/translation-editor/lazy/translation-editor/translation-editor.component.mjs +124 -0
  240. package/esm2022/translation-editor/translation-editor-naviagtor-factory.service.mjs +31 -0
  241. package/esm2022/widgets/cockpit/index.mjs +4 -2
  242. package/esm2022/widgets/cockpit-exports/index.mjs +8 -1
  243. package/esm2022/widgets/definitions/alarms/alarm-list/index.mjs +6 -2
  244. package/esm2022/widgets/definitions/alarms/all-critical-alarms/index.mjs +6 -2
  245. package/esm2022/widgets/definitions/alarms/recent-alarms/index.mjs +6 -2
  246. package/esm2022/widgets/definitions/datapoints-table/c8y-ngx-components-widgets-definitions-datapoints-table.mjs +5 -0
  247. package/esm2022/widgets/definitions/datapoints-table/index.mjs +34 -0
  248. package/esm2022/widgets/definitions/index.mjs +2 -1
  249. package/esm2022/widgets/definitions/map/index.mjs +4 -2
  250. package/esm2022/widgets/implementations/alarms/alarm-list-widget-config/alarm-list-widget-config.component.mjs +29 -15
  251. package/esm2022/widgets/implementations/alarms/alarm-list-widget-view/alarm-list-widget.component.mjs +57 -25
  252. package/esm2022/widgets/implementations/alarms/alarm-list-widget.model.mjs +2 -1
  253. package/esm2022/widgets/implementations/alarms/alarm-widget-alarms-reload.component.mjs +19 -21
  254. package/esm2022/widgets/implementations/datapoints-table/c8y-ngx-components-widgets-implementations-datapoints-table.mjs +5 -0
  255. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-config/datapoints-table-config.component.mjs +383 -0
  256. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-config/datapoints-table-config.service.mjs +124 -0
  257. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/adjust-aggregated-time-range.pipe.mjs +191 -0
  258. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/apply-range-class.pipe.mjs +36 -0
  259. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/column-title.pipe.mjs +45 -0
  260. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-reload/datapoints-reload.component.mjs +116 -0
  261. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table/datapoints-table.component.mjs +116 -0
  262. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table/dynamic-column.directive.mjs +43 -0
  263. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table-view.component.mjs +282 -0
  264. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table-view.service.mjs +430 -0
  265. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-view/virtual-scroll-listener.directive.mjs +75 -0
  266. package/esm2022/widgets/implementations/datapoints-table/datapoints-table-widget.model.mjs +76 -0
  267. package/esm2022/widgets/implementations/datapoints-table/datapoints-table.service.mjs +52 -0
  268. package/esm2022/widgets/implementations/datapoints-table/date-range-picker.component.mjs +68 -0
  269. package/esm2022/widgets/implementations/datapoints-table/index.mjs +10 -0
  270. package/esm2022/widgets/implementations/map/map-widget-config.component.mjs +19 -11
  271. package/esm2022/widgets/implementations/map/map-widget.component.mjs +20 -2
  272. package/esm2022/widgets/implementations/map/map-widget.model.mjs +1 -1
  273. package/fesm2022/c8y-ngx-components-alarm-event-selector.mjs +5 -5
  274. package/fesm2022/c8y-ngx-components-alarm-event-selector.mjs.map +1 -1
  275. package/fesm2022/c8y-ngx-components-alarms.mjs +189 -31
  276. package/fesm2022/c8y-ngx-components-alarms.mjs.map +1 -1
  277. package/fesm2022/c8y-ngx-components-branding-shared-data.mjs +23 -0
  278. package/fesm2022/c8y-ngx-components-branding-shared-data.mjs.map +1 -1
  279. package/fesm2022/c8y-ngx-components-branding-shared-lazy.mjs +2 -0
  280. package/fesm2022/c8y-ngx-components-branding-shared-lazy.mjs.map +1 -1
  281. package/fesm2022/c8y-ngx-components-context-dashboard.mjs +135 -91
  282. package/fesm2022/c8y-ngx-components-context-dashboard.mjs.map +1 -1
  283. package/fesm2022/c8y-ngx-components-datapoint-selector.mjs +37 -13
  284. package/fesm2022/c8y-ngx-components-datapoint-selector.mjs.map +1 -1
  285. package/fesm2022/c8y-ngx-components-datapoints-export-selector.mjs +1928 -0
  286. package/fesm2022/c8y-ngx-components-datapoints-export-selector.mjs.map +1 -0
  287. package/fesm2022/c8y-ngx-components-device-list.mjs +24 -5
  288. package/fesm2022/c8y-ngx-components-device-list.mjs.map +1 -1
  289. package/fesm2022/c8y-ngx-components-interval-picker.mjs +120 -0
  290. package/fesm2022/c8y-ngx-components-interval-picker.mjs.map +1 -0
  291. package/fesm2022/c8y-ngx-components-map.mjs +52 -19
  292. package/fesm2022/c8y-ngx-components-map.mjs.map +1 -1
  293. package/fesm2022/c8y-ngx-components-translation-editor-data.mjs +181 -0
  294. package/fesm2022/c8y-ngx-components-translation-editor-data.mjs.map +1 -0
  295. package/fesm2022/c8y-ngx-components-translation-editor-lazy.mjs +236 -0
  296. package/fesm2022/c8y-ngx-components-translation-editor-lazy.mjs.map +1 -0
  297. package/fesm2022/c8y-ngx-components-translation-editor.mjs +46 -0
  298. package/fesm2022/c8y-ngx-components-translation-editor.mjs.map +1 -0
  299. package/fesm2022/c8y-ngx-components-widgets-cockpit-exports.mjs +7 -0
  300. package/fesm2022/c8y-ngx-components-widgets-cockpit-exports.mjs.map +1 -1
  301. package/fesm2022/c8y-ngx-components-widgets-cockpit.mjs +3 -1
  302. package/fesm2022/c8y-ngx-components-widgets-cockpit.mjs.map +1 -1
  303. package/fesm2022/c8y-ngx-components-widgets-definitions-alarms-alarm-list.mjs +5 -1
  304. package/fesm2022/c8y-ngx-components-widgets-definitions-alarms-alarm-list.mjs.map +1 -1
  305. package/fesm2022/c8y-ngx-components-widgets-definitions-alarms-all-critical-alarms.mjs +5 -1
  306. package/fesm2022/c8y-ngx-components-widgets-definitions-alarms-all-critical-alarms.mjs.map +1 -1
  307. package/fesm2022/c8y-ngx-components-widgets-definitions-alarms-recent-alarms.mjs +5 -1
  308. package/fesm2022/c8y-ngx-components-widgets-definitions-alarms-recent-alarms.mjs.map +1 -1
  309. package/fesm2022/c8y-ngx-components-widgets-definitions-datapoints-table.mjs +41 -0
  310. package/fesm2022/c8y-ngx-components-widgets-definitions-datapoints-table.mjs.map +1 -0
  311. package/fesm2022/c8y-ngx-components-widgets-definitions-map.mjs +3 -1
  312. package/fesm2022/c8y-ngx-components-widgets-definitions-map.mjs.map +1 -1
  313. package/fesm2022/c8y-ngx-components-widgets-definitions.mjs +1 -0
  314. package/fesm2022/c8y-ngx-components-widgets-definitions.mjs.map +1 -1
  315. package/fesm2022/c8y-ngx-components-widgets-implementations-alarms.mjs +101 -58
  316. package/fesm2022/c8y-ngx-components-widgets-implementations-alarms.mjs.map +1 -1
  317. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-table.mjs +1967 -0
  318. package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-table.mjs.map +1 -0
  319. package/fesm2022/c8y-ngx-components-widgets-implementations-map.mjs +37 -11
  320. package/fesm2022/c8y-ngx-components-widgets-implementations-map.mjs.map +1 -1
  321. package/fesm2022/c8y-ngx-components.mjs +5093 -4369
  322. package/fesm2022/c8y-ngx-components.mjs.map +1 -1
  323. package/interval-picker/c8y-ngx-components-interval-picker.d.ts.map +1 -0
  324. package/interval-picker/index.d.ts +3 -0
  325. package/interval-picker/index.d.ts.map +1 -0
  326. package/{core/dashboard/wiget-time-context/interval-picker → interval-picker}/interval-picker.component.d.ts +10 -2
  327. package/interval-picker/interval-picker.component.d.ts.map +1 -0
  328. package/interval-picker/interval-picker.model.d.ts +15 -0
  329. package/interval-picker/interval-picker.model.d.ts.map +1 -0
  330. package/locales/de.po +289 -18
  331. package/locales/en.po +12 -0
  332. package/locales/en_US.po +3 -0
  333. package/locales/es.po +253 -18
  334. package/locales/fr.po +253 -18
  335. package/locales/ja_JP.po +282 -18
  336. package/locales/locales.pot +266 -21
  337. package/locales/nl.po +253 -18
  338. package/locales/pl.po +253 -18
  339. package/locales/pt_BR.po +253 -18
  340. package/map/cluster-map.component.d.ts +8 -3
  341. package/map/cluster-map.component.d.ts.map +1 -1
  342. package/map/map-status.component.d.ts +0 -3
  343. package/map/map-status.component.d.ts.map +1 -1
  344. package/map/map.component.d.ts +4 -2
  345. package/map/map.component.d.ts.map +1 -1
  346. package/map/map.model.d.ts +2 -1
  347. package/map/map.model.d.ts.map +1 -1
  348. package/package.json +1 -1
  349. package/translation-editor/c8y-ngx-components-translation-editor.d.ts.map +1 -0
  350. package/translation-editor/data/c8y-ngx-components-translation-editor-data.d.ts.map +1 -0
  351. package/translation-editor/data/index.d.ts +2 -0
  352. package/translation-editor/data/index.d.ts.map +1 -0
  353. package/translation-editor/data/translation-store.service.d.ts +61 -0
  354. package/translation-editor/data/translation-store.service.d.ts.map +1 -0
  355. package/translation-editor/index.d.ts +2 -0
  356. package/translation-editor/index.d.ts.map +1 -0
  357. package/translation-editor/lazy/add-translation-modal/add-translation-modal.component.d.ts +30 -0
  358. package/translation-editor/lazy/add-translation-modal/add-translation-modal.component.d.ts.map +1 -0
  359. package/translation-editor/lazy/c8y-ngx-components-translation-editor-lazy.d.ts.map +1 -0
  360. package/translation-editor/lazy/index.d.ts +2 -0
  361. package/translation-editor/lazy/index.d.ts.map +1 -0
  362. package/translation-editor/lazy/manage-translation-cell-renderer/manage-translation-cell-renderer.component.d.ts +22 -0
  363. package/translation-editor/lazy/manage-translation-cell-renderer/manage-translation-cell-renderer.component.d.ts.map +1 -0
  364. package/translation-editor/lazy/translation-editor/translation-editor.component.d.ts +34 -0
  365. package/translation-editor/lazy/translation-editor/translation-editor.component.d.ts.map +1 -0
  366. package/translation-editor/translation-editor-naviagtor-factory.service.d.ts +11 -0
  367. package/translation-editor/translation-editor-naviagtor-factory.service.d.ts.map +1 -0
  368. package/widgets/cockpit/index.d.ts +13 -0
  369. package/widgets/cockpit/index.d.ts.map +1 -1
  370. package/widgets/cockpit-exports/index.d.ts +6 -0
  371. package/widgets/cockpit-exports/index.d.ts.map +1 -1
  372. package/widgets/definitions/alarms/alarm-list/index.d.ts +2 -0
  373. package/widgets/definitions/alarms/alarm-list/index.d.ts.map +1 -1
  374. package/widgets/definitions/alarms/all-critical-alarms/index.d.ts +2 -0
  375. package/widgets/definitions/alarms/all-critical-alarms/index.d.ts.map +1 -1
  376. package/widgets/definitions/alarms/recent-alarms/index.d.ts +2 -0
  377. package/widgets/definitions/alarms/recent-alarms/index.d.ts.map +1 -1
  378. package/widgets/definitions/datapoints-table/c8y-ngx-components-widgets-definitions-datapoints-table.d.ts.map +1 -0
  379. package/widgets/definitions/datapoints-table/index.d.ts +14 -0
  380. package/widgets/definitions/datapoints-table/index.d.ts.map +1 -0
  381. package/widgets/definitions/index.d.ts +1 -0
  382. package/widgets/definitions/index.d.ts.map +1 -1
  383. package/widgets/definitions/map/index.d.ts +2 -1
  384. package/widgets/definitions/map/index.d.ts.map +1 -1
  385. package/widgets/implementations/alarms/alarm-list-widget-config/alarm-list-widget-config.component.d.ts +5 -9
  386. package/widgets/implementations/alarms/alarm-list-widget-config/alarm-list-widget-config.component.d.ts.map +1 -1
  387. package/widgets/implementations/alarms/alarm-list-widget-view/alarm-list-widget.component.d.ts +17 -10
  388. package/widgets/implementations/alarms/alarm-list-widget-view/alarm-list-widget.component.d.ts.map +1 -1
  389. package/widgets/implementations/alarms/alarm-list-widget.model.d.ts +5 -1
  390. package/widgets/implementations/alarms/alarm-list-widget.model.d.ts.map +1 -1
  391. package/widgets/implementations/alarms/alarm-widget-alarms-reload.component.d.ts +5 -11
  392. package/widgets/implementations/alarms/alarm-widget-alarms-reload.component.d.ts.map +1 -1
  393. package/widgets/implementations/datapoints-table/c8y-ngx-components-widgets-implementations-datapoints-table.d.ts.map +1 -0
  394. package/widgets/implementations/datapoints-table/datapoints-table-config/datapoints-table-config.component.d.ts +129 -0
  395. package/widgets/implementations/datapoints-table/datapoints-table-config/datapoints-table-config.component.d.ts.map +1 -0
  396. package/widgets/implementations/datapoints-table/datapoints-table-config/datapoints-table-config.service.d.ts +56 -0
  397. package/widgets/implementations/datapoints-table/datapoints-table-config/datapoints-table-config.service.d.ts.map +1 -0
  398. package/widgets/implementations/datapoints-table/datapoints-table-view/adjust-aggregated-time-range.pipe.d.ts +88 -0
  399. package/widgets/implementations/datapoints-table/datapoints-table-view/adjust-aggregated-time-range.pipe.d.ts.map +1 -0
  400. package/widgets/implementations/datapoints-table/datapoints-table-view/apply-range-class.pipe.d.ts +19 -0
  401. package/widgets/implementations/datapoints-table/datapoints-table-view/apply-range-class.pipe.d.ts.map +1 -0
  402. package/widgets/implementations/datapoints-table/datapoints-table-view/column-title.pipe.d.ts +26 -0
  403. package/widgets/implementations/datapoints-table/datapoints-table-view/column-title.pipe.d.ts.map +1 -0
  404. package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-reload/datapoints-reload.component.d.ts +63 -0
  405. package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-reload/datapoints-reload.component.d.ts.map +1 -0
  406. package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table/datapoints-table.component.d.ts +38 -0
  407. package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table/datapoints-table.component.d.ts.map +1 -0
  408. package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table/dynamic-column.directive.d.ts +13 -0
  409. package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table/dynamic-column.directive.d.ts.map +1 -0
  410. package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table-view.component.d.ts +123 -0
  411. package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table-view.component.d.ts.map +1 -0
  412. package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table-view.service.d.ts +196 -0
  413. package/widgets/implementations/datapoints-table/datapoints-table-view/datapoints-table-view.service.d.ts.map +1 -0
  414. package/widgets/implementations/datapoints-table/datapoints-table-view/virtual-scroll-listener.directive.d.ts +41 -0
  415. package/widgets/implementations/datapoints-table/datapoints-table-view/virtual-scroll-listener.directive.d.ts.map +1 -0
  416. package/widgets/implementations/datapoints-table/datapoints-table-widget.model.d.ts +175 -0
  417. package/widgets/implementations/datapoints-table/datapoints-table-widget.model.d.ts.map +1 -0
  418. package/widgets/implementations/datapoints-table/datapoints-table.service.d.ts +12 -0
  419. package/widgets/implementations/datapoints-table/datapoints-table.service.d.ts.map +1 -0
  420. package/widgets/implementations/datapoints-table/date-range-picker.component.d.ts +34 -0
  421. package/widgets/implementations/datapoints-table/date-range-picker.component.d.ts.map +1 -0
  422. package/widgets/implementations/datapoints-table/index.d.ts +10 -0
  423. package/widgets/implementations/datapoints-table/index.d.ts.map +1 -0
  424. package/widgets/implementations/map/map-widget-config.component.d.ts +4 -4
  425. package/widgets/implementations/map/map-widget-config.component.d.ts.map +1 -1
  426. package/widgets/implementations/map/map-widget.component.d.ts +8 -3
  427. package/widgets/implementations/map/map-widget.component.d.ts.map +1 -1
  428. package/widgets/implementations/map/map-widget.model.d.ts +4 -3
  429. package/widgets/implementations/map/map-widget.model.d.ts.map +1 -1
  430. package/core/dashboard/wiget-time-context/interval-picker/interval-picker.component.d.ts.map +0 -1
  431. package/core/i18n/translate.loader.d.ts +0 -31
  432. package/core/i18n/translate.loader.d.ts.map +0 -1
  433. package/esm2022/core/dashboard/wiget-time-context/interval-picker/interval-picker.component.mjs +0 -55
  434. package/esm2022/core/i18n/translate.loader.mjs +0 -71
@@ -0,0 +1,1928 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, Component, Input, EventEmitter, Output, InjectionToken, Inject, ViewChild, HostListener } from '@angular/core';
3
+ import * as i3 from '@angular/platform-browser';
4
+ import * as i2 from '@c8y/client';
5
+ import * as i1 from '@c8y/ngx-components';
6
+ import { gettext, AGGREGATION_VALUES, CoreModule, AGGREGATION_LABELS, AGGREGATION_VALUES_ARR, CommonModule, ModalModule } from '@c8y/ngx-components';
7
+ import * as i4 from '@ngx-translate/core';
8
+ import JSZip from 'jszip';
9
+ import * as i2$1 from '@angular/common';
10
+ import { KeyValuePipe } from '@angular/common';
11
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
12
+ import * as i3$2 from '@angular/forms';
13
+ import { FormsModule, FormControl, ReactiveFormsModule } from '@angular/forms';
14
+ import saveAs from 'file-saver';
15
+ import { Subject, debounceTime, merge, takeUntil } from 'rxjs';
16
+ import { TimeSpanInMs } from '@c8y/ngx-components/interval-picker';
17
+ import { A11yModule } from '@angular/cdk/a11y';
18
+ import * as i3$1 from 'ngx-bootstrap/popover';
19
+ import { PopoverModule } from 'ngx-bootstrap/popover';
20
+ import { isNil, isEmpty, isNumber } from 'lodash-es';
21
+ import * as i1$1 from 'ngx-bootstrap/modal';
22
+ import * as i3$3 from 'ngx-bootstrap/tooltip';
23
+ import { TooltipModule } from 'ngx-bootstrap/tooltip';
24
+
25
+ const HAS_ERROR = 'has-error';
26
+ const MEASUREMENTS_PREVIEW_ITEMS_LIMIT = 5;
27
+ const SERIES_DATA_MERGED_FILE_NAME = 'seriesDataMergedFileName';
28
+ const EXPORT_MODE_LABELS = {
29
+ FULL: gettext('Full`export type`'),
30
+ COMPACT: gettext('Compact`export type`')
31
+ };
32
+ /**
33
+ * Each export type is based on a different API:
34
+ * - COMPACT - series
35
+ * - FULL - measurements
36
+ * All differences between export modes:
37
+ * Compact:
38
+ * Processes up to 5,000 records per data point, or up to the data retention limit (API limit)
39
+ * Creates a single merged file containing all the data
40
+ * Provides minimum and maximum values (API feature)
41
+ * Preview is not available
42
+ * Supports optional data aggregation (API feature)
43
+ * Full:
44
+ * Processes up to 1,000,000 records per data point, or up to the data retention limit (API limit)
45
+ * For exports exceeding 50,000 records, data will be sent via email
46
+ * Creates a compressed ZIP file containing separate data files for each selected data point
47
+ * Preview is available
48
+ * Does not support data aggregation
49
+ */
50
+ const EXPORT_MODE_VALUES = {
51
+ full: 'FULL',
52
+ compact: 'COMPACT'
53
+ };
54
+ const FILE_COMPRESSION_TYPES_VALUES = {
55
+ store: 'STORE',
56
+ deflate: 'DEFLATE'
57
+ };
58
+ const TIME_RANGE_INTERVAL_UNITS_VALUES = {
59
+ minutes: 'minutes',
60
+ hours: 'hours',
61
+ days: 'days',
62
+ weeks: 'weeks',
63
+ months: 'months',
64
+ custom: 'custom'
65
+ };
66
+
67
+ class UtilsService {
68
+ transformDataStructure(data) {
69
+ const result = {};
70
+ data.forEach(sourceItem => {
71
+ const { source: sourceId, data: sourceData } = sourceItem;
72
+ result[sourceId] = {};
73
+ sourceData.series.forEach((seriesInfo, index) => {
74
+ const { type, name, unit } = seriesInfo;
75
+ /**
76
+ * Unique key to distinguish between different series from same source,
77
+ * e.g.: c8y_Acceleration.accelerationX, c8y_Acceleration.accelerationY, c8y_Acceleration.accelerationZ
78
+ */
79
+ const seriesKey = `${type}.${name}`;
80
+ result[sourceId][seriesKey] = {
81
+ values: {},
82
+ seriesDetails: { name, unit, type }
83
+ };
84
+ let hasValues = false;
85
+ Object.entries(sourceData.values).forEach(([timestamp, measurements]) => {
86
+ if (!measurements) {
87
+ return;
88
+ }
89
+ const measurement = measurements[index];
90
+ if (measurement !== null && measurement !== undefined) {
91
+ result[sourceId][seriesKey].values[timestamp] = measurement;
92
+ hasValues = true;
93
+ }
94
+ });
95
+ if (!hasValues) {
96
+ result[sourceId][seriesKey].values = null;
97
+ }
98
+ });
99
+ });
100
+ return result;
101
+ }
102
+ /**
103
+ * Formats a date range into a specific string format, handling UTC dates correctly.
104
+ *
105
+ * @param fromDate - The start date in ISO format (e.g., "2023-12-04T12:40:00.000Z")
106
+ * @param toDate - The end date in ISO format (e.g., "2023-12-06T23:50:00.000Z")
107
+ * @returns A string representing the date range in the format "ddmmmyyhhmm-ddmmmyyhhmm"
108
+ * where d=day, m=month, y=year, h=hour, m=minute
109
+ *
110
+ * Example
111
+ * ```typescript
112
+ * const fromDate = "2023-12-04T12:40:00.000Z";
113
+ * const toDate = "2023-12-06T23:50:00.000Z";
114
+ * const formattedRange = getFormattedDateRange(fromDate, toDate);
115
+ * console.log(formattedRange); // Output: "04dec231240-06dec232350"
116
+ * ```
117
+ */
118
+ getFormattedDateRange(fromDate, toDate) {
119
+ const formatDate = (dateString) => {
120
+ const date = new Date(dateString);
121
+ if (isNaN(date.getTime())) {
122
+ throw new Error(`Invalid date string: ${dateString}`);
123
+ }
124
+ const day = date.getUTCDate().toString().padStart(2, '0');
125
+ const month = date.toLocaleString('en', { month: 'short', timeZone: 'UTC' }).toLowerCase();
126
+ const hours = date.getUTCHours().toString().padStart(2, '0');
127
+ const minutes = date.getUTCMinutes().toString().padStart(2, '0');
128
+ const seconds = date.getUTCSeconds().toString().padStart(2, '0');
129
+ return `${day}${month}${hours}${minutes}${seconds}`;
130
+ };
131
+ return `${formatDate(fromDate)}-${formatDate(toDate)}`;
132
+ }
133
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: UtilsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
134
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: UtilsService, providedIn: 'root' }); }
135
+ }
136
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: UtilsService, decorators: [{
137
+ type: Injectable,
138
+ args: [{ providedIn: 'root' }]
139
+ }] });
140
+
141
+ class DataFetchingService {
142
+ constructor(alertService, measurementService, sanitizer, translateService, utilsService) {
143
+ this.alertService = alertService;
144
+ this.measurementService = measurementService;
145
+ this.sanitizer = sanitizer;
146
+ this.translateService = translateService;
147
+ this.utilsService = utilsService;
148
+ }
149
+ /**
150
+ * Checks if any of measurements requests may exceeded the limit,
151
+ * after which the export data is processed by the backend and the generated CSV/Excel file is sent by email.
152
+ *
153
+ * The threshold is set to 50_000 records in application's core properties (export.data.synchronous.limit).
154
+ *
155
+ * @param exportConfig - The export configuration.
156
+ * @returns A promise that returns an array of objects representing datapoints files that will be sent by email.
157
+ */
158
+ async getDatapointsExceedingLimit(exportConfig) {
159
+ const backendProcessingLimit = 50_000;
160
+ const promises = exportConfig.datapointDetails.map(async (details) => {
161
+ try {
162
+ const filter = {
163
+ dateFrom: exportConfig.dateFrom,
164
+ dateTo: exportConfig.dateTo,
165
+ pageSize: 1,
166
+ source: details.source,
167
+ valueFragmentSeries: details.valueFragmentSeries,
168
+ valueFragmentType: details.valueFragmentType,
169
+ withTotalElements: true
170
+ };
171
+ const response = await this.measurementService.list(filter);
172
+ if (response?.paging?.totalElements > backendProcessingLimit) {
173
+ return {
174
+ datapointDetail: {
175
+ source: details.source,
176
+ valueFragmentSeries: details.valueFragmentSeries,
177
+ valueFragmentType: details.valueFragmentType
178
+ },
179
+ totalElements: response.paging.totalElements
180
+ };
181
+ }
182
+ else {
183
+ return null;
184
+ }
185
+ }
186
+ catch (error) {
187
+ this.alertService.addServerFailure(error);
188
+ }
189
+ });
190
+ const results = await Promise.all(promises);
191
+ const datapointsExceedingLimit = results.filter(result => result !== null);
192
+ return datapointsExceedingLimit;
193
+ }
194
+ /**
195
+ * Retrieves the message to be displayed when the limit of datapoints is exceeded during file export.
196
+ *
197
+ * @param hasNoExportableData - Indicates if there is no exportable data because the date range of all selected datapoints exceeds 1,000,000 records.
198
+ * @param emailDeliverableCount - The number of datapoint exports that exceed the limit, will be proceeded by the backend and sent by email.
199
+ * @param browserDownloadableCount - The number of datapoint exports that can be downloaded directly.
200
+ * @param skippedDatapointCount - The number of datapoint exports skipped due to exceeding the measurements API limit of 1,000,000 records.
201
+ * @param totalDatapointsSelectedForExportCount - Total number of datapoint selected for exports.
202
+ * @returns The message that can be injected.
203
+ */
204
+ getLimitExceededMessage(hasNoExportableData, emailDeliverableCount, browserDownloadableCount, nonRetrievableCount, totalDatapointsSelectedForExportCount) {
205
+ if (hasNoExportableData) {
206
+ const message = this.translateService.instant(gettext(`The data for selected datapoint(s) exceed 1,000,000 records, which is the limit for backend processing. To export this data, please reduce the date range.`));
207
+ return message;
208
+ }
209
+ const message = this.translateService.instant(gettext(`After clicking "Download":<br>
210
+ <ul>
211
+ <li><strong>{{$browserDownloadableCount}}</strong> datapoint(s) exports will be downloaded directly within one file: <em>exported_[csv/excel].zip</em></li>
212
+ <li><strong>{{$emailDeliverableCount}}</strong> datapoint(s) exports require further processing. The files will be sent to you via separate emails once completed, which may take some time.</li>
213
+ <li><strong>{{$nonRetrievableCount}}</strong> datapoint(s) exports exceeded 1,000,000 records, which is the limit for backend processing. To export these data, please reduce the date range. Otherwise, the data will neither be downloaded nor sent via email.</li>
214
+ </ul>
215
+ <p>The total number of data points that can be exported is: <strong>{{$totalExportableDatapointsCount}} out of {{$totalDatapointsSelectedForExportCount}}</strong>.
216
+ <p><strong>Note:</strong> The file name convention of files within zip file is: <code>[source]_[fragment_series].[csv/xls]</code></p>`), {
217
+ $browserDownloadableCount: browserDownloadableCount,
218
+ $emailDeliverableCount: emailDeliverableCount,
219
+ $nonRetrievableCount: nonRetrievableCount,
220
+ $totalDatapointsSelectedForExportCount: totalDatapointsSelectedForExportCount,
221
+ $totalExportableDatapointsCount: browserDownloadableCount + emailDeliverableCount
222
+ });
223
+ const trimmedMessage = this.removeZeroCountListItems(message, [
224
+ browserDownloadableCount,
225
+ emailDeliverableCount,
226
+ nonRetrievableCount
227
+ ]);
228
+ return trimmedMessage;
229
+ }
230
+ /**
231
+ * Displays an information alert about sending data via email.
232
+ *
233
+ * Only measurements API can send files via email.
234
+ *
235
+ * @param fileType - The type of file to be sent.
236
+ * @param datapointsExceedingLimit - The array of datapoints exceeding the limit.
237
+ */
238
+ showSendViaEmailInfoAlert(fileType, datapointsExceedingLimit) {
239
+ const messageTemplate = this.translateService.instant(gettext(`You will get {{$fileTypeText}} file(s) for {{$count}} series via email.`), { $count: datapointsExceedingLimit.length, $fileTypeText: fileType });
240
+ const formattedList = datapointsExceedingLimit
241
+ .map(datapoint => `• ${datapoint.datapointDetail.source}_${datapoint.datapointDetail.valueFragmentType}_${datapoint.datapointDetail.valueFragmentSeries}`)
242
+ .join('\n');
243
+ this.alertService.info(messageTemplate, formattedList);
244
+ }
245
+ async fetchMeasurementDataFilesAndPairWithSourceDetails(acceptFileType, exportConfig) {
246
+ const measurementFileConfig = {
247
+ exportConfig,
248
+ acceptFileType
249
+ };
250
+ const filePromises = exportConfig.datapointDetails.map(details => this.fetchAndProcessMeasurementFile(details, measurementFileConfig));
251
+ return Promise.all(filePromises);
252
+ }
253
+ async fetchAndProcessMeasurementFile(details, measurementFileConfig) {
254
+ const filter = this.prepareMeasurementsFilter(details, measurementFileConfig.exportConfig);
255
+ const header = { accept: measurementFileConfig.acceptFileType };
256
+ try {
257
+ const measurementFileResponse = await this.measurementService.getMeasurementsFile(filter, header);
258
+ // If the status is 200, then the file is in the response.
259
+ // If the status is 202, then the file is being processed by the backend and will be sent by email.
260
+ if (measurementFileResponse.status === 200) {
261
+ return this.mergeMeasurementsWithItsSourceDetails(details, measurementFileResponse);
262
+ }
263
+ }
264
+ catch (error) {
265
+ this.alertService.addServerFailure(error);
266
+ }
267
+ }
268
+ async mergeMeasurementsWithItsSourceDetails(details, measurementFile) {
269
+ return {
270
+ source: details.source,
271
+ valueFragmentSeries: details.valueFragmentSeries,
272
+ valueFragmentType: details.valueFragmentType,
273
+ fetchedMeasurementsBlobFile: await measurementFile.blob()
274
+ };
275
+ }
276
+ async fetchAndPrepareDataToExport(exportConfig, isMeasurement) {
277
+ return isMeasurement
278
+ ? await this.fetchAndPrepareMeasurementDataToExportForPreview(exportConfig)
279
+ : await this.fetchAndPrepareSeriesDataToExport(exportConfig);
280
+ }
281
+ async getSourcesWithPermissionsToRead(datapointDetails) {
282
+ const dateFrom = new Date();
283
+ const dateTo = new Date(dateFrom);
284
+ const fetchedDataPromises = datapointDetails.map(async (datapoint) => {
285
+ const rawFilter = {
286
+ dateFrom,
287
+ dateTo,
288
+ source: datapoint.source
289
+ };
290
+ try {
291
+ const { res } = await this.fetchSeriesData(rawFilter);
292
+ if (res?.status === 200) {
293
+ return datapoint.source;
294
+ }
295
+ return null;
296
+ }
297
+ catch (error) {
298
+ // If an error occurs during fetch, return null to prevent promise rejection
299
+ return null;
300
+ }
301
+ });
302
+ const fetchedSources = await Promise.all(fetchedDataPromises);
303
+ return fetchedSources.filter((source) => source !== null);
304
+ }
305
+ /**
306
+ * Fetches and prepares measurement data for preview.
307
+ *
308
+ * Empty DataToExport object can be returned, because unlike series data,
309
+ * measurement data is not further processed besides showing only in the preview.
310
+ * CSV/Excel files are generated by the backend for measurements.
311
+ *
312
+ * @param exportConfig - The export configuration.
313
+ * @returns A promise that resolves to an array of DataToExport objects or null when no data is fetched.
314
+ */
315
+ async fetchAndPrepareMeasurementDataToExportForPreview(exportConfig) {
316
+ const dataToExportPromises = exportConfig.datapointDetails.map(async (details) => {
317
+ const fetchedMeasurementData = await this.fetchMeasurementDataForPreview(details, exportConfig);
318
+ if (fetchedMeasurementData?.data?.length) {
319
+ return this.processMeasurementDataForPreview(details, fetchedMeasurementData.data);
320
+ }
321
+ return null;
322
+ });
323
+ return await Promise.all(dataToExportPromises);
324
+ }
325
+ async fetchMeasurementDataForPreview(details, exportConfig) {
326
+ let measurements;
327
+ const filter = this.prepareMeasurementsFilter(details, exportConfig, MEASUREMENTS_PREVIEW_ITEMS_LIMIT);
328
+ try {
329
+ // TODO: consider adding a cache mechanism -> MTM-60289
330
+ // When user switches between report types, and rest of the settings are the same, then the data should not be fetched again?
331
+ measurements = await this.measurementService.list(filter);
332
+ }
333
+ catch (error) {
334
+ this.alertService.addServerFailure(error);
335
+ }
336
+ return measurements;
337
+ }
338
+ prepareMeasurementsFilter(details, exportConfig, pageSize) {
339
+ const filter = {
340
+ // Round the 'from' time to the previous full minute.
341
+ dateFrom: this.adjustDate(exportConfig.dateFrom, -1),
342
+ // Round the 'to' time to the nearest full minute.
343
+ dateTo: this.adjustDate(exportConfig.dateTo, 1),
344
+ source: details.source,
345
+ valueFragmentSeries: details.valueFragmentSeries,
346
+ valueFragmentType: details.valueFragmentType
347
+ };
348
+ if (pageSize) {
349
+ filter.pageSize = pageSize;
350
+ }
351
+ return filter;
352
+ }
353
+ processMeasurementDataForPreview(details, data) {
354
+ const unit = data[0][details.valueFragmentType][details.valueFragmentSeries]?.unit || '';
355
+ const values = {};
356
+ data.forEach(measurement => {
357
+ values[measurement.time] =
358
+ measurement[details.valueFragmentType][details.valueFragmentSeries].value;
359
+ });
360
+ return { ...details, unit, timeValueMap: values };
361
+ }
362
+ async fetchAndPrepareSeriesDataToExport(exportConfig) {
363
+ const datapointsValuesDataMap = this.groupSeriesByDeviceId(exportConfig.datapointDetails);
364
+ const fetchedDataPromises = Array.from(datapointsValuesDataMap).map(async ([source, series]) => {
365
+ const { dateFrom, dateTo, aggregation } = exportConfig;
366
+ const rawFilter = {
367
+ dateFrom,
368
+ dateTo,
369
+ source,
370
+ series,
371
+ aggregationType: aggregation
372
+ };
373
+ const { data } = await this.fetchSeriesData(rawFilter);
374
+ return { source, data };
375
+ });
376
+ const fetchedDataGroupedBySource = await Promise.all(fetchedDataPromises);
377
+ return this.processSeriesData(exportConfig.datapointDetails, fetchedDataGroupedBySource);
378
+ }
379
+ /**
380
+ * Returns a map of active data points device IDs with their corresponding series.
381
+ *
382
+ * Example output:
383
+ * ```typescript
384
+ * new Map([
385
+ * ['844657202', ['c8y_Temperature.T']],
386
+ * ['32666427', ['c8y_Battery.Battery']]
387
+ * ]);
388
+ * ```
389
+ * @param datapointDetails - An array of data points details.
390
+ * @returns A map where the key is the data point ID and the value is an array of data point series.
391
+ */
392
+ groupSeriesByDeviceId(datapointDetails) {
393
+ return datapointDetails.reduce((map, { valueFragmentType, valueFragmentSeries, source }) => {
394
+ const value = `${valueFragmentType}.${valueFragmentSeries}`;
395
+ const existingValue = map.get(source) ?? [];
396
+ map.set(source, [...existingValue, value]);
397
+ return map;
398
+ }, new Map());
399
+ }
400
+ async fetchSeriesData(rawFilter) {
401
+ let seriesData;
402
+ const filter = this.prepareSeriesFilter({
403
+ dateFrom: rawFilter.dateFrom,
404
+ dateTo: rawFilter.dateTo,
405
+ source: rawFilter.source,
406
+ series: rawFilter.series,
407
+ aggregationType: rawFilter.aggregationType
408
+ });
409
+ try {
410
+ // TODO: consider adding a cache mechanism -> MTM-60289
411
+ // When user switches between report types, and rest of the settings are the same, then the data should not be fetched again?
412
+ seriesData = await this.measurementService.listSeries(filter);
413
+ }
414
+ catch (error) {
415
+ this.alertService.addServerFailure(error);
416
+ }
417
+ return seriesData;
418
+ }
419
+ prepareSeriesFilter(filters) {
420
+ const { dateFrom, dateTo, source, series, aggregationType } = filters;
421
+ // Round the 'from' time to the previous full minute.
422
+ const from = this.adjustDate(dateFrom, -1);
423
+ // Round the 'to' time to the nearest full minute.
424
+ const to = this.adjustDate(dateTo, 1);
425
+ const filter = {
426
+ dateFrom: from,
427
+ dateTo: to,
428
+ source: source,
429
+ revert: true
430
+ };
431
+ // The 'NONE' aggregation type is not handled by a backend, so even when is selected, it is not passed as a filter parameter.
432
+ if (aggregationType && aggregationType !== AGGREGATION_VALUES.none) {
433
+ filter.aggregationType = aggregationType;
434
+ }
435
+ // TODO: it is a temporal workaround for a bug in the backend,
436
+ // where the series with more than one dot are impossible to retrieve:
437
+ // https://cumulocity.atlassian.net/browse/MTM-59277
438
+ if (series) {
439
+ const hasMoreThanOneDot = series.some(s => s.split('.').length > 2);
440
+ if (!hasMoreThanOneDot) {
441
+ filter.series = series;
442
+ }
443
+ }
444
+ return filter;
445
+ }
446
+ /**
447
+ * Processes the fetched series data and prepares it for export.
448
+ *
449
+ * @param datapointDetails - An array of data point details.
450
+ * @param fetchedDataMap - A map of fetched series data grouped by source.
451
+ * @returns An array of DataToExport objects.
452
+ */
453
+ processSeriesData(datapointDetails, fetchedDataMap) {
454
+ const valuesGroupedBySource = this.utilsService.transformDataStructure(fetchedDataMap);
455
+ return datapointDetails.map((details) => {
456
+ let unit;
457
+ let data;
458
+ /**
459
+ * Unique key to distinguish between different series from same source,
460
+ * e.g.: c8y_Acceleration.accelerationX, c8y_Acceleration.accelerationY, c8y_Acceleration.accelerationZ
461
+ */
462
+ const seriesKey = `${details.valueFragmentType}.${details.valueFragmentSeries}`;
463
+ if (valuesGroupedBySource[details.source][seriesKey]) {
464
+ unit = valuesGroupedBySource[details.source][seriesKey].seriesDetails.unit;
465
+ data = valuesGroupedBySource[details.source][seriesKey].values;
466
+ }
467
+ return {
468
+ ...details,
469
+ unit,
470
+ timeValueMap: data
471
+ };
472
+ });
473
+ }
474
+ /**
475
+ * Adjusts the given date by adding the specified number of minutes and setting seconds to 0.
476
+ *
477
+ * @param date - The date to be adjusted in string format.
478
+ * @param minutes - The number of minutes to add to the date.
479
+ * @returns The adjusted date in the format 'YYYY-MM-DDTHH:mm:ss+HH:MM'.
480
+ */
481
+ adjustDate(date, minutes) {
482
+ const dateTime = new Date(date);
483
+ dateTime.setMinutes(dateTime.getMinutes() + minutes);
484
+ dateTime.setSeconds(0);
485
+ const offset = -dateTime.getTimezoneOffset();
486
+ const offsetSign = offset >= 0 ? '+' : '-';
487
+ const pad = (num) => (num < 10 ? '0' + num : num.toString());
488
+ const offsetHours = pad(Math.floor(Math.abs(offset) / 60));
489
+ const offsetMinutes = pad(Math.abs(offset) % 60);
490
+ const year = dateTime.getFullYear();
491
+ const month = pad(dateTime.getMonth() + 1);
492
+ const day = pad(dateTime.getDate());
493
+ const hours = pad(dateTime.getHours());
494
+ const minutesString = pad(dateTime.getMinutes());
495
+ const seconds = '00';
496
+ return `${year}-${month}-${day}T${hours}:${minutesString}:${seconds}${offsetSign}${offsetHours}:${offsetMinutes}`;
497
+ }
498
+ /**
499
+ * Trims the given HTML message by removing list items that correspond to zero counts.
500
+ *
501
+ * @param message - The HTML string containing the message with list items.
502
+ * @param counts - An array of number values corresponding to each list item.
503
+ * @param countToTrim - A count that will be trimmed with corresponding list item.
504
+ * @returns A trimmed HTML string with list items removed where the corresponding count is zero.
505
+ *
506
+ * Example:
507
+ * ```typescript
508
+ * const message = '<ul><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>';
509
+ * const counts = [1, 0, 2];
510
+ * const trimmedMessage = this.removeZeroCountListItems(message, counts);
511
+ * // Result: '<ul><li>Item 1</li><li>Item 3</li></ul>'
512
+ * ```
513
+ */
514
+ removeZeroCountListItems(message, counts, countToTrim = 0) {
515
+ const parser = new DOMParser();
516
+ const doc = parser.parseFromString(message, 'text/html');
517
+ const listItems = doc.querySelectorAll('ul li');
518
+ listItems.forEach((item, index) => {
519
+ if (counts[index] === countToTrim) {
520
+ item.remove();
521
+ }
522
+ });
523
+ return doc.body.innerHTML;
524
+ }
525
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataFetchingService, deps: [{ token: i1.AlertService }, { token: i2.MeasurementService }, { token: i3.DomSanitizer }, { token: i4.TranslateService }, { token: UtilsService }], target: i0.ɵɵFactoryTarget.Injectable }); }
526
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataFetchingService, providedIn: 'root' }); }
527
+ }
528
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataFetchingService, decorators: [{
529
+ type: Injectable,
530
+ args: [{
531
+ providedIn: 'root'
532
+ }]
533
+ }], ctorParameters: () => [{ type: i1.AlertService }, { type: i2.MeasurementService }, { type: i3.DomSanitizer }, { type: i4.TranslateService }, { type: UtilsService }] });
534
+
535
+ class DatapointsExportSelectorFileExporterService {
536
+ constructor(aggregationService, alertService, datapointsExportSelectorDataProcessingService, translateService) {
537
+ this.aggregationService = aggregationService;
538
+ this.alertService = alertService;
539
+ this.datapointsExportSelectorDataProcessingService = datapointsExportSelectorDataProcessingService;
540
+ this.translateService = translateService;
541
+ }
542
+ /**
543
+ * Determines the interval between two dates.
544
+ *
545
+ * @param dateFrom - The starting date in ISO 8601 string format.
546
+ * @param dateTo - The ending date in ISO 8601 string format.
547
+ * @returns The time range interval unit.
548
+ */
549
+ determineInterval(dateFrom, dateTo) {
550
+ const from = new Date(dateFrom);
551
+ const to = new Date(dateTo);
552
+ const differenceInMilliseconds = to.getTime() - from.getTime();
553
+ const intervals = [
554
+ { value: TimeSpanInMs.MINUTE, label: TIME_RANGE_INTERVAL_UNITS_VALUES.minutes }, // milliseconds in one minute
555
+ { value: TimeSpanInMs.HOUR, label: TIME_RANGE_INTERVAL_UNITS_VALUES.hours }, // milliseconds in one hour
556
+ { value: TimeSpanInMs.DAY, label: TIME_RANGE_INTERVAL_UNITS_VALUES.days }, // milliseconds in one day
557
+ { value: TimeSpanInMs.WEEK, label: TIME_RANGE_INTERVAL_UNITS_VALUES.weeks }, // milliseconds in one week
558
+ { value: TimeSpanInMs.MONTH, label: TIME_RANGE_INTERVAL_UNITS_VALUES.months } // approximation for milliseconds in one month
559
+ ];
560
+ for (let i = 0; i < intervals.length; i++) {
561
+ if (differenceInMilliseconds <= intervals[i].value) {
562
+ return intervals[i].label;
563
+ }
564
+ }
565
+ return TIME_RANGE_INTERVAL_UNITS_VALUES.custom;
566
+ }
567
+ /**
568
+ * Updates the disabled state of aggregation options based on the current value of the time interval form control.
569
+ *
570
+ * This method:
571
+ * - Retrieves the current date range from the form controls.
572
+ * - Determines the current time interval based on the date range.
573
+ * - Sets the disabled state for each aggregation option based on predefined conditions.
574
+ *
575
+ * The disabled state is stored in the `disabledAggregationOptions` object,
576
+ * where the key is the aggregation value and the value is a boolean indicating whether the option should be disabled.
577
+ *
578
+ * The `disabledConditions` object defines the conditions under which each aggregation option should be disabled.
579
+ *
580
+ * @param dateFrom - The value of the "date from" input.
581
+ * @param dateTo - The value of the "date to" input.
582
+ * @param disabledAggregationOptions - The object containing the disabled state of aggregation options.
583
+ * @returns The updated object with the disabled state of aggregation options.
584
+ */
585
+ updateDisabledStateOfAggregationOptionEntries(dateFrom, dateTo, disabledAggregationOptions) {
586
+ disabledAggregationOptions = this.aggregationService.getDisabledAggregationOptions(dateFrom, dateTo);
587
+ return disabledAggregationOptions;
588
+ }
589
+ /**
590
+ * Sets the aggregation control to the first available (non-disabled) option if the current option is disabled.
591
+ *
592
+ * This method:
593
+ * - Retrieves the current value of the aggregation control.
594
+ * - Checks if the current aggregation option is disabled.
595
+ * - If the current option is disabled, sets the control to the first available (non-disabled) option based on the following order:
596
+ * - If the current value is `DAILY`, it switches to `HOURLY` if it's not disabled, otherwise to `MINUTELY` if `HOURLY` is also disabled.
597
+ * - If the current value is `HOURLY`, it switches to `MINUTELY` if it's not disabled.
598
+ * - If all options are disabled, it sets the value to `NONE`.
599
+ *
600
+ * The disabled state is stored in the `disabledAggregationOptions` object,
601
+ * where the key is the aggregation value and the value is a boolean indicating whether the option is disabled.
602
+ *
603
+ * The `AGGREGATION_VALUES` object defines the possible aggregation values.
604
+ *
605
+ * @param aggregationValue - The value of the current aggregation option.
606
+ * @param disabledAggregationOptions - An object containing disabled aggregation options.
607
+ * @returns The new aggregation option to be set.
608
+ */
609
+ setToFirstAvailableAggregationOptionIfCurrentIsDisabled(aggregationValue, disabledAggregationOptions) {
610
+ const currentValue = aggregationValue;
611
+ const disabledOptions = disabledAggregationOptions;
612
+ if (disabledOptions[currentValue]) {
613
+ const { daily, hourly, minutely, none } = AGGREGATION_VALUES;
614
+ let newAggregationValue = none;
615
+ if (currentValue === daily) {
616
+ if (!disabledOptions[hourly]) {
617
+ newAggregationValue = hourly;
618
+ }
619
+ else if (!disabledOptions[minutely]) {
620
+ newAggregationValue = minutely;
621
+ }
622
+ }
623
+ else if (currentValue === hourly && !disabledOptions[minutely]) {
624
+ newAggregationValue = minutely;
625
+ }
626
+ return newAggregationValue;
627
+ }
628
+ }
629
+ async getMeasurementExportedDataBlob(extension, dataToExport) {
630
+ try {
631
+ return await this.getMeasurementDataZipBlob(extension, dataToExport);
632
+ }
633
+ catch (error) {
634
+ this.showZipCreationErrorAlert();
635
+ return null;
636
+ }
637
+ }
638
+ async getSeriesExportedDataBlob(fileType, dataToExport, mergedExportDetails) {
639
+ try {
640
+ return await this.getSeriesDataBlob(fileType, dataToExport, mergedExportDetails);
641
+ }
642
+ catch (error) {
643
+ this.showZipCreationErrorAlert();
644
+ return null;
645
+ }
646
+ }
647
+ cleanupCachedData() {
648
+ this.cachedRawExportSeriesData = null;
649
+ this.cachedFlatteredAndSortedSeriesExportData = null;
650
+ }
651
+ showZipCreationErrorAlert() {
652
+ const alertMessage = this.translateService.instant(gettext('Could not create zip file.'));
653
+ this.alertService.danger(alertMessage);
654
+ return null;
655
+ }
656
+ getMeasurementDataZipBlob(extension, dataToExportWithBackendCreatedFile) {
657
+ const files = [];
658
+ this.createRawMeasurementExportedFiles(dataToExportWithBackendCreatedFile, extension, files);
659
+ return this.datapointsExportSelectorDataProcessingService.zipFiles(files);
660
+ }
661
+ createRawMeasurementExportedFiles(dataToExportWithBackendCreatedFile, fileExtension, files) {
662
+ dataToExportWithBackendCreatedFile.forEach(data => {
663
+ if (data) {
664
+ const fragmentSeries = `${data.valueFragmentType}_${data.valueFragmentSeries}`;
665
+ const fileName = this.datapointsExportSelectorDataProcessingService.createFileName(data.source, fragmentSeries, fileExtension);
666
+ const fileData = data.fetchedMeasurementsBlobFile;
667
+ const exportedFile = { fileName, fileData };
668
+ files.push(exportedFile);
669
+ }
670
+ });
671
+ }
672
+ /**
673
+ * Converts data to a specified file type and returns the generated blob.
674
+ *
675
+ * Unlike measurements, data must be transformed to an exportable file structure before exporting.
676
+ *
677
+ * @param fileType - The type of file to which the data points should be exported. This can be 'csv' or 'excel'.
678
+ * @param dataToExport - An array of processed measurement data combined with the respective properties of the datapoint.
679
+ * @param mergedExportDetails - The details for the merged export, contains date range and aggregation.
680
+ * @returns A promise that resolves to the generated ZIP blob or null if an error occurs.
681
+ */
682
+ async getSeriesDataBlob(fileType, dataToExport, mergedExportDetails) {
683
+ this.transformSeriesDataToExportableFileStructure(dataToExport);
684
+ const exportParams = {
685
+ flattenedAndSortedExportData: this.cachedFlatteredAndSortedSeriesExportData,
686
+ fileType,
687
+ mergedExportDetails: mergedExportDetails
688
+ };
689
+ return await this.datapointsExportSelectorDataProcessingService.exportSeriesData(exportParams);
690
+ }
691
+ transformSeriesDataToExportableFileStructure(dataToExport) {
692
+ if (!this.cachedRawExportSeriesData) {
693
+ this.cachedRawExportSeriesData =
694
+ this.datapointsExportSelectorDataProcessingService.transformToExportFileStructure(dataToExport);
695
+ this.cachedFlatteredAndSortedSeriesExportData = this.cachedRawExportSeriesData
696
+ .flat()
697
+ .sort((a, b) => {
698
+ return new Date(a.time).getTime() - new Date(b.time).getTime();
699
+ });
700
+ }
701
+ }
702
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsExportSelectorFileExporterService, deps: [{ token: i1.AggregationService }, { token: i1.AlertService }, { token: DataProcessingService }, { token: i4.TranslateService }], target: i0.ɵɵFactoryTarget.Injectable }); }
703
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsExportSelectorFileExporterService, providedIn: 'root' }); }
704
+ }
705
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsExportSelectorFileExporterService, decorators: [{
706
+ type: Injectable,
707
+ args: [{
708
+ providedIn: 'root'
709
+ }]
710
+ }], ctorParameters: () => [{ type: i1.AggregationService }, { type: i1.AlertService }, { type: DataProcessingService }, { type: i4.TranslateService }] });
711
+
712
+ class DataPointsExportSelectorPreviewComponent {
713
+ constructor() {
714
+ this.MEASUREMENTS_PREVIEW_ITEMS_LIMIT = MEASUREMENTS_PREVIEW_ITEMS_LIMIT;
715
+ }
716
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataPointsExportSelectorPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
717
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DataPointsExportSelectorPreviewComponent, isStandalone: true, selector: "c8y-datapoints-export-selector-preview", inputs: { hasFetchedDataAnyValuesToExport: "hasFetchedDataAnyValuesToExport", isPreviewLoading: "isPreviewLoading", previewTableData: "previewTableData" }, ngImport: i0, template: "<div class=\"p-t-16 p-l-16 p-r-16 m-b-0\">\n <div class=\"d-flex a-i-center\">\n <label\n class=\"m-b-0 d-flex a-i-center gap-4\"\n [title]=\"'Preview`of exported file`' | translate\"\n >\n {{ 'Preview`of exported file`' | translate }}\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"popoverPreviewTemplate\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n <ng-template #popoverPreviewTemplate>\n <span translate>\n <p>The preview shows the structure of the raw file from a single source.</p>\n <p>If no data is available, only the column headers are visible.</p>\n <p>\n The preview is limited to\n <b>{{ MEASUREMENTS_PREVIEW_ITEMS_LIMIT }}</b>\n records.\n </p>\n </span>\n </ng-template>\n </label>\n </div>\n <div\n class=\"table-responsive\"\n style=\"min-height: 275px\"\n >\n <table class=\"table\">\n <thead>\n <tr>\n <th>{{ 'Time' | translate }}</th>\n <th>{{ 'Source' | translate }}</th>\n <th>{{ 'Device name' | translate }}</th>\n <th>\n {{ 'Fragment and series' | translate }}\n </th>\n <th>{{ 'Value' | translate }}</th>\n <th>{{ 'Unit' | translate }}</th>\n </tr>\n </thead>\n <ng-container *ngIf=\"hasFetchedDataAnyValuesToExport || isPreviewLoading; else emptyState\">\n <ng-container *ngIf=\"!isPreviewLoading; else loading\">\n <tbody>\n <tr *ngFor=\"let row of previewTableData\">\n <td>{{ row.time }}</td>\n <td>{{ row.source }}</td>\n <td>{{ row.device_name }}</td>\n <td>\n {{ row.fragment_series }}\n </td>\n <td>{{ row.value }}</td>\n <td>{{ row.unit }}</td>\n </tr>\n </tbody>\n </ng-container>\n </ng-container>\n <ng-template #emptyState>\n <tbody>\n <tr>\n <td colspan=\"8\">\n <div class=\"d-col a-i-center\">\n <c8y-ui-empty-state\n [icon]=\"'search'\"\n [title]=\"'No data available.' | translate\"\n [horizontal]=\"true\"\n data-cy=\"datapoints-table-list--empty-state\"\n ></c8y-ui-empty-state>\n </div>\n </td>\n </tr>\n </tbody>\n </ng-template>\n <ng-template #loading>\n <tbody>\n <tr>\n <td colspan=\"8\">\n <c8y-loading></c8y-loading>\n </td>\n </tr>\n </tbody>\n </ng-template>\n </table>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: A11yModule }, { kind: "ngmodule", type: CoreModule }, { kind: "component", type: i1.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1.LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i3$1.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }] }); }
718
+ }
719
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataPointsExportSelectorPreviewComponent, decorators: [{
720
+ type: Component,
721
+ args: [{ selector: 'c8y-datapoints-export-selector-preview', standalone: true, imports: [A11yModule, CoreModule, PopoverModule], template: "<div class=\"p-t-16 p-l-16 p-r-16 m-b-0\">\n <div class=\"d-flex a-i-center\">\n <label\n class=\"m-b-0 d-flex a-i-center gap-4\"\n [title]=\"'Preview`of exported file`' | translate\"\n >\n {{ 'Preview`of exported file`' | translate }}\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"popoverPreviewTemplate\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n <ng-template #popoverPreviewTemplate>\n <span translate>\n <p>The preview shows the structure of the raw file from a single source.</p>\n <p>If no data is available, only the column headers are visible.</p>\n <p>\n The preview is limited to\n <b>{{ MEASUREMENTS_PREVIEW_ITEMS_LIMIT }}</b>\n records.\n </p>\n </span>\n </ng-template>\n </label>\n </div>\n <div\n class=\"table-responsive\"\n style=\"min-height: 275px\"\n >\n <table class=\"table\">\n <thead>\n <tr>\n <th>{{ 'Time' | translate }}</th>\n <th>{{ 'Source' | translate }}</th>\n <th>{{ 'Device name' | translate }}</th>\n <th>\n {{ 'Fragment and series' | translate }}\n </th>\n <th>{{ 'Value' | translate }}</th>\n <th>{{ 'Unit' | translate }}</th>\n </tr>\n </thead>\n <ng-container *ngIf=\"hasFetchedDataAnyValuesToExport || isPreviewLoading; else emptyState\">\n <ng-container *ngIf=\"!isPreviewLoading; else loading\">\n <tbody>\n <tr *ngFor=\"let row of previewTableData\">\n <td>{{ row.time }}</td>\n <td>{{ row.source }}</td>\n <td>{{ row.device_name }}</td>\n <td>\n {{ row.fragment_series }}\n </td>\n <td>{{ row.value }}</td>\n <td>{{ row.unit }}</td>\n </tr>\n </tbody>\n </ng-container>\n </ng-container>\n <ng-template #emptyState>\n <tbody>\n <tr>\n <td colspan=\"8\">\n <div class=\"d-col a-i-center\">\n <c8y-ui-empty-state\n [icon]=\"'search'\"\n [title]=\"'No data available.' | translate\"\n [horizontal]=\"true\"\n data-cy=\"datapoints-table-list--empty-state\"\n ></c8y-ui-empty-state>\n </div>\n </td>\n </tr>\n </tbody>\n </ng-template>\n <ng-template #loading>\n <tbody>\n <tr>\n <td colspan=\"8\">\n <c8y-loading></c8y-loading>\n </td>\n </tr>\n </tbody>\n </ng-template>\n </table>\n </div>\n</div>\n" }]
722
+ }], propDecorators: { hasFetchedDataAnyValuesToExport: [{
723
+ type: Input
724
+ }], isPreviewLoading: [{
725
+ type: Input
726
+ }], previewTableData: [{
727
+ type: Input
728
+ }] } });
729
+
730
+ class DataPointsExportSelectorDataScopeComponent {
731
+ constructor() {
732
+ this.onAggregationChange = new EventEmitter();
733
+ this.onExportTypeChange = new EventEmitter();
734
+ this.AGGREGATION_LABELS = AGGREGATION_LABELS;
735
+ this.AGGREGATION_VALUES_ARR = AGGREGATION_VALUES_ARR;
736
+ this.EXPORT_MODE_LABELS = EXPORT_MODE_LABELS;
737
+ this.EXPORT_MODE_VALUES_ARR = [EXPORT_MODE_VALUES.full, EXPORT_MODE_VALUES.compact];
738
+ }
739
+ emitAggregationChange(aggregation) {
740
+ this.onAggregationChange.emit(aggregation);
741
+ }
742
+ emitExportTypeChange(exportType) {
743
+ this.onExportTypeChange.emit(exportType);
744
+ }
745
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataPointsExportSelectorDataScopeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
746
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DataPointsExportSelectorDataScopeComponent, isStandalone: true, selector: "c8y-datapoints-export-selector-data-scope", inputs: { disabledAggregationOptions: "disabledAggregationOptions", formGroup: "formGroup" }, outputs: { onAggregationChange: "onAggregationChange", onExportTypeChange: "onExportTypeChange" }, ngImport: i0, template: "<fieldset class=\"c8y-fieldset\">\n <legend class=\"d-flex a-i-center\">\n {{ 'Data scope' | translate }}\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"popoverDataScopeTemplate\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n <ng-template #popoverDataScopeTemplate>\n <p\n class=\"m-b-8\"\n translate\n >\n Choose type of an export to generate, the available options are:\n </p>\n <p><strong translate>Compact</strong></p>\n <ul class=\"p-l-16\">\n <li translate>\n Up to 5,000 records per data point, or up to the data retention limit\n </li>\n <li translate>Creates a single merged file containing all the data</li>\n <li translate>Preview is not available</li>\n <li translate>Supports data aggregation (optional)</li>\n </ul>\n <p><strong translate>Full</strong></p>\n <ul class=\"p-l-16\">\n <li translate>\n Up to 1,000,000 records per data point, or up to the data retention limit\n </li>\n <li translate>\n If the export exceeds 50,000 records, the data will be sent via email\n </li>\n <li translate>\n Creates a compressed ZIP file that contains separate data files for each of your\n selected points\n </li>\n <li translate>Preview is available</li>\n <li translate>No data aggregation</li>\n </ul>\n </ng-template>\n </legend>\n <c8y-form-group class=\"m-b-8\">\n <label>\n {{ 'Export mode' | translate }}\n </label>\n <div\n class=\"c8y-select-wrapper\"\n [formGroup]=\"formGroup\"\n >\n <select\n class=\"form-control text-12\"\n [title]=\"'Export mode' | translate\"\n id=\"exportMode\"\n formControlName=\"exportMode\"\n (ngModelChange)=\"emitExportTypeChange($event)\"\n >\n <option\n *ngFor=\"let exportModeValue of EXPORT_MODE_VALUES_ARR\"\n [ngValue]=\"exportModeValue\"\n >\n {{ EXPORT_MODE_LABELS[exportModeValue] | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n <c8y-form-group class=\"m-b-8\">\n <label>\n {{ 'Aggregation' | translate }}\n </label>\n <div\n class=\"c8y-select-wrapper\"\n [formGroup]=\"formGroup\"\n >\n <select\n class=\"form-control text-12\"\n [title]=\"'Aggregation' | translate\"\n id=\"aggregation\"\n formControlName=\"aggregation\"\n (ngModelChange)=\"emitAggregationChange($event)\"\n >\n <option\n *ngFor=\"let aggregationValue of AGGREGATION_VALUES_ARR\"\n [ngValue]=\"aggregationValue\"\n [disabled]=\"disabledAggregationOptions[aggregationValue]\"\n >\n {{ AGGREGATION_LABELS[aggregationValue] | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </fieldset>", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i3$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i3$1.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }] }); }
747
+ }
748
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataPointsExportSelectorDataScopeComponent, decorators: [{
749
+ type: Component,
750
+ args: [{ selector: 'c8y-datapoints-export-selector-data-scope', standalone: true, imports: [CoreModule, FormsModule, PopoverModule], template: "<fieldset class=\"c8y-fieldset\">\n <legend class=\"d-flex a-i-center\">\n {{ 'Data scope' | translate }}\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"popoverDataScopeTemplate\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n [adaptivePosition]=\"true\"\n ></button>\n <ng-template #popoverDataScopeTemplate>\n <p\n class=\"m-b-8\"\n translate\n >\n Choose type of an export to generate, the available options are:\n </p>\n <p><strong translate>Compact</strong></p>\n <ul class=\"p-l-16\">\n <li translate>\n Up to 5,000 records per data point, or up to the data retention limit\n </li>\n <li translate>Creates a single merged file containing all the data</li>\n <li translate>Preview is not available</li>\n <li translate>Supports data aggregation (optional)</li>\n </ul>\n <p><strong translate>Full</strong></p>\n <ul class=\"p-l-16\">\n <li translate>\n Up to 1,000,000 records per data point, or up to the data retention limit\n </li>\n <li translate>\n If the export exceeds 50,000 records, the data will be sent via email\n </li>\n <li translate>\n Creates a compressed ZIP file that contains separate data files for each of your\n selected points\n </li>\n <li translate>Preview is available</li>\n <li translate>No data aggregation</li>\n </ul>\n </ng-template>\n </legend>\n <c8y-form-group class=\"m-b-8\">\n <label>\n {{ 'Export mode' | translate }}\n </label>\n <div\n class=\"c8y-select-wrapper\"\n [formGroup]=\"formGroup\"\n >\n <select\n class=\"form-control text-12\"\n [title]=\"'Export mode' | translate\"\n id=\"exportMode\"\n formControlName=\"exportMode\"\n (ngModelChange)=\"emitExportTypeChange($event)\"\n >\n <option\n *ngFor=\"let exportModeValue of EXPORT_MODE_VALUES_ARR\"\n [ngValue]=\"exportModeValue\"\n >\n {{ EXPORT_MODE_LABELS[exportModeValue] | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n <c8y-form-group class=\"m-b-8\">\n <label>\n {{ 'Aggregation' | translate }}\n </label>\n <div\n class=\"c8y-select-wrapper\"\n [formGroup]=\"formGroup\"\n >\n <select\n class=\"form-control text-12\"\n [title]=\"'Aggregation' | translate\"\n id=\"aggregation\"\n formControlName=\"aggregation\"\n (ngModelChange)=\"emitAggregationChange($event)\"\n >\n <option\n *ngFor=\"let aggregationValue of AGGREGATION_VALUES_ARR\"\n [ngValue]=\"aggregationValue\"\n [disabled]=\"disabledAggregationOptions[aggregationValue]\"\n >\n {{ AGGREGATION_LABELS[aggregationValue] | translate }}\n </option>\n </select>\n </div>\n </c8y-form-group>\n </fieldset>" }]
751
+ }], propDecorators: { disabledAggregationOptions: [{
752
+ type: Input
753
+ }], formGroup: [{
754
+ type: Input
755
+ }], onAggregationChange: [{
756
+ type: Output
757
+ }], onExportTypeChange: [{
758
+ type: Output
759
+ }] } });
760
+
761
+ class DataPointsExportSelectorFileTypesComponent {
762
+ constructor() {
763
+ this.dynamicFilesTypeMetadata = {};
764
+ }
765
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataPointsExportSelectorFileTypesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
766
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DataPointsExportSelectorFileTypesComponent, isStandalone: true, selector: "c8y-datapoints-export-selector-file-types", inputs: { dynamicFilesTypeMetadata: "dynamicFilesTypeMetadata", formGroup: "formGroup" }, ngImport: i0, template: "<fieldset class=\"c8y-fieldset\">\n <legend class=\"d-flex a-i-center\">{{ 'File types' | translate }}</legend>\n <div [formGroup]=\"formGroup\">\n <div formGroupName=\"fileTypes\">\n <c8y-form-group\n class=\"m-b-8\"\n *ngFor=\"let item of dynamicFilesTypeMetadata | keyvalue\"\n >\n <label>{{ item.value.label | translate }}</label>\n <label\n class=\"c8y-checkbox m-t-0\"\n title=\"{{ item.value.title }}\"\n >\n <input\n type=\"checkbox\"\n formControlName=\"{{ item.key }}\"\n />\n <span></span>\n <i\n class=\"m-l-8 m-r-4\"\n c8yIcon=\"{{ item.value.icon }}\"\n ></i>\n </label>\n </c8y-form-group>\n </div>\n </div>\n</fieldset>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "pipe", type: i2$1.KeyValuePipe, name: "keyvalue" }, { kind: "directive", type: i3$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i3$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i3$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i3$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i3$2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "ngmodule", type: FormsModule }] }); }
767
+ }
768
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataPointsExportSelectorFileTypesComponent, decorators: [{
769
+ type: Component,
770
+ args: [{ selector: 'c8y-datapoints-export-selector-file-types', standalone: true, imports: [CoreModule, FormsModule], template: "<fieldset class=\"c8y-fieldset\">\n <legend class=\"d-flex a-i-center\">{{ 'File types' | translate }}</legend>\n <div [formGroup]=\"formGroup\">\n <div formGroupName=\"fileTypes\">\n <c8y-form-group\n class=\"m-b-8\"\n *ngFor=\"let item of dynamicFilesTypeMetadata | keyvalue\"\n >\n <label>{{ item.value.label | translate }}</label>\n <label\n class=\"c8y-checkbox m-t-0\"\n title=\"{{ item.value.title }}\"\n >\n <input\n type=\"checkbox\"\n formControlName=\"{{ item.key }}\"\n />\n <span></span>\n <i\n class=\"m-l-8 m-r-4\"\n c8yIcon=\"{{ item.value.icon }}\"\n ></i>\n </label>\n </c8y-form-group>\n </div>\n </div>\n</fieldset>\n" }]
771
+ }], propDecorators: { dynamicFilesTypeMetadata: [{
772
+ type: Input
773
+ }], formGroup: [{
774
+ type: Input
775
+ }] } });
776
+
777
+ class DataPointsExportSelectorTimeRangeComponent {
778
+ constructor() {
779
+ this.onDateFromChange = new EventEmitter();
780
+ this.onDateToChange = new EventEmitter();
781
+ this.DATE_FROM = 'dateFrom';
782
+ this.DATE_TO = 'dateTo';
783
+ this.FROM_DATE = gettext('From`date`');
784
+ this.HAS_ERROR = HAS_ERROR;
785
+ this.INVALID_DATE_TIME = 'invalidDateTime';
786
+ this.THIS_DATE_IS_INVALID = gettext('This date is invalid.');
787
+ this.THIS_DATE_IS_AFTER_THE_LAST_ALLOWED_DATE = gettext('This date is after the latest allowed date.');
788
+ this.THIS_DATE_IS_BEFORE_THE_EARLIEST_ALLOWED_DATE = gettext('This date is before the earliest allowed date.');
789
+ this.TO_DATE = gettext('To`date`');
790
+ }
791
+ emitDateFromChange(updatedDate) {
792
+ this.onDateFromChange.emit(updatedDate);
793
+ }
794
+ emitDateToChange(updatedDate) {
795
+ this.onDateToChange.emit(updatedDate);
796
+ }
797
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataPointsExportSelectorTimeRangeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
798
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DataPointsExportSelectorTimeRangeComponent, isStandalone: true, selector: "c8y-datapoints-export-selector-time-range", inputs: { formGroup: "formGroup" }, outputs: { onDateFromChange: "onDateFromChange", onDateToChange: "onDateToChange" }, ngImport: i0, template: "<fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Time range' | translate }}</legend>\n <c8y-form-group\n class=\"m-b-8\"\n [ngClass]=\"formGroup.controls.dateFrom.errors ? HAS_ERROR : ''\"\n >\n <label\n [title]=\"FROM_DATE | translate\"\n [for]=\"DATE_FROM\"\n >\n {{ FROM_DATE | translate }}\n </label>\n <div>\n <c8y-date-time-picker\n id=\"DATE_FROM\"\n [maxDate]=\"formGroup.value.dateTo\"\n [placeholder]=\"FROM_DATE | translate\"\n [formControl]=\"formGroup.controls.dateFrom\"\n [ngClass]=\"formGroup.controls.dateFrom.errors ? HAS_ERROR : ''\"\n (ngModelChange)=\"emitDateFromChange($event)\"\n ></c8y-date-time-picker>\n <c8y-messages [show]=\"formGroup.controls.dateFrom.errors\">\n <c8y-message\n name=\"dateAfterRangeMax\"\n [text]=\"THIS_DATE_IS_AFTER_THE_LAST_ALLOWED_DATE | translate\"\n ></c8y-message>\n <c8y-message\n name=\"INVALID_DATE_TIME\"\n [text]=\"THIS_DATE_IS_INVALID | translate\"\n ></c8y-message>\n </c8y-messages>\n </div>\n </c8y-form-group>\n <c8y-form-group\n class=\"m-b-8\"\n [ngClass]=\"formGroup.controls.dateTo.errors ? HAS_ERROR : ''\"\n >\n <label\n [title]=\"TO_DATE | translate\"\n [for]=\"DATE_TO\"\n >\n {{ TO_DATE | translate }}\n </label>\n <div>\n <c8y-date-time-picker\n id=\"DATE_TO\"\n [minDate]=\"formGroup.value.dateFrom\"\n [placeholder]=\"TO_DATE | translate\"\n [formControl]=\"formGroup.controls.dateTo\"\n [ngClass]=\"formGroup.controls.dateTo.errors ? HAS_ERROR : ''\"\n (ngModelChange)=\"emitDateToChange($event)\"\n ></c8y-date-time-picker>\n <c8y-messages [show]=\"formGroup.controls.dateTo.errors\">\n <c8y-message\n name=\"dateBeforeRangeMin\"\n [text]=\"THIS_DATE_IS_BEFORE_THE_EARLIEST_ALLOWED_DATE | translate\"\n ></c8y-message>\n <c8y-message\n name=\"INVALID_DATE_TIME\"\n [text]=\"THIS_DATE_IS_INVALID | translate\"\n ></c8y-message>\n </c8y-messages>\n </div>\n </c8y-form-group>\n</fieldset>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "component", type: i1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i1.MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: i1.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "directive", type: i3$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: i1.DateTimePickerComponent, selector: "c8y-date-time-picker", inputs: ["minDate", "maxDate", "placeholder", "dateInputFormat", "adaptivePosition", "size", "dateType", "config"], outputs: ["onDateSelected"] }, { kind: "ngmodule", type: FormsModule }] }); }
799
+ }
800
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataPointsExportSelectorTimeRangeComponent, decorators: [{
801
+ type: Component,
802
+ args: [{ selector: 'c8y-datapoints-export-selector-time-range', standalone: true, imports: [CoreModule, FormsModule], template: "<fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Time range' | translate }}</legend>\n <c8y-form-group\n class=\"m-b-8\"\n [ngClass]=\"formGroup.controls.dateFrom.errors ? HAS_ERROR : ''\"\n >\n <label\n [title]=\"FROM_DATE | translate\"\n [for]=\"DATE_FROM\"\n >\n {{ FROM_DATE | translate }}\n </label>\n <div>\n <c8y-date-time-picker\n id=\"DATE_FROM\"\n [maxDate]=\"formGroup.value.dateTo\"\n [placeholder]=\"FROM_DATE | translate\"\n [formControl]=\"formGroup.controls.dateFrom\"\n [ngClass]=\"formGroup.controls.dateFrom.errors ? HAS_ERROR : ''\"\n (ngModelChange)=\"emitDateFromChange($event)\"\n ></c8y-date-time-picker>\n <c8y-messages [show]=\"formGroup.controls.dateFrom.errors\">\n <c8y-message\n name=\"dateAfterRangeMax\"\n [text]=\"THIS_DATE_IS_AFTER_THE_LAST_ALLOWED_DATE | translate\"\n ></c8y-message>\n <c8y-message\n name=\"INVALID_DATE_TIME\"\n [text]=\"THIS_DATE_IS_INVALID | translate\"\n ></c8y-message>\n </c8y-messages>\n </div>\n </c8y-form-group>\n <c8y-form-group\n class=\"m-b-8\"\n [ngClass]=\"formGroup.controls.dateTo.errors ? HAS_ERROR : ''\"\n >\n <label\n [title]=\"TO_DATE | translate\"\n [for]=\"DATE_TO\"\n >\n {{ TO_DATE | translate }}\n </label>\n <div>\n <c8y-date-time-picker\n id=\"DATE_TO\"\n [minDate]=\"formGroup.value.dateFrom\"\n [placeholder]=\"TO_DATE | translate\"\n [formControl]=\"formGroup.controls.dateTo\"\n [ngClass]=\"formGroup.controls.dateTo.errors ? HAS_ERROR : ''\"\n (ngModelChange)=\"emitDateToChange($event)\"\n ></c8y-date-time-picker>\n <c8y-messages [show]=\"formGroup.controls.dateTo.errors\">\n <c8y-message\n name=\"dateBeforeRangeMin\"\n [text]=\"THIS_DATE_IS_BEFORE_THE_EARLIEST_ALLOWED_DATE | translate\"\n ></c8y-message>\n <c8y-message\n name=\"INVALID_DATE_TIME\"\n [text]=\"THIS_DATE_IS_INVALID | translate\"\n ></c8y-message>\n </c8y-messages>\n </div>\n </c8y-form-group>\n</fieldset>\n" }]
803
+ }], propDecorators: { formGroup: [{
804
+ type: Input
805
+ }], onDateFromChange: [{
806
+ type: Output
807
+ }], onDateToChange: [{
808
+ type: Output
809
+ }] } });
810
+
811
+ class CSVDataTransformer {
812
+ transformToMergedFormat(exportData) {
813
+ const timeSeries = new Map();
814
+ const uniqueColumnIdentifiers = new Set();
815
+ exportData.forEach(({ time, device_name, fragment_series, unit, value_min, value_max, source }) => {
816
+ const measurementIdentifier = this.createMeasurementIdentifier(device_name, fragment_series, unit, source);
817
+ if (!timeSeries.has(time)) {
818
+ timeSeries.set(time, {});
819
+ }
820
+ const timeEntry = timeSeries.get(time);
821
+ if (timeEntry) {
822
+ const minKey = this.formatMinKey(measurementIdentifier);
823
+ const maxKey = this.formatMaxKey(measurementIdentifier);
824
+ timeEntry[minKey] = value_min;
825
+ timeEntry[maxKey] = value_max;
826
+ uniqueColumnIdentifiers.add(minKey);
827
+ uniqueColumnIdentifiers.add(maxKey);
828
+ }
829
+ });
830
+ return {
831
+ timeSeries,
832
+ uniqueColumnIdentifiers: Array.from(uniqueColumnIdentifiers)
833
+ };
834
+ }
835
+ createMeasurementIdentifier(deviceName, fragmentSeries, unit, source) {
836
+ return unit
837
+ ? `(${source}) ${deviceName} -> ${fragmentSeries} [${unit}]`
838
+ : `(${source}) ${deviceName} -> ${fragmentSeries}`;
839
+ }
840
+ formatMinKey(baseKey) {
841
+ return `${baseKey} (min)`;
842
+ }
843
+ formatMaxKey(baseKey) {
844
+ return `${baseKey} (max)`;
845
+ }
846
+ }
847
+ class CSVFileCreator {
848
+ async createFile(content, mimeType) {
849
+ return new Blob([content], { type: mimeType });
850
+ }
851
+ }
852
+ class CSVGenerator {
853
+ static getLabel() {
854
+ return gettext('Comma-separated value');
855
+ }
856
+ static getTitle() {
857
+ return 'CSV';
858
+ }
859
+ static getType() {
860
+ return 'csv';
861
+ }
862
+ static getIcon() {
863
+ return 'table';
864
+ }
865
+ static getFileExtension() {
866
+ return 'csv';
867
+ }
868
+ static getAcceptType() {
869
+ return 'text/csv';
870
+ }
871
+ static getMimeType() {
872
+ return 'text/csv';
873
+ }
874
+ static getZipName() {
875
+ return 'export_csv.zip';
876
+ }
877
+ static async convertToMergedCSV(exportData, mergedExportDetails) {
878
+ const instance = CSVGenerator.getInstance();
879
+ const { timeSeries, uniqueColumnIdentifiers } = instance.dataTransformer.transformToMergedFormat(exportData);
880
+ const rows = [
881
+ instance.createMergedFileHeaderRow(uniqueColumnIdentifiers, mergedExportDetails),
882
+ ...instance.createMergedFileDataRows(timeSeries, uniqueColumnIdentifiers)
883
+ ];
884
+ const csvContent = rows.map(row => row.join(',')).join('\n');
885
+ return instance.fileCreator.createFile(csvContent, CSVGenerator.getMimeType());
886
+ }
887
+ static getInstance() {
888
+ if (!CSVGenerator.instance) {
889
+ CSVGenerator.instance = new CSVGenerator();
890
+ }
891
+ return CSVGenerator.instance;
892
+ }
893
+ constructor() {
894
+ this.dataTransformer = new CSVDataTransformer();
895
+ this.fileCreator = new CSVFileCreator();
896
+ }
897
+ createMergedFileHeaderRow(uniqueColumnIdentifiers, details) {
898
+ const time = details ? `time => from ${details.dateFrom} to ${details.dateTo}` : 'time';
899
+ return [time, ...uniqueColumnIdentifiers];
900
+ }
901
+ createMergedFileDataRows(timeSeries, uniqueColumnIdentifiers) {
902
+ return Array.from(timeSeries.entries())
903
+ .filter(([time, values]) => this.isValidEntry(time, values))
904
+ .map(([time, values]) => this.createDataRow(time, values, uniqueColumnIdentifiers));
905
+ }
906
+ isValidEntry(time, values) {
907
+ return time !== null && Object.values(values).some(value => value != null);
908
+ }
909
+ createDataRow(time, values, uniqueColumnIdentifiers) {
910
+ const row = [time];
911
+ uniqueColumnIdentifiers.forEach(key => {
912
+ const value = values[key];
913
+ row.push(value?.toString() || '');
914
+ });
915
+ return row;
916
+ }
917
+ }
918
+ const CSVGeneratorAdapter = {
919
+ getLabel: CSVGenerator.getLabel.bind(CSVGenerator),
920
+ getIcon: CSVGenerator.getIcon.bind(CSVGenerator),
921
+ getFileExtension: CSVGenerator.getFileExtension.bind(CSVGenerator),
922
+ getMimeType: CSVGenerator.getMimeType.bind(CSVGenerator),
923
+ generateMerged: CSVGenerator.convertToMergedCSV.bind(CSVGenerator),
924
+ getType: CSVGenerator.getType.bind(CSVGenerator),
925
+ getTitle: CSVGenerator.getTitle.bind(CSVGenerator),
926
+ getZipName: CSVGenerator.getZipName.bind(CSVGenerator),
927
+ getAcceptType: CSVGenerator.getAcceptType.bind(CSVGenerator)
928
+ };
929
+
930
+ // Utility functions
931
+ class XmlUtils {
932
+ static escapeXml(unsafe) {
933
+ return unsafe.replace(/[<>&'"]/g, c => {
934
+ switch (c) {
935
+ case '<':
936
+ return '&lt;';
937
+ case '>':
938
+ return '&gt;';
939
+ case '&':
940
+ return '&amp;';
941
+ case "'":
942
+ return '&apos;';
943
+ case '"':
944
+ return '&quot;';
945
+ }
946
+ });
947
+ }
948
+ }
949
+ class ExcelDataTransformer {
950
+ transformToMergedFormat(exportData) {
951
+ const timeSeries = new Map();
952
+ const uniqueColumnIdentifiers = new Set();
953
+ exportData.forEach(({ time, device_name, fragment_series, unit, value_min, value_max, source }) => {
954
+ const measurementIdentifier = this.createMeasurementIdentifier(device_name, fragment_series, unit, source);
955
+ if (!timeSeries.has(time)) {
956
+ timeSeries.set(time, {});
957
+ }
958
+ const timeEntry = timeSeries.get(time);
959
+ if (timeEntry) {
960
+ const minKey = this.formatMinKey(measurementIdentifier);
961
+ const maxKey = this.formatMaxKey(measurementIdentifier);
962
+ timeEntry[minKey] = value_min;
963
+ timeEntry[maxKey] = value_max;
964
+ uniqueColumnIdentifiers.add(minKey);
965
+ uniqueColumnIdentifiers.add(maxKey);
966
+ }
967
+ });
968
+ return {
969
+ timeSeries,
970
+ uniqueColumnIdentifiers: Array.from(uniqueColumnIdentifiers)
971
+ };
972
+ }
973
+ createMeasurementIdentifier(deviceName, fragmentSeries, unit, source) {
974
+ return unit
975
+ ? `(${source}) ${deviceName} -> ${fragmentSeries} [${unit}]`
976
+ : `(${source}) ${deviceName} -> ${fragmentSeries}`;
977
+ }
978
+ formatMinKey(baseKey) {
979
+ return `${baseKey} (min)`;
980
+ }
981
+ formatMaxKey(baseKey) {
982
+ return `${baseKey} (max)`;
983
+ }
984
+ }
985
+ class ExcelXmlGenerator {
986
+ constructor() {
987
+ this.columnWidths = {
988
+ time: 25,
989
+ source: 10,
990
+ device_name: 20,
991
+ 'fragment.series': 20,
992
+ value_min: 15,
993
+ value_max: 15,
994
+ unit: 5
995
+ };
996
+ this.defaultWidth = 30;
997
+ this.RELS_XML = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
998
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n' +
999
+ ' <Relationship Id="rId1" Target="xl/workbook.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"/>\n' +
1000
+ ' <Relationship Id="rId2" Target="docProps/app.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"/>\n' +
1001
+ ' <Relationship Id="rId3" Target="docProps/core.xml" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"/>\n' +
1002
+ '</Relationships>';
1003
+ this.CONTENT_TYPES_XML = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n' +
1004
+ '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">\n' +
1005
+ ' <Default ContentType="application/vnd.openxmlformats-package.relationships+xml" Extension="rels"/>\n' +
1006
+ ' <Default ContentType="application/xml" Extension="xml"/>\n' +
1007
+ ' <Override ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml" PartName="/docProps/app.xml"/>\n' +
1008
+ ' <Override ContentType="application/vnd.openxmlformats-package.core-properties+xml" PartName="/docProps/core.xml"/>\n' +
1009
+ ' <Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml" PartName="/xl/sharedStrings.xml"/>\n' +
1010
+ ' <Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" PartName="/xl/workbook.xml"/>\n' +
1011
+ ' <Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" PartName="/xl/worksheets/sheet1.xml"/>\n' +
1012
+ '</Types>';
1013
+ this.WORKBOOK_RELS_XML = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
1014
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">\n' +
1015
+ ' <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>\n' +
1016
+ ' <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>\n' +
1017
+ '</Relationships>';
1018
+ this.SHARED_STRINGS_XML = '<?xml version="1.0" encoding="UTF-8"?>\n' +
1019
+ '<sst count="0" uniqueCount="0" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"/>';
1020
+ }
1021
+ generateWorkbookXml(sheetName = 'Measurements') {
1022
+ return ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
1023
+ `<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name='${sheetName}' r:id="rId3" sheetId="1"/></sheets></workbook>`);
1024
+ }
1025
+ generateSheetXml(data) {
1026
+ const headers = data[0];
1027
+ const colsXml = this.generateColsXml(headers);
1028
+ const rowsXml = this.generateRowsXml(data);
1029
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1030
+ <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
1031
+ <cols>${colsXml}</cols>
1032
+ <sheetData>${rowsXml}</sheetData>
1033
+ </worksheet>`;
1034
+ }
1035
+ generateAppXml(name) {
1036
+ return ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
1037
+ '<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">\n' +
1038
+ ` <Application>${name}</Application>\n` +
1039
+ ' <DocSecurity>0</DocSecurity>\n' +
1040
+ ' <ScaleCrop>false</ScaleCrop>\n' +
1041
+ ' <HeadingPairs>\n' +
1042
+ ' <vt:vector size="2" baseType="variant">\n' +
1043
+ ' <vt:variant>\n' +
1044
+ ' <vt:lpstr>Worksheets</vt:lpstr>\n' +
1045
+ ' </vt:variant>\n' +
1046
+ ' <vt:variant>\n' +
1047
+ ' <vt:i4>1</vt:i4>\n' +
1048
+ ' </vt:variant>\n' +
1049
+ ' </vt:vector>\n' +
1050
+ ' </HeadingPairs>\n' +
1051
+ ' <TitlesOfParts>\n' +
1052
+ ' <vt:vector size="1" baseType="lpstr">\n' +
1053
+ ' <vt:lpstr>Sheet1</vt:lpstr>\n' +
1054
+ ' </vt:vector>\n' +
1055
+ ' </TitlesOfParts>\n' +
1056
+ ' <Company></Company>\n' +
1057
+ ' <LinksUpToDate>false</LinksUpToDate>\n' +
1058
+ ' <SharedDoc>false</SharedDoc>\n' +
1059
+ ' <HyperlinksChanged>false</HyperlinksChanged>\n' +
1060
+ ' <AppVersion>16.0300</AppVersion>\n' +
1061
+ '</Properties>');
1062
+ }
1063
+ generateCoreXml(name) {
1064
+ const now = new Date().toISOString();
1065
+ return ('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
1066
+ '<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">\n' +
1067
+ ` <dc:creator>${name}</dc:creator>\n` +
1068
+ ` <cp:lastModifiedBy>${name}</cp:lastModifiedBy>\n` +
1069
+ ` <dcterms:created xsi:type="dcterms:W3CDTF">${now}</dcterms:created>\n` +
1070
+ ` <dcterms:modified xsi:type="dcterms:W3CDTF">${now}</dcterms:modified>\n` +
1071
+ '</cp:coreProperties>');
1072
+ }
1073
+ generateColsXml(headers) {
1074
+ return headers
1075
+ .map((header, index) => {
1076
+ const width = this.columnWidths[header] || this.defaultWidth;
1077
+ return `<col min="${index + 1}" max="${index + 1}" width="${width}" customWidth="1"/>`;
1078
+ })
1079
+ .join('');
1080
+ }
1081
+ generateRowsXml(data) {
1082
+ return data
1083
+ .map(row => `<row>${row
1084
+ .map(cell => {
1085
+ if (isNil(cell) || isEmpty(cell)) {
1086
+ return `<c t="inlineStr"><is><t></t></is></c>`; // String cell
1087
+ }
1088
+ else if (isNumber(Number(cell)) && !isNaN(Number(cell))) {
1089
+ return `<c t="n"><v>${Number(cell)}</v></c>`; // Number cell
1090
+ }
1091
+ else {
1092
+ return `<c t="inlineStr"><is><t>${XmlUtils.escapeXml(cell)}</t></is></c>`; // String cell
1093
+ }
1094
+ })
1095
+ .join('')}</row>`)
1096
+ .join('');
1097
+ }
1098
+ }
1099
+ class ZipFileCreator {
1100
+ async createZipFile(contents, compressionType, compressionLevel) {
1101
+ const zip = new JSZip();
1102
+ const blob = 'blob';
1103
+ contents.forEach((content, filename) => {
1104
+ zip.file(filename, content);
1105
+ });
1106
+ const options = { type: blob };
1107
+ if (compressionType !== FILE_COMPRESSION_TYPES_VALUES.store) {
1108
+ options.compression = compressionType;
1109
+ options.compressionOptions = { level: compressionLevel };
1110
+ }
1111
+ return (await zip.generateAsync(options));
1112
+ }
1113
+ }
1114
+ class ExcelGenerator {
1115
+ static getLabel() {
1116
+ return gettext('Microsoft Excel');
1117
+ }
1118
+ static getTitle() {
1119
+ return 'Excel';
1120
+ }
1121
+ static getType() {
1122
+ return 'excel';
1123
+ }
1124
+ static getIcon() {
1125
+ return 'excel';
1126
+ }
1127
+ static getFileExtension() {
1128
+ return 'xlsx';
1129
+ }
1130
+ static getAcceptType() {
1131
+ return 'application/vnd.ms-excel';
1132
+ }
1133
+ static getMimeType() {
1134
+ return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
1135
+ }
1136
+ static getZipName() {
1137
+ return 'export_excel.zip';
1138
+ }
1139
+ static async convertToMergedExcel(exportData, mergedExportDetails) {
1140
+ const instance = ExcelGenerator.getInstance();
1141
+ const { timeSeries, uniqueColumnIdentifiers } = instance.dataTransformer.transformToMergedFormat(exportData);
1142
+ const rows = [
1143
+ instance.createMergedFileHeaderRow(uniqueColumnIdentifiers, mergedExportDetails),
1144
+ ...instance.createMergedFileDataRows(timeSeries, uniqueColumnIdentifiers)
1145
+ ];
1146
+ return instance.createExcelFile(rows, mergedExportDetails);
1147
+ }
1148
+ static getInstance() {
1149
+ if (!ExcelGenerator.instance) {
1150
+ ExcelGenerator.instance = new ExcelGenerator();
1151
+ }
1152
+ return ExcelGenerator.instance;
1153
+ }
1154
+ constructor() {
1155
+ this.dataTransformer = new ExcelDataTransformer();
1156
+ this.xmlGenerator = new ExcelXmlGenerator();
1157
+ this.fileCreator = new ZipFileCreator();
1158
+ }
1159
+ async createExcelFile(data, details) {
1160
+ const contents = new Map([
1161
+ ['[Content_Types].xml', this.xmlGenerator.CONTENT_TYPES_XML],
1162
+ ['_rels/.rels', this.xmlGenerator.RELS_XML],
1163
+ [
1164
+ 'xl/workbook.xml',
1165
+ this.xmlGenerator.generateWorkbookXml(details?.aggregation ? details?.aggregation : 'NO AGGREGATION')
1166
+ ],
1167
+ ['xl/worksheets/sheet1.xml', this.xmlGenerator.generateSheetXml(data)],
1168
+ ['xl/sharedStrings.xml', this.xmlGenerator.SHARED_STRINGS_XML],
1169
+ ['docProps/app.xml', this.xmlGenerator.generateAppXml('Custom Script')],
1170
+ ['docProps/core.xml', this.xmlGenerator.generateCoreXml('Custom Script')],
1171
+ ['xl/_rels/workbook.xml.rels', this.xmlGenerator.WORKBOOK_RELS_XML]
1172
+ ]);
1173
+ return this.fileCreator.createZipFile(contents, FILE_COMPRESSION_TYPES_VALUES.deflate, 9);
1174
+ }
1175
+ createMergedFileHeaderRow(uniqueColumnIdentifiers, details) {
1176
+ const time = details ? `time => from ${details.dateFrom} to ${details.dateTo}` : 'time';
1177
+ return [time, ...uniqueColumnIdentifiers];
1178
+ }
1179
+ createMergedFileDataRows(timeSeries, uniqueColumnIdentifiers) {
1180
+ return Array.from(timeSeries.entries())
1181
+ .filter(([time, values]) => this.isValidEntry(time, values))
1182
+ .map(([time, values]) => this.createDataRow(time, values, uniqueColumnIdentifiers));
1183
+ }
1184
+ isValidEntry(time, values) {
1185
+ return (time !== null && Object.values(values).some(value => value !== null && value !== undefined));
1186
+ }
1187
+ createDataRow(time, values, uniqueColumnIdentifiers) {
1188
+ const row = [time];
1189
+ uniqueColumnIdentifiers.forEach(key => {
1190
+ const value = values[key];
1191
+ row.push(value !== undefined && value !== null ? value.toString() : '');
1192
+ });
1193
+ return row;
1194
+ }
1195
+ }
1196
+ const ExcelGeneratorAdapter = {
1197
+ getLabel: ExcelGenerator.getLabel.bind(ExcelGenerator),
1198
+ getIcon: ExcelGenerator.getIcon.bind(ExcelGenerator),
1199
+ getFileExtension: ExcelGenerator.getFileExtension.bind(ExcelGenerator),
1200
+ getMimeType: ExcelGenerator.getMimeType.bind(ExcelGenerator),
1201
+ generateMerged: ExcelGenerator.convertToMergedExcel.bind(ExcelGenerator),
1202
+ getType: ExcelGenerator.getType.bind(ExcelGenerator),
1203
+ getTitle: ExcelGenerator.getTitle.bind(ExcelGenerator),
1204
+ getZipName: ExcelGenerator.getZipName.bind(ExcelGenerator),
1205
+ getAcceptType: ExcelGenerator.getAcceptType.bind(ExcelGenerator)
1206
+ };
1207
+
1208
+ const FILE_GENERATORS = new InjectionToken('FILE_GENERATORS', {
1209
+ providedIn: 'root',
1210
+ factory: () => [ExcelGeneratorAdapter, CSVGeneratorAdapter]
1211
+ });
1212
+ const dateRangeValidator = (control) => {
1213
+ const dateFrom = control.get('dateFrom');
1214
+ const dateTo = control.get('dateTo');
1215
+ if (!dateFrom || !dateTo) {
1216
+ return null;
1217
+ }
1218
+ const dateFromValue = dateFrom.value;
1219
+ const dateToValue = dateTo.value;
1220
+ if (dateFromValue && dateToValue) {
1221
+ if (new Date(dateFromValue) > new Date(dateToValue)) {
1222
+ dateFrom.setErrors({ dateAfterRangeMax: true });
1223
+ dateTo.setErrors({ dateBeforeRangeMin: true });
1224
+ }
1225
+ else {
1226
+ dateFrom.setErrors(null);
1227
+ dateTo.setErrors(null);
1228
+ }
1229
+ }
1230
+ return null;
1231
+ };
1232
+ class DatapointsExportSelectorFileExporterComponent {
1233
+ constructor(datapointsExportModalService, datapointsExportSelectorService, datapointsExportSelectorFileExporterService, formBuilder, generators) {
1234
+ this.datapointsExportModalService = datapointsExportModalService;
1235
+ this.datapointsExportSelectorService = datapointsExportSelectorService;
1236
+ this.datapointsExportSelectorFileExporterService = datapointsExportSelectorFileExporterService;
1237
+ this.formBuilder = formBuilder;
1238
+ this.generators = generators;
1239
+ this.onDownloadButtonStateChange = new EventEmitter();
1240
+ /**
1241
+ * Represents aggregation selector options that are disabled.
1242
+ * This state is determined based on the current date range.
1243
+ */
1244
+ this.disabledAggregationOptions = {};
1245
+ /**
1246
+ * Contains all datapoints that number of records exceeds a threshold, where data for these datapoints will be processed by the backend.
1247
+ * Applies only to measurement API which can return processed CSV, Excel or XML data.
1248
+ */
1249
+ this.datapointsExceedingBrowserDownloadLimit = [];
1250
+ /**
1251
+ * Indicates whether any of DataToExport objects timeValueMap property is not undefined (series data)
1252
+ * or dataPointToExport is not undefined at all (measurement data).
1253
+ */
1254
+ this.hasFetchedDataAnyValuesToExport = false;
1255
+ this.hasPermissionToReadAnyMeasurements = false;
1256
+ this.isPreviewLoading = true;
1257
+ /**
1258
+ * Indicates whether the full or compact type of export is selected.
1259
+ * Full type of export is based on measurement API data.
1260
+ * Compact type of export is based on series API data.
1261
+ */
1262
+ this.isFullTypeOfExport = false;
1263
+ /**
1264
+ * Indicates whether the aggregation value was changed programmatically.
1265
+ * It prevents a case where fetching may be triggered twice when export type is changed.
1266
+ * Both options alone can trigger fetching data.
1267
+ */
1268
+ this.isAggregationChangedProgrammatically = false;
1269
+ /**
1270
+ * Indicates whether any of the export file types (CSV, Excel) are checked.
1271
+ */
1272
+ this.isAnyOfExportFileTypeChecked = false;
1273
+ this.dateFromSubject = new Subject();
1274
+ this.dateToSubject = new Subject();
1275
+ this.destroy$ = new Subject();
1276
+ this.dynamicFilesTypeMetadata = {};
1277
+ this.setFilesType(this.generators);
1278
+ this.dateFromSubject
1279
+ .pipe(debounceTime(300), takeUntilDestroyed())
1280
+ .subscribe(async (updatedDate) => {
1281
+ await this.updateDateAndFetchData('dateFrom', updatedDate);
1282
+ });
1283
+ this.dateToSubject
1284
+ .pipe(debounceTime(300), takeUntilDestroyed())
1285
+ .subscribe(async (updatedDate) => {
1286
+ await this.updateDateAndFetchData('dateTo', updatedDate);
1287
+ });
1288
+ }
1289
+ setFilesType(generators) {
1290
+ generators.forEach(g => {
1291
+ this.dynamicFilesTypeMetadata[g.getType()] = {
1292
+ label: g.getLabel(),
1293
+ icon: g.getIcon(),
1294
+ title: g.getTitle(),
1295
+ extension: g.getFileExtension(),
1296
+ zipName: g.getZipName(),
1297
+ type: g.getType()
1298
+ };
1299
+ });
1300
+ }
1301
+ async ngOnInit() {
1302
+ const sourcesPermittedToRead = await this.datapointsExportSelectorService.getSourcesWithPermissionsToRead(this.exportConfig.datapointDetails);
1303
+ if (sourcesPermittedToRead.length === 0) {
1304
+ return;
1305
+ }
1306
+ if (sourcesPermittedToRead.length !== this.exportConfig.datapointDetails.length) {
1307
+ this.exportConfig.datapointDetails = this.exportConfig.datapointDetails.filter(dataPoint => sourcesPermittedToRead.includes(String(dataPoint.source)));
1308
+ }
1309
+ this.hasPermissionToReadAnyMeasurements = true;
1310
+ this.storeInitialChangeableConfig();
1311
+ this.fileTypeSelectionState = this.getInitialSelection(this.dynamicFilesTypeMetadata, [0]);
1312
+ this.formGroup = this.createForm();
1313
+ this.dataToExport = await this.datapointsExportSelectorService.fetchAndPrepareDataToExport(this.exportConfig, this.isFullTypeOfExport);
1314
+ this.handleDateSelectorChanges();
1315
+ this.determineShowingPreviewOrEmptyState();
1316
+ this.handleExportModeChanges();
1317
+ this.handleFileTypeSelectionChanges();
1318
+ this.handleFormValidationChanges();
1319
+ this.updateFileTypeControl();
1320
+ this.updateDisabledAggregationOptionsState();
1321
+ }
1322
+ ngOnDestroy() {
1323
+ this.destroy$.next();
1324
+ this.destroy$.complete();
1325
+ this.loadInitialChangeableConfig();
1326
+ }
1327
+ async exportAndDownload() {
1328
+ const fileExports = Object.entries(this.dynamicFilesTypeMetadata).map(([_, value]) => ({
1329
+ fileType: value.type,
1330
+ zipName: value.zipName
1331
+ }));
1332
+ const selectedFileExports = this.getOnlySelectedFileExports(fileExports);
1333
+ await Promise.all(selectedFileExports.map(async ({ fileType, zipName }) => {
1334
+ const blob = await this.exportFile(fileType);
1335
+ await this.downloadFile(blob, fileType, zipName);
1336
+ }));
1337
+ this.datapointsExportSelectorFileExporterService.cleanupCachedData();
1338
+ }
1339
+ getOnlySelectedFileExports(fileExports) {
1340
+ return fileExports.filter(f => this.fileTypeSelectionState[f.fileType] === true);
1341
+ }
1342
+ async exportFile(fileType) {
1343
+ return this.isFullTypeOfExport
1344
+ ? await this.exportMeasurementFile(fileType)
1345
+ : await this.exportSeriesFile(fileType);
1346
+ }
1347
+ async downloadFile(blob, fileType, measurementsZipFileName) {
1348
+ this.datapointsExceedingBrowserDownloadLimit =
1349
+ this.datapointsExceedingBrowserDownloadLimit.filter(datapoint => !this.dataPointsExceedingOneMillionLimit.includes(datapoint));
1350
+ const shouldShowAlert = this.datapointsExceedingBrowserDownloadLimit.length > 0;
1351
+ // Handles a case where all files are sent by email.
1352
+ if (!blob && shouldShowAlert) {
1353
+ this.datapointsExportSelectorService.showSendViaEmailInfoAlert(fileType, this.datapointsExceedingBrowserDownloadLimit);
1354
+ return;
1355
+ }
1356
+ if (blob) {
1357
+ // Handles the case where some of the files are sent by email.
1358
+ if (shouldShowAlert) {
1359
+ this.datapointsExportSelectorService.showSendViaEmailInfoAlert(fileType, this.datapointsExceedingBrowserDownloadLimit);
1360
+ }
1361
+ this.isFullTypeOfExport
1362
+ ? saveAs(blob, measurementsZipFileName)
1363
+ : saveAs(blob, blob[SERIES_DATA_MERGED_FILE_NAME]);
1364
+ }
1365
+ }
1366
+ /**
1367
+ * Exports a measurement file of the specified type.
1368
+ *
1369
+ * Measurements API does provide a way to fetch a preprocessed CSV/Excel file.
1370
+ *
1371
+ * At this point a backed file needs to be fetched.
1372
+ * Measurement data used for a preview contains just 5 first records
1373
+ * and it's not fetched with CSV/Excel headers.
1374
+ *
1375
+ * @param fileType - The type of file to export.
1376
+ * @returns A Promise that resolves to a Blob representing the exported file or null when no data to export or it's sent via email.
1377
+ */
1378
+ async exportMeasurementFile(fileType) {
1379
+ const { acceptType, extension } = this.datapointsExportModalService.fileTypesConfigs[fileType];
1380
+ const updatedExportConfig = this.excludeDatapointsThatExceedsApiLimit(this.exportConfig);
1381
+ const dataToExport = await this.datapointsExportSelectorService.fetchMeasurementDataFilesAndPairWithSourceDetails(acceptType, updatedExportConfig);
1382
+ const isAllUndefined = dataToExport.every(data => data === undefined);
1383
+ if (isAllUndefined) {
1384
+ return null;
1385
+ }
1386
+ return this.datapointsExportSelectorFileExporterService.getMeasurementExportedDataBlob(extension, dataToExport);
1387
+ }
1388
+ excludeDatapointsThatExceedsApiLimit(exportConfig) {
1389
+ const skippedDatapointsSource = this.dataPointsExceedingOneMillionLimit.map(datapoint => datapoint.datapointDetail.source.toString());
1390
+ const filteredDatapoints = exportConfig.datapointDetails.filter(datapoint => !skippedDatapointsSource.includes(String(datapoint.source)));
1391
+ exportConfig.datapointDetails = filteredDatapoints;
1392
+ return exportConfig;
1393
+ }
1394
+ /**
1395
+ * Exports the series data to a file of the specified type.
1396
+ *
1397
+ * Series API does not provide a way to fetch a preprocessed CSV/Excel file.
1398
+ *
1399
+ * Series data used for exporting a file is already fetched for a preview
1400
+ * and can be reused for exporting.
1401
+ *
1402
+ * @param fileType - The type of file to export (e.g., CSV, Excel).
1403
+ * @returns A Promise that resolves to a Blob representing the exported file.
1404
+ */
1405
+ async exportSeriesFile(fileType) {
1406
+ const mergedExportDetails = {
1407
+ aggregation: this.exportConfig.aggregation,
1408
+ dateFrom: this.exportConfig.dateFrom,
1409
+ dateTo: this.exportConfig.dateTo
1410
+ };
1411
+ return this.datapointsExportSelectorFileExporterService.getSeriesExportedDataBlob(fileType, this.dataToExport, mergedExportDetails);
1412
+ }
1413
+ async onAggregationChange(aggregation) {
1414
+ this.exportConfig.aggregation = aggregation;
1415
+ if (!this.isAggregationChangedProgrammatically) {
1416
+ await this.updateExportConfigAndFetchData();
1417
+ this.updateDownloadButtonState();
1418
+ }
1419
+ }
1420
+ onDateFromChange(updatedDate) {
1421
+ this.dateFromSubject.next(updatedDate);
1422
+ }
1423
+ onDateToChange(updatedDate) {
1424
+ this.dateToSubject.next(updatedDate);
1425
+ }
1426
+ async updateDateAndFetchData(dateType, updatedDate) {
1427
+ this.exportConfig[dateType] = updatedDate;
1428
+ await this.updateExportConfigAndFetchData();
1429
+ if (this.isFullTypeOfExport) {
1430
+ await this.determineIfMeasurementResponseWillBeProcessedByBackend();
1431
+ }
1432
+ else {
1433
+ this.updateDownloadButtonState();
1434
+ }
1435
+ }
1436
+ async onExportTypeChange(exportType) {
1437
+ if (exportType === EXPORT_MODE_VALUES.full) {
1438
+ this.isAggregationChangedProgrammatically = true;
1439
+ this.formGroup.controls.aggregation.setValue(AGGREGATION_VALUES.none);
1440
+ this.isAggregationChangedProgrammatically = false;
1441
+ this.isFullTypeOfExport = true;
1442
+ await this.updateExportConfigAndFetchData();
1443
+ await this.determineIfMeasurementResponseWillBeProcessedByBackend();
1444
+ return;
1445
+ }
1446
+ this.resetFullExportRelatedProperties();
1447
+ this.isFullTypeOfExport = false;
1448
+ await this.updateExportConfigAndFetchData();
1449
+ this.updateDisabledAggregationOptionsState();
1450
+ this.updateDownloadButtonState();
1451
+ }
1452
+ updateDisabledAggregationOptionsState() {
1453
+ this.disabledAggregationOptions =
1454
+ this.datapointsExportSelectorFileExporterService.updateDisabledStateOfAggregationOptionEntries(this.formGroup.controls.dateFrom.value, this.formGroup.controls.dateTo.value, this.disabledAggregationOptions);
1455
+ }
1456
+ createForm() {
1457
+ return this.formBuilder.group({
1458
+ dateFrom: new FormControl(this.exportConfig.dateFrom),
1459
+ dateTo: new FormControl(this.exportConfig.dateTo),
1460
+ aggregation: new FormControl(this.exportConfig.aggregation ? this.exportConfig.aggregation : AGGREGATION_VALUES.none),
1461
+ exportMode: new FormControl(EXPORT_MODE_VALUES.compact),
1462
+ fileTypes: this.formBuilder.group(this.generators.reduce((a, c) => {
1463
+ a[c.getType()] = false;
1464
+ return a;
1465
+ }, {}))
1466
+ }, {
1467
+ validators: [dateRangeValidator]
1468
+ });
1469
+ }
1470
+ preparePreview() {
1471
+ this.isPreviewLoading = true;
1472
+ this.previewTableData =
1473
+ this.datapointsExportModalService.transformToExportFileStructureForPreview(this.dataToExport);
1474
+ this.isPreviewLoading = false;
1475
+ }
1476
+ handleDateSelectorChanges() {
1477
+ const aggregationControl = this.formGroup.controls.aggregation;
1478
+ const dateFromSelector = this.formGroup.controls.dateFrom;
1479
+ const dateToSelector = this.formGroup.controls.dateTo;
1480
+ const updateAggregationOptions = () => {
1481
+ if (!this.isFullTypeOfExport) {
1482
+ this.updateDisabledAggregationOptionsState();
1483
+ const firstAvailableAggregationValue = this.datapointsExportSelectorFileExporterService.setToFirstAvailableAggregationOptionIfCurrentIsDisabled(aggregationControl.value, this.disabledAggregationOptions);
1484
+ if (firstAvailableAggregationValue) {
1485
+ aggregationControl.setValue(firstAvailableAggregationValue);
1486
+ }
1487
+ }
1488
+ };
1489
+ merge(dateFromSelector.valueChanges, dateToSelector.valueChanges)
1490
+ .pipe(takeUntil(this.destroy$))
1491
+ .subscribe(updateAggregationOptions);
1492
+ }
1493
+ handleExportModeChanges() {
1494
+ const aggregationControl = this.formGroup.controls.aggregation;
1495
+ this.formGroup.controls.exportMode.valueChanges
1496
+ .pipe(takeUntil(this.destroy$))
1497
+ .subscribe(exportModeValue => {
1498
+ if (exportModeValue.toString() === EXPORT_MODE_VALUES.full) {
1499
+ aggregationControl.disable();
1500
+ }
1501
+ else {
1502
+ aggregationControl.enable();
1503
+ }
1504
+ });
1505
+ }
1506
+ handleFileTypeSelectionChanges() {
1507
+ this.formGroup.controls.fileTypes.valueChanges
1508
+ .pipe(takeUntil(this.destroy$))
1509
+ .subscribe(fileTypesSelectionState => {
1510
+ this.updateFileTypeSelectionState(fileTypesSelectionState);
1511
+ });
1512
+ }
1513
+ updateFileTypeSelectionState(checkboxesState) {
1514
+ this.isAnyOfExportFileTypeChecked = Object.values(checkboxesState).some(value => value === true);
1515
+ this.fileTypeSelectionState = checkboxesState;
1516
+ this.updateDownloadButtonState();
1517
+ }
1518
+ handleFormValidationChanges() {
1519
+ this.formGroup.statusChanges.pipe(takeUntil(this.destroy$)).subscribe(() => {
1520
+ this.updateDownloadButtonState();
1521
+ });
1522
+ }
1523
+ updateFileTypeControl() {
1524
+ const fileTypesControl = this.formGroup.controls.fileTypes;
1525
+ if (fileTypesControl) {
1526
+ const currentValue = fileTypesControl.value || {};
1527
+ const updatedValue = { ...currentValue };
1528
+ Object.entries(this.fileTypeSelectionState).forEach(([fileType, isSelected]) => {
1529
+ if (fileType in currentValue) {
1530
+ updatedValue[fileType] = isSelected;
1531
+ }
1532
+ });
1533
+ fileTypesControl.patchValue(updatedValue);
1534
+ }
1535
+ }
1536
+ determineShowingPreviewOrEmptyState() {
1537
+ const hasDataToShow = this.dataToExport.some(dataToExportItem => this.isFullTypeOfExport
1538
+ ? !!dataToExportItem
1539
+ : dataToExportItem.timeValueMap && Object.keys(dataToExportItem.timeValueMap).length > 0);
1540
+ this.hasFetchedDataAnyValuesToExport = hasDataToShow;
1541
+ if (hasDataToShow) {
1542
+ this.preparePreview();
1543
+ }
1544
+ else {
1545
+ this.isPreviewLoading = false;
1546
+ }
1547
+ }
1548
+ async determineIfMeasurementResponseWillBeProcessedByBackend() {
1549
+ this.isPreviewLoading = true;
1550
+ this.datapointsExceedingBrowserDownloadLimit =
1551
+ await this.datapointsExportSelectorService.getDatapointsExceedingLimit(this.exportConfig);
1552
+ const { browserDownloadableCount, emailDeliverableCount, nonRetrievableCount } = this.calculateDatapointCounts();
1553
+ this.hasNoExportableData =
1554
+ browserDownloadableCount === 0 && emailDeliverableCount === 0 && nonRetrievableCount > 0;
1555
+ if (this.datapointsExceedingBrowserDownloadLimit.length > 0 && !this.hasNoExportableData) {
1556
+ this.limitExceededMessage = this.datapointsExportSelectorService.getLimitExceededMessage(this.hasNoExportableData, emailDeliverableCount, browserDownloadableCount, nonRetrievableCount, this.exportConfig.datapointDetails.length);
1557
+ }
1558
+ if (this.datapointsExceedingBrowserDownloadLimit.length > 0 && this.hasNoExportableData) {
1559
+ this.limitExceededMessage = this.datapointsExportSelectorService.getLimitExceededMessage(this.hasNoExportableData);
1560
+ }
1561
+ this.updateDownloadButtonState();
1562
+ this.isPreviewLoading = false;
1563
+ }
1564
+ calculateDatapointCounts() {
1565
+ const RECORD_API_LIMIT = 1_000_000;
1566
+ this.dataPointsExceedingOneMillionLimit = this.datapointsExceedingBrowserDownloadLimit.filter(datapointsExceedingBrowserDownloadLimit => datapointsExceedingBrowserDownloadLimit.totalElements > RECORD_API_LIMIT);
1567
+ const nonRetrievableCount = this.dataPointsExceedingOneMillionLimit.length;
1568
+ const emailDeliverableCount = this.datapointsExceedingBrowserDownloadLimit.length - nonRetrievableCount;
1569
+ const browserDownloadableCount = this.exportConfig.datapointDetails.length - emailDeliverableCount - nonRetrievableCount;
1570
+ return {
1571
+ browserDownloadableCount,
1572
+ emailDeliverableCount,
1573
+ nonRetrievableCount
1574
+ };
1575
+ }
1576
+ storeInitialChangeableConfig() {
1577
+ this.initialDateFrom = this.exportConfig.dateFrom;
1578
+ this.initialDateTo = this.exportConfig.dateTo;
1579
+ this.initialDatapointDetails = this.exportConfig.datapointDetails;
1580
+ if (!this.isFullTypeOfExport) {
1581
+ this.initialAggregation = this.exportConfig.aggregation;
1582
+ }
1583
+ }
1584
+ loadInitialChangeableConfig() {
1585
+ this.exportConfig.dateFrom = this.initialDateFrom;
1586
+ this.exportConfig.dateTo = this.initialDateTo;
1587
+ this.exportConfig.datapointDetails = this.initialDatapointDetails;
1588
+ if (!this.isFullTypeOfExport) {
1589
+ this.exportConfig.aggregation = this.initialAggregation;
1590
+ }
1591
+ }
1592
+ async updateExportConfigAndFetchData() {
1593
+ if (this.formGroup.invalid) {
1594
+ return;
1595
+ }
1596
+ this.isPreviewLoading = true;
1597
+ this.onDownloadButtonStateChange.emit(false);
1598
+ this.dataToExport = await this.datapointsExportSelectorService.fetchAndPrepareDataToExport(this.exportConfig, this.isFullTypeOfExport);
1599
+ this.determineShowingPreviewOrEmptyState();
1600
+ }
1601
+ updateDownloadButtonState() {
1602
+ const canEnableDownloadButton = this.hasFetchedDataAnyValuesToExport &&
1603
+ !this.formGroup.invalid &&
1604
+ this.isAnyOfExportFileTypeChecked &&
1605
+ !this.hasNoExportableData;
1606
+ this.onDownloadButtonStateChange.emit(canEnableDownloadButton);
1607
+ }
1608
+ resetFullExportRelatedProperties() {
1609
+ this.hasNoExportableData = false;
1610
+ this.datapointsExceedingBrowserDownloadLimit = [];
1611
+ }
1612
+ getInitialSelection(fileType, selectByDefault) {
1613
+ const keys = Object.keys(fileType);
1614
+ if (keys.length === 0) {
1615
+ return {};
1616
+ }
1617
+ const initialSelection = keys.reduce((acc, key) => {
1618
+ acc[key] = false;
1619
+ return acc;
1620
+ }, {});
1621
+ selectByDefault.forEach(index => {
1622
+ if (index >= 0 && index < keys.length) {
1623
+ initialSelection[keys[index]] = true;
1624
+ }
1625
+ });
1626
+ return initialSelection;
1627
+ }
1628
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsExportSelectorFileExporterComponent, deps: [{ token: DataProcessingService }, { token: DataFetchingService }, { token: DatapointsExportSelectorFileExporterService }, { token: i3$2.FormBuilder }, { token: FILE_GENERATORS }], target: i0.ɵɵFactoryTarget.Component }); }
1629
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DatapointsExportSelectorFileExporterComponent, isStandalone: true, selector: "c8y-datapoints-export-selector-file-exporter", inputs: { exportConfig: "exportConfig" }, outputs: { onDownloadButtonStateChange: "onDownloadButtonStateChange" }, ngImport: i0, template: "<div class=\"p-b-16\">\n <ng-container *ngIf=\"hasPermissionToReadAnyMeasurements; else hasNoRoleToReadAnyMeasurements\">\n <div class=\"p-16 text-center separator-bottom sticky-top bg-component\">\n <p class=\"text-medium text-16\">\n {{ 'Configure export' | translate }}\n </p>\n </div>\n <div\n class=\"p-t-24 p-r-16 p-l-16 gap-8 p-b-16 flex-wrap\"\n [ngClass]=\"{ 'separator-bottom': isFullTypeOfExport }\"\n >\n <div class=\"row\">\n <div class=\"col-md-4\">\n <c8y-datapoints-export-selector-time-range\n *ngIf=\"this.hasPermissionToReadAnyMeasurements\"\n [formGroup]=\"formGroup\"\n (onDateFromChange)=\"onDateFromChange($event)\"\n (onDateToChange)=\"onDateToChange($event)\"\n ></c8y-datapoints-export-selector-time-range>\n </div>\n <div class=\"col-md-4\">\n <c8y-datapoints-export-selector-data-scope\n *ngIf=\"this.hasPermissionToReadAnyMeasurements\"\n [disabledAggregationOptions]=\"disabledAggregationOptions\"\n [formGroup]=\"formGroup\"\n (onAggregationChange)=\"onAggregationChange($event)\"\n (onExportTypeChange)=\"onExportTypeChange($event)\"\n ></c8y-datapoints-export-selector-data-scope>\n </div>\n <div class=\"col-md-4\">\n <c8y-datapoints-export-selector-file-types\n *ngIf=\"this.hasPermissionToReadAnyMeasurements\"\n [dynamicFilesTypeMetadata]=\"dynamicFilesTypeMetadata\"\n [formGroup]=\"formGroup\"\n ></c8y-datapoints-export-selector-file-types>\n </div>\n </div>\n </div>\n <ng-container\n *ngIf=\"!isFullTypeOfExport && !hasFetchedDataAnyValuesToExport && !isPreviewLoading\"\n >\n <div class=\"p-t-24 p-r-16 p-l-16 p-b-16 separator-bottom d-flex\">\n <div\n class=\"alert alert-warning center-block\"\n role=\"alert\"\n data-cy=\"file-exporter--no-data-available\"\n translate\n >\n No data available.\n </div>\n </div>\n </ng-container>\n <ng-container *ngIf=\"datapointsExceedingBrowserDownloadLimit.length > 0\">\n <div class=\"p-t-24 p-r-16 p-l-16 p-b-16 separator-bottom d-flex\">\n <div\n [class]=\"\n hasNoExportableData\n ? 'alert alert-warning center-block'\n : 'alert alert-info center-block'\n \"\n role=\"alert\"\n [innerHTML]=\"limitExceededMessage\"\n ></div>\n </div>\n </ng-container>\n <ng-container *ngIf=\"isFullTypeOfExport\">\n <c8y-datapoints-export-selector-preview\n *ngIf=\"this.hasPermissionToReadAnyMeasurements\"\n [hasFetchedDataAnyValuesToExport]=\"hasFetchedDataAnyValuesToExport\"\n [isPreviewLoading]=\"isPreviewLoading\"\n [previewTableData]=\"previewTableData\"\n ></c8y-datapoints-export-selector-preview>\n </ng-container>\n </ng-container>\n <ng-template #hasNoRoleToReadAnyMeasurements>\n <div class=\"p-t-24 p-r-16 p-l-16 p-b-16 d-flex\">\n <div\n class=\"alert alert-info center-block\"\n role=\"alert\"\n translate\n >\n <p>To export data, you must meet at least one of these criteria:</p>\n <ul>\n <li>\n Have\n <b>READ permission for \"Measurements\" permission type</b>\n (either as a global role or for the specific source)\n </li>\n <li>\n Be the\n <b>owner of the source</b>\n you want to export data from\n </li>\n </ul>\n <p>Don't meet these requirements? Contact your system administrator for assistance.</p>\n </div>\n </div>\n </ng-template>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: DataPointsExportSelectorDataScopeComponent, selector: "c8y-datapoints-export-selector-data-scope", inputs: ["disabledAggregationOptions", "formGroup"], outputs: ["onAggregationChange", "onExportTypeChange"] }, { kind: "component", type: DataPointsExportSelectorFileTypesComponent, selector: "c8y-datapoints-export-selector-file-types", inputs: ["dynamicFilesTypeMetadata", "formGroup"] }, { kind: "component", type: DataPointsExportSelectorPreviewComponent, selector: "c8y-datapoints-export-selector-preview", inputs: ["hasFetchedDataAnyValuesToExport", "isPreviewLoading", "previewTableData"] }, { kind: "component", type: DataPointsExportSelectorTimeRangeComponent, selector: "c8y-datapoints-export-selector-time-range", inputs: ["formGroup"], outputs: ["onDateFromChange", "onDateToChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }] }); }
1630
+ }
1631
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsExportSelectorFileExporterComponent, decorators: [{
1632
+ type: Component,
1633
+ args: [{ selector: 'c8y-datapoints-export-selector-file-exporter', standalone: true, imports: [
1634
+ CoreModule,
1635
+ DataPointsExportSelectorDataScopeComponent,
1636
+ DataPointsExportSelectorFileTypesComponent,
1637
+ DataPointsExportSelectorPreviewComponent,
1638
+ DataPointsExportSelectorTimeRangeComponent,
1639
+ KeyValuePipe,
1640
+ ReactiveFormsModule
1641
+ ], template: "<div class=\"p-b-16\">\n <ng-container *ngIf=\"hasPermissionToReadAnyMeasurements; else hasNoRoleToReadAnyMeasurements\">\n <div class=\"p-16 text-center separator-bottom sticky-top bg-component\">\n <p class=\"text-medium text-16\">\n {{ 'Configure export' | translate }}\n </p>\n </div>\n <div\n class=\"p-t-24 p-r-16 p-l-16 gap-8 p-b-16 flex-wrap\"\n [ngClass]=\"{ 'separator-bottom': isFullTypeOfExport }\"\n >\n <div class=\"row\">\n <div class=\"col-md-4\">\n <c8y-datapoints-export-selector-time-range\n *ngIf=\"this.hasPermissionToReadAnyMeasurements\"\n [formGroup]=\"formGroup\"\n (onDateFromChange)=\"onDateFromChange($event)\"\n (onDateToChange)=\"onDateToChange($event)\"\n ></c8y-datapoints-export-selector-time-range>\n </div>\n <div class=\"col-md-4\">\n <c8y-datapoints-export-selector-data-scope\n *ngIf=\"this.hasPermissionToReadAnyMeasurements\"\n [disabledAggregationOptions]=\"disabledAggregationOptions\"\n [formGroup]=\"formGroup\"\n (onAggregationChange)=\"onAggregationChange($event)\"\n (onExportTypeChange)=\"onExportTypeChange($event)\"\n ></c8y-datapoints-export-selector-data-scope>\n </div>\n <div class=\"col-md-4\">\n <c8y-datapoints-export-selector-file-types\n *ngIf=\"this.hasPermissionToReadAnyMeasurements\"\n [dynamicFilesTypeMetadata]=\"dynamicFilesTypeMetadata\"\n [formGroup]=\"formGroup\"\n ></c8y-datapoints-export-selector-file-types>\n </div>\n </div>\n </div>\n <ng-container\n *ngIf=\"!isFullTypeOfExport && !hasFetchedDataAnyValuesToExport && !isPreviewLoading\"\n >\n <div class=\"p-t-24 p-r-16 p-l-16 p-b-16 separator-bottom d-flex\">\n <div\n class=\"alert alert-warning center-block\"\n role=\"alert\"\n data-cy=\"file-exporter--no-data-available\"\n translate\n >\n No data available.\n </div>\n </div>\n </ng-container>\n <ng-container *ngIf=\"datapointsExceedingBrowserDownloadLimit.length > 0\">\n <div class=\"p-t-24 p-r-16 p-l-16 p-b-16 separator-bottom d-flex\">\n <div\n [class]=\"\n hasNoExportableData\n ? 'alert alert-warning center-block'\n : 'alert alert-info center-block'\n \"\n role=\"alert\"\n [innerHTML]=\"limitExceededMessage\"\n ></div>\n </div>\n </ng-container>\n <ng-container *ngIf=\"isFullTypeOfExport\">\n <c8y-datapoints-export-selector-preview\n *ngIf=\"this.hasPermissionToReadAnyMeasurements\"\n [hasFetchedDataAnyValuesToExport]=\"hasFetchedDataAnyValuesToExport\"\n [isPreviewLoading]=\"isPreviewLoading\"\n [previewTableData]=\"previewTableData\"\n ></c8y-datapoints-export-selector-preview>\n </ng-container>\n </ng-container>\n <ng-template #hasNoRoleToReadAnyMeasurements>\n <div class=\"p-t-24 p-r-16 p-l-16 p-b-16 d-flex\">\n <div\n class=\"alert alert-info center-block\"\n role=\"alert\"\n translate\n >\n <p>To export data, you must meet at least one of these criteria:</p>\n <ul>\n <li>\n Have\n <b>READ permission for \"Measurements\" permission type</b>\n (either as a global role or for the specific source)\n </li>\n <li>\n Be the\n <b>owner of the source</b>\n you want to export data from\n </li>\n </ul>\n <p>Don't meet these requirements? Contact your system administrator for assistance.</p>\n </div>\n </div>\n </ng-template>\n</div>\n" }]
1642
+ }], ctorParameters: () => [{ type: DataProcessingService }, { type: DataFetchingService }, { type: DatapointsExportSelectorFileExporterService }, { type: i3$2.FormBuilder }, { type: undefined, decorators: [{
1643
+ type: Inject,
1644
+ args: [FILE_GENERATORS]
1645
+ }] }], propDecorators: { exportConfig: [{
1646
+ type: Input
1647
+ }], onDownloadButtonStateChange: [{
1648
+ type: Output
1649
+ }] } });
1650
+
1651
+ class DataProcessingService {
1652
+ constructor(fileGenerators, utils) {
1653
+ this.fileGenerators = fileGenerators;
1654
+ this.utils = utils;
1655
+ this.mergeMapping = {};
1656
+ this.setGenerators(this.fileGenerators);
1657
+ this.fileTypesConfigs = this.fileGenerators.reduce((acc, g) => {
1658
+ acc[g.getType()] = {
1659
+ extension: g.getFileExtension(),
1660
+ mimeType: g.getMimeType(),
1661
+ acceptType: g.getAcceptType()
1662
+ };
1663
+ return acc;
1664
+ }, {});
1665
+ }
1666
+ setGenerators(fileGenerators) {
1667
+ fileGenerators.forEach(g => {
1668
+ this.mergeMapping[g.getType()] = g.generateMerged;
1669
+ });
1670
+ }
1671
+ /**
1672
+ * Transforms the data into a structured format for an export.
1673
+ *
1674
+ * @param dataToExport - An array of processed measurement data combined with the respective properties of the datapoint.
1675
+ * @returns Provides a two-dimensional array of ExportData,
1676
+ * where each inner array contains ExportData entries representing the transformed measurements and metadata of each datapoint.
1677
+ */
1678
+ transformToExportFileStructure(dataToExport) {
1679
+ const exportData = [];
1680
+ for (const data of dataToExport) {
1681
+ const rowsData = this.processDataToExport(data);
1682
+ exportData.push(rowsData);
1683
+ }
1684
+ return exportData;
1685
+ }
1686
+ /**
1687
+ * Transforms the input datapoints with values into a structured format for a preview.
1688
+ *
1689
+ * @param dataToExport - An array of processed measurement data combined with the respective properties of the datapoint.
1690
+ * @returns Provides an array of up to 5 ExportData elements (no more is needed for a preview) or an empty array,
1691
+ */
1692
+ transformToExportFileStructureForPreview(dataToExport) {
1693
+ const data = dataToExport.find(data => {
1694
+ return data !== null && data.timeValueMap && data.unit;
1695
+ });
1696
+ if (!data) {
1697
+ return [];
1698
+ }
1699
+ const exportData = this.processDataToExport(data);
1700
+ return exportData.slice(0, MEASUREMENTS_PREVIEW_ITEMS_LIMIT);
1701
+ }
1702
+ /**
1703
+ * Processes a single dataToExport and transforms it into an array of ExportData.
1704
+ *
1705
+ * Used further for creating series data based export and also for measurements and series data based preview.
1706
+ * Series data provides min and max values for each timestamp.
1707
+ *
1708
+ * @param dataToExport - A processed measurement or series data combined with the respective properties of the datapoint to be precessed
1709
+ * @returns An array of ExportData representing the processed datapoint,
1710
+ * that can be used as a row data in the exported files or for preview.
1711
+ */
1712
+ processDataToExport(dataToExport) {
1713
+ const rowsData = [];
1714
+ // When measurements or series data is not available, add an empty header row with only device_name, source and fragment_series.
1715
+ // This data is needed later to be used to create the file name and merged file header string.
1716
+ const responseDoesNotContainData = !dataToExport.timeValueMap?.size && !dataToExport.unit;
1717
+ if (responseDoesNotContainData) {
1718
+ rowsData.push({
1719
+ time: null,
1720
+ source: dataToExport.source,
1721
+ device_name: dataToExport.deviceName,
1722
+ fragment_series: `${dataToExport.valueFragmentType}.${dataToExport.valueFragmentSeries}`,
1723
+ value: null,
1724
+ value_min: null,
1725
+ value_max: null,
1726
+ unit: null
1727
+ });
1728
+ return rowsData;
1729
+ }
1730
+ for (const [time, value] of Object.entries(dataToExport.timeValueMap)) {
1731
+ // Measurements data contains only one value as a number, series data contains object with min and max values.
1732
+ const isMeasurementData = typeof value === 'number';
1733
+ if (isMeasurementData) {
1734
+ rowsData.push(this.createExportData(dataToExport, time, isMeasurementData, value));
1735
+ }
1736
+ else {
1737
+ rowsData.push(this.createExportData(dataToExport, time, isMeasurementData, value.min, value.max));
1738
+ }
1739
+ }
1740
+ return rowsData;
1741
+ }
1742
+ createExportData(dataToExport, time, isMeasurement, value, valueMax) {
1743
+ const commonExportData = {
1744
+ time: new Date(time).toISOString(),
1745
+ source: dataToExport.source,
1746
+ device_name: dataToExport.deviceName,
1747
+ fragment_series: `${dataToExport.valueFragmentType}.${dataToExport.valueFragmentSeries}`,
1748
+ unit: dataToExport.unit
1749
+ };
1750
+ return isMeasurement
1751
+ ? { ...commonExportData, value }
1752
+ : { ...commonExportData, value_min: value, value_max: valueMax };
1753
+ }
1754
+ /**
1755
+ * Exports the given data to merged CSV or Excel files.
1756
+ *
1757
+ * @param exportData - An array containing ExportData objects that will be used as parts of the merged file.
1758
+ * @param fileType - Indicates the type of the file to be exported.
1759
+ * @param mergedExportDetails - An object containing the date range from which the export was created and aggregation type.
1760
+ * @returns An objects containing file name and its respective content as Blob.
1761
+ *
1762
+ * Example of exported merged file structure:
1763
+ * date from date to
1764
+ * 2024-04-15T12:14:00.000Z 2024-07-16T14:14:28+02:00
1765
+ * time G6Fit -> c8y_Acceleration.accelerationX [G] G6Fit -> c8y_Acceleration.accelerationY [G]
1766
+ * 2024-05-13T13:45:10.500+02:00 0.0109026359624273 0.789461628278069
1767
+ *
1768
+ * Example of exported file name format:
1769
+ * 04dec231240-06dec232350.xlsx
1770
+ */
1771
+ async exportSeriesDataToMergedFiles(exportData, fileType, mergedExportDetails) {
1772
+ const fileTypeConfig = this.fileTypesConfigs[fileType];
1773
+ mergedExportDetails.dateFrom = new Date(mergedExportDetails.dateFrom).toISOString();
1774
+ mergedExportDetails.dateTo = new Date(mergedExportDetails.dateTo).toISOString();
1775
+ const mergedFileContent = this.mergeMapping[fileType](exportData, mergedExportDetails);
1776
+ const mergedFileNameDateRange = this.utils.getFormattedDateRange(mergedExportDetails.dateFrom, mergedExportDetails.dateTo);
1777
+ return {
1778
+ fileName: `${mergedFileNameDateRange}.${fileTypeConfig.extension}`,
1779
+ fileData: new Blob([await mergedFileContent], { type: fileTypeConfig.mimeType })
1780
+ };
1781
+ }
1782
+ /**
1783
+ * Zips all created files.
1784
+ *
1785
+ * @param files - An array of objects containing file names and their respective content as Blobs.
1786
+ * @returns A Promise that resolves to a Blob representing the generated zip file.
1787
+ */
1788
+ async zipFiles(files) {
1789
+ const zip = new JSZip();
1790
+ files.forEach(file => {
1791
+ zip.file(file.fileName, file.fileData);
1792
+ });
1793
+ return await this.generateZipBlob(zip, FILE_COMPRESSION_TYPES_VALUES.deflate, 9);
1794
+ }
1795
+ /**
1796
+ * Exports the given data to CSV or Excel files.
1797
+ *
1798
+ * @param params - An object containing all the necessary parameters for the export and zip process.
1799
+ * @returns A Promise that resolves to a Blob representing the exported file.
1800
+ */
1801
+ async exportSeriesData(params) {
1802
+ const { flattenedAndSortedExportData, fileType, mergedExportDetails } = params;
1803
+ const exportedMergedFile = await this.exportSeriesDataToMergedFiles(flattenedAndSortedExportData, fileType, mergedExportDetails);
1804
+ const blob = new Blob([exportedMergedFile.fileData], { type: fileType });
1805
+ Object.defineProperty(blob, SERIES_DATA_MERGED_FILE_NAME, {
1806
+ value: exportedMergedFile.fileName,
1807
+ writable: false,
1808
+ enumerable: false,
1809
+ configurable: true
1810
+ });
1811
+ return blob;
1812
+ }
1813
+ createFileName(source, fragmentSeries, fileExtension) {
1814
+ return `${source}_${fragmentSeries}.${fileExtension}`;
1815
+ }
1816
+ /**
1817
+ * Generates a zip blob using the provided zip object.
1818
+ *
1819
+ * @param zip - The zip object used for generating the blob.
1820
+ * @param compressionType - The compression type for the zip file. 'STORE' is no compression, 'DEFLATE' is compression.
1821
+ * @param compressionLevel - The compression level for the zip file.
1822
+ * Level 1 is quickest, 9 is best compressed.
1823
+ * @returns A Promise that resolves to a Blob containing the generated zip file.
1824
+ */
1825
+ async generateZipBlob(zip, compressionType = FILE_COMPRESSION_TYPES_VALUES.store, compressionLevel = 1) {
1826
+ const blob = 'blob';
1827
+ if (compressionType === FILE_COMPRESSION_TYPES_VALUES.store) {
1828
+ return await zip.generateAsync({ type: blob });
1829
+ }
1830
+ return await zip.generateAsync({
1831
+ type: blob,
1832
+ compression: compressionType,
1833
+ compressionOptions: {
1834
+ level: compressionLevel
1835
+ }
1836
+ });
1837
+ }
1838
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataProcessingService, deps: [{ token: FILE_GENERATORS }, { token: UtilsService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1839
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataProcessingService, providedIn: 'root' }); }
1840
+ }
1841
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DataProcessingService, decorators: [{
1842
+ type: Injectable,
1843
+ args: [{
1844
+ providedIn: 'root'
1845
+ }]
1846
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1847
+ type: Inject,
1848
+ args: [FILE_GENERATORS]
1849
+ }] }, { type: UtilsService }] });
1850
+
1851
+ class DatapointsExportSelectorModalComponent {
1852
+ constructor(bsModalRef) {
1853
+ this.bsModalRef = bsModalRef;
1854
+ this.isDownloadEnabled = false;
1855
+ this.labels = { ok: gettext('Download'), cancel: gettext('Cancel') };
1856
+ this.result = new Promise(resolve => {
1857
+ this._close = resolve;
1858
+ });
1859
+ }
1860
+ handleKeyboardEvent(event) {
1861
+ if (event.key === 'Escape') {
1862
+ this.dismiss();
1863
+ }
1864
+ }
1865
+ dismiss() {
1866
+ this._close(false);
1867
+ this.bsModalRef.hide();
1868
+ }
1869
+ async exportAndDownload() {
1870
+ await this.datapointsExportSelectorFileExporterComponent.exportAndDownload();
1871
+ this.dismiss();
1872
+ }
1873
+ changeDownloadButtonState(isEnabled) {
1874
+ this.isDownloadEnabled = isEnabled;
1875
+ }
1876
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsExportSelectorModalComponent, deps: [{ token: i1$1.BsModalRef }], target: i0.ɵɵFactoryTarget.Component }); }
1877
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DatapointsExportSelectorModalComponent, isStandalone: true, selector: "c8y-datapoints-export-selector-modal", host: { listeners: { "document:keydown": "handleKeyboardEvent($event)" } }, viewQueries: [{ propertyName: "datapointsExportSelectorFileExporterComponent", first: true, predicate: DatapointsExportSelectorFileExporterComponent, descendants: true }], ngImport: i0, template: "<c8y-modal\n [title]=\"'Generate export' | translate\"\n [labels]=\"labels\"\n [disabled]=\"!isDownloadEnabled\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"dismiss()\"\n (onClose)=\"exportAndDownload()\"\n>\n <ng-container c8y-modal-title>\n <span [c8yIcon]=\"'data-export'\"></span>\n </ng-container>\n <c8y-datapoints-export-selector-file-exporter\n [exportConfig]=\"exportConfig\"\n (onDownloadButtonStateChange)=\"changeDownloadButtonState($event)\"\n ></c8y-datapoints-export-selector-file-exporter>\n</c8y-modal>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "component", type: DatapointsExportSelectorFileExporterComponent, selector: "c8y-datapoints-export-selector-file-exporter", inputs: ["exportConfig"], outputs: ["onDownloadButtonStateChange"] }, { kind: "ngmodule", type: ModalModule }, { kind: "component", type: i1.ModalComponent, selector: "c8y-modal", inputs: ["disabled", "close", "dismiss", "title", "body", "customFooter", "headerClasses", "labels"], outputs: ["onDismiss", "onClose"] }] }); }
1878
+ }
1879
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsExportSelectorModalComponent, decorators: [{
1880
+ type: Component,
1881
+ args: [{ selector: 'c8y-datapoints-export-selector-modal', standalone: true, imports: [CommonModule, DatapointsExportSelectorFileExporterComponent, ModalModule], template: "<c8y-modal\n [title]=\"'Generate export' | translate\"\n [labels]=\"labels\"\n [disabled]=\"!isDownloadEnabled\"\n [headerClasses]=\"'dialog-header'\"\n (onDismiss)=\"dismiss()\"\n (onClose)=\"exportAndDownload()\"\n>\n <ng-container c8y-modal-title>\n <span [c8yIcon]=\"'data-export'\"></span>\n </ng-container>\n <c8y-datapoints-export-selector-file-exporter\n [exportConfig]=\"exportConfig\"\n (onDownloadButtonStateChange)=\"changeDownloadButtonState($event)\"\n ></c8y-datapoints-export-selector-file-exporter>\n</c8y-modal>\n" }]
1882
+ }], ctorParameters: () => [{ type: i1$1.BsModalRef }], propDecorators: { datapointsExportSelectorFileExporterComponent: [{
1883
+ type: ViewChild,
1884
+ args: [DatapointsExportSelectorFileExporterComponent, { static: false }]
1885
+ }], handleKeyboardEvent: [{
1886
+ type: HostListener,
1887
+ args: ['document:keydown', ['$event']]
1888
+ }] } });
1889
+
1890
+ class DatapointsExportSelectorComponent {
1891
+ constructor(bsModalService) {
1892
+ this.bsModalService = bsModalService;
1893
+ this.isOpen = new EventEmitter();
1894
+ }
1895
+ async openExportModal() {
1896
+ const exportConfig = this.exportConfig;
1897
+ const initialState = {
1898
+ exportConfig
1899
+ };
1900
+ this.isOpen.emit(true);
1901
+ const modalRef = this.bsModalService.show(DatapointsExportSelectorModalComponent, {
1902
+ class: 'modal-lg',
1903
+ ariaDescribedby: 'modal-body',
1904
+ ariaLabelledBy: 'modal-title',
1905
+ initialState,
1906
+ ignoreBackdropClick: true
1907
+ }).content;
1908
+ // Result will be 'false' on modal close or dismiss
1909
+ this.isOpen.emit(await modalRef.result);
1910
+ }
1911
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsExportSelectorComponent, deps: [{ token: i1$1.BsModalService }], target: i0.ɵɵFactoryTarget.Component }); }
1912
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: DatapointsExportSelectorComponent, isStandalone: true, selector: "c8y-datapoints-export-selector", inputs: { exportConfig: "exportConfig" }, outputs: { isOpen: "isOpen" }, ngImport: i0, template: "<div class=\"input-group p-t-4 p-b-4 max-width-fit m-l-auto\">\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n [attr.aria-label]=\"'Generate export' | translate\"\n tooltip=\"{{ 'Generate export' | translate }}\"\n container=\"body\"\n type=\"button\"\n (click)=\"openExportModal()\"\n [adaptivePosition]=\"false\"\n [disabled]=\"!exportConfig\"\n [delay]=\"500\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"data-export\"\n ></i>\n </button>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: i1.C8yTranslatePipe, name: "translate" }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3$3.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }] }); }
1913
+ }
1914
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: DatapointsExportSelectorComponent, decorators: [{
1915
+ type: Component,
1916
+ args: [{ selector: 'c8y-datapoints-export-selector', standalone: true, imports: [CommonModule, TooltipModule], template: "<div class=\"input-group p-t-4 p-b-4 max-width-fit m-l-auto\">\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-default\"\n [attr.aria-label]=\"'Generate export' | translate\"\n tooltip=\"{{ 'Generate export' | translate }}\"\n container=\"body\"\n type=\"button\"\n (click)=\"openExportModal()\"\n [adaptivePosition]=\"false\"\n [disabled]=\"!exportConfig\"\n [delay]=\"500\"\n >\n <i\n class=\"icon-14\"\n c8yIcon=\"data-export\"\n ></i>\n </button>\n </div>\n</div>\n" }]
1917
+ }], ctorParameters: () => [{ type: i1$1.BsModalService }], propDecorators: { exportConfig: [{
1918
+ type: Input
1919
+ }], isOpen: [{
1920
+ type: Output
1921
+ }] } });
1922
+
1923
+ /**
1924
+ * Generated bundle index. Do not edit.
1925
+ */
1926
+
1927
+ export { CSVGeneratorAdapter, DataFetchingService, DataPointsExportSelectorDataScopeComponent, DataPointsExportSelectorFileTypesComponent, DataPointsExportSelectorPreviewComponent, DataPointsExportSelectorTimeRangeComponent, DataProcessingService, DatapointsExportSelectorComponent, DatapointsExportSelectorFileExporterComponent, DatapointsExportSelectorFileExporterService, DatapointsExportSelectorModalComponent, EXPORT_MODE_LABELS, EXPORT_MODE_VALUES, ExcelDataTransformer, ExcelGeneratorAdapter, FILE_COMPRESSION_TYPES_VALUES, FILE_GENERATORS, HAS_ERROR, MEASUREMENTS_PREVIEW_ITEMS_LIMIT, SERIES_DATA_MERGED_FILE_NAME, TIME_RANGE_INTERVAL_UNITS_VALUES, UtilsService, dateRangeValidator };
1928
+ //# sourceMappingURL=c8y-ngx-components-datapoints-export-selector.mjs.map