createUserReportData creates a QDevUserReport from a new-format CSV record
(logger interface {
Debug(format string, a ...interface{})
}, headers []string, record []string, fileMeta *models.QDevS3FileMeta, identityClient UserDisplayNameResolver)
| 182 | |
| 183 | // createUserReportData creates a QDevUserReport from a new-format CSV record |
| 184 | func createUserReportData(logger interface { |
| 185 | Debug(format string, a ...interface{}) |
| 186 | }, headers []string, record []string, fileMeta *models.QDevS3FileMeta, identityClient UserDisplayNameResolver) (*models.QDevUserReport, errors.Error) { |
| 187 | report := &models.QDevUserReport{ |
| 188 | ConnectionId: fileMeta.ConnectionId, |
| 189 | ScopeId: fileMeta.ScopeId, |
| 190 | } |
| 191 | |
| 192 | // Build field map |
| 193 | fieldMap := make(map[string]string) |
| 194 | for i, header := range headers { |
| 195 | if i < len(record) { |
| 196 | logger.Debug("Mapping header[%d]: '%s' -> '%s'", i, header, record[i]) |
| 197 | fieldMap[header] = record[i] |
| 198 | trimmedHeader := strings.TrimSpace(header) |
| 199 | if trimmedHeader != header { |
| 200 | logger.Debug("Also adding trimmed header: '%s'", trimmedHeader) |
| 201 | fieldMap[trimmedHeader] = record[i] |
| 202 | } |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | // UserId (normalize to strip "d-{directoryId}." prefix if present) |
| 207 | report.UserId = normalizeUserId(getStringField(fieldMap, "UserId")) |
| 208 | if report.UserId == "" { |
| 209 | return nil, errors.Default.New("UserId not found in CSV record") |
| 210 | } |
| 211 | |
| 212 | // DisplayName |
| 213 | report.DisplayName = resolveDisplayName(logger, report.UserId, identityClient) |
| 214 | |
| 215 | // Date |
| 216 | dateStr := getStringField(fieldMap, "Date") |
| 217 | if dateStr == "" { |
| 218 | return nil, errors.Default.New("Date not found in CSV record") |
| 219 | } |
| 220 | var err error |
| 221 | report.Date, err = parseDate(dateStr) |
| 222 | if err != nil { |
| 223 | return nil, errors.Default.Wrap(err, "failed to parse date") |
| 224 | } |
| 225 | |
| 226 | // String fields |
| 227 | report.ClientType = getStringField(fieldMap, "Client_Type") |
| 228 | report.SubscriptionTier = getStringField(fieldMap, "Subscription_Tier") |
| 229 | report.ProfileId = getStringField(fieldMap, "ProfileId") |
| 230 | |
| 231 | // Numeric fields |
| 232 | report.ChatConversations = parseInt(fieldMap, "Chat_Conversations") |
| 233 | report.CreditsUsed = parseFloat(fieldMap, "Credits_Used") |
| 234 | report.OverageCap = parseFloat(fieldMap, "Overage_Cap") |
| 235 | report.OverageCreditsUsed = parseFloat(fieldMap, "Overage_Credits_Used") |
| 236 | report.OverageEnabled = parseBool(fieldMap, "Overage_Enabled") |
| 237 | report.TotalMessages = parseInt(fieldMap, "Total_Messages") |
| 238 | |
| 239 | return report, nil |
| 240 | } |
| 241 |