django-spire 0.16.11__py3-none-any.whl → 0.16.12__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.
django_spire/consts.py CHANGED
@@ -1,4 +1,4 @@
1
- __VERSION__ = '0.16.11'
1
+ __VERSION__ = '0.16.12'
2
2
 
3
3
 
4
4
  AI_CHAT_WORKFLOW_SENDER_SETTINGS_NAME = 'AI_CHAT_WORKFLOW_NAME'
django_spire/file/apps.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from django.apps import AppConfig
2
+ from django.conf import settings
2
3
 
3
4
  from django_spire.utils import check_required_apps
4
5
 
@@ -13,4 +14,16 @@ class FileConfig(AppConfig):
13
14
  URLPATTERNS_NAMESPACE = 'file'
14
15
 
15
16
  def ready(self) -> None:
17
+ if not hasattr(settings, 'BASE_FOLDER_NAME'):
18
+ raise ValueError(
19
+ f'"BASE_FOLDER_NAME" must be set in the django settings when '
20
+ f'using "{self.label}".'
21
+ )
22
+
23
+ elif not isinstance(getattr(settings, 'BASE_FOLDER_NAME'), str):
24
+ raise ValueError(
25
+ f'"BASE_FOLDER_NAME" must be a string in the django settings when '
26
+ f'using "{self.label}".'
27
+ )
28
+
16
29
  check_required_apps(self.label)
@@ -2,6 +2,9 @@ from __future__ import annotations
2
2
 
3
3
  from abc import ABC, abstractmethod
4
4
  from dataclasses import dataclass
5
+
6
+ from django.conf import settings
7
+ from django.core.files.base import ContentFile
5
8
  from typing_extensions import TYPE_CHECKING
6
9
 
7
10
  from django.contrib.contenttypes.models import ContentType
@@ -35,16 +38,27 @@ class FileFormatter:
35
38
 
36
39
  @property
37
40
  def location(self) -> str:
38
- return'django-spire/' + str(self.related_field) + '/' + '/' + str(self.app_name) + '/' + random_64_char_token() + '/' + self.name
41
+ location = settings.BASE_FOLDER_NAME + '/'
42
+ location += str(self.app_name) + '/'
43
+
44
+ if self.related_field is not None:
45
+ location += str(self.related_field) + '/'
46
+
47
+ location += random_64_char_token() + '/'
48
+ location += self.name
49
+
50
+ return location
39
51
 
40
52
  def null_file_obj(self) -> File:
41
- return File(
42
- file=self.file,
53
+ file_obj = File(
43
54
  name=self.name,
44
55
  size=self.size_verbose(),
45
56
  type=self.type,
46
- related_field=self.related_field
57
+ related_field=self.related_field,
47
58
  )
59
+ self.save_file(file_obj)
60
+
61
+ return file_obj
48
62
 
49
63
  def size_verbose(self) -> str:
50
64
  if self.file.size < 512000:
@@ -62,6 +76,10 @@ class FileFormatter:
62
76
 
63
77
  return str(value) + ext
64
78
 
79
+ def save_file(self, file_obj: File):
80
+ file_path = self.location + '.' + self.type
81
+ file_obj.file.save(file_path, ContentFile(self.file.read()), save=False)
82
+
65
83
 
66
84
  @dataclass
67
85
  class FileContentObjectFormatter(FileFormatter):
@@ -69,26 +87,41 @@ class FileContentObjectFormatter(FileFormatter):
69
87
 
70
88
  @property
71
89
  def location(self) -> str:
72
- return 'django-spire/' + '/' + str(self.content_object._meta.app_label) + '/' + random_64_char_token() + '/' + self.name
90
+ location = settings.BASE_FOLDER_NAME + '/'
91
+ location += str(self.content_object._meta.app_label) + '/'
92
+
93
+ if self.related_field is not None:
94
+ location += str(self.related_field) + '/'
95
+
96
+ location += random_64_char_token() + '/'
97
+ location += self.name
98
+ return location
73
99
 
74
100
  def null_file_obj(self) -> File:
75
- return File(
101
+ file_obj = File(
76
102
  content_type=ContentType.objects.get_for_model(self.content_object),
77
103
  object_id=self.content_object.id,
78
- file=self.file,
79
104
  name=self.name,
80
105
  size=self.size_verbose(),
81
106
  type=self.type,
82
- related_field=self.related_field
107
+ related_field=self.related_field,
83
108
  )
109
+ self.save_file(file_obj)
110
+
111
+ return file_obj
84
112
 
85
113
 
86
114
  @dataclass
87
115
  class FileUploader(ABC):
88
116
  related_field: str | None
117
+ app_name: str = 'Uncategorized'
89
118
 
90
119
  def null_file_obj(self, file):
91
- formatted_file = FileFormatter(file, self.related_field)
120
+ formatted_file = FileFormatter(
121
+ file=file,
122
+ related_field=self.related_field,
123
+ app_name=self.app_name
124
+ )
92
125
  return formatted_file.null_file_obj()
93
126
 
94
127
  @abstractmethod
@@ -35,11 +35,9 @@ class EntryAutomationService(BaseDjangoModelService['Entry']):
35
35
  )
36
36
  except Exception as e:
37
37
  errored.append({'file': file_object.name, 'error': str(e)})
38
- file_object.file.delete()
39
- file_object.delete()
38
+ file_object.set_deleted()
40
39
  else:
41
- file_object.file.delete()
42
- file_object.delete()
40
+ file_object.set_deleted()
43
41
 
44
42
  message = f'Files Converted: {len(file_objects) - len(errored)}'
45
43
  if errored:
@@ -7,7 +7,6 @@ from django.contrib.contenttypes.models import ContentType
7
7
  from django_spire.auth.user.models import AuthUser
8
8
  from django_spire.contrib.service import BaseDjangoModelService
9
9
  from django_spire.file.models import File
10
- from django_spire.knowledge.entry.tests.constants import ENTRY_IMPORT_RELATED_FIELD
11
10
 
12
11
  if TYPE_CHECKING:
13
12
  from django_spire.knowledge.entry.models import Entry
@@ -34,7 +33,7 @@ class EntryFactoryService(BaseDjangoModelService['Entry']):
34
33
 
35
34
  file.content_type = ContentType.objects.get_for_model(entry.__class__)
36
35
  file.object_id = entry.id
37
- file.related_field = ENTRY_IMPORT_RELATED_FIELD
36
+ file.related_field = None
38
37
 
39
38
  entry.ordering_services.processor.move_to_position(
40
39
  destination_objects=collection.entries.active(),
@@ -7,7 +7,6 @@ from django.contrib.contenttypes.models import ContentType
7
7
 
8
8
  from django_spire.contrib.service import BaseDjangoModelService
9
9
  from django_spire.file.models import File
10
- from django_spire.knowledge.entry.tests.constants import ENTRY_IMPORT_RELATED_FIELD
11
10
 
12
11
  if TYPE_CHECKING:
13
12
  from django.db.models import QuerySet
@@ -19,7 +18,7 @@ class EntryToolService(BaseDjangoModelService['Entry']):
19
18
 
20
19
  def get_files_to_convert(self) -> QuerySet[File]:
21
20
  return (
22
- File.objects.related_field(field_name=ENTRY_IMPORT_RELATED_FIELD)
21
+ File.objects
23
22
  .filter(content_type=ContentType.objects.get_for_model(self.obj_class))
24
23
  .active()
25
24
  .order_by('object_id')
@@ -29,7 +29,7 @@ class DocxConverter(BaseConverter):
29
29
 
30
30
  def convert_file_to_blocks(self, file: File) -> list[models.EntryVersionBlock]:
31
31
  markitdown = MarkItDown()
32
- markdown_result = markitdown.convert(file.file.path)
32
+ markdown_result = markitdown.convert(file.file.url)
33
33
  markdown_content = markdown_result.markdown
34
34
 
35
35
  markdown_converter = MarkdownConverter(entry_version=self.entry_version)
@@ -7,6 +7,7 @@ import marko
7
7
 
8
8
  from typing import TYPE_CHECKING
9
9
 
10
+ from django.core.files.storage import default_storage
10
11
  from marko.element import Element
11
12
  from marko.block import Heading, List, ListItem
12
13
 
@@ -31,7 +32,7 @@ class MarkdownConverter(BaseConverter):
31
32
  self._order = 0
32
33
 
33
34
  def convert_file_to_blocks(self, file: File) -> list[models.EntryVersionBlock]:
34
- with open(file.file.path, 'r') as f:
35
+ with default_storage.open(file.file.name, 'r') as f:
35
36
  return self.convert_markdown_to_blocks(f.read())
36
37
 
37
38
  def _convert_heading_block(
@@ -84,9 +84,11 @@ def import_form_view(
84
84
  file_form = EntryFilesForm(request.POST, request.FILES)
85
85
 
86
86
  if file_form.is_valid():
87
- file_objects = MultiFileUploader(related_field=None).upload(
88
- request.FILES.getlist('import_files')
87
+ file_uploader = MultiFileUploader(
88
+ related_field=None,
89
+ app_name='knowledge'
89
90
  )
91
+ file_objects = file_uploader.upload(request.FILES.getlist('import_files'))
90
92
 
91
93
  _ = Entry.services.factory.create_from_files(
92
94
  author=request.user,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-spire
3
- Version: 0.16.11
3
+ Version: 0.16.12
4
4
  Summary: A project for Django Spire
5
5
  Author-email: Brayden Carlson <braydenc@stratusadv.com>, Nathan Johnson <nathanj@stratusadv.com>
6
6
  License: Copyright (c) 2024 Stratus Advanced Technologies and Contributors.
@@ -45,12 +45,15 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
45
45
  Requires-Python: >=3.11
46
46
  Description-Content-Type: text/markdown
47
47
  License-File: LICENSE.md
48
+ Requires-Dist: boto3==1.24.18
49
+ Requires-Dist: botocore==1.27.18
48
50
  Requires-Dist: crispy-bootstrap5==2024.10
49
51
  Requires-Dist: dandy==0.20.0
50
52
  Requires-Dist: django>=5.1.8
51
53
  Requires-Dist: django-crispy-forms==2.3
52
54
  Requires-Dist: django-glue>=0.8.1
53
55
  Requires-Dist: django-sendgrid-v5
56
+ Requires-Dist: django-storages==1.14.5
54
57
  Requires-Dist: docutils
55
58
  Requires-Dist: faker
56
59
  Requires-Dist: marko==2.1.4
@@ -60,6 +63,7 @@ Requires-Dist: pydantic
60
63
  Requires-Dist: Pygments
61
64
  Requires-Dist: Pillow==11.2.1
62
65
  Requires-Dist: python-dateutil
66
+ Requires-Dist: robit==0.4.8
63
67
  Requires-Dist: typing_extensions
64
68
  Requires-Dist: twilio
65
69
  Provides-Extra: development
@@ -1,6 +1,6 @@
1
1
  django_spire/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  django_spire/conf.py,sha256=EYC1hXqYqheYrb_b6Q93LrSgNl91JV633E4N5j-KGTo,1012
3
- django_spire/consts.py,sha256=450A_OavSGuOlbl0yDxCchhMQh83uHnPXGnw9DVihPc,391
3
+ django_spire/consts.py,sha256=-9nkgFl6ok0FLu5IQ1hiAmlRr5UX4PCizQh3QqImeWM,391
4
4
  django_spire/exceptions.py,sha256=L5ndRO5ftMmh0pHkO2z_NG3LSGZviJ-dDHNT73SzTNw,48
5
5
  django_spire/settings.py,sha256=tGxgEri3TQRBaiwX7YRcWifMf_g1xv1RznplFR6zZnI,821
6
6
  django_spire/urls.py,sha256=mKeZszb5U4iIGqddMb5Tt5fRC72U2wABEOi6mvOfEBU,656
@@ -607,10 +607,10 @@ django_spire/core/tests/tests_redirect.py,sha256=z-E1ffePbpcq4FwmJjMYaVso4RSV-Q0
607
607
  django_spire/core/tests/tests_shortcuts.py,sha256=4yJx_n6uFv99dOUNMfw4H91bFhS2cJ46vItnvSnW7WE,2634
608
608
  django_spire/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
609
609
  django_spire/file/admin.py,sha256=3zTkTiDt9NjLGLW-mZxsPjdnqD5mk4fkjdcIu3VkP_c,1034
610
- django_spire/file/apps.py,sha256=cSBOGyxYWRQg2lE49JLewgES0vwOBtweESEKMAmvx1U,437
610
+ django_spire/file/apps.py,sha256=AGDhviujlmpQLYjNdF6UKnHYjo7wsYVCQUR5gZ8DAS4,932
611
611
  django_spire/file/fields.py,sha256=olFpAdeU0Hs0ZfrxE1q9VcOhsfHgRTGCZSYe3xPWEuM,1207
612
612
  django_spire/file/forms.py,sha256=pEAW1y2bCzYgS_RJaJGTcePjT0Ip1ayNJh1_dTM0HC4,657
613
- django_spire/file/interfaces.py,sha256=hqWtuFP6Taoon0AAkToFL_LYNDjzIo1F9lBqBD5jonY,5485
613
+ django_spire/file/interfaces.py,sha256=i4T3-d8f-5Dfi4-exWAofNNZui9FXbyizQ0OQsbcXY4,6288
614
614
  django_spire/file/mixins.py,sha256=jSRljAO61EEVs74L0Ap74zfnE6Ful4Gsw7pe4wu7HGU,618
615
615
  django_spire/file/models.py,sha256=FZCuThGV6cjKRvXWaDfxwzZTopVvGw51eEarcLHmiJ8,1506
616
616
  django_spire/file/queryset.py,sha256=RNIVCFd-Z0sQCz2UfHfvR-fYOhoztVduf9WO-NlhiKM,289
@@ -755,14 +755,13 @@ django_spire/knowledge/entry/seeding/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeu
755
755
  django_spire/knowledge/entry/seeding/seed.py,sha256=n7ereq118GuqV1luNih7nuVpkrZpkBgPWt0FuE4Bl88,105
756
756
  django_spire/knowledge/entry/seeding/seeder.py,sha256=m5X9iqa9UnbtvmjFdtPsx2dcFA1Q0se4ZF2nps6oaNE,1955
757
757
  django_spire/knowledge/entry/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
758
- django_spire/knowledge/entry/services/automation_service.py,sha256=JB3hy8yT1fFeJeidr4NwGp-fua10TpLOJXPWJD48GAk,1759
759
- django_spire/knowledge/entry/services/factory_service.py,sha256=dpBZRKFtkMtLpzWuZDq7lcxJaOlQAk0zf3pjHKVaStg,1485
758
+ django_spire/knowledge/entry/services/automation_service.py,sha256=-WnEuexvcugK_MUKehN_CpYPtQIiumr8YStj2aygDNw,1685
759
+ django_spire/knowledge/entry/services/factory_service.py,sha256=bgUEHjdc9u2mxUJFy26qYOxUiJRHmhtcoH6AG3xzU0I,1379
760
760
  django_spire/knowledge/entry/services/processor_service.py,sha256=sfAM5ku7Td4k0jCe2szyoR72Bt1_OS75UKh7YPL4l2s,494
761
761
  django_spire/knowledge/entry/services/service.py,sha256=n8N2pshbE977bNoN5zf0gO-Kyy9LQLcGOJ4MMNT8Uj8,1806
762
- django_spire/knowledge/entry/services/tool_service.py,sha256=xZtYk5vL51OPMnmPPpRX3ph_rzpnjdLXJuSQQRTaQo8,1219
762
+ django_spire/knowledge/entry/services/tool_service.py,sha256=gByjiA5O5ts9_OA2YOANZnJzFVdZAE3UQSpwfGKGlZA,1082
763
763
  django_spire/knowledge/entry/services/transformation_services.py,sha256=bP2es0NvzLcJD4oVdJ32oTyXfae_yuAzXelvpmpD72o,2684
764
764
  django_spire/knowledge/entry/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
765
- django_spire/knowledge/entry/tests/constants.py,sha256=MCQ4WtDrpMzD0TzLoMM-n0JbdY5Ay1kKWjPOabbTWpM,35
766
765
  django_spire/knowledge/entry/tests/factories.py,sha256=KvsHVDycuP6mSITgxDpPmThuleknzNWGePPFsUuw_jQ,411
767
766
  django_spire/knowledge/entry/tests/test_urls/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
768
767
  django_spire/knowledge/entry/tests/test_urls/test_form_urls.py,sha256=aFkfwBW35VbNrnKz1CF8Fdjb4iZc8sBt6q7Krngzjfs,1720
@@ -810,8 +809,8 @@ django_spire/knowledge/entry/version/block/views/__init__.py,sha256=47DEQpj8HBSa
810
809
  django_spire/knowledge/entry/version/block/views/json_views.py,sha256=UlSFzhBuUbt9vY73smHgLPHIyDqKUqwe6uDCG2Mj3vA,960
811
810
  django_spire/knowledge/entry/version/converters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
812
811
  django_spire/knowledge/entry/version/converters/converter.py,sha256=0zbM7FTCpXe_O8gfsVc4V0Pg4cks7HMZi4SXJQ_HIEw,584
813
- django_spire/knowledge/entry/version/converters/docx_converter.py,sha256=SkJriO39mo5vCJ3f8yGNTUqKU-JUStprMZCUT--rydg,2625
814
- django_spire/knowledge/entry/version/converters/markdown_converter.py,sha256=-YDTo1AnE1eVoqXfGHZbj-vq2xUB3lxGj0XYDAVSa4U,4920
812
+ django_spire/knowledge/entry/version/converters/docx_converter.py,sha256=zMPYoro4H3l993vSUoDSOjYubWlEfPJ0hwlM8Uw9W9k,2624
813
+ django_spire/knowledge/entry/version/converters/markdown_converter.py,sha256=MsSqdwNBQkk3pV086pKrXJ7al7-FrQRQD75WXbVB2_0,4990
815
814
  django_spire/knowledge/entry/version/intelligence/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
816
815
  django_spire/knowledge/entry/version/intelligence/bots/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
817
816
  django_spire/knowledge/entry/version/intelligence/bots/markdown_format_llm_bot.py,sha256=OqKE_k7SvRuwsYSO-71kYBgIjgQ4XImpZrVxFNL0p2o,526
@@ -839,7 +838,7 @@ django_spire/knowledge/entry/version/views/json_views.py,sha256=bSM0SA7E4sM5N6pi
839
838
  django_spire/knowledge/entry/version/views/page_views.py,sha256=QtwI2CM0dcSkKApXeplggHn7vEKCIqsHt5r3gmz5zVY,1588
840
839
  django_spire/knowledge/entry/version/views/redirect_views.py,sha256=VCtgwXIzzpYx7vufDQB4G04cDoX_ceH_KL-yZ7WxGSg,718
841
840
  django_spire/knowledge/entry/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
842
- django_spire/knowledge/entry/views/form_views.py,sha256=X0S8tQsONjuK46Y4F-a0blB1KydoJ34q8kIzJdQxmo8,4461
841
+ django_spire/knowledge/entry/views/form_views.py,sha256=ovbA9O6gqXs25CQZ9Tl34E1jrFAzlc0pRNRzwjOH1l4,4541
843
842
  django_spire/knowledge/entry/views/json_views.py,sha256=UqfKcJZ908y2UmKBBF0PoQj1-a6liQBbEt4RD7dwEeI,1761
844
843
  django_spire/knowledge/entry/views/page_views.py,sha256=fsS4G58o1ZsFG2JlGgjUYhWAMY22C3FjmWCXh5JFZXs,914
845
844
  django_spire/knowledge/entry/views/template_views.py,sha256=U3pqndOpPQlC2zJBKcOIM4IUXK870FH_Wpna5imIkm0,1782
@@ -1044,8 +1043,8 @@ django_spire/theme/urls/page_urls.py,sha256=S8nkKkgbhG3XHI3uMUL-piOjXIrRkuY2UlM_
1044
1043
  django_spire/theme/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1045
1044
  django_spire/theme/views/json_views.py,sha256=W1khC2K_EMbEzAFmMxC_P76_MFnkRH4-eVdodrRfAhw,1904
1046
1045
  django_spire/theme/views/page_views.py,sha256=pHr8iekjtR99xs7w1taj35HEo133Svq1dvDD0y0VL1c,3933
1047
- django_spire-0.16.11.dist-info/licenses/LICENSE.md,sha256=tlTbOtgKoy-xAQpUk9gPeh9O4oRXCOzoWdW3jJz0wnA,1091
1048
- django_spire-0.16.11.dist-info/METADATA,sha256=suvksWwEC57dQN6HWtC_reQ63ghDIFSeZ4uKhRIR7FI,4763
1049
- django_spire-0.16.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1050
- django_spire-0.16.11.dist-info/top_level.txt,sha256=xf3QV1e--ONkVpgMDQE9iqjQ1Vg4--_6C8wmO-KxPHQ,13
1051
- django_spire-0.16.11.dist-info/RECORD,,
1046
+ django_spire-0.16.12.dist-info/licenses/LICENSE.md,sha256=tlTbOtgKoy-xAQpUk9gPeh9O4oRXCOzoWdW3jJz0wnA,1091
1047
+ django_spire-0.16.12.dist-info/METADATA,sha256=l5QhgE2CQGmwABLwBjsDPapXgI8Wpe-KsGW6gZ1gyHg,4893
1048
+ django_spire-0.16.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1049
+ django_spire-0.16.12.dist-info/top_level.txt,sha256=xf3QV1e--ONkVpgMDQE9iqjQ1Vg4--_6C8wmO-KxPHQ,13
1050
+ django_spire-0.16.12.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- ENTRY_IMPORT_RELATED_FIELD = 'kbi'