endoreg-db 0.3.0__py3-none-any.whl → 0.3.2__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 (46) hide show
  1. endoreg_db/data/__init__.py +3 -1
  2. endoreg_db/data/center/data.yaml +45 -0
  3. endoreg_db/data/pdf_type/data.yaml +28 -0
  4. endoreg_db/data/report_reader_flag/ukw-examination-generic.yaml +26 -0
  5. endoreg_db/data/report_reader_flag/ukw-histology-generic.yaml +19 -0
  6. endoreg_db/management/commands/load_base_db_data.py +12 -3
  7. endoreg_db/management/commands/load_center_data.py +3 -3
  8. endoreg_db/management/commands/load_pdf_type_data.py +61 -0
  9. endoreg_db/management/commands/load_report_reader_flag.py +46 -0
  10. endoreg_db/management/commands/reset_celery_schedule.py +9 -0
  11. endoreg_db/migrations/0013_rawpdffile.py +31 -0
  12. endoreg_db/migrations/0014_pdftype_alter_rawpdffile_file_pdfmeta.py +38 -0
  13. endoreg_db/migrations/0015_rename_report_processed_rawpdffile_state_report_processed_and_more.py +31 -0
  14. endoreg_db/migrations/0016_rawpdffile_state_report_processing_required.py +18 -0
  15. endoreg_db/migrations/0017_firstname_lastname_center_first_names_and_more.py +37 -0
  16. endoreg_db/migrations/0018_reportreaderflag_reportreaderconfig.py +37 -0
  17. endoreg_db/migrations/0019_pdftype_cut_off_above_lines_and_more.py +42 -0
  18. endoreg_db/migrations/0020_rename_endoscopy_info_line_pdftype_endoscope_info_line.py +18 -0
  19. endoreg_db/migrations/0021_alter_pdftype_endoscope_info_line.py +19 -0
  20. endoreg_db/migrations/0022_alter_pdftype_endoscope_info_line.py +19 -0
  21. endoreg_db/models/__init__.py +2 -0
  22. endoreg_db/models/center.py +6 -0
  23. endoreg_db/models/data_file/__init__.py +2 -3
  24. endoreg_db/models/data_file/import_classes/__init__.py +1 -0
  25. endoreg_db/models/data_file/import_classes/processing_functions/__init__.py +35 -0
  26. endoreg_db/models/data_file/import_classes/processing_functions/pdf.py +28 -0
  27. endoreg_db/models/data_file/import_classes/{processing_functions.py → processing_functions/video.py} +12 -21
  28. endoreg_db/models/data_file/import_classes/raw_pdf.py +185 -0
  29. endoreg_db/models/data_file/import_classes/raw_video.py +8 -6
  30. endoreg_db/models/data_file/metadata/__init__.py +2 -132
  31. endoreg_db/models/data_file/metadata/pdf_meta.py +70 -0
  32. endoreg_db/models/data_file/metadata/sensitive_meta.py +19 -1
  33. endoreg_db/models/data_file/metadata/video_meta.py +132 -0
  34. endoreg_db/models/data_file/report_file.py +1 -0
  35. endoreg_db/models/persons/__init__.py +3 -1
  36. endoreg_db/models/persons/first_name.py +18 -0
  37. endoreg_db/models/persons/last_name.py +20 -0
  38. endoreg_db/models/report_reader/__init__.py +2 -0
  39. endoreg_db/models/report_reader/report_reader_config.py +53 -0
  40. endoreg_db/models/report_reader/report_reader_flag.py +20 -0
  41. endoreg_db/utils/dataloader.py +172 -56
  42. endoreg_db/utils/hashs.py +18 -0
  43. {endoreg_db-0.3.0.dist-info → endoreg_db-0.3.2.dist-info}/METADATA +2 -1
  44. {endoreg_db-0.3.0.dist-info → endoreg_db-0.3.2.dist-info}/RECORD +46 -20
  45. {endoreg_db-0.3.0.dist-info → endoreg_db-0.3.2.dist-info}/LICENSE +0 -0
  46. {endoreg_db-0.3.0.dist-info → endoreg_db-0.3.2.dist-info}/WHEEL +0 -0
@@ -0,0 +1,132 @@
1
+ from django.db import models
2
+ import ffmpeg
3
+ from pathlib import Path
4
+
5
+ # import endoreg_center_id from django settings
6
+ from django.conf import settings
7
+
8
+ # check if endoreg_center_id is set
9
+ if not hasattr(settings, 'ENDOREG_CENTER_ID'):
10
+ ENDOREG_CENTER_ID = 9999
11
+ else:
12
+ ENDOREG_CENTER_ID = settings.ENDOREG_CENTER_ID
13
+
14
+ # VideoMeta
15
+ class VideoMeta(models.Model):
16
+ processor = models.ForeignKey('EndoscopyProcessor', on_delete=models.CASCADE, blank=True, null=True)
17
+ endoscope = models.ForeignKey('Endoscope', on_delete=models.CASCADE, blank=True, null=True)
18
+ center = models.ForeignKey('Center', on_delete=models.CASCADE)
19
+ import_meta = models.OneToOneField('VideoImportMeta', on_delete=models.CASCADE, blank=True, null=True)
20
+ ffmpeg_meta = models.OneToOneField('FFMpegMeta', on_delete=models.CASCADE, blank=True, null=True)
21
+
22
+ def __str__(self):
23
+
24
+ processor_name = self.processor.name if self.processor is not None else "None"
25
+ endoscope_name = self.endoscope.name if self.endoscope is not None else "None"
26
+ center_name = self.center.name if self.center is not None else "None"
27
+
28
+ result_html = ""
29
+
30
+ result_html += f"Processor: {processor_name}<br>"
31
+ result_html += f"Endoscope: {endoscope_name}<br>"
32
+ result_html += f"Center: {center_name}<br>"
33
+
34
+ return result_html
35
+
36
+ # import meta should be created when video meta is created
37
+ def save(self, *args, **kwargs):
38
+ if self.import_meta is None:
39
+ self.import_meta = VideoImportMeta.objects.create()
40
+ super(VideoMeta, self).save(*args, **kwargs)
41
+
42
+ def initialize_ffmpeg_meta(self, file_path):
43
+ """Initializes FFMpeg metadata for the video file if not already done."""
44
+ self.ffmpeg_meta = FFMpegMeta.create_from_file(Path(file_path))
45
+ self.save()
46
+
47
+ def update_meta(self, file_path):
48
+ """Updates the video metadata from the file."""
49
+ self.initialize_ffmpeg_meta(file_path)
50
+ self.save()
51
+
52
+ def get_endo_roi(self):
53
+ endo_roi = self.processor.get_roi_endoscope_image()
54
+ return endo_roi
55
+
56
+ def get_fps(self):
57
+ if not self.ffmpeg_meta:
58
+ return None
59
+
60
+ return self.ffmpeg_meta.frame_rate
61
+
62
+
63
+ class FFMpegMeta(models.Model):
64
+ # Existing fields
65
+ duration = models.FloatField(blank=True, null=True)
66
+ width = models.IntegerField(blank=True, null=True)
67
+ height = models.IntegerField(blank=True, null=True)
68
+ frame_rate = models.FloatField(blank=True, null=True)
69
+
70
+ # New fields for comprehensive information
71
+ video_codec = models.CharField(max_length=50, blank=True, null=True)
72
+ audio_codec = models.CharField(max_length=50, blank=True, null=True)
73
+ audio_channels = models.IntegerField(blank=True, null=True)
74
+ audio_sample_rate = models.IntegerField(blank=True, null=True)
75
+
76
+ # Existing __str__ method can be updated to include new fields
77
+
78
+ @classmethod
79
+ def create_from_file(cls, file_path: Path):
80
+ """Creates an FFMpegMeta instance from a video file using ffmpeg probe."""
81
+ try:
82
+ probe = ffmpeg.probe(str(file_path))
83
+ except ffmpeg.Error as e:
84
+ print(e.stderr)
85
+ return None
86
+
87
+ video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)
88
+ audio_streams = [stream for stream in probe['streams'] if stream['codec_type'] == 'audio']
89
+
90
+ # Check for the existence of a video stream
91
+ if video_stream is None:
92
+ print(f"No video stream found in {file_path}")
93
+ return None
94
+
95
+ # Extract and store video metadata
96
+ metadata = {
97
+ 'duration': float(video_stream.get('duration', 0)),
98
+ 'width': int(video_stream.get('width', 0)),
99
+ 'height': int(video_stream.get('height', 0)),
100
+ 'frame_rate': float(next(iter(video_stream.get('avg_frame_rate', '').split('/')), 0)),
101
+ 'video_codec': video_stream.get('codec_name', ''),
102
+ }
103
+
104
+ # If there are audio streams, extract and store audio metadata from the first stream
105
+ if audio_streams:
106
+ first_audio_stream = audio_streams[0]
107
+ metadata.update({
108
+ 'audio_codec': first_audio_stream.get('codec_name', ''),
109
+ 'audio_channels': int(first_audio_stream.get('channels', 0)),
110
+ 'audio_sample_rate': int(first_audio_stream.get('sample_rate', 0)),
111
+ })
112
+
113
+ # Create and return the FFMpegMeta instance
114
+ return cls.objects.create(**metadata)
115
+
116
+ class VideoImportMeta(models.Model):
117
+
118
+ video_anonymized = models.BooleanField(default=False)
119
+ video_patient_data_detected = models.BooleanField(default=False)
120
+ outside_detected = models.BooleanField(default=False)
121
+ patient_data_removed = models.BooleanField(default=False)
122
+ outside_removed = models.BooleanField(default=False)
123
+
124
+ def __str__(self):
125
+ result_html = ""
126
+
127
+ result_html += f"Video anonymized: {self.video_anonymized}<br>"
128
+ result_html += f"Video patient data detected: {self.video_patient_data_detected}<br>"
129
+ result_html += f"Outside detected: {self.outside_detected}<br>"
130
+ result_html += f"Patient data removed: {self.patient_data_removed}<br>"
131
+ result_html += f"Outside removed: {self.outside_removed}<br>"
132
+ return result_html
@@ -16,6 +16,7 @@ class ReportFile(models.Model):
16
16
  time = models.TimeField(blank=True, null=True)
17
17
 
18
18
  def get_pdf_hash(self):
19
+ #FIXME should use endoreg_db.utils.get_pdf_hash in future
19
20
  pdf = self.pdf
20
21
  pdf_hash = None
21
22
 
@@ -1,4 +1,6 @@
1
1
  from .person import Person
2
2
  from .patient import Patient, PatientForm
3
3
  from .examiner import Examiner, ExaminerSerializer
4
- from .portal_user_information import PortalUserInfo, Profession
4
+ from .portal_user_information import PortalUserInfo, Profession
5
+ from .first_name import FirstName
6
+ from .last_name import LastName
@@ -0,0 +1,18 @@
1
+ # class to represent unique first-names
2
+ # name attribute is natural key
3
+
4
+ from django.db import models
5
+
6
+ class FirstNameManager(models.Manager):
7
+ def get_by_natural_key(self, name):
8
+ return self.get(name=name)
9
+
10
+ class FirstName(models.Model):
11
+ objects = FirstNameManager()
12
+ name = models.CharField(max_length=255, unique=True)
13
+
14
+ def natural_key(self):
15
+ return (self.name,)
16
+
17
+ def __str__(self):
18
+ return self.name
@@ -0,0 +1,20 @@
1
+ # class to represent unique last_names
2
+ # name attribute is natural key
3
+ from django.db import models
4
+
5
+ class LastNameManager(models.Manager):
6
+ def get_by_natural_key(self, name):
7
+ return self.get(name=name)
8
+
9
+ class LastName(models.Model):
10
+ objects = LastNameManager()
11
+ name = models.CharField(max_length=255, unique=True)
12
+
13
+ def natural_key(self):
14
+ return (self.name,)
15
+
16
+ def __str__(self):
17
+ return self.name
18
+
19
+ # Path: endoreg_db/models/persons/first_name.py
20
+
@@ -0,0 +1,2 @@
1
+ from .report_reader_config import ReportReaderConfig
2
+ from .report_reader_flag import ReportReaderFlag
@@ -0,0 +1,53 @@
1
+ # ReportReaderConfig Class
2
+ # Description: This class is used to store the configuration of the ReportReader
3
+
4
+ # PATIENT_INFO_LINE_FLAG = "Patient: "
5
+ # ENDOSCOPE_INFO_LINE_FLAG = "Gerät: "
6
+ # EXAMINER_INFO_LINE_FLAG = "1. Unters.:"
7
+ # CUT_OFF_BELOW_LINE_FLAG = "________________"
8
+
9
+
10
+ # CUT_OFF_ABOVE_LINE_FLAGS = [
11
+ # ENDOSCOPE_INFO_LINE_FLAG,
12
+ # EXAMINER_INFO_LINE_FLAG,
13
+ # ]
14
+
15
+ # CUT_OFF_BELOW_LINE_FLAGS = [
16
+ # CUT_OFF_BELOW_LINE_FLAG
17
+ # ]
18
+
19
+ from django.db import models
20
+ from .report_reader_flag import ReportReaderFlag
21
+ from ..center import Center
22
+ from ..data_file import PdfType
23
+
24
+ class ReportReaderConfig(models.Model):
25
+
26
+ locale = models.CharField(default="de_DE", max_length=10)
27
+ first_names = models.ManyToManyField('FirstName', related_name='report_reader_configs')
28
+ last_names = models.ManyToManyField('LastName', related_name='report_reader_configs')
29
+ text_date_format = models.CharField(default = "%d.%m.%Y", max_length=10)
30
+ patient_info_line_flag = models.ForeignKey(ReportReaderFlag, related_name='report_reader_configs_patient_info_line', on_delete=models.CASCADE)
31
+ endoscope_info_line_flag = models.ForeignKey(ReportReaderFlag, related_name='report_reader_configs_endoscope_info_line', on_delete=models.CASCADE)
32
+ examiner_info_line_flag = models.ForeignKey(ReportReaderFlag, related_name='report_reader_configs_examiner_info_line', on_delete=models.CASCADE)
33
+ cut_off_below = models.ManyToManyField(ReportReaderFlag, related_name='report_reader_configs_cut_off_below')
34
+ cut_off_above = models.ManyToManyField(ReportReaderFlag, related_name='report_reader_configs_cut_off_above')
35
+
36
+ def __str__(self):
37
+ return self.locale
38
+
39
+ def update_names_by_center(self, center:Center, save = True):
40
+ self.first_names.set(center.first_names.all())
41
+ self.last_names.set(center.last_names.all())
42
+ if save:
43
+ self.save()
44
+
45
+ def update_flags_by_pdf_type(self, pdf_type:PdfType, save = True):
46
+ self.patient_info_line_flag = pdf_type.patient_info_line_flag
47
+ self.endoscope_info_line_flag = pdf_type.endoscope_info_line_flag
48
+ self.examiner_info_line_flag = pdf_type.examiner_info_line_flag
49
+ self.cut_off_below.set(pdf_type.cut_off_below.all())
50
+ self.cut_off_above.set(pdf_type.cut_off_above.all())
51
+ if save:
52
+ self.save()
53
+
@@ -0,0 +1,20 @@
1
+ # Django model for the report reader flag
2
+ # have name and value
3
+ # name is natural key
4
+
5
+ from django.db import models
6
+
7
+ class ReportReaderFlagManager(models.Manager):
8
+ def get_by_natural_key(self, name):
9
+ return self.get(name=name)
10
+
11
+ class ReportReaderFlag(models.Model):
12
+ objects = ReportReaderFlagManager()
13
+ name = models.CharField(max_length=255, unique=True)
14
+ value = models.CharField(max_length=255)
15
+
16
+ def natural_key(self):
17
+ return (self.name,)
18
+
19
+ def __str__(self):
20
+ return self.name
@@ -1,69 +1,185 @@
1
1
  import os
2
2
  import yaml
3
+ from django.core.exceptions import ObjectDoesNotExist
3
4
 
4
- def load_model_data_from_yaml(
5
- command,
6
- model_name,
7
- metadata,
8
- verbose,
9
- ):
10
-
5
+ def load_model_data_from_yaml(command, model_name, metadata, verbose):
6
+ """
7
+ Load model data from YAML files.
8
+
9
+ Args:
10
+ command: Command object for stdout writing.
11
+ model_name: Name of the model being loaded.
12
+ metadata: Metadata including directory and foreign key information.
13
+ verbose: Boolean indicating whether to print verbose output.
14
+ """
11
15
  if verbose:
12
- command.stdout.write(f"Start Loading {model_name}")
16
+ command.stdout.write(f"Start loading {model_name}")
13
17
  model = metadata["model"]
14
- dir = metadata["dir"]
18
+ dir_path = metadata["dir"]
15
19
  foreign_keys = metadata["foreign_keys"]
16
20
  foreign_key_models = metadata["foreign_key_models"]
17
21
 
18
- for file in [f for f in os.listdir(dir) if f.endswith('.yaml')]:
19
- with open(os.path.join(dir, file), 'r') as f:
20
- yaml_data = yaml.safe_load(f)
22
+ for file in [f for f in os.listdir(dir_path) if f.endswith('.yaml')]:
23
+ with open(os.path.join(dir_path, file), 'r') as file:
24
+ yaml_data = yaml.safe_load(file)
21
25
 
22
- load_data_with_foreign_keys(
23
- command,
24
- model,
25
- yaml_data,
26
- foreign_keys,
27
- foreign_key_models,
28
- verbose
29
- )
26
+ load_data_with_foreign_keys(command, model, yaml_data, foreign_keys, foreign_key_models, verbose)
30
27
 
31
28
  def load_data_with_foreign_keys(command, model, yaml_data, foreign_keys, foreign_key_models, verbose):
32
- # Since pathology types is a ManyToMany field, we need to hack arount
33
- for entry in yaml_data:
34
- fields = entry.get('fields', {})
35
- name = fields.pop('name', None)
36
- many_to_many_tuples = []
37
- foreign_key_tuples = zip(foreign_keys, foreign_key_models)
38
- for foreign_key, foreign_key_model in foreign_key_tuples:
39
- target_natural_key = fields.pop(foreign_key, None)
29
+ """
30
+ Load data handling foreign keys and many-to-many relationships.
31
+
32
+ Args:
33
+ command: Command object for stdout writing.
34
+ model: The Django model for the data.
35
+ yaml_data: Data loaded from YAML.
36
+ foreign_keys: List of foreign keys.
37
+ foreign_key_models: Corresponding models for the foreign keys.
38
+ verbose: Boolean indicating whether to print verbose output.
39
+ """
40
+ for entry in yaml_data:
41
+ fields = entry.get('fields', {})
42
+ name = fields.pop('name', None)
43
+ m2m_relationships = {} # Store many-to-many relationships
44
+
45
+ # Handle foreign keys and many-to-many relationships
46
+ for fk_field, fk_model in zip(foreign_keys, foreign_key_models):
47
+ target_keys = fields.pop(fk_field, None)
48
+
49
+ # Ensure the foreign key exists
50
+ if target_keys is None:
51
+ if verbose:
52
+ command.stdout.write(command.style.WARNING(f"Foreign key {fk_field} not found in fields"))
53
+ continue # Skip if no foreign key provided
54
+
55
+ # Process many-to-many fields or foreign keys
56
+ if isinstance(target_keys, list): # Assume many-to-many relationship
57
+ related_objects = []
58
+ for key in target_keys:
59
+ obj, created = fk_model.objects.get_or_create(name=key)
60
+ if created and verbose:
61
+ command.stdout.write(command.style.SUCCESS(f"Created {fk_model.__name__} {key}"))
62
+ related_objects.append(obj)
63
+ m2m_relationships[fk_field] = related_objects
64
+ else: # Single foreign key relationship
65
+ try:
66
+ obj = fk_model.objects.get_by_natural_key(target_keys)
67
+ except ObjectDoesNotExist:
68
+ if verbose:
69
+ command.stdout.write(command.style.WARNING(f"{fk_model.__name__} with key {target_keys} not found"))
70
+ continue
71
+ fields[fk_field] = obj
72
+
73
+ # Create or update the main object
74
+ obj, created = model.objects.update_or_create(defaults=fields, name=name)
75
+ if created and verbose:
76
+ command.stdout.write(command.style.SUCCESS(f'Created {model.__name__} {name}'))
77
+ elif verbose:
78
+ command.stdout.write(command.style.WARNING(f'Skipped {model.__name__} {name}, already exists'))
79
+
80
+ # Set many-to-many relationships
81
+ for field_name, related_objs in m2m_relationships.items():
82
+ getattr(obj, field_name).set(related_objs)
83
+
84
+
85
+ # def load_model_data_from_yaml(
86
+ # command,
87
+ # model_name,
88
+ # metadata,
89
+ # verbose,
90
+ # ):
91
+
92
+ # if verbose:
93
+ # command.stdout.write(f"Start Loading {model_name}")
94
+ # model = metadata["model"]
95
+ # dir = metadata["dir"]
96
+ # foreign_keys = metadata["foreign_keys"]
97
+ # foreign_key_models = metadata["foreign_key_models"]
98
+
99
+ # for file in [f for f in os.listdir(dir) if f.endswith('.yaml')]:
100
+ # with open(os.path.join(dir, file), 'r') as f:
101
+ # yaml_data = yaml.safe_load(f)
102
+
103
+ # load_data_with_foreign_keys(
104
+ # command,
105
+ # model,
106
+ # yaml_data,
107
+ # foreign_keys,
108
+ # foreign_key_models,
109
+ # verbose
110
+ # )
111
+
112
+ # def load_data_with_foreign_keys(command, model, yaml_data, foreign_keys, foreign_key_models, verbose):
113
+ # # Since pathology types is a ManyToMany field, we need to hack arount
114
+ # for entry in yaml_data:
115
+ # fields = entry.get('fields', {})
116
+ # name = fields.pop('name', None)
117
+ # many_to_many_tuples = []
118
+ # foreign_key_tuples = zip(foreign_keys, foreign_key_models)
119
+ # for foreign_key, foreign_key_model in foreign_key_tuples:
120
+ # # target_natural_key = fields.pop(foreign_key, None)
121
+ # # get the target natural key, if it exists, should not alter fields
122
+ # target_natural_key = fields.get(foreign_key, None)
123
+ # assert target_natural_key, f"Foreign Key {foreign_key} not found in fields {fields}"
40
124
 
125
+ # if (foreign_key == "first_names") or (foreign_key == "last_names"):
126
+ # if isinstance(target_natural_key, list):
127
+ # # For first_names and last_names, the field is a Many to Many field
128
+ # # if names dont exist yet, we create them
129
+ # fk_objects = []
130
+ # for __name in target_natural_key:
131
+ # obj, created = foreign_key_model.objects.get_or_create(name=__name)
132
+ # if created:
133
+ # command.stdout.write(command.style.SUCCESS(f'Created {foreign_key_model.__name__} {__name}'))
134
+ # # fk_objects.append(obj)
135
+
136
+ # fk_tuple = (foreign_key, fk_objects)
137
+ # many_to_many_tuples.append(fk_tuple)
138
+ # continue
139
+
140
+ # if isinstance(target_natural_key, list):
141
+ # # the field is a Many to X field.
142
+ # fk_objects = [foreign_key_model.objects.get_by_natural_key(_) for _ in target_natural_key]
143
+ # fk_tuple = (foreign_key, fk_objects)
144
+ # many_to_many_tuples.append(fk_tuple)
145
+ # continue
146
+ # # Use the natural key to look up the related object
147
+ # try:
148
+ # obj = foreign_key_model.objects.get_by_natural_key(target_natural_key)
149
+ # except:
150
+ # # commandline log that the object was not found
151
+ # command.stdout.write(command.style.WARNING(f'Object {foreign_key_model.__name__} {target_natural_key} not found'))
152
+ # # commandline log entry
153
+ # command.stdout.write(command.style.WARNING(_log))
154
+ # # try to create by name if name is available
155
+ # # create defaults dict from fields using the models fields
156
+ # _field_names = [_.name for _ in foreign_key_model._meta.fields]
157
+ # _defaults = {k: v for k, v in fields.items() if (k in _field_names) and v}
158
+
159
+ # if target_natural_key:
160
+ # # commandlie log
161
+ # command.stdout.write(command.style.SUCCESS(f'Creating {foreign_key_model.__name__} {name} with defaults {_defaults}'))
162
+ # obj, created = model.objects.get_or_create(
163
+ # name = target_natural_key,
164
+ # defaults=_defaults
165
+ # )
166
+ # # Assign the related object to the field
167
+ # fields[foreign_key] = obj
168
+
169
+ # if name:
170
+ # try:
171
+ # obj, created = model.objects.get_or_create(name=name, defaults=fields)
172
+ # except:
173
+ # # commandlinelog to print name, fields, target foreign key
174
+ # command.stdout.write(command.style.WARNING(f'Object {model.__name__} {name} not found'))
175
+ # else:
176
+ # obj, created = model.objects.get_or_create(**fields)
177
+ # if many_to_many_tuples:
178
+
179
+ # for fk, fk_objects in many_to_many_tuples:
180
+ # getattr(obj, fk).set(fk_objects)
41
181
 
42
- if isinstance(target_natural_key, list):
43
- # the field is a Many to X field.
44
- fk_objects = [foreign_key_model.objects.get_by_natural_key(_) for _ in target_natural_key]
45
- fk_tuple = (foreign_key, fk_objects)
46
- many_to_many_tuples.append(fk_tuple)
47
- continue
48
- # Use the natural key to look up the related object
49
- try:
50
- obj = foreign_key_model.objects.get_by_natural_key(target_natural_key)
51
- except model.DoesNotExist:
52
- command.stderr.write(command.style.ERROR(f'{model.__name__} with natural key {target_natural_key} does not exist. Skipping {name}.'))
53
- raise Exception(f'{model.__name__} with natural key {target_natural_key} does not exist. Skipping {name}.')
54
-
55
- # Assign the related object to the field
56
- fields[foreign_key] = obj
57
-
58
- if name:
59
- obj, created = model.objects.get_or_create(name=name, defaults=fields)
60
- else:
61
- obj, created = model.objects.get_or_create(**fields)
62
- if many_to_many_tuples:
63
- for fk, fk_objects in many_to_many_tuples:
64
- getattr(obj, fk).set(fk_objects)
65
-
66
- if created and verbose:
67
- command.stdout.write(command.style.SUCCESS(f'Created {model.__name__} {name}'))
68
- elif verbose:
69
- command.stdout.write(command.style.WARNING(f'Skipped {model.__name__} {name}, already exists'))
182
+ # if created and verbose:
183
+ # command.stdout.write(command.style.SUCCESS(f'Created {model.__name__} {name}'))
184
+ # elif verbose:
185
+ # command.stdout.write(command.style.WARNING(f'Skipped {model.__name__} {name}, already exists'))
endoreg_db/utils/hashs.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import hashlib
2
+ from pathlib import Path
2
3
 
3
4
  def get_video_hash(video_path):
4
5
  """
@@ -14,3 +15,20 @@ def get_video_hash(video_path):
14
15
 
15
16
  return video_hash
16
17
 
18
+ def get_pdf_hash(pdf_path:Path):
19
+ """
20
+ Get the hash of a pdf file.
21
+ """
22
+ pdf_hash = None
23
+
24
+ # Open the file in binary mode and read its contents
25
+ with open(pdf_path, 'rb') as f:
26
+ pdf_contents = f.read()
27
+ # Create a hash object using SHA-256 algorithm
28
+
29
+ hash_object = hashlib.sha256(pdf_contents, usedforsecurity=False)
30
+ # Get the hexadecimal representation of the hash
31
+ pdf_hash = hash_object.hexdigest()
32
+ assert len(pdf_hash) <= 255, "Hash length exceeds 255 characters"
33
+
34
+ return pdf_hash
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: endoreg-db
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: EndoReg Db Django App
5
5
  License: GNU3
6
6
  Author: Thomas J. Lux
@@ -9,6 +9,7 @@ Classifier: License :: Other/Proprietary License
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
+ Requires-Dist: agl-report-reader (>=0.2.0,<0.3.0)
12
13
  Requires-Dist: django (>=4.2,<4.3)
13
14
  Requires-Dist: django-bootstrap5 (>=23.4,<24.0)
14
15
  Requires-Dist: djangorestframework (>=3.14.0,<4.0.0)