@@ -115,6 +115,7 @@ def to_content_file(self) -> ContentFile:
115115
116116class BaseBulkUpload (Generic [ContextType ]):
117117 serializer_class : Type [Serializer ]
118+ HEADER_MAP : dict [str , str ]
118119
119120 def __init__ (self , bulk_upload : LocalUnitBulkUpload ):
120121 if self .serializer_class is None :
@@ -152,70 +153,71 @@ def process_row(self, data: Dict[str, Any]) -> bool:
152153 return False
153154
154155 def run (self ) -> None :
155- with self .bulk_upload .file .open ("rb" ) as f :
156- try :
157- # TODO(sudip): Use read_only while reading xlsx file
158- workbook = openpyxl .load_workbook (f , data_only = True )
156+ header_row_index = 2
157+ data_row_index = header_row_index + 2
158+
159+ try :
160+ with self .bulk_upload .file .open ("rb" ) as f :
161+ workbook = openpyxl .load_workbook (f , data_only = True , read_only = True )
159162 sheet = workbook .active
160- header_row_index = 2
161- data_row_index = header_row_index + 2
162163
163- # Read header row
164164 headers = next (sheet .iter_rows (values_only = True , min_row = header_row_index , max_row = header_row_index ))
165165 raw_fieldnames = [str (h ).strip () for h in headers if h and str (h ).strip ()]
166- header_map = getattr ( self , " HEADER_MAP" , {}) or {}
166+ header_map = self . HEADER_MAP or {}
167167 mapped_fieldnames = [header_map .get (h , h ) for h in raw_fieldnames ]
168168 fieldnames = mapped_fieldnames
169169
170170 if self .is_excel_data_empty (sheet , data_start_row = data_row_index ):
171- raise BulkUploadError ("The uploaded Excel file is empty. Please provide at least one data row." )
171+ raise BulkUploadError ("The uploaded file is empty. Please provide at least one data row." )
172172
173173 self ._validate_type (fieldnames )
174- data_rows = (
175- row
176- for row in sheet .iter_rows (values_only = True , min_row = 4 )
177- if any (cell is not None for cell in row ) # skip the empty rows
178- )
179- except Exception as e :
180- self .bulk_upload .status = LocalUnitBulkUpload .Status .FAILED
181- self .bulk_upload .error_message = str (e )
182- self .bulk_upload .save (update_fields = ["status" , "error_message" ])
183- logger .warning (f"[BulkUpload:{ self .bulk_upload .pk } ] Validation error: { str (e )} " )
184- return
185-
186- context = self .get_context ().__dict__
187- self .error_writer = ErrorWriter (fieldnames = raw_fieldnames , header_map = header_map )
188-
189- try :
190- with transaction .atomic ():
191- self .delete_existing_local_unit ()
192-
193- for row_index , row_values in enumerate (data_rows , start = data_row_index ):
194- row_dict = dict (zip (fieldnames , row_values ))
195- row_dict = {** row_dict , ** context }
196- # Convert datetime objects to strings
197- for key , value in row_dict .items ():
198- if isinstance (value , (datetime , date )):
199- row_dict [key ] = value .strftime ("%Y-%m-%d" )
200- if self .process_row (row_dict ):
201- self .success_count += 1
202- self .error_writer .write (row_dict , status = LocalUnitBulkUpload .Status .SUCCESS )
203- else :
204- self .failed_count += 1
205- self .error_writer .write (
206- row_dict ,
207- status = LocalUnitBulkUpload .Status .FAILED ,
208- error_detail = self .error_detail ,
209- )
210- logger .warning (f"[BulkUpload:{ self .bulk_upload .pk } ] Row { row_index } failed" )
211-
212- if self .failed_count > 0 :
213- raise BulkUploadError ("Bulk upload failed with some errors." )
214-
215- self .bulk_manager .done ()
216- self ._finalize_success ()
217-
218- except BulkUploadError :
174+ self .error_writer = ErrorWriter (fieldnames = raw_fieldnames , header_map = header_map )
175+ context = self .get_context ().__dict__
176+
177+ with transaction .atomic ():
178+ self .delete_existing_local_unit ()
179+
180+ for row_index , row_values in enumerate (
181+ sheet .iter_rows (values_only = True , min_row = data_row_index ),
182+ start = data_row_index ,
183+ ):
184+ if not any (cell is not None for cell in row_values ):
185+ continue # skip empty rows
186+
187+ row_dict = dict (zip (fieldnames , row_values ))
188+ row_dict = {** row_dict , ** context }
189+
190+ # Convert date/datetime to str
191+ for key , value in row_dict .items ():
192+ if isinstance (value , (datetime , date )):
193+ row_dict [key ] = value .strftime ("%Y-%m-%d" )
194+
195+ if self .process_row (row_dict ):
196+ self .success_count += 1
197+ self .error_writer .write (row_dict , status = LocalUnitBulkUpload .Status .SUCCESS )
198+ else :
199+ self .failed_count += 1
200+ self .error_writer .write (
201+ row_dict ,
202+ status = LocalUnitBulkUpload .Status .FAILED ,
203+ error_detail = self .error_detail ,
204+ )
205+ logger .warning (f"[BulkUpload:{ self .bulk_upload .pk } ] Row { row_index } failed" )
206+
207+ if self .failed_count > 0 :
208+ raise BulkUploadError ()
209+
210+ self .bulk_manager .done ()
211+ self ._finalize_success ()
212+
213+ workbook .close ()
214+
215+ except Exception as e :
216+ self .bulk_upload .status = LocalUnitBulkUpload .Status .FAILED
217+ self .bulk_upload .error_message = str (e )
218+ self .bulk_upload .save (update_fields = ["status" , "error_message" ])
219+ if isinstance (e , BulkUploadError ):
220+ logger .warning (f"[BulkUpload:{ self .bulk_upload .pk } ] error: { e } " )
219221 self ._finalize_failure ()
220222
221223 def _finalize_success (self ) -> None :
0 commit comments