ethyca-fides 2.63.3rc0__py2.py3-none-any.whl → 2.64.1b0__py2.py3-none-any.whl

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 (362) hide show
  1. {ethyca_fides-2.63.3rc0.dist-info → ethyca_fides-2.64.1b0.dist-info}/METADATA +2 -2
  2. {ethyca_fides-2.63.3rc0.dist-info → ethyca_fides-2.64.1b0.dist-info}/RECORD +249 -242
  3. fides/_version.py +3 -3
  4. fides/api/alembic/migrations/versions/ba414a58ba90_create_manual_task_config_tables.py +121 -0
  5. fides/api/api/v1/endpoints/connection_endpoints.py +16 -3
  6. fides/api/api/v1/endpoints/oauth_endpoints.py +8 -2
  7. fides/api/db/base.py +4 -0
  8. fides/api/models/attachment.py +37 -23
  9. fides/api/models/manual_tasks/__init__.py +6 -0
  10. fides/api/models/manual_tasks/manual_task.py +11 -1
  11. fides/api/models/manual_tasks/manual_task_config.py +136 -0
  12. fides/api/models/manual_tasks/manual_task_log.py +13 -9
  13. fides/api/schemas/connection_configuration/connection_config.py +19 -4
  14. fides/api/schemas/manual_tasks/manual_task_config.py +311 -0
  15. fides/api/schemas/manual_tasks/manual_task_schemas.py +1 -1
  16. fides/api/schemas/manual_tasks/manual_task_status.py +1 -1
  17. fides/api/schemas/privacy_request.py +2 -0
  18. fides/api/service/connectors/query_configs/saas_query_config.py +15 -0
  19. fides/api/service/privacy_request/attachment_handling.py +132 -0
  20. fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +264 -46
  21. fides/api/service/privacy_request/dsr_package/templates/attachments_index.html +33 -0
  22. fides/api/service/privacy_request/dsr_package/templates/collection_index.html +34 -9
  23. fides/api/service/privacy_request/dsr_package/templates/main.css +45 -2
  24. fides/api/service/privacy_request/dsr_package/templates/welcome.html +12 -8
  25. fides/api/service/privacy_request/request_runner_service.py +258 -139
  26. fides/api/service/storage/gcs.py +15 -3
  27. fides/api/service/storage/s3.py +28 -14
  28. fides/api/service/storage/util.py +45 -7
  29. fides/api/tasks/csv_utils.py +170 -0
  30. fides/api/tasks/encryption_utils.py +42 -0
  31. fides/api/tasks/storage.py +85 -91
  32. fides/api/util/cache.py +6 -3
  33. fides/api/util/saas_util.py +1 -0
  34. fides/config/redis_settings.py +105 -44
  35. fides/service/manual_tasks/manual_task_config_service.py +370 -0
  36. fides/service/manual_tasks/manual_task_service.py +231 -87
  37. fides/service/manual_tasks/utils.py +185 -0
  38. fides/ui-build/static/admin/404.html +1 -1
  39. fides/ui-build/static/admin/_next/static/chunks/1040-630c7f4284dc6f70.js +1 -0
  40. fides/ui-build/static/admin/_next/static/chunks/1100-d43cb04522a6505c.js +1 -0
  41. fides/ui-build/static/admin/_next/static/chunks/1316-6cc72a45ebf7ff81.js +1 -0
  42. fides/ui-build/static/admin/_next/static/chunks/1817-b4688ba5042ec687.js +1 -0
  43. fides/ui-build/static/admin/_next/static/chunks/2430-b480401d44c55416.js +1 -0
  44. fides/ui-build/static/admin/_next/static/chunks/2599-6c4d22e75028d8b6.js +1 -0
  45. fides/ui-build/static/admin/_next/static/chunks/2921-aabf41bf3d7573f9.js +1 -0
  46. fides/ui-build/static/admin/_next/static/chunks/3450-bdaeb35442d810b4.js +1 -0
  47. fides/ui-build/static/admin/_next/static/chunks/3505-a79256cd851dfab4.js +1 -0
  48. fides/ui-build/static/admin/_next/static/chunks/3513-8677ee280eaef0da.js +1 -0
  49. fides/ui-build/static/admin/_next/static/chunks/3855-0dec3e7d9e886550.js +1 -0
  50. fides/ui-build/static/admin/_next/static/chunks/3872-ffa16c2df7ef0ab6.js +1 -0
  51. fides/ui-build/static/admin/_next/static/chunks/3923-5d580fbb1dd6ae01.js +1 -0
  52. fides/ui-build/static/admin/_next/static/chunks/401-fceaae662cfca5a5.js +1 -0
  53. fides/ui-build/static/admin/_next/static/chunks/4060-71cd041e5a57ca5a.js +1 -0
  54. fides/ui-build/static/admin/_next/static/chunks/409-037cfc3f096150f0.js +1 -0
  55. fides/ui-build/static/admin/_next/static/chunks/4121-78a76e980acbd539.js +1 -0
  56. fides/ui-build/static/admin/_next/static/chunks/4230-1cdb7ea1be370ed3.js +1 -0
  57. fides/ui-build/static/admin/_next/static/chunks/431-f72599f01b98f07d.js +1 -0
  58. fides/ui-build/static/admin/_next/static/chunks/4481-f597a7cf03f8c9e1.js +1 -0
  59. fides/ui-build/static/admin/_next/static/chunks/5309-4511df9708d5a63c.js +1 -0
  60. fides/ui-build/static/admin/_next/static/chunks/5574-3cd33b3a6c937899.js +1 -0
  61. fides/ui-build/static/admin/_next/static/chunks/6277-3fb4c7fc790a6d20.js +1 -0
  62. fides/ui-build/static/admin/_next/static/chunks/6662-cb11881dcaabe5e2.js +1 -0
  63. fides/ui-build/static/admin/_next/static/chunks/6853-a4097260e402980e.js +1 -0
  64. fides/ui-build/static/admin/_next/static/chunks/6882-3cc73d407a088d7d.js +1 -0
  65. fides/ui-build/static/admin/_next/static/chunks/69-6889d6674c95e7b5.js +1 -0
  66. fides/ui-build/static/admin/_next/static/chunks/6954-13a25cef3a8fdd76.js +1 -0
  67. fides/ui-build/static/admin/_next/static/chunks/699-8ca44b0de9fa20f0.js +1 -0
  68. fides/ui-build/static/admin/_next/static/chunks/7553-a95939c32d54b5b7.js +1 -0
  69. fides/ui-build/static/admin/_next/static/chunks/79-5670e31eb65d0a53.js +1 -0
  70. fides/ui-build/static/admin/_next/static/chunks/796-9a6b13c838e25538.js +1 -0
  71. fides/ui-build/static/admin/_next/static/chunks/8433-adb1fcb29d82f1b7.js +1 -0
  72. fides/ui-build/static/admin/_next/static/chunks/9046-fdf53cc7e926a8c1.js +1 -0
  73. fides/ui-build/static/admin/_next/static/chunks/{905-8ab919e7b274ed50.js → 905-742074a074be1055.js} +2 -2
  74. fides/ui-build/static/admin/_next/static/chunks/9226-ba6587e46ec7659a.js +1 -0
  75. fides/ui-build/static/admin/_next/static/chunks/{9392.81edc11e3a175275.js → 9392.25024e070026343d.js} +1 -1
  76. fides/ui-build/static/admin/_next/static/chunks/9676.e60a53f1f5890847.js +1 -0
  77. fides/ui-build/static/admin/_next/static/chunks/9767-e49b065d03ce4e80.js +1 -0
  78. fides/ui-build/static/admin/_next/static/chunks/9951-f9ab5cac7e2c05ab.js +1 -0
  79. fides/ui-build/static/admin/_next/static/chunks/pages/404-ac2f0844e5c4b4cd.js +1 -0
  80. fides/ui-build/static/admin/_next/static/chunks/pages/{_app-f70bffa5a7903c46.js → _app-8ec6e466f3373ac0.js} +10 -10
  81. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/manual-0a5f2310ce6b1059.js +1 -0
  82. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/multiple-00cb904825aad7e3.js +1 -0
  83. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-587e21d14e0a5196.js +1 -0
  84. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/add-vendors-fa7305b88c1afd20.js +1 -0
  85. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-f140ec9d8e8a0f7a.js +1 -0
  86. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-0edb7c92518e7d21.js +1 -0
  87. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-d8d926f0735a2546.js +1 -0
  88. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/[id]-5c949f2e3cef2398.js +1 -0
  89. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/new-130155cfb4a0bcc7.js +1 -0
  90. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-1135ad8924d32c36.js +1 -0
  91. fides/ui-build/static/admin/_next/static/chunks/pages/consent/properties-776855e370414beb.js +1 -0
  92. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-21c23f75ff1135d9.js +1 -0
  93. fides/ui-build/static/admin/_next/static/chunks/pages/consent-4d5ea70a77df1bb8.js +1 -0
  94. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-18e3faf7963962e4.js → [resourceUrn]-c623d6f1a61c8ea9.js} +1 -1
  95. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]-030ee304cbe5e530.js +1 -0
  96. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-cfac259a30641e68.js +1 -0
  97. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-7320524a47104798.js → [resourceUrn]-57bd5cdf784f059f.js} +1 -1
  98. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-7b5aec33da578745.js +1 -0
  99. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-c948f93c833d4358.js +1 -0
  100. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-333f53caab751428.js +1 -0
  101. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-27205f8457a1ecb0.js +1 -0
  102. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-20a1043f6a06198f.js +1 -0
  103. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-4c526db0c30c488a.js → [resourceUrn]-06edce289876dea1.js} +1 -1
  104. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-6f27dbb7c8edc69d.js → detection-faf326a6200637d0.js} +1 -1
  105. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-562d2b8ae90dd1f0.js → [resourceUrn]-64acf269256ee74f.js} +1 -1
  106. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-fe7f51502eda57c9.js → discovery-8c3e4be6d36da66d.js} +1 -1
  107. fides/ui-build/static/admin/_next/static/chunks/pages/datamap-fb50de22f83edd4a.js +1 -0
  108. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/[...subfieldNames]-41ab27c4195cfa93.js +1 -0
  109. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]-6e2caba24b3e78c2.js +1 -0
  110. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-904d43e31157aa55.js +1 -0
  111. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-d4e31a1c4a58800e.js +1 -0
  112. fides/ui-build/static/admin/_next/static/chunks/pages/dataset-8f7d16bc5e9229c8.js +1 -0
  113. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/[id]-4a33dd0371dbaebc.js +1 -0
  114. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-e88509346b2d2851.js +1 -0
  115. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-8f9b8890018e1ea5.js +1 -0
  116. fides/ui-build/static/admin/_next/static/chunks/pages/{fides-js-docs-80b241bf6cddb72e.js → fides-js-docs-5d8fd1af75f19e2f.js} +1 -1
  117. fides/ui-build/static/admin/_next/static/chunks/pages/index-1adb6bcb71701ac2.js +1 -0
  118. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-fdc2964fa7d1d8fd.js +1 -0
  119. fides/ui-build/static/admin/_next/static/chunks/pages/integrations-adc286ff254e7f41.js +1 -0
  120. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/[id]-9b4d1d61c7c97509.js +1 -0
  121. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/add-template-f8fd4795e260887c.js +1 -0
  122. fides/ui-build/static/admin/_next/static/chunks/pages/messaging-aa744ae8b61e5ff2.js +1 -0
  123. fides/ui-build/static/admin/_next/static/chunks/pages/poc/ant-components-3407158757fb3627.js +1 -0
  124. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/AntForm-47e947c02ae90fd0.js +1 -0
  125. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikAntFormItem-24f9a44512ce8827.js +1 -0
  126. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikControlled-e567a69f8c37fb9a.js +1 -0
  127. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikField-35a33f9c7def2220.js +1 -0
  128. fides/ui-build/static/admin/_next/static/chunks/pages/poc/forms-780e18dde8e38099.js +1 -0
  129. fides/ui-build/static/admin/_next/static/chunks/pages/poc/table-migration-7a17dffa515e5560.js +1 -0
  130. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-91dd0039cc2b70bc.js +1 -0
  131. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-b01cbbaac34dadbc.js +1 -0
  132. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-a15410de7f1d0f84.js +1 -0
  133. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure-1a1aa83a3f88844c.js +1 -0
  134. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-c4e1b69fb5bab61a.js +1 -0
  135. fides/ui-build/static/admin/_next/static/chunks/pages/properties/[id]-19737d4f21aadbee.js +1 -0
  136. fides/ui-build/static/admin/_next/static/chunks/pages/properties/add-property-9ccb295110feee20.js +1 -0
  137. fides/ui-build/static/admin/_next/static/chunks/pages/properties-3a1037a2e036212a.js +1 -0
  138. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-4cdbbb8cd1d8698e.js +1 -0
  139. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/alpha-965cc21889b25e44.js +1 -0
  140. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about-0e1c381d488a7ada.js +1 -0
  141. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/[purpose_id]-d0e350d2a2667883.js +1 -0
  142. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-7796fbf458f8f159.js +1 -0
  143. fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-6f866bc799a5f67f.js +1 -0
  144. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domain-records-0b44b2b224077dcd.js +1 -0
  145. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-5d00e1155bd11178.js +1 -0
  146. fides/ui-build/static/admin/_next/static/chunks/pages/settings/email-templates-d2cdbe770683e9df.js +1 -0
  147. fides/ui-build/static/admin/_next/static/chunks/pages/settings/locations-811dadb489f23d7d.js +1 -0
  148. fides/ui-build/static/admin/_next/static/chunks/pages/settings/organization-24108f615ff5e1e7.js +1 -0
  149. fides/ui-build/static/admin/_next/static/chunks/pages/settings/regulations-54f142bc3e4c95ba.js +1 -0
  150. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/test-datasets-fef247a87baeb080.js +1 -0
  151. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]-6eb886e7b7e6785b.js +1 -0
  152. fides/ui-build/static/admin/_next/static/chunks/pages/systems-7b71274334c559a4.js +1 -0
  153. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-18534d2a79a3850b.js +1 -0
  154. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-75d41fde668b9025.js +1 -0
  155. fides/ui-build/static/admin/_next/static/chunks/pages/user-management-dd43755b687c09a7.js +1 -0
  156. fides/ui-build/static/admin/_next/static/chunks/webpack-0a61b5bd21a41fe6.js +1 -0
  157. fides/ui-build/static/admin/_next/static/css/c693338e3bc8dcc6.css +1 -0
  158. fides/ui-build/static/admin/_next/static/nRQ3pmK_d3F5PJE39rP2h/_buildManifest.js +1 -0
  159. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  160. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  161. fides/ui-build/static/admin/add-systems.html +1 -1
  162. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  163. fides/ui-build/static/admin/consent/configure.html +1 -1
  164. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  165. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  166. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  167. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  168. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  169. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  170. fides/ui-build/static/admin/consent/properties.html +1 -1
  171. fides/ui-build/static/admin/consent/reporting.html +1 -1
  172. fides/ui-build/static/admin/consent.html +1 -1
  173. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  174. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  175. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  176. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  177. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  178. fides/ui-build/static/admin/data-catalog.html +1 -1
  179. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  180. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  181. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  182. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  183. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  184. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  185. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  186. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  187. fides/ui-build/static/admin/datamap.html +1 -1
  188. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  189. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  190. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  191. fides/ui-build/static/admin/dataset/new.html +1 -1
  192. fides/ui-build/static/admin/dataset.html +1 -1
  193. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  194. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  195. fides/ui-build/static/admin/datastore-connection.html +1 -1
  196. fides/ui-build/static/admin/images/connector-logos/salesforce.svg +41 -0
  197. fides/ui-build/static/admin/index.html +1 -1
  198. fides/ui-build/static/admin/integrations/[id].html +1 -1
  199. fides/ui-build/static/admin/integrations.html +1 -1
  200. fides/ui-build/static/admin/lib/fides-ext-gpp.js +1 -1
  201. fides/ui-build/static/admin/lib/fides-headless.js +1 -1
  202. fides/ui-build/static/admin/lib/fides-preview.js +1 -1
  203. fides/ui-build/static/admin/lib/fides-tcf.js +2 -2
  204. fides/ui-build/static/admin/lib/fides.js +2 -2
  205. fides/ui-build/static/admin/login/[provider].html +1 -1
  206. fides/ui-build/static/admin/login.html +1 -1
  207. fides/ui-build/static/admin/messaging/[id].html +1 -1
  208. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  209. fides/ui-build/static/admin/messaging.html +1 -1
  210. fides/ui-build/static/admin/poc/ant-components.html +1 -1
  211. fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
  212. fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
  213. fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
  214. fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
  215. fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
  216. fides/ui-build/static/admin/poc/forms.html +1 -1
  217. fides/ui-build/static/admin/poc/table-migration.html +1 -1
  218. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  219. fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
  220. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  221. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  222. fides/ui-build/static/admin/privacy-requests.html +1 -1
  223. fides/ui-build/static/admin/properties/[id].html +1 -1
  224. fides/ui-build/static/admin/properties/add-property.html +1 -1
  225. fides/ui-build/static/admin/properties.html +1 -1
  226. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  227. fides/ui-build/static/admin/settings/about/alpha.html +1 -1
  228. fides/ui-build/static/admin/settings/about.html +1 -1
  229. fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
  230. fides/ui-build/static/admin/settings/consent.html +1 -1
  231. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  232. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  233. fides/ui-build/static/admin/settings/domains.html +1 -1
  234. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  235. fides/ui-build/static/admin/settings/locations.html +1 -1
  236. fides/ui-build/static/admin/settings/organization.html +1 -1
  237. fides/ui-build/static/admin/settings/regulations.html +1 -1
  238. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  239. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  240. fides/ui-build/static/admin/systems.html +1 -1
  241. fides/ui-build/static/admin/taxonomy.html +1 -1
  242. fides/ui-build/static/admin/user-management/new.html +1 -1
  243. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  244. fides/ui-build/static/admin/user-management.html +1 -1
  245. fides/api/service/privacy_request/dsr_package/templates/item.html +0 -37
  246. fides/ui-build/static/admin/_next/static/chunks/1099-667e84655846e78c.js +0 -1
  247. fides/ui-build/static/admin/_next/static/chunks/1100-0b01f500b1eaf60d.js +0 -1
  248. fides/ui-build/static/admin/_next/static/chunks/1817-951f58ddd5d46081.js +0 -1
  249. fides/ui-build/static/admin/_next/static/chunks/1904-964f8eb328dc2715.js +0 -1
  250. fides/ui-build/static/admin/_next/static/chunks/2310-744354001d01f366.js +0 -1
  251. fides/ui-build/static/admin/_next/static/chunks/2921-49af55ad7c631c93.js +0 -1
  252. fides/ui-build/static/admin/_next/static/chunks/3119-e36ae5071c8f27a5.js +0 -1
  253. fides/ui-build/static/admin/_next/static/chunks/3505-113f9c95d34b7aae.js +0 -1
  254. fides/ui-build/static/admin/_next/static/chunks/3513-5c2d412a84a78971.js +0 -1
  255. fides/ui-build/static/admin/_next/static/chunks/3872-45cc725e241211f0.js +0 -1
  256. fides/ui-build/static/admin/_next/static/chunks/3923-b4f701ada3ef0ee0.js +0 -1
  257. fides/ui-build/static/admin/_next/static/chunks/401-7e800aed05537126.js +0 -1
  258. fides/ui-build/static/admin/_next/static/chunks/4060-90a52a9afc655bfc.js +0 -1
  259. fides/ui-build/static/admin/_next/static/chunks/4121-66b0e00d5e7ae817.js +0 -1
  260. fides/ui-build/static/admin/_next/static/chunks/4132-9b1731bfec6ee537.js +0 -1
  261. fides/ui-build/static/admin/_next/static/chunks/4481-7f6710c928bb0cb0.js +0 -1
  262. fides/ui-build/static/admin/_next/static/chunks/5258-2079138c8cc34f6c.js +0 -1
  263. fides/ui-build/static/admin/_next/static/chunks/5404-2694509cf5fa96da.js +0 -1
  264. fides/ui-build/static/admin/_next/static/chunks/5487-2cbd8d2169eb2a65.js +0 -1
  265. fides/ui-build/static/admin/_next/static/chunks/5683-37137111b3e769fb.js +0 -1
  266. fides/ui-build/static/admin/_next/static/chunks/6202-081545c7822d09af.js +0 -1
  267. fides/ui-build/static/admin/_next/static/chunks/6277-459e054b2702c60e.js +0 -1
  268. fides/ui-build/static/admin/_next/static/chunks/641-7e4eef3222cbda70.js +0 -1
  269. fides/ui-build/static/admin/_next/static/chunks/6853-688df0b88fe65fd5.js +0 -1
  270. fides/ui-build/static/admin/_next/static/chunks/69-00cba94689b9e740.js +0 -1
  271. fides/ui-build/static/admin/_next/static/chunks/6954-4e313b4599b8763d.js +0 -1
  272. fides/ui-build/static/admin/_next/static/chunks/7553-08bc3d9f66695111.js +0 -1
  273. fides/ui-build/static/admin/_next/static/chunks/79-fc548561beed136f.js +0 -1
  274. fides/ui-build/static/admin/_next/static/chunks/796-d6586308ed44334b.js +0 -1
  275. fides/ui-build/static/admin/_next/static/chunks/7980-736f9fd1e749ddd2.js +0 -1
  276. fides/ui-build/static/admin/_next/static/chunks/8032-74d94f7bd5b81c89.js +0 -1
  277. fides/ui-build/static/admin/_next/static/chunks/8433-51870336908fb253.js +0 -1
  278. fides/ui-build/static/admin/_next/static/chunks/8499-1fd392d0be9e8ee4.js +0 -1
  279. fides/ui-build/static/admin/_next/static/chunks/8702-d1c8296f9f6afc10.js +0 -1
  280. fides/ui-build/static/admin/_next/static/chunks/9033-fdd87182c15b91e2.js +0 -1
  281. fides/ui-build/static/admin/_next/static/chunks/9187-851756440f79cd75.js +0 -1
  282. fides/ui-build/static/admin/_next/static/chunks/9327-6a53461c9764b16f.js +0 -1
  283. fides/ui-build/static/admin/_next/static/chunks/9494-1ffb888b95289891.js +0 -1
  284. fides/ui-build/static/admin/_next/static/chunks/9676.61cf5f6d6a083175.js +0 -1
  285. fides/ui-build/static/admin/_next/static/chunks/9767-afd674014a1d1152.js +0 -1
  286. fides/ui-build/static/admin/_next/static/chunks/pages/404-ec04f826f3dd79ea.js +0 -1
  287. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/manual-1bec086d567aacff.js +0 -1
  288. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/multiple-f68bf100e4e60e88.js +0 -1
  289. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-effde5cfe49de55f.js +0 -1
  290. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/add-vendors-ec95ed8cb0712828.js +0 -1
  291. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-4cc195e15e0c3cc8.js +0 -1
  292. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-be8972c79fb39705.js +0 -1
  293. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-ccf96207de3b74ea.js +0 -1
  294. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/[id]-b9823cc008372cee.js +0 -1
  295. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/new-373f5772ab41bc93.js +0 -1
  296. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-b9beeee1bde5ca79.js +0 -1
  297. fides/ui-build/static/admin/_next/static/chunks/pages/consent/properties-6ab3a5baafebd199.js +0 -1
  298. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-8562790ae07f17c9.js +0 -1
  299. fides/ui-build/static/admin/_next/static/chunks/pages/consent-42d4d2d6e0253671.js +0 -1
  300. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]-9d1df6d890671b0c.js +0 -1
  301. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-7eb08af333baaa8e.js +0 -1
  302. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-bb57eed84f59a932.js +0 -1
  303. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-47eb34aef3e31ac6.js +0 -1
  304. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-76cefefc53e84c7f.js +0 -1
  305. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-9d8e84a8b4bf568d.js +0 -1
  306. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-f208fef5e72dafd6.js +0 -1
  307. fides/ui-build/static/admin/_next/static/chunks/pages/datamap-db45aa864e9dda8a.js +0 -1
  308. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/[...subfieldNames]-d5cfdebc74654337.js +0 -1
  309. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]-618b89228f83435e.js +0 -1
  310. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-12fc86f15a4c764e.js +0 -1
  311. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-c23d18a9b56c2e3c.js +0 -1
  312. fides/ui-build/static/admin/_next/static/chunks/pages/dataset-e3b2d8e3980ab093.js +0 -1
  313. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/[id]-6e27257eccf65f01.js +0 -1
  314. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-bbac1f624424282d.js +0 -1
  315. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-84eb56f5a07bdf4f.js +0 -1
  316. fides/ui-build/static/admin/_next/static/chunks/pages/index-773182b5e35d0045.js +0 -1
  317. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-eb28c927da5bd653.js +0 -1
  318. fides/ui-build/static/admin/_next/static/chunks/pages/integrations-781808bca01f8048.js +0 -1
  319. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/[id]-b51bee8680d66b20.js +0 -1
  320. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/add-template-1de479533f733fbd.js +0 -1
  321. fides/ui-build/static/admin/_next/static/chunks/pages/messaging-7dc295b3ed3d2224.js +0 -1
  322. fides/ui-build/static/admin/_next/static/chunks/pages/poc/ant-components-f64dac3392f5ded7.js +0 -1
  323. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/AntForm-eb020bfac4bee532.js +0 -1
  324. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikAntFormItem-0bd62e28b539e114.js +0 -1
  325. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikControlled-936877004113c2a6.js +0 -1
  326. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/FormikField-6416f01524482af9.js +0 -1
  327. fides/ui-build/static/admin/_next/static/chunks/pages/poc/forms-3f4e1313d1f2969b.js +0 -1
  328. fides/ui-build/static/admin/_next/static/chunks/pages/poc/table-migration-1db7a54437db7db0.js +0 -1
  329. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-64236fd0141414fd.js +0 -1
  330. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-8cab04871908cfeb.js +0 -1
  331. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-150d40428245ee0c.js +0 -1
  332. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure-b6c876dceb16ad1b.js +0 -1
  333. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-20cdb2c8a03deae1.js +0 -1
  334. fides/ui-build/static/admin/_next/static/chunks/pages/properties/[id]-54bcc875592d1fb9.js +0 -1
  335. fides/ui-build/static/admin/_next/static/chunks/pages/properties/add-property-644dc669b508a79a.js +0 -1
  336. fides/ui-build/static/admin/_next/static/chunks/pages/properties-9a88220d03e7e02f.js +0 -1
  337. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-70dc1081df37ea69.js +0 -1
  338. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/alpha-3c71b4dbcb6fd6c9.js +0 -1
  339. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about-7cb16e0000dd16c9.js +0 -1
  340. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/[purpose_id]-a18d8f12ee8dcf5d.js +0 -1
  341. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-dc220e116ad5c09e.js +0 -1
  342. fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-55ddc5f25b86f28e.js +0 -1
  343. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domain-records-5ca7decded228bc8.js +0 -1
  344. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-a6fafa1be2834c40.js +0 -1
  345. fides/ui-build/static/admin/_next/static/chunks/pages/settings/email-templates-e633750d2b45ddd6.js +0 -1
  346. fides/ui-build/static/admin/_next/static/chunks/pages/settings/locations-3948686cbd372969.js +0 -1
  347. fides/ui-build/static/admin/_next/static/chunks/pages/settings/organization-04ece499c22d23f4.js +0 -1
  348. fides/ui-build/static/admin/_next/static/chunks/pages/settings/regulations-7752305084280cca.js +0 -1
  349. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/test-datasets-9e5f9066be6f218d.js +0 -1
  350. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]-86de9e6d72098e45.js +0 -1
  351. fides/ui-build/static/admin/_next/static/chunks/pages/systems-cac52e6c3abf6a15.js +0 -1
  352. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-60d5930b6855679b.js +0 -1
  353. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-b0f4c34752d32c73.js +0 -1
  354. fides/ui-build/static/admin/_next/static/chunks/pages/user-management-7dee83dd6d9292aa.js +0 -1
  355. fides/ui-build/static/admin/_next/static/chunks/webpack-8457bad7859c44f0.js +0 -1
  356. fides/ui-build/static/admin/_next/static/css/1fdf5c593349dbc6.css +0 -1
  357. fides/ui-build/static/admin/_next/static/f_8LkSZkhtDM14f8gbC0b/_buildManifest.js +0 -1
  358. {ethyca_fides-2.63.3rc0.dist-info → ethyca_fides-2.64.1b0.dist-info}/WHEEL +0 -0
  359. {ethyca_fides-2.63.3rc0.dist-info → ethyca_fides-2.64.1b0.dist-info}/entry_points.txt +0 -0
  360. {ethyca_fides-2.63.3rc0.dist-info → ethyca_fides-2.64.1b0.dist-info}/licenses/LICENSE +0 -0
  361. {ethyca_fides-2.63.3rc0.dist-info → ethyca_fides-2.64.1b0.dist-info}/top_level.txt +0 -0
  362. /fides/ui-build/static/admin/_next/static/{f_8LkSZkhtDM14f8gbC0b → nRQ3pmK_d3F5PJE39rP2h}/_ssgManifest.js +0 -0
@@ -1,13 +1,15 @@
1
1
  import json
2
2
  import os
3
+ import time as time_module
3
4
  import zipfile
4
5
  from collections import defaultdict
5
6
  from io import BytesIO
6
7
  from pathlib import Path
7
- from typing import Any, Dict, List, Optional
8
+ from typing import Any, Optional
8
9
 
9
10
  import jinja2
10
11
  from jinja2 import Environment, FileSystemLoader
12
+ from loguru import logger
11
13
 
12
14
  from fides.api.models.privacy_request import PrivacyRequest
13
15
  from fides.api.schemas.policy import ActionType
@@ -22,54 +24,79 @@ BORDER_COLOR = "#E2E8F0"
22
24
 
23
25
  # pylint: disable=too-many-instance-attributes
24
26
  class DsrReportBuilder:
27
+ """
28
+ Manages populating HTML templates from the given data and adding the generated
29
+ pages to a zip file in a way that the pages can be navigated between.
30
+
31
+ The zip file is structured as follows:
32
+ - welcome.html: the main index page
33
+ - data/dataset_name/index.html: the index page for the dataset
34
+ - data/dataset_name/collection_name/index.html: the index page for the collection
35
+ - data/dataset_name/collection_name/item_index.html: the detail page for the item
36
+ - attachments/index.html: the index page for the attachments
37
+
38
+ Args:
39
+ privacy_request: the privacy request object
40
+ dsr_data: the DSR data
41
+ """
42
+
25
43
  def __init__(
26
44
  self,
27
45
  privacy_request: PrivacyRequest,
28
- dsr_data: Dict[str, Any],
46
+ dsr_data: dict[str, Any],
29
47
  ):
30
48
  """
31
- Manages populating HTML templates from the given data and adding the generated
32
- pages to a zip file in a way that the pages can be navigated between.
49
+ Initializes the DSR report builder.
33
50
  """
51
+ # Define pretty_print function for Jinja templates
52
+ jinja2.filters.FILTERS["pretty_print"] = lambda value, indent=4: json.dumps(
53
+ value, indent=indent, cls=StorageJSONEncoder
54
+ )
34
55
 
35
- # zip file variables
56
+ # Initialize instance zip file variables
36
57
  self.baos = BytesIO()
37
58
 
38
59
  # we close this in the finally block of generate()
39
60
  # pylint: disable=consider-using-with
40
61
  self.out = zipfile.ZipFile(self.baos, "w")
41
-
42
- # Jinja template environment initialization
43
- def pretty_print(value: str, indent: int = 4) -> str:
44
- return json.dumps(
45
- value, indent=indent, default=StorageJSONEncoder().default
46
- )
47
-
48
- jinja2.filters.FILTERS["pretty_print"] = pretty_print
49
62
  self.template_loader = Environment(
50
63
  loader=FileSystemLoader(DSR_DIRECTORY), autoescape=True
51
64
  )
52
65
 
53
66
  # to pass in custom colors in the future
54
- self.template_data: Dict[str, Any] = {
67
+ self.template_data: dict[str, Any] = {
55
68
  "text_color": TEXT_COLOR,
56
69
  "header_color": HEADER_COLOR,
57
70
  "border_color": BORDER_COLOR,
58
71
  }
59
- self.main_links: Dict[str, Any] = {} # used to track the generated pages
72
+ self.main_links: dict[str, Any] = {} # used to track the generated pages
60
73
 
61
74
  # report data to populate the templates
62
75
  self.request_data = _map_privacy_request(privacy_request)
63
76
  self.dsr_data = dsr_data
64
77
 
78
+ # Track used filenames across all attachments
79
+ self.used_filenames: set[str] = set()
80
+
65
81
  def _populate_template(
66
82
  self,
67
83
  template_path: str,
68
84
  heading: Optional[str] = None,
69
85
  description: Optional[str] = None,
70
- data: Optional[Dict[str, Any]] = None,
86
+ data: Optional[dict[str, Any]] = None,
71
87
  ) -> str:
72
- """Generates a file from the template and data"""
88
+ """
89
+ Populates the template with the given data.
90
+
91
+ Args:
92
+ template_path: the path to the template to populate
93
+ heading: the heading to display on the template
94
+ description: the description to display on the template
95
+ data: the data to populate the template with
96
+
97
+ Returns:
98
+ The rendered template as a string.
99
+ """
73
100
  report_data = {
74
101
  "heading": heading,
75
102
  "description": description,
@@ -82,14 +109,24 @@ class DsrReportBuilder:
82
109
  return rendered_template
83
110
 
84
111
  def _add_file(self, filename: str, contents: str) -> None:
85
- """Helper to add a file to the zip archive"""
112
+ """
113
+ Adds a file to the zip file.
114
+
115
+ Args:
116
+ filename: the name of the file to add
117
+ contents: the contents of the file to add
118
+ """
86
119
  if filename and contents:
87
120
  self.out.writestr(f"{filename}", contents.encode("utf-8"))
88
121
 
89
- def _add_dataset(self, dataset_name: str, collections: Dict[str, Any]) -> None:
122
+ def _add_dataset(self, dataset_name: str, collections: dict[str, Any]) -> None:
90
123
  """
91
124
  Generates a page for each collection in the dataset and an index page for the dataset.
92
125
  Tracks the generated links to build a root level index after each collection has been processed.
126
+
127
+ Args:
128
+ dataset_name: the name of the dataset to add
129
+ collections: the collections to add to the dataset
93
130
  """
94
131
  # track links to collection indexes
95
132
  collection_links = {}
@@ -109,40 +146,203 @@ class DsrReportBuilder:
109
146
  ),
110
147
  )
111
148
 
149
+ def _get_unique_filename(self, filename: str) -> str:
150
+ """
151
+ Generates a unique filename by appending a counter if the file already exists.
152
+ Now tracks filenames across all directories to ensure global uniqueness.
153
+
154
+ Args:
155
+ filename: The original filename
156
+
157
+ Returns:
158
+ A unique filename that won't conflict with existing files
159
+ """
160
+ base_name, extension = os.path.splitext(filename)
161
+ counter = 1
162
+ unique_filename = filename
163
+
164
+ # Check if file exists in used_filenames set
165
+ while unique_filename in self.used_filenames:
166
+ unique_filename = f"{base_name}_{counter}{extension}"
167
+ counter += 1
168
+
169
+ # Add the new filename to the set
170
+ self.used_filenames.add(unique_filename)
171
+ return unique_filename
172
+
173
+ def _write_attachment_content(
174
+ self,
175
+ attachments: list[dict[str, Any]],
176
+ directory: str,
177
+ ) -> dict[str, dict[str, str]]:
178
+ """
179
+ Processes attachments and returns a dictionary mapping filenames to their download URLs and sizes.
180
+
181
+ Args:
182
+ attachments: The attachments to process
183
+ directory: The directory path (unused for presigned URLs)
184
+
185
+ Returns:
186
+ Dictionary mapping filenames to dictionaries containing url and size
187
+ """
188
+ # First process all attachments into a list of tuples (filename, data)
189
+ processed_attachments = []
190
+
191
+ for attachment in attachments:
192
+ if not isinstance(attachment, dict):
193
+ continue
194
+
195
+ file_name = attachment.get("file_name")
196
+ if not file_name:
197
+ logger.warning("Skipping attachment with no file name")
198
+ continue
199
+
200
+ download_url = attachment.get("download_url")
201
+ if not download_url:
202
+ logger.warning("Skipping attachment with no download URL")
203
+ continue
204
+
205
+ file_size = attachment.get("file_size")
206
+ if isinstance(file_size, (int, float)):
207
+ file_size = self._format_size(float(file_size))
208
+ else:
209
+ file_size = "Unknown"
210
+
211
+ # Get a unique filename to prevent duplicates
212
+ unique_filename = self._get_unique_filename(file_name)
213
+
214
+ # Add to processed attachments
215
+ processed_attachments.append(
216
+ (unique_filename, {"url": download_url, "size": file_size})
217
+ )
218
+
219
+ # Convert list of tuples to dictionary
220
+ return dict(processed_attachments)
221
+
112
222
  def _add_collection(
113
- self, rows: List[Dict[str, Any]], dataset_name: str, collection_name: str
223
+ self, rows: list[dict[str, Any]], dataset_name: str, collection_name: str
114
224
  ) -> None:
115
- # track links to detail pages
116
- detail_links = {}
117
- for index, item in enumerate(rows, 1):
118
- detail_url = f"{index}.html"
119
- self._add_file(
120
- f"data/{dataset_name}/{collection_name}/{index}.html",
121
- self._populate_template(
122
- "templates/item.html",
123
- f"{collection_name} (item #{index})",
124
- None,
125
- item,
126
- ),
225
+ """
226
+ Adds a collection to the zip file.
227
+
228
+ Args:
229
+ rows: the rows to add to the collection
230
+ dataset_name: the name of the dataset to add the collection to
231
+ collection_name: the name of the collection to add
232
+ """
233
+ items_content = []
234
+
235
+ for index, collection_item in enumerate(rows, 1):
236
+ # Create a copy of the item data to avoid modifying the original
237
+ item_data = collection_item.copy()
238
+
239
+ # Process any attachments in the item
240
+ if "attachments" in item_data and isinstance(
241
+ item_data["attachments"], list
242
+ ):
243
+ # Process attachments and get their URLs
244
+ attachment_links = self._write_attachment_content(
245
+ item_data["attachments"],
246
+ f"data/{dataset_name}/{collection_name}",
247
+ )
248
+ # Add the attachment URLs to the item data
249
+ item_data["attachments"] = attachment_links
250
+
251
+ # Add item content to the list
252
+ items_content.append(
253
+ {
254
+ "index": index,
255
+ "heading": f"{collection_name} (item #{index})",
256
+ "data": item_data,
257
+ }
127
258
  )
128
- detail_links[f"item #{index}"] = detail_url
129
259
 
130
- # generate detail index page
260
+ # Generate the collection index page
131
261
  self._add_file(
132
262
  f"data/{dataset_name}/{collection_name}/index.html",
133
263
  self._populate_template(
134
264
  "templates/collection_index.html",
135
265
  collection_name,
136
266
  None,
137
- detail_links,
267
+ {"collection_items": items_content},
268
+ ),
269
+ )
270
+
271
+ def _add_attachments(self, attachments: list[dict[str, Any]]) -> None:
272
+ """
273
+ Adds top-level attachments to the zip file.
274
+
275
+ Args:
276
+ attachments: the attachments to add
277
+ """
278
+ if not attachments or not isinstance(attachments, list):
279
+ return
280
+
281
+ # Process attachments and get the links
282
+ attachment_links = self._write_attachment_content(attachments, "attachments")
283
+
284
+ # Generate attachments index page using the attachments index template
285
+ self._add_file(
286
+ "attachments/index.html",
287
+ self._populate_template(
288
+ "templates/attachments_index.html",
289
+ "Attachments",
290
+ "Files attached to this privacy request",
291
+ attachment_links,
138
292
  ),
139
293
  )
140
294
 
295
+ def _get_datasets_from_dsr_data(self) -> dict[str, Any]:
296
+ """
297
+ Returns the datasets from the DSR data.
298
+ """
299
+ # pre-process data to split the dataset:collection keys
300
+ datasets: dict[str, Any] = defaultdict(lambda: defaultdict(list))
301
+ for key, rows in self.dsr_data.items():
302
+
303
+ # we handle attachments separately
304
+ if key == "attachments":
305
+ continue
306
+
307
+ parts = key.split(":", 1)
308
+ if len(parts) > 1:
309
+ dataset_name, collection_name = parts
310
+ else:
311
+ for row in rows:
312
+ if "system_name" in row:
313
+ dataset_name = row["system_name"]
314
+ collection_name = parts[0]
315
+ break
316
+ else:
317
+ dataset_name = "manual"
318
+ collection_name = parts[0]
319
+
320
+ datasets[dataset_name][collection_name].extend(rows)
321
+
322
+ return datasets
323
+
324
+ def _format_size(self, size_bytes: float) -> str:
325
+ """
326
+ Format size in bytes to human readable format.
327
+
328
+ Args:
329
+ size_bytes: Size in bytes
330
+
331
+ Returns:
332
+ Formatted string with appropriate unit (B, KB, MB, GB)
333
+ """
334
+ for unit in ["B", "KB", "MB", "GB"]:
335
+ if size_bytes < 1024.0:
336
+ return f"{size_bytes:.1f} {unit}"
337
+ size_bytes /= 1024.0
338
+ return f"{size_bytes:.1f} TB"
339
+
141
340
  def generate(self) -> BytesIO:
142
341
  """
143
342
  Processes the request and DSR data to build zip file containing the DSR report.
144
343
  Returns the zip file as an in-memory byte array.
145
344
  """
345
+ start_time = time_module.time()
146
346
  try:
147
347
  # all the css for the pages is in main.css
148
348
  self._add_file(
@@ -157,18 +357,28 @@ class DsrReportBuilder:
157
357
  )
158
358
 
159
359
  # pre-process data to split the dataset:collection keys
160
- datasets: Dict[str, Any] = defaultdict(lambda: defaultdict(list))
161
- for key, rows in self.dsr_data.items():
162
- parts = key.split(":", 1)
163
- dataset_name, collection_name = (
164
- parts if len(parts) > 1 else ("manual", parts[0])
165
- )
166
- datasets[dataset_name][collection_name].extend(rows)
360
+ datasets: dict[str, Any] = self._get_datasets_from_dsr_data()
167
361
 
168
- for dataset_name, collections in datasets.items():
169
- self._add_dataset(dataset_name, collections)
362
+ # Sort datasets alphabetically, excluding special cases
363
+ regular_datasets = [
364
+ name for name in sorted(datasets.keys()) if name != "dataset"
365
+ ] # pylint: disable=invalid-name
366
+
367
+ # Add regular datasets in alphabetical order
368
+ for dataset_name in regular_datasets:
369
+ self._add_dataset(dataset_name, datasets[dataset_name])
170
370
  self.main_links[dataset_name] = f"data/{dataset_name}/index.html"
171
371
 
372
+ # Add Additional Data if it exists
373
+ if "dataset" in datasets:
374
+ self._add_dataset("dataset", datasets["dataset"])
375
+ self.main_links["Additional Data"] = "data/dataset/index.html"
376
+
377
+ # Add Additional Attachments last if it exists
378
+ if "attachments" in self.dsr_data:
379
+ self._add_attachments(self.dsr_data["attachments"])
380
+ self.main_links["Additional Attachments"] = "attachments/index.html"
381
+
172
382
  # create the main index once all the datasets have been added
173
383
  self._add_file(
174
384
  "welcome.html",
@@ -182,12 +392,20 @@ class DsrReportBuilder:
182
392
 
183
393
  # reset the file pointer so the file can be fully read by the caller
184
394
  self.baos.seek(0)
395
+
396
+ # Calculate time taken and file size
397
+ time_taken = time_module.time() - start_time
398
+ file_size = self._format_size(float(len(self.baos.getvalue())))
399
+
400
+ logger.bind(time_to_generate=time_taken, dsr_package_size=file_size).info(
401
+ "DSR report generation complete."
402
+ )
185
403
  return self.baos
186
404
 
187
405
 
188
- def _map_privacy_request(privacy_request: PrivacyRequest) -> Dict[str, Any]:
406
+ def _map_privacy_request(privacy_request: PrivacyRequest) -> dict[str, Any]:
189
407
  """Creates a map with a subset of values from the privacy request"""
190
- request_data: Dict[str, Any] = {}
408
+ request_data: dict[str, Any] = {}
191
409
  request_data["id"] = privacy_request.id
192
410
 
193
411
  action_type: Optional[ActionType] = privacy_request.policy.get_action_type()
@@ -0,0 +1,33 @@
1
+ <html>
2
+ <head>
3
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600">
4
+ <link rel="stylesheet" href="../data/main.css">
5
+ </head>
6
+ <body>
7
+ <div class="container">
8
+ <div class="header"></div>
9
+ <div class="content">
10
+ <div class="button-container">
11
+ <a href="../welcome.html">
12
+ <div class="button"><img src="../data/back.svg"></div>
13
+ <span>Back to main page</span>
14
+ </a>
15
+ </div>
16
+ <h1>Attachments</h1>
17
+ <p class="expiration-notice">Note: All download links will expire in 7 days.</p>
18
+ <div class="table table-hover">
19
+ <div class="table-row">
20
+ <div class="table-cell" style="text-align: left;">File Name</div>
21
+ <div class="table-cell" style="text-align: left;">Size</div>
22
+ </div>
23
+ {% for name, info in data.items() %}
24
+ <a href="{{ info.url }}" class="table-row" target="_blank">
25
+ <div class="table-cell" style="text-align: left;">{{ name }}</div>
26
+ <div class="table-cell" style="text-align: left;">{{ info.size }}</div>
27
+ </a>
28
+ {% endfor %}
29
+ </div>
30
+ </div>
31
+ </div>
32
+ </body>
33
+ </html>
@@ -14,17 +14,42 @@
14
14
  </a>
15
15
  </div>
16
16
  <h1>{{ heading }}</h1>
17
- <div class="table table-hover">
18
- <div class="table-row">
19
- <div class="table-cell">Items</div>
17
+ {% for item in data.collection_items %}
18
+ <div class="item-section">
19
+ <h2>{{ item.heading }}</h2>
20
+ <div class="table">
21
+ <div class="table-row">
22
+ <div class="table-cell">Field</div>
23
+ <div class="table-cell">Value</div>
24
+ </div>
25
+ {% for field, value in item.data.items() %}
26
+ <div class="table-row">
27
+ <div class="table-cell">{{ field }}</div>
28
+ <div class="table-cell">
29
+ {% if field == "attachments" and value is mapping and value|length > 0 %}
30
+ <p class="expiration-notice">Note: All download links will expire in 7 days.</p>
31
+ <div class="table table-hover">
32
+ <div class="table-row">
33
+ <div class="table-cell" style="text-align: left;">File Name</div>
34
+ <div class="table-cell" style="text-align: left;">Size</div>
35
+ </div>
36
+ {% for attachment_name, attachment_info in value.items() %}
37
+ <a href="{{ attachment_info.url }}" class="table-row" target="_blank">
38
+ <div class="table-cell" style="text-align: left;">{{ attachment_name }}</div>
39
+ <div class="table-cell" style="text-align: left;">{{ attachment_info.size }}</div>
40
+ </a>
41
+ {% endfor %}
42
+ </div>
43
+ {% else %}
44
+ <pre>{{ value | pretty_print }}</pre>
45
+ {% endif %}
46
+ </div>
47
+ </div>
48
+ {% endfor %}
20
49
  </div>
21
- {% for name, link in data.items() %}
22
- <a href="{{ link }}" class="table-row">
23
- <div class="table-cell">{{ name }}</div>
24
- </a>
25
- {% endfor %}
26
50
  </div>
51
+ {% endfor %}
27
52
  </div>
28
53
  </div>
29
54
  </body>
30
- </html>
55
+ </html>
@@ -35,7 +35,7 @@ h1 {
35
35
  margin-bottom: 24px;
36
36
  width: 100%;
37
37
  display: flex;
38
- justify-content: center;
38
+ justify-content: center;
39
39
  }
40
40
 
41
41
  .header div {
@@ -176,4 +176,47 @@ h1 {
176
176
 
177
177
  a {
178
178
  text-decoration: none;
179
- }
179
+ }
180
+
181
+ .attachment-link {
182
+ color: var(--text-color);
183
+ text-decoration: underline;
184
+ cursor: pointer;
185
+ }
186
+
187
+ .attachment-link:hover {
188
+ opacity: 0.8;
189
+ }
190
+
191
+ .dataset-list {
192
+ padding: 20px 0 100px;
193
+ }
194
+
195
+ .dataset-list h2 {
196
+ font-size: 16px;
197
+ font-weight: 600;
198
+ margin-bottom: 16px;
199
+ }
200
+
201
+ .dataset-item {
202
+ margin-bottom: 8px;
203
+ }
204
+
205
+ .dataset-link {
206
+ display: block;
207
+ color: var(--text-color);
208
+ text-decoration: none;
209
+ font-size: 14px;
210
+ line-height: 20px;
211
+ font-weight: 600;
212
+ background-color: var(--header-color);
213
+ padding: 16px;
214
+ border: 1px solid var(--border-color);
215
+ border-radius: 6px;
216
+ transition: opacity 0.2s ease;
217
+ }
218
+
219
+ .dataset-link:hover {
220
+ opacity: 0.8;
221
+ cursor: pointer;
222
+ }
@@ -45,18 +45,22 @@
45
45
  <div id="requestedAt">{{ request.requested_at }}</div>
46
46
  </div>
47
47
  </div>
48
- <div class="table table-hover">
49
- <div class="table-row">
50
- <div class="table-cell">Dataset</div>
51
- </div>
48
+ <div class="dsr-information-text">
49
+ <p>
50
+ This web link contains all data requested as part of your Data Subject Request (DSR). Your
51
+ information has been compiled from the following areas. Click on each section to open a file and view
52
+ your data in PDF format.
53
+ </p>
54
+ </div>
55
+ <div class="dataset-list">
52
56
  {% for name, link in data.items() %}
53
- <a href="{{ link }}" class="table-row">
54
- <div class="table-cell">{{ name }}</div>
55
- </a>
57
+ <div class="dataset-item">
58
+ <a href="{{ link }}" class="dataset-link">{{ name }}</a>
59
+ </div>
56
60
  {% endfor %}
57
61
  </div>
58
62
  </div>
59
63
  </div>
60
64
  </body>
61
65
 
62
- </html>
66
+ </html>