django-pfx 1.7.6.dev37__tar.gz → 1.7.6.dev39__tar.gz

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 (330) hide show
  1. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/PKG-INFO +3 -1
  2. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/django_pfx.egg-info/PKG-INFO +3 -1
  3. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/django_pfx.egg-info/SOURCES.txt +1 -0
  4. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/django_pfx.egg-info/requires.txt +3 -0
  5. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/django_pfx.egg-info/scm_file_list.json +1 -0
  6. django_pfx-1.7.6.dev39/django_pfx.egg-info/scm_version.json +8 -0
  7. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/source/testing.md +36 -0
  8. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/test.py +127 -0
  9. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/requirements.txt +1 -0
  10. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/setup.cfg +1 -0
  11. django_pfx-1.7.6.dev39/tests/tests/test_table_file_helpers.py +144 -0
  12. django_pfx-1.7.6.dev37/django_pfx.egg-info/scm_version.json +0 -8
  13. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/.gitignore +0 -0
  14. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/.gitlab-ci.yml +0 -0
  15. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/.pre-commit-config.yaml +0 -0
  16. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/LICENSE +0 -0
  17. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/MANIFEST.in +0 -0
  18. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/README.md +0 -0
  19. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/django_pfx.egg-info/dependency_links.txt +0 -0
  20. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/django_pfx.egg-info/top_level.txt +0 -0
  21. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/Makefile +0 -0
  22. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/conf.py +0 -0
  23. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/index.rst +0 -0
  24. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/source/api.views.rst +0 -0
  25. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/source/authentication.md +0 -0
  26. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/source/decorator.md +0 -0
  27. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/source/generate_openapi.md +0 -0
  28. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/source/getting_started.md +0 -0
  29. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/source/internationalisation.md +0 -0
  30. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/source/model.md +0 -0
  31. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/source/pfx_views.md +0 -0
  32. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/source/profiling.md +0 -0
  33. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/source/settings.md +0 -0
  34. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/doc/source/user_jobs.md +0 -0
  35. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/img/pfx.png +0 -0
  36. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/img/pfx.svg +0 -0
  37. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/make_messages +0 -0
  38. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/manage.py +0 -0
  39. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/__init__.py +0 -0
  40. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/__init__.py +0 -0
  41. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/apps.py +0 -0
  42. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/email_backend/__init__.py +0 -0
  43. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/email_backend/memory_email_backend.py +0 -0
  44. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/email_backend/q_mailjet_email_backend.py +0 -0
  45. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/exceptions.py +0 -0
  46. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/locale/fr/LC_MESSAGES/django.mo +0 -0
  47. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/locale/fr/LC_MESSAGES/django.po +0 -0
  48. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/mailjet/__init__.py +0 -0
  49. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/mailjet/mailjet.py +0 -0
  50. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/mailjet/message_result.py +0 -0
  51. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/migrations/0001_initial.py +0 -0
  52. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/migrations/0002_alter_email_headers.py +0 -0
  53. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/migrations/0003_alter_email_options.py +0 -0
  54. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/migrations/0004_alter_email_mail_from.py +0 -0
  55. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/migrations/0005_lowercase_verbose_names.py +0 -0
  56. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/migrations/0006_alter_email_attachments.py +0 -0
  57. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/migrations/__init__.py +0 -0
  58. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/models/__init__.py +0 -0
  59. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/models/email.py +0 -0
  60. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/providers/__init__.py +0 -0
  61. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/providers/email_django_q_executor.py +0 -0
  62. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/shortcuts.py +0 -0
  63. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/tasks.py +0 -0
  64. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/templates/emails/base_template.html +0 -0
  65. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/templates/emails/base_template.txt +0 -0
  66. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/templates/emails/base_template_subject.txt +0 -0
  67. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/test.py +0 -0
  68. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/urls.py +0 -0
  69. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/views/__init__.py +0 -0
  70. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/emails/views/email_rest_view.py +0 -0
  71. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/__init__.py +0 -0
  72. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/apidoc/__init__.py +0 -0
  73. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/apidoc/parameters.py +0 -0
  74. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/apidoc/schema.py +0 -0
  75. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/apidoc/tags.py +0 -0
  76. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/apps.py +0 -0
  77. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/decorator/__init__.py +0 -0
  78. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/decorator/rest.py +0 -0
  79. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/default_settings.py +0 -0
  80. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/exceptions.py +0 -0
  81. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/fields/__init__.py +0 -0
  82. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/fields/decimal_field.py +0 -0
  83. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/fields/media_field.py +0 -0
  84. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/fields/minutes_duration_field.py +0 -0
  85. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/fields/rich_text_field.py +0 -0
  86. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/http/__init__.py +0 -0
  87. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/http/json_response.py +0 -0
  88. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
  89. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +0 -0
  90. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/management/__init__.py +0 -0
  91. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/management/commands/__init__.py +0 -0
  92. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
  93. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/management/commands/profile.py +0 -0
  94. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/mfa/__init__.py +0 -0
  95. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/mfa/authenticator.py +0 -0
  96. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/mfa/base.py +0 -0
  97. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/mfa/email.py +0 -0
  98. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/mfa/legacy.py +0 -0
  99. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/mfa/sms.py +0 -0
  100. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/middleware/__init__.py +0 -0
  101. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/middleware/authentication.py +0 -0
  102. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/middleware/locale.py +0 -0
  103. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/middleware/profiling.py +0 -0
  104. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/middleware/request_too_big_middleware.py +0 -0
  105. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/middleware/ws_cookie_authentication_middleware.py +0 -0
  106. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/migrations/0001_initial.py +0 -0
  107. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/migrations/0002_pfxpermissionsuser.py +0 -0
  108. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/migrations/0003_delete_pfxpermissionsuser.py +0 -0
  109. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/migrations/0004_alter_loginban_failed_counter_and_more.py +0 -0
  110. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/migrations/0005_mfadevice_mfadevice_mfa_device_unique_backend.py +0 -0
  111. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/migrations/0006_alter_pfxuser_managers.py +0 -0
  112. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/migrations/0007_alter_pfxuser_options.py +0 -0
  113. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/migrations/0008_lowercase_verbose_names.py +0 -0
  114. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/migrations/__init__.py +0 -0
  115. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/migrations/operations/__init__.py +0 -0
  116. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/migrations/operations/permissions.py +0 -0
  117. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/models/__init__.py +0 -0
  118. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
  119. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/models/attachment_mixin.py +0 -0
  120. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/models/cache_mixins.py +0 -0
  121. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/models/login_ban.py +0 -0
  122. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/models/mfa_device.py +0 -0
  123. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/models/mfa_user_mixin.py +0 -0
  124. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/models/not_null_fields.py +0 -0
  125. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/models/ordered_model_mixin.py +0 -0
  126. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/models/pfx_models.py +0 -0
  127. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/models/pfx_user.py +0 -0
  128. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
  129. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/serializers/__init__.py +0 -0
  130. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/serializers/json.py +0 -0
  131. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/settings.py +0 -0
  132. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/shortcuts.py +0 -0
  133. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/sms/__init__.py +0 -0
  134. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/sms/backends/__init__.py +0 -0
  135. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/sms/backends/base.py +0 -0
  136. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/sms/backends/console.py +0 -0
  137. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/sms/backends/memory.py +0 -0
  138. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/storage/__init__.py +0 -0
  139. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/storage/exceptions.py +0 -0
  140. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/storage/local_storage.py +0 -0
  141. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/storage/s3_storage.py +0 -0
  142. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
  143. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
  144. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
  145. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
  146. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
  147. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
  148. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/urls.py +0 -0
  149. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/version_check.py +0 -0
  150. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/__init__.py +0 -0
  151. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/authentication_views.py +0 -0
  152. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/fields.py +0 -0
  153. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/filters_views.py +0 -0
  154. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/locale_views.py +0 -0
  155. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/media_rest_view_mixin.py +0 -0
  156. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/mfa_authentication_views.py +0 -0
  157. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/ordered_rest_view_mixin.py +0 -0
  158. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/__init__.py +0 -0
  159. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/date_format.py +0 -0
  160. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/groups.py +0 -0
  161. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/list_count.py +0 -0
  162. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/list_items.py +0 -0
  163. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
  164. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/list_order.py +0 -0
  165. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/list_search.py +0 -0
  166. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
  167. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
  168. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
  169. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
  170. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/subset.py +0 -0
  171. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
  172. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
  173. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
  174. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
  175. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
  176. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/views/rest_views.py +0 -0
  177. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/pfxcore/xml_test_runner.py +0 -0
  178. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/settings/__init__.py +0 -0
  179. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/settings/dev.py +0 -0
  180. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/__init__.py +0 -0
  181. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/apps.py +0 -0
  182. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/locale/fr/LC_MESSAGES/django.mo +0 -0
  183. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/locale/fr/LC_MESSAGES/django.po +0 -0
  184. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/migrations/0001_initial.py +0 -0
  185. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/migrations/0002_alter_userjob_options_alter_userjob_auto_queued_and_more.py +0 -0
  186. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/migrations/0003_clean_scheduled_tasks.py +0 -0
  187. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/migrations/0004_userjob_summary_url.py +0 -0
  188. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/migrations/0005_swappable_user_job.py +0 -0
  189. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/migrations/0006_lowercase_verbose_names.py +0 -0
  190. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/migrations/__init__.py +0 -0
  191. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/models/__init__.py +0 -0
  192. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/models/user_job.py +0 -0
  193. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/models/user_job_attachment.py +0 -0
  194. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/providers/__init__.py +0 -0
  195. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/providers/user_job_django_q_executor.py +0 -0
  196. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/providers/user_job_websocket_channel_provider.py +0 -0
  197. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/tasks.py +0 -0
  198. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/urls.py +0 -0
  199. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/views/__init__.py +0 -0
  200. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/views/user_job_attachment_view.py +0 -0
  201. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/views/user_job_view.py +0 -0
  202. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pfx/userjobs/views/user_websocket_consumer.py +0 -0
  203. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/pyproject.toml +0 -0
  204. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/serve-doc +0 -0
  205. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/setup.py +0 -0
  206. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/__init__.py +0 -0
  207. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/apps.py +0 -0
  208. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/channel_group_send_calls.py +0 -0
  209. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
  210. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/mfa_urls.py +0 -0
  211. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/migrations/0001_initial.py +0 -0
  212. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/migrations/__init__.py +0 -0
  213. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/models.py +0 -0
  214. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/settings/__init__.py +0 -0
  215. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/settings/ci.py +0 -0
  216. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/settings/common.py +0 -0
  217. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/settings/dev.py +0 -0
  218. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/settings/dev_custom_example.py +0 -0
  219. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/settings/dev_default.py +0 -0
  220. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/__init__.py +0 -0
  221. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/basic_api_errors.py +0 -0
  222. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/basic_api_test.py +0 -0
  223. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/mfa_test_utils.py +0 -0
  224. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/parallel_scenarios.py +0 -0
  225. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_api_doc.py +0 -0
  226. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_api_doc_search.py +0 -0
  227. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_auth_api.py +0 -0
  228. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_body_mixin.py +0 -0
  229. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_cache.py +0 -0
  230. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_client.py +0 -0
  231. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_emails_email_api.py +0 -0
  232. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_emails_email_model.py +0 -0
  233. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_emails_q_mailjet_email_backend.py +0 -0
  234. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_emails_send_email_message.py +0 -0
  235. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_emails_test_tools.py +0 -0
  236. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_fields_choices.py +0 -0
  237. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_fields_date.py +0 -0
  238. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_fields_decimal.py +0 -0
  239. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_fields_help_text.py +0 -0
  240. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_fields_minutes_duration.py +0 -0
  241. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_fields_one2many.py +0 -0
  242. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_fields_rich_text.py +0 -0
  243. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_fields_time.py +0 -0
  244. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_filters.py +0 -0
  245. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_locale_api.py +0 -0
  246. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_mfa_legacy.py +0 -0
  247. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_mfa_login_api.py +0 -0
  248. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_mfa_setup_api.py +0 -0
  249. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_ordered_rest_view_mixin.py +0 -0
  250. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_perm_tests.py +0 -0
  251. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_permissions.py +0 -0
  252. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_perms_api.py +0 -0
  253. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_post_migrate_groups_update.py +0 -0
  254. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_profiling_middleware.py +0 -0
  255. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_settings.py +0 -0
  256. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_shortcuts.py +0 -0
  257. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_test_runner.py +0 -0
  258. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_timezone_middleware.py +0 -0
  259. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_tools.py +0 -0
  260. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_user_job_api.py +0 -0
  261. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_user_job_messages.py +0 -0
  262. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_user_job_model.py +0 -0
  263. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_user_queryset.py +0 -0
  264. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_user_websocket.py +0 -0
  265. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_verbose_names.py +0 -0
  266. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_version_check.py +0 -0
  267. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_view_decorators.py +0 -0
  268. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_view_fields.py +0 -0
  269. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/tests/test_ws_cookie_middleware.py +0 -0
  270. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/urls.py +0 -0
  271. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests/views.py +0 -0
  272. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/__init__.py +0 -0
  273. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/migrations/__init__.py +0 -0
  274. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/settings/__init__.py +0 -0
  275. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/settings/ci.py +0 -0
  276. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/settings/common.py +0 -0
  277. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/settings/dev.py +0 -0
  278. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/settings/dev_custom_example.py +0 -0
  279. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/settings/dev_default.py +0 -0
  280. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/tests/__init__.py +0 -0
  281. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/tests/test_api.py +0 -0
  282. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/tests/test_auth_api.py +0 -0
  283. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/urls.py +0 -0
  284. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_base_user/views.py +0 -0
  285. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/__init__.py +0 -0
  286. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/migrations/0001_initial.py +0 -0
  287. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/migrations/__init__.py +0 -0
  288. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/models.py +0 -0
  289. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/settings/__init__.py +0 -0
  290. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/settings/ci.py +0 -0
  291. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/settings/common.py +0 -0
  292. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/settings/dev.py +0 -0
  293. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/settings/dev_custom_example.py +0 -0
  294. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/settings/dev_default.py +0 -0
  295. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/tests/__init__.py +0 -0
  296. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/tests/test_api.py +0 -0
  297. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/tests/test_auth_api.py +0 -0
  298. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/urls.py +0 -0
  299. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user/views.py +0 -0
  300. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/__init__.py +0 -0
  301. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/migrations/0001_initial.py +0 -0
  302. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/migrations/0002_lowercase_verbose_names.py +0 -0
  303. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/migrations/__init__.py +0 -0
  304. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/models.py +0 -0
  305. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/providers.py +0 -0
  306. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/scope.py +0 -0
  307. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/settings/__init__.py +0 -0
  308. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/settings/ci.py +0 -0
  309. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/settings/common.py +0 -0
  310. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/settings/dev.py +0 -0
  311. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/settings/dev_custom_example.py +0 -0
  312. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/settings/dev_default.py +0 -0
  313. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/tests/__init__.py +0 -0
  314. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/tests/test_user_job.py +0 -0
  315. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_custom_user_job/urls.py +0 -0
  316. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/__init__.py +0 -0
  317. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/migrations/0001_initial.py +0 -0
  318. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/migrations/__init__.py +0 -0
  319. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/models.py +0 -0
  320. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/settings/__init__.py +0 -0
  321. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/settings/ci.py +0 -0
  322. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/settings/common.py +0 -0
  323. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/settings/dev.py +0 -0
  324. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/settings/dev_custom_example.py +0 -0
  325. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/settings/dev_default.py +0 -0
  326. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/tests/__init__.py +0 -0
  327. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/tests/test_api.py +0 -0
  328. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/tests/test_auth_api.py +0 -0
  329. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/urls.py +0 -0
  330. {django_pfx-1.7.6.dev37 → django_pfx-1.7.6.dev39}/tests_user_email_login/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-pfx
3
- Version: 1.7.6.dev37
3
+ Version: 1.7.6.dev39
4
4
  Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
5
5
  Author: Hervé Martinet
6
6
  Author-email: herve.martinet@gmail.com
@@ -41,6 +41,8 @@ Requires-Dist: pyotp>=2.10.0; extra == "otp"
41
41
  Provides-Extra: heic-converter
42
42
  Requires-Dist: python-magic; extra == "heic-converter"
43
43
  Requires-Dist: pillow-heif; extra == "heic-converter"
44
+ Provides-Extra: xlsx
45
+ Requires-Dist: openpyxl; extra == "xlsx"
44
46
  Dynamic: license-file
45
47
 
46
48
  # Django PFX
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-pfx
3
- Version: 1.7.6.dev37
3
+ Version: 1.7.6.dev39
4
4
  Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
5
5
  Author: Hervé Martinet
6
6
  Author-email: herve.martinet@gmail.com
@@ -41,6 +41,8 @@ Requires-Dist: pyotp>=2.10.0; extra == "otp"
41
41
  Provides-Extra: heic-converter
42
42
  Requires-Dist: python-magic; extra == "heic-converter"
43
43
  Requires-Dist: pillow-heif; extra == "heic-converter"
44
+ Provides-Extra: xlsx
45
+ Requires-Dist: openpyxl; extra == "xlsx"
44
46
  Dynamic: license-file
45
47
 
46
48
  # Django PFX
@@ -253,6 +253,7 @@ tests/tests/test_post_migrate_groups_update.py
253
253
  tests/tests/test_profiling_middleware.py
254
254
  tests/tests/test_settings.py
255
255
  tests/tests/test_shortcuts.py
256
+ tests/tests/test_table_file_helpers.py
256
257
  tests/tests/test_test_runner.py
257
258
  tests/tests/test_timezone_middleware.py
258
259
  tests/tests/test_tools.py
@@ -16,3 +16,6 @@ pillow-heif
16
16
 
17
17
  [otp]
18
18
  pyotp>=2.10.0
19
+
20
+ [xlsx]
21
+ openpyxl
@@ -243,6 +243,7 @@
243
243
  "tests/tests/test_profiling_middleware.py",
244
244
  "tests/tests/test_settings.py",
245
245
  "tests/tests/test_shortcuts.py",
246
+ "tests/tests/test_table_file_helpers.py",
246
247
  "tests/tests/test_test_runner.py",
247
248
  "tests/tests/test_timezone_middleware.py",
248
249
  "tests/tests/test_tools.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "1.7.5",
3
+ "distance": 39,
4
+ "node": "g471828c66bafecdb4544e8e23075df5d7b128c66",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-10-21"
8
+ }
@@ -430,3 +430,39 @@ class ATestClass(EmailTestMixin, SMSTestMixin, TestCase):
430
430
  [(to, message)] = self.sms_outbox()
431
431
  self.assertEqual(to, '+41791234567')
432
432
  ```
433
+
434
+ ## CSV and Excel files
435
+
436
+ `CSVFileHelper` and `XLSXFileHelper` read back a table file of a test: an
437
+ export, a file attached to a user job, a file to import.
438
+
439
+ ```python
440
+ from pfx.pfxcore.test import CSVFileHelper, XLSXFileHelper
441
+
442
+ f = XLSXFileHelper(response) # or CSVFileHelper(response)
443
+ f.header() # ['Name', 'Birth']
444
+ f.dicts() # [{'Name': 'Alice', 'Birth': datetime(…)}, …]
445
+ f.lines()[0][f.column('Name')] # 'Alice'
446
+ ```
447
+
448
+ The file can be given as bytes, a string (its content), a `Path`, an HTTP
449
+ response (streamed or not),
450
+ an iterable of chunks, or the value of a `MediaField` on the local storage
451
+ (`XLSXFileHelper(job.attachments.get().file)`).
452
+
453
+ - `header=2`: the number of the line of the headers, for a file with a title
454
+ above them. The lines after it are the data.
455
+ - `header()`, `lines()`, `dicts()`: the headers, the data as lists, the data
456
+ as dicts by header. `line(n)` and `line_dict(n)` count from 0, the first
457
+ line of the file; `len()` counts every line.
458
+ - `column(title)`: the index of a column.
459
+ - `write_file(path)` and `pretty_print()`: to look at the file while writing a
460
+ test.
461
+
462
+ `CSVFileHelper(data, delimiter=';', encoding='utf-8-sig')` reads the CSV of a
463
+ French Excel; its values are strings.
464
+
465
+ `XLSXFileHelper(data, sheet='Totals')` reads a sheet by name or by index, the
466
+ active one by default. Its values keep their type (numbers, dates), and
467
+ `workbook` and `sheet` give the formats and the styles. It needs
468
+ [openpyxl](https://openpyxl.readthedocs.io/): install `django-pfx[xlsx]`.
@@ -1,4 +1,6 @@
1
1
  import copy
2
+ import csv
3
+ import io
2
4
  import json
3
5
  import logging
4
6
  import re
@@ -6,6 +8,7 @@ import traceback
6
8
  from functools import reduce
7
9
  from http.cookies import SimpleCookie
8
10
  from json import JSONDecodeError
11
+ from pathlib import Path
9
12
 
10
13
  from django.apps import apps
11
14
  from django.db import transaction
@@ -327,6 +330,130 @@ class TestPermsAssertMixin(TestAssertMixin):
327
330
  self.assertPerms(**perms)
328
331
 
329
332
 
333
+ def file_content(data):
334
+ """The bytes of a file given to a test.
335
+
336
+ ``data`` can be bytes, a string, a ``Path``, an HTTP response (streamed
337
+ or not), an iterable of chunks, or the value of a ``MediaField`` on the
338
+ local storage, such as the attachment of a user job.
339
+ """
340
+ if isinstance(data, bytes):
341
+ return data
342
+ if isinstance(data, Path):
343
+ return data.read_bytes()
344
+ if isinstance(data, str):
345
+ return data.encode()
346
+ if isinstance(data, dict):
347
+ return Path(settings.STORAGE_LOCAL_ROOT, data['key']).read_bytes()
348
+ if hasattr(data, 'streaming_content'):
349
+ data = data.streaming_content
350
+ elif hasattr(data, 'content'):
351
+ return data.content
352
+ return b"".join(
353
+ chunk.encode() if isinstance(chunk, str) else chunk
354
+ for chunk in data)
355
+
356
+
357
+ class TableFileResponseHelper:
358
+ """A table file of a test, read back as rows of values.
359
+
360
+ ``header`` is the number of the line of the headers, the first by
361
+ default: the lines after it are the data. :meth:`line` counts from 0,
362
+ the first line of the file.
363
+ """
364
+
365
+ def __init__(self, data, header=1):
366
+ self.content = file_content(data)
367
+ self.header_index = header - 1
368
+ self.rows = self.read_rows()
369
+
370
+ def read_rows(self):
371
+ raise NotImplementedError # pragma: no cover
372
+
373
+ def write_file(self, path):
374
+ """Write the file, to open it while writing a test."""
375
+ Path(path).write_bytes(self.content)
376
+
377
+ def pretty_print(self):
378
+ """Print the rows as a table, to read them while writing a test."""
379
+ cells = [
380
+ ['' if v is None else str(v) for v in row] for row in self.rows]
381
+ widths = [
382
+ max(len(row[i]) for row in cells if i < len(row))
383
+ for i in range(max((len(row) for row in cells), default=0))]
384
+ print()
385
+ for row in cells:
386
+ print(' '.join(v.ljust(w) for v, w in zip(row, widths)).rstrip())
387
+
388
+ def header(self):
389
+ return self.rows[self.header_index]
390
+
391
+ def line(self, n):
392
+ return self.rows[n]
393
+
394
+ def len(self):
395
+ return len(self.rows)
396
+
397
+ def lines(self):
398
+ """The rows after the headers."""
399
+ return self.rows[self.header_index + 1:]
400
+
401
+ def column(self, title):
402
+ """The index of the column ``title``."""
403
+ return self.header().index(title)
404
+
405
+ def line_dict(self, n):
406
+ """The line ``n`` by header."""
407
+ return dict(zip(self.header(), self.line(n)))
408
+
409
+ def dicts(self):
410
+ """The rows after the headers, by header."""
411
+ return [dict(zip(self.header(), row)) for row in self.lines()]
412
+
413
+
414
+ class CSVFileHelper(TableFileResponseHelper):
415
+ """A CSV file of a test; the values are strings."""
416
+
417
+ def __init__(self, data, header=1, delimiter=',', encoding='utf-8-sig'):
418
+ self.delimiter = delimiter
419
+ self.encoding = encoding
420
+ super().__init__(data, header)
421
+
422
+ def read_rows(self):
423
+ return list(csv.reader(
424
+ io.StringIO(self.content.decode(self.encoding)),
425
+ delimiter=self.delimiter))
426
+
427
+
428
+ class XLSXFileHelper(TableFileResponseHelper):
429
+ """An Excel file of a test, read with openpyxl.
430
+
431
+ ``sheet`` is the name or the index of the sheet read, the active one
432
+ by default. The values keep their type (numbers, dates); ``workbook``
433
+ and ``sheet`` give the formats and the styles.
434
+ """
435
+
436
+ def __init__(self, data, header=1, sheet=None):
437
+ self.sheet_name = sheet
438
+ super().__init__(data, header)
439
+
440
+ def read_rows(self):
441
+ try:
442
+ import openpyxl
443
+ except ImportError: # pragma: no cover
444
+ raise ImportError(
445
+ "XLSXFileHelper needs openpyxl: install django-pfx[xlsx].")
446
+
447
+ self.workbook = openpyxl.load_workbook(io.BytesIO(self.content))
448
+ if self.sheet_name is None:
449
+ self.sheet = self.workbook.active
450
+ elif isinstance(self.sheet_name, int):
451
+ self.sheet = self.workbook.worksheets[self.sheet_name]
452
+ else:
453
+ self.sheet = self.workbook[self.sheet_name]
454
+ return [list(row) for row in self.sheet.iter_rows(values_only=True)]
455
+
456
+
330
457
  class MockBoto3Client:
331
458
  def generate_presigned_url(self, *args, **kwargs):
332
459
  params = kwargs.get('Params', {})
@@ -33,3 +33,4 @@ munch
33
33
  # bypasses the error handling of pfx.emails.models.Email.send.
34
34
  mailjet_rest<1.8
35
35
  responses
36
+ openpyxl
@@ -46,6 +46,7 @@ install_requires =
46
46
  [options.extras_require]
47
47
  otp = pyotp>=2.10.0
48
48
  heic-converter = python-magic; pillow-heif
49
+ xlsx = openpyxl
49
50
 
50
51
  [options.packages.find]
51
52
  exclude =
@@ -0,0 +1,144 @@
1
+ import io
2
+ import os
3
+ import tempfile
4
+ from contextlib import redirect_stdout
5
+ from datetime import date
6
+ from pathlib import Path
7
+
8
+ from django.http import HttpResponse, StreamingHttpResponse
9
+ from django.test import SimpleTestCase, override_settings
10
+
11
+ from openpyxl import Workbook
12
+
13
+ from pfx.pfxcore.test import CSVFileHelper, XLSXFileHelper
14
+
15
+ CSV = "Name,Age\nAlice,30\nBob,\n"
16
+
17
+
18
+ def xlsx(*sheets):
19
+ """An Excel file of `sheets`, (title, rows) pairs."""
20
+ workbook = Workbook()
21
+ workbook.remove(workbook.active)
22
+ for title, rows in sheets:
23
+ sheet = workbook.create_sheet(title)
24
+ for row in rows:
25
+ sheet.append(row)
26
+ output = io.BytesIO()
27
+ workbook.save(output)
28
+ return output.getvalue()
29
+
30
+
31
+ PEOPLE = [['Name', 'Birth'], ['Alice', date(1990, 5, 1)], ['Bob', None]]
32
+
33
+
34
+ class TestCSVFileHelper(SimpleTestCase):
35
+ def test_the_sources_of_a_file(self):
36
+ """Bytes, a string, a response, a streamed one, chunks of text."""
37
+ for data in [
38
+ CSV.encode(), CSV, HttpResponse(CSV),
39
+ StreamingHttpResponse(iter([CSV[:10], CSV[10:]])),
40
+ [CSV[:10].encode(), CSV[10:].encode()]]:
41
+ with self.subTest(data=type(data).__name__):
42
+ f = CSVFileHelper(data)
43
+ self.assertEqual(f.header(), ['Name', 'Age'])
44
+ self.assertEqual(f.len(), 3)
45
+
46
+ def test_lines(self):
47
+ """`line` counts from the headers, `lines` and `dicts` are the data;
48
+ a CSV value is a string."""
49
+ f = CSVFileHelper(CSV)
50
+
51
+ self.assertEqual(f.line(1), ['Alice', '30'])
52
+ self.assertEqual(f.lines(), [['Alice', '30'], ['Bob', '']])
53
+ self.assertEqual(f.column('Age'), 1)
54
+ self.assertEqual(f.line_dict(2), {'Name': 'Bob', 'Age': ''})
55
+ self.assertEqual(
56
+ f.dicts(),
57
+ [{'Name': 'Alice', 'Age': '30'}, {'Name': 'Bob', 'Age': ''}])
58
+
59
+ def test_a_french_excel_csv(self):
60
+ """Semicolons, and the byte order mark Excel writes."""
61
+ f = CSVFileHelper(
62
+ "Nom;Âge\nAlice;30\n".encode(), delimiter=';')
63
+
64
+ self.assertEqual(f.dicts(), [{'Nom': 'Alice', 'Âge': '30'}])
65
+
66
+ def test_headers_below_a_title(self):
67
+ f = CSVFileHelper("Report\nName,Age\nAlice,30\n", header=2)
68
+
69
+ self.assertEqual(f.header(), ['Name', 'Age'])
70
+ self.assertEqual(f.dicts(), [{'Name': 'Alice', 'Age': '30'}])
71
+
72
+ def test_unknown_column(self):
73
+ with self.assertRaises(ValueError):
74
+ CSVFileHelper(CSV).column('Size')
75
+
76
+ def test_a_path(self):
77
+ """A Path is a file to read; a string is the content itself."""
78
+ with tempfile.TemporaryDirectory() as tmp:
79
+ path = Path(tmp, 'file.csv')
80
+ path.write_text(CSV)
81
+
82
+ self.assertEqual(CSVFileHelper(path).len(), 3)
83
+
84
+ def test_write_file_and_pretty_print(self):
85
+ f = CSVFileHelper(CSV)
86
+ with tempfile.TemporaryDirectory() as tmp:
87
+ path = os.path.join(tmp, 'file.csv')
88
+ f.write_file(path)
89
+ with open(path, 'rb') as written:
90
+ self.assertEqual(written.read(), CSV.encode())
91
+
92
+ out = io.StringIO()
93
+ with redirect_stdout(out):
94
+ f.pretty_print()
95
+ self.assertEqual(
96
+ out.getvalue(), "\nName Age\nAlice 30\nBob\n")
97
+
98
+
99
+ class TestXLSXFileHelper(SimpleTestCase):
100
+ def test_values_keep_their_type(self):
101
+ """A date is a datetime, an empty cell None."""
102
+ f = XLSXFileHelper(xlsx(('People', PEOPLE)))
103
+
104
+ self.assertEqual(f.header(), ['Name', 'Birth'])
105
+ self.assertEqual(f.line_dict(1)['Birth'].date(), date(1990, 5, 1))
106
+ self.assertEqual(f.dicts()[1], {'Name': 'Bob', 'Birth': None})
107
+ self.assertEqual(f.len(), 3)
108
+ self.assertEqual(f.column('Birth'), 1)
109
+
110
+ def test_the_sheet_read(self):
111
+ """The active one by default, else by name or by index; the sheet
112
+ is there for the formats and the styles."""
113
+ content = xlsx(('People', PEOPLE), ('Totals', [['Total'], [2]]))
114
+
115
+ self.assertEqual(XLSXFileHelper(content).header(), ['Name', 'Birth'])
116
+ f = XLSXFileHelper(content, sheet='Totals')
117
+ self.assertEqual(f.dicts(), [{'Total': 2}])
118
+ self.assertEqual(f.sheet.title, 'Totals')
119
+ self.assertEqual(
120
+ XLSXFileHelper(content, sheet=1).header(), ['Total'])
121
+
122
+ def test_a_response(self):
123
+ f = XLSXFileHelper(StreamingHttpResponse(
124
+ iter([xlsx(('People', PEOPLE))])))
125
+
126
+ self.assertEqual(f.lines()[0][0], 'Alice')
127
+
128
+ def test_headers_below_a_title(self):
129
+ f = XLSXFileHelper(
130
+ xlsx(('Report', [['Report 2026'], *PEOPLE])), header=2)
131
+
132
+ self.assertEqual(f.header(), ['Name', 'Birth'])
133
+ self.assertEqual(len(f.dicts()), 2)
134
+
135
+ def test_a_file_of_the_local_storage(self):
136
+ """The value of a MediaField, such as the attachment of a job."""
137
+ with tempfile.TemporaryDirectory() as tmp:
138
+ with open(os.path.join(tmp, 'export.xlsx'), 'wb') as f:
139
+ f.write(xlsx(('People', PEOPLE)))
140
+ with override_settings(STORAGE_LOCAL_ROOT=tmp):
141
+ helper = XLSXFileHelper(
142
+ {'key': 'export.xlsx', 'name': 'export.xlsx'})
143
+
144
+ self.assertEqual(helper.header(), ['Name', 'Birth'])
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "1.7.5",
3
- "distance": 37,
4
- "node": "g213d2ffb86d53a7b1417f5fa4929daa928146735",
5
- "dirty": false,
6
- "branch": "HEAD",
7
- "node_date": "2026-10-20"
8
- }