diff --git a/Services/GoogleSheetService.cs b/Services/GoogleSheetService.cs index e4a3771..6e97e29 100644 --- a/Services/GoogleSheetService.cs +++ b/Services/GoogleSheetService.cs @@ -2,7 +2,6 @@ using Disco.Plugins.ServiceTracker.Models; using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Text; @@ -35,32 +34,62 @@ namespace Disco.Plugins.ServiceTracker.Services { var cacheAge = DateTime.Now - File.GetLastWriteTime(_cachePath); if (cacheAge.TotalMinutes < _config.RefreshMinutes) - { csvData = File.ReadAllText(_cachePath); - } } - // Fetch fresh if no cache if (csvData == null) { try { - var url = string.Format( - "https://docs.google.com/spreadsheets/d/{0}/export?format=csv&gid={1}", - _config.SpreadsheetId, _config.GId); + // Build URL based on ID format + string url; + var id = _config.SpreadsheetId.Trim(); + + if (id.StartsWith("2PACX", StringComparison.OrdinalIgnoreCase)) + { + // Published key format: /d/e/{publishedKey}/pub?output=csv&gid={gid} + url = string.Format( + "https://docs.google.com/spreadsheets/d/e/{0}/pub?output=csv&gid={1}", + id, _config.GId); + } + else + { + // Original spreadsheet ID format: /d/{id}/export?format=csv&gid={gid} + url = string.Format( + "https://docs.google.com/spreadsheets/d/{0}/export?format=csv&gid={1}", + id, _config.GId); + } using (var client = new WebClient()) { client.Encoding = Encoding.UTF8; + // Some Google responses require a user-agent + client.Headers.Add("User-Agent", "Mozilla/5.0 DiscoServiceTracker/1.0"); csvData = client.DownloadString(url); } - // Cache it - File.WriteAllText(_cachePath, csvData); + // Validate we got actual CSV, not an HTML error page + if (csvData != null && csvData.TrimStart().StartsWith(" Share > Publish to web > select the tab > choose 'Comma-separated values (.csv)' > Publish."; + return result; + } + } + else + { + File.WriteAllText(_cachePath, csvData); + } } catch (WebException ex) { - // Try cached data as fallback if (File.Exists(_cachePath)) { csvData = File.ReadAllText(_cachePath); @@ -68,7 +97,14 @@ namespace Disco.Plugins.ServiceTracker.Services } else { - result.Error = "Could not fetch Google Sheet. Ensure it is published to web (File > Share > Publish to web > CSV). Error: " + ex.Message; + result.Error = "Could not fetch Google Sheet. Error: " + ex.Message + + "\n\nTroubleshooting:" + + "\n1. Go to your Google Sheet" + + "\n2. File > Share > Publish to web" + + "\n3. Select the correct tab" + + "\n4. Choose 'Comma-separated values (.csv)'" + + "\n5. Click Publish" + + "\n6. In Config, set Spreadsheet ID to the published key (starts with 2PACX-...)"; return result; } } @@ -79,10 +115,11 @@ namespace Disco.Plugins.ServiceTracker.Services } } - // Parse CSV try { result.Tickets = ParseCsv(csvData); + if (result.Tickets.Count == 0 && string.IsNullOrEmpty(result.Warning)) + result.Warning = "Sheet fetched OK but no open tickets found. Check column indices and status values."; } catch (Exception ex) { @@ -103,6 +140,14 @@ namespace Disco.Plugins.ServiceTracker.Services var fields = ParseCsvLine(lines[i]); if (fields.Count == 0) continue; + // Skip completely empty rows + bool allEmpty = true; + for (int j = 0; j < fields.Count; j++) + { + if (!string.IsNullOrWhiteSpace(fields[j])) { allEmpty = false; break; } + } + if (allEmpty) continue; + var ticket = new ExternalTicket { InternalId = nextId++, @@ -117,14 +162,13 @@ namespace Disco.Plugins.ServiceTracker.Services AssignedTo = SafeGet(fields, _config.ColAssignedTo) }; - // Generate stable ID from key fields ticket.ExternalId = GenerateStableId(ticket); - // Determine if open var status = (ticket.RawStatus ?? "").ToLower().Trim(); ticket.IsOpen = status != "closed" && status != "resolved" && status != "completed" && status != "done"; - if (ticket.IsOpen && !string.IsNullOrWhiteSpace(ticket.IssueDescription)) + // Need at least an issue description or device name to be a valid ticket + if (ticket.IsOpen && (!string.IsNullOrWhiteSpace(ticket.IssueDescription) || !string.IsNullOrWhiteSpace(ticket.DeviceName))) tickets.Add(ticket); } @@ -161,31 +205,16 @@ namespace Disco.Plugins.ServiceTracker.Services var fields = new List(); bool inQuotes = false; var current = new StringBuilder(); - for (int i = 0; i < line.Length; i++) { char c = line[i]; if (c == '"') { - if (inQuotes && i + 1 < line.Length && line[i + 1] == '"') - { - current.Append('"'); - i++; - } - else - { - inQuotes = !inQuotes; - } - } - else if (c == ',' && !inQuotes) - { - fields.Add(current.ToString()); - current.Clear(); - } - else if (c != '\r') - { - current.Append(c); + if (inQuotes && i + 1 < line.Length && line[i + 1] == '"') { current.Append('"'); i++; } + else inQuotes = !inQuotes; } + else if (c == ',' && !inQuotes) { fields.Add(current.ToString()); current.Clear(); } + else if (c != '\r') current.Append(c); } fields.Add(current.ToString()); return fields; @@ -197,10 +226,6 @@ namespace Disco.Plugins.ServiceTracker.Services public List Tickets { get; set; } public string Error { get; set; } public string Warning { get; set; } - - public GoogleSheetResult() - { - Tickets = new List(); - } + public GoogleSheetResult() { Tickets = new List(); } } }