Each of the four oversized files (training/store.go 1569 LOC, ucca/rules.go 1231 LOC, ucca_handlers.go 1135 LOC, document_export.go 1101 LOC) is split by logical group into same-package files, all under the 500-line hard cap. Zero behavior changes, no renamed exported symbols. Also fixed pre-existing hazard_library split (missing functions and duplicate UUID keys from a prior session). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
193 lines
6.3 KiB
Go
193 lines
6.3 KiB
Go
package training
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
// CreateMedia creates a new media record
|
|
func (s *Store) CreateMedia(ctx context.Context, media *TrainingMedia) error {
|
|
media.ID = uuid.New()
|
|
media.CreatedAt = time.Now().UTC()
|
|
media.UpdatedAt = media.CreatedAt
|
|
if media.Metadata == nil {
|
|
media.Metadata = json.RawMessage("{}")
|
|
}
|
|
|
|
_, err := s.pool.Exec(ctx, `
|
|
INSERT INTO training_media (
|
|
id, module_id, content_id, media_type, status,
|
|
bucket, object_key, file_size_bytes, duration_seconds,
|
|
mime_type, voice_model, language, metadata,
|
|
error_message, generated_by, is_published, created_at, updated_at
|
|
) VALUES (
|
|
$1, $2, $3, $4, $5,
|
|
$6, $7, $8, $9,
|
|
$10, $11, $12, $13,
|
|
$14, $15, $16, $17, $18
|
|
)
|
|
`,
|
|
media.ID, media.ModuleID, media.ContentID, string(media.MediaType), string(media.Status),
|
|
media.Bucket, media.ObjectKey, media.FileSizeBytes, media.DurationSeconds,
|
|
media.MimeType, media.VoiceModel, media.Language, media.Metadata,
|
|
media.ErrorMessage, media.GeneratedBy, media.IsPublished, media.CreatedAt, media.UpdatedAt,
|
|
)
|
|
|
|
return err
|
|
}
|
|
|
|
// GetMedia retrieves a media record by ID
|
|
func (s *Store) GetMedia(ctx context.Context, id uuid.UUID) (*TrainingMedia, error) {
|
|
var media TrainingMedia
|
|
var mediaType, status string
|
|
|
|
err := s.pool.QueryRow(ctx, `
|
|
SELECT id, module_id, content_id, media_type, status,
|
|
bucket, object_key, file_size_bytes, duration_seconds,
|
|
mime_type, voice_model, language, metadata,
|
|
error_message, generated_by, is_published, created_at, updated_at
|
|
FROM training_media WHERE id = $1
|
|
`, id).Scan(
|
|
&media.ID, &media.ModuleID, &media.ContentID, &mediaType, &status,
|
|
&media.Bucket, &media.ObjectKey, &media.FileSizeBytes, &media.DurationSeconds,
|
|
&media.MimeType, &media.VoiceModel, &media.Language, &media.Metadata,
|
|
&media.ErrorMessage, &media.GeneratedBy, &media.IsPublished, &media.CreatedAt, &media.UpdatedAt,
|
|
)
|
|
|
|
if err == pgx.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
media.MediaType = MediaType(mediaType)
|
|
media.Status = MediaStatus(status)
|
|
return &media, nil
|
|
}
|
|
|
|
// GetMediaForModule retrieves all media for a module
|
|
func (s *Store) GetMediaForModule(ctx context.Context, moduleID uuid.UUID) ([]TrainingMedia, error) {
|
|
rows, err := s.pool.Query(ctx, `
|
|
SELECT id, module_id, content_id, media_type, status,
|
|
bucket, object_key, file_size_bytes, duration_seconds,
|
|
mime_type, voice_model, language, metadata,
|
|
error_message, generated_by, is_published, created_at, updated_at
|
|
FROM training_media WHERE module_id = $1
|
|
ORDER BY media_type, created_at DESC
|
|
`, moduleID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var mediaList []TrainingMedia
|
|
for rows.Next() {
|
|
var media TrainingMedia
|
|
var mediaType, status string
|
|
if err := rows.Scan(
|
|
&media.ID, &media.ModuleID, &media.ContentID, &mediaType, &status,
|
|
&media.Bucket, &media.ObjectKey, &media.FileSizeBytes, &media.DurationSeconds,
|
|
&media.MimeType, &media.VoiceModel, &media.Language, &media.Metadata,
|
|
&media.ErrorMessage, &media.GeneratedBy, &media.IsPublished, &media.CreatedAt, &media.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
media.MediaType = MediaType(mediaType)
|
|
media.Status = MediaStatus(status)
|
|
mediaList = append(mediaList, media)
|
|
}
|
|
|
|
if mediaList == nil {
|
|
mediaList = []TrainingMedia{}
|
|
}
|
|
return mediaList, nil
|
|
}
|
|
|
|
// UpdateMediaStatus updates the status and related fields of a media record
|
|
func (s *Store) UpdateMediaStatus(ctx context.Context, id uuid.UUID, status MediaStatus, sizeBytes int64, duration float64, errMsg string) error {
|
|
_, err := s.pool.Exec(ctx, `
|
|
UPDATE training_media
|
|
SET status = $2, file_size_bytes = $3, duration_seconds = $4,
|
|
error_message = $5, updated_at = NOW()
|
|
WHERE id = $1
|
|
`, id, string(status), sizeBytes, duration, errMsg)
|
|
return err
|
|
}
|
|
|
|
// PublishMedia publishes or unpublishes a media record
|
|
func (s *Store) PublishMedia(ctx context.Context, id uuid.UUID, publish bool) error {
|
|
_, err := s.pool.Exec(ctx, `
|
|
UPDATE training_media SET is_published = $2, updated_at = NOW() WHERE id = $1
|
|
`, id, publish)
|
|
return err
|
|
}
|
|
|
|
// GetPublishedAudio gets the published audio for a module
|
|
func (s *Store) GetPublishedAudio(ctx context.Context, moduleID uuid.UUID) (*TrainingMedia, error) {
|
|
var media TrainingMedia
|
|
var mediaType, status string
|
|
|
|
err := s.pool.QueryRow(ctx, `
|
|
SELECT id, module_id, content_id, media_type, status,
|
|
bucket, object_key, file_size_bytes, duration_seconds,
|
|
mime_type, voice_model, language, metadata,
|
|
error_message, generated_by, is_published, created_at, updated_at
|
|
FROM training_media
|
|
WHERE module_id = $1 AND media_type = 'audio' AND is_published = true
|
|
ORDER BY created_at DESC LIMIT 1
|
|
`, moduleID).Scan(
|
|
&media.ID, &media.ModuleID, &media.ContentID, &mediaType, &status,
|
|
&media.Bucket, &media.ObjectKey, &media.FileSizeBytes, &media.DurationSeconds,
|
|
&media.MimeType, &media.VoiceModel, &media.Language, &media.Metadata,
|
|
&media.ErrorMessage, &media.GeneratedBy, &media.IsPublished, &media.CreatedAt, &media.UpdatedAt,
|
|
)
|
|
|
|
if err == pgx.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
media.MediaType = MediaType(mediaType)
|
|
media.Status = MediaStatus(status)
|
|
return &media, nil
|
|
}
|
|
|
|
// GetPublishedVideo gets the published video for a module
|
|
func (s *Store) GetPublishedVideo(ctx context.Context, moduleID uuid.UUID) (*TrainingMedia, error) {
|
|
var media TrainingMedia
|
|
var mediaType, status string
|
|
|
|
err := s.pool.QueryRow(ctx, `
|
|
SELECT id, module_id, content_id, media_type, status,
|
|
bucket, object_key, file_size_bytes, duration_seconds,
|
|
mime_type, voice_model, language, metadata,
|
|
error_message, generated_by, is_published, created_at, updated_at
|
|
FROM training_media
|
|
WHERE module_id = $1 AND media_type = 'video' AND is_published = true
|
|
ORDER BY created_at DESC LIMIT 1
|
|
`, moduleID).Scan(
|
|
&media.ID, &media.ModuleID, &media.ContentID, &mediaType, &status,
|
|
&media.Bucket, &media.ObjectKey, &media.FileSizeBytes, &media.DurationSeconds,
|
|
&media.MimeType, &media.VoiceModel, &media.Language, &media.Metadata,
|
|
&media.ErrorMessage, &media.GeneratedBy, &media.IsPublished, &media.CreatedAt, &media.UpdatedAt,
|
|
)
|
|
|
|
if err == pgx.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
media.MediaType = MediaType(mediaType)
|
|
media.Status = MediaStatus(status)
|
|
return &media, nil
|
|
}
|