@@ -313,50 +313,66 @@ def fetch_all_installs(network_numbers):
313313def process_support_row (unit , issue , raw_date_reported , raw_date_resolved , all_active_installs , install_to_building_map , filter_year = None , filter_month = None ):
314314 """
315315 Helper function to process a single row of support data.
316- Returns a 'visit' dict or None if dates are invalid or match doesn't occur .
316+ Handles standard dates, dates with times, and empty 'Resolved' dates .
317317 """
318- # Supported formats: US (MM/DD/YYYY), ISO (YYYY-MM-DD), ISO with time (YYYY-MM-DD HH:MM:SS)
319- formats = ["%m/%d/%Y" , "%Y-%m-%d" , "%Y-%m-%d %H:%M:%S" ]
318+ # 1. Clean up the inputs
319+ raw_date_reported = raw_date_reported .strip ()
320+ raw_date_resolved = raw_date_resolved .strip () if raw_date_resolved else ""
321+
322+ # 2. Define supported formats
323+ # We check "ISO with time" first, then "ISO", then "US"
324+ formats = ["%Y-%m-%d %H:%M:%S" , "%Y-%m-%d" , "%m/%d/%Y" ]
320325
321326 date_reported = None
322327 date_resolved = None
323328
324- # Parse Date Reported
329+ # 3. Parse Date Reported
325330 for fmt in formats :
326331 try :
327- date_reported = datetime .strptime (raw_date_reported , fmt )
332+ # Try to parse. If the string is longer than the format (e.g. has milliseconds),
333+ # we might need to substring it, but usually strptime is strict.
334+ # A simple hack for milliseconds: split by '.'
335+ clean_date = raw_date_reported .split ('.' )[0 ]
336+ date_reported = datetime .strptime (clean_date , fmt )
328337 break
329338 except ValueError :
330339 pass
331340
332- # Parse Date Resolved (might be empty)
341+ if not date_reported :
342+ return None # Cannot process a ticket without a reported date
343+
344+ # 4. Filter by Month (If filter is active)
345+ if filter_year and filter_month :
346+ if date_reported .year != filter_year or date_reported .month != filter_month :
347+ return None
348+
349+ # 5. Parse Date Resolved
333350 if raw_date_resolved :
334351 for fmt in formats :
335352 try :
336- date_resolved = datetime .strptime (raw_date_resolved , fmt )
353+ clean_date = raw_date_resolved .split ('.' )[0 ]
354+ date_resolved = datetime .strptime (clean_date , fmt )
337355 break
338356 except ValueError :
339357 pass
340358
341- if not date_reported or not date_resolved :
342- return None # Skip invalid date rows
343-
344- # --- FILTERING LOGIC ---
345- if filter_year and filter_month :
346- if date_reported .year != filter_year or date_reported .month != filter_month :
347- return None
348- # -----------------------
349-
350- wait = (date_resolved - date_reported ).days
359+ # 6. Calculate Wait Time
360+ if date_resolved :
361+ wait = (date_resolved - date_reported ).days
362+ else :
363+ # If not resolved yet, calculate wait time relative to NOW
364+ wait = (datetime .now () - date_reported ).days
351365
352- # Determine Mesh Status
366+ # 7. Determine Mesh Status
353367 mesh = False
354368 building = "0"
355369 apt = unit
356370
357371 if '-' in unit :
358- building = unit .split ('-' )[0 ]
359- apt = unit .split ('-' )[1 ]
372+ parts = unit .split ('-' )
373+ if len (parts ) >= 2 :
374+ building = parts [0 ].strip ()
375+ apt = parts [1 ].strip ()
360376
361377 nn = 0
362378 for install in all_active_installs :
@@ -369,18 +385,22 @@ def process_support_row(unit, issue, raw_date_reported, raw_date_resolved, all_a
369385 # Check if this install matches the unit
370386 try :
371387 if int (install ["node" ]["network_number" ]) == nn :
372- if install ["unit" ] == apt :
388+ # Case insensitive check for unit letter (e.g. 14D vs 14d)
389+ if install ["unit" ].lower () == apt .lower ():
373390 mesh = True
374391 break
375392 except (KeyError , ValueError ):
376393 continue
377394
395+ # 8. Format dates for display (Handle empty resolved date)
396+ display_resolved = date_resolved .strftime ("%m/%d/%Y" ) if date_resolved else "Open"
397+
378398 return {
379399 "unit" : unit ,
380400 "mesh" : mesh ,
381401 "issue" : issue ,
382402 "date_reported" : date_reported .strftime ("%m/%d/%Y" ),
383- "date_resolved" : date_resolved . strftime ( "%m/%d/%Y" ) ,
403+ "date_resolved" : display_resolved ,
384404 "wait" : wait
385405 }
386406
@@ -505,11 +525,19 @@ def reports(request):
505525 report = {}
506526 support = []
507527 stats = {}
528+ error_message = None # New variable to pass errors to template
529+
508530 if request .method == 'POST' :
509531 form = ReportForm (request .POST , request .FILES )
532+
533+ # Debugging Print
534+ print (f"DEBUG: Form is valid: { form .is_valid ()} " , file = sys .stderr )
535+
510536 if form .is_valid ():
511537 value = form .cleaned_data ['report' ]
512538 action = request .POST .get ('action' )
539+
540+ print (f"DEBUG: Action received: { action } " , file = sys .stderr )
513541
514542 formatted = None
515543 for month in months :
@@ -674,6 +702,7 @@ def update_totals(category, date):
674702 # OPTION A: File Upload (CSV)
675703 # ---------------------------
676704 if request .FILES :
705+ print ("DEBUG: Processing File Upload" , file = sys .stderr )
677706 support_file = request .FILES ['file' ].read ().decode ("utf-8-sig" )
678707 lines = support_file .splitlines ()
679708 reader = csv .reader (lines , delimiter = ',' )
@@ -683,17 +712,17 @@ def update_totals(category, date):
683712 if not row : continue
684713
685714 # CSV format expected:
686- # 0: ID , 1: Apt , 2: Issue, 3: Reported By, 4: Reported, 5 : Resolved
715+ # 0: Unit , 1: Issue , 2: Reported, 3 : Resolved
687716 # (Assuming User's provided CSV structure)
688- unit = row [1 ]
689- issue = row [2 ]
690- raw_date_reported = row [4 ]
691- raw_date_resolved = row [5 ]
717+ unit = row [0 ]
718+ issue = row [1 ]
719+ raw_date_reported = row [2 ]
720+ raw_date_resolved = row [3 ]
692721
693722 visit = process_support_row (
694723 unit , issue , raw_date_reported , raw_date_resolved ,
695724 all_active_installs , install_to_building_map ,
696- current_year , current_month # PASSING FILTER DATES
725+ current_year , current_month
697726 )
698727
699728 if visit :
@@ -705,10 +734,13 @@ def update_totals(category, date):
705734 # OPTION B: Google Sheets (SIMPLE PUBLISHED CSV)
706735 # -----------------------
707736 elif action == 'google_sheets' :
737+ print ("DEBUG: Processing Google Sheets" , file = sys .stderr )
708738 try :
709739 if not GOOGLE_SHEET_CSV_URL :
710740 raise Exception ("Missing GOOGLE_SHEET_CSV_URL environment variable" )
711741
742+ print (f"DEBUG: Fetching { GOOGLE_SHEET_CSV_URL } " , file = sys .stderr )
743+
712744 # Download the published CSV
713745 response = requests .get (GOOGLE_SHEET_CSV_URL )
714746 response .raise_for_status () # Raise error if download failed
@@ -733,18 +765,17 @@ def update_totals(category, date):
733765 visit = process_support_row (
734766 unit , issue , raw_date_reported , raw_date_resolved ,
735767 all_active_installs , install_to_building_map ,
736- current_year , current_month # PASSING FILTER DATES
768+ current_year , current_month
737769 )
738770
739771 if visit :
740772 support .append (visit )
741773 if visit ['mesh' ]: mesh_count += 1
742774 if visit ['issue' ] == "Internet" : internet_count += 1
743775 avg_wait += visit ['wait' ]
744-
745776 except Exception as e :
746- print ( f"Error connecting to Google Sheets: { e } " )
747- # Optional: Add an error message to 'report' or 'stats' to display in UI
777+ error_message = f"Error connecting to Google Sheets: { e } "
778+ print ( error_message , file = sys . stderr )
748779
749780 # Calculate Statistics if support data exists
750781 if len (support ) > 0 :
@@ -755,11 +786,14 @@ def update_totals(category, date):
755786 "avg_wait" : round (avg_wait / len (support ), 2 )
756787 }
757788
789+ # NOTE: We are passing 'error_message' to the template.
790+ # Ensure your template can display {{ error_message }} if it's not None.
758791 return render (request , 'dashboard/gsg-reports.html' , {
759792 'months' : months ,
760793 'report' : report ,
761794 'support' : support ,
762- 'stats' : stats
795+ 'stats' : stats ,
796+ 'error_message' : error_message
763797 })
764798
765799@login_required
0 commit comments