from __future__ import annotations
from string import Template
ENTITIES = [
dict(
entity="Patient",
table="patients",
entity_lower="patient",
entity_plural="patients",
fields='''\tOrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
\tName string `gorm:"type:varchar(255);not null" json:"name" validate:"required"`
\tContact string `gorm:"type:varchar(50);not null" json:"contact" validate:"required"`
\tCPF string `gorm:"type:varchar(14);uniqueIndex" json:"cpf,omitempty"`
\tSSN string `gorm:"type:varchar(11);uniqueIndex" json:"ssn,omitempty"`
\tDOB time.Time `gorm:"type:date" json:"dob"`
\tGender string `gorm:"type:varchar(20)" json:"gender,omitempty"`
\tAddress string `gorm:"type:text" json:"address,omitempty"`''',
lookup_field="CPF",
lookup_column="cpf",
),
dict(
entity="Doctor",
table="doctors",
entity_lower="doctor",
entity_plural="doctors",
fields='''\tOrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
\tFullName string `gorm:"type:varchar(255);not null" json:"full_name" validate:"required"`
\tCPF string `gorm:"type:varchar(14);uniqueIndex" json:"cpf"`
\tCRM string `gorm:"type:varchar(20);uniqueIndex" json:"crm"`
\tSpecialty string `gorm:"type:varchar(100);not null" json:"specialty" validate:"required"`
\tEmail string `gorm:"type:varchar(255);uniqueIndex" json:"email" validate:"required,email"`
\tPhone string `gorm:"type:varchar(20)" json:"phone,omitempty"`''',
lookup_field="CRM",
lookup_column="crm",
),
dict(
entity="Appointment",
table="appointments",
entity_lower="appointment",
entity_plural="appointments",
fields='''\tOrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
\tPatientID uuid.UUID `gorm:"type:uuid;not null;index" json:"patient_id"`
\tDoctorID uuid.UUID `gorm:"type:uuid;not null;index" json:"doctor_id"`
\tUserID uuid.UUID `gorm:"type:uuid;not null" json:"user_id"`
\tSpecialization string `gorm:"type:varchar(100);not null" json:"specialization"`
\tDateTime time.Time `gorm:"type:timestamptz;not null" json:"date_time"`
\tStatus string `gorm:"type:varchar(50);default:scheduled" json:"status"`
\tNotes string `gorm:"type:text" json:"notes,omitempty"`''',
lookup_field="Status",
lookup_column="status",
),
dict(
entity="User",
table="users",
entity_lower="user",
entity_plural="users",
fields='''\tOrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
\tName string `gorm:"type:varchar(255);not null" json:"name" validate:"required"`
\tEmail string `gorm:"type:varchar(255);uniqueIndex;not null" json:"email" validate:"required,email"`
\tPassword string `gorm:"type:varchar(255);not null" json:"-"` // never serialised
\tRole string `gorm:"type:varchar(50);not null;default:user" json:"role" validate:"required,oneof=admin user doctor nurse"`''',
lookup_field="Email",
lookup_column="email",
),
]
GO_VERSIONS = ["1.21", "1.22", "1.23", "1.24"]
# ---------------------------------------------------------------------------
# Templates
# ---------------------------------------------------------------------------
_ENTITY_TEMPLATE = Template("""\
go$go_version
entities
// domain/entities/${entity_lower}.go
// Pattern: GORM entity with UUID PK + soft delete + JSON/validate tags
// Observed in: Medical-App-Core/domain/entities/
package entities
import (
\t"time"
\t"github.com/google/uuid"
\t"gorm.io/gorm"
)
// ${entity} represents the ${table} database table.
type ${entity} struct {
\tID uuid.UUID `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
\tCreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
\tUpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
\tDeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // soft delete
$fields
}
// TableName overrides GORM's convention-based table name.
func (${entity}) TableName() string { return "${table}" }
// BeforeCreate sets a UUID before the record is inserted.
func (e *${entity}) BeforeCreate(tx *gorm.DB) error {
\tif e.ID == uuid.Nil {
\t\te.ID = uuid.New()
\t}
\treturn nil
}
""")
_REPO_INTERFACE_TEMPLATE = Template("""\
go$go_version
contracts
// domain/repositories/contracts/${entity_lower}Repository.go
// Pattern: repository interface (contract) — domain layer knows nothing about GORM
// Observed in: Medical-App-Core/domain/repositories/contracts/
package contracts
import (
\t"$module/domain/dtos"
\t"github.com/google/uuid"
)
// ${entity}Repository defines the persistence contract for ${entity} entities.
// Implementations live in infra/repositories/ and depend on GORM or any other ORM.
type ${entity}Repository interface {
\tCreate(dto dtos.${entity}DTO) (uuid.UUID, error)
\tFindByID(id uuid.UUID) (*dtos.${entity}DTO, error)
\tFindBy${lookup_field}(${lookup_column} string) (*dtos.${entity}DTO, error)
\tList(page, limit int) ([]dtos.${entity}DTO, error)
\tUpdate(id uuid.UUID, dto dtos.${entity}DTO) error
\tDelete(id uuid.UUID) error // soft delete
}
""")
_REPO_IMPL_TEMPLATE = Template("""\
go$go_version
repositories
// infra/repositories/${entity_lower}Repository.go
// Pattern: GORM implementation of the repository interface
// Observed in: Medical-App-Core/infra/repositories/
package repositories
import (
\t"fmt"
\t"$module/domain/dtos"
\t"$module/domain/entities"
\t"github.com/google/uuid"
\t"gorm.io/gorm"
)
// ${entity}RepositoryImpl implements contracts.${entity}Repository using GORM.
type ${entity}RepositoryImpl struct {
\tdb *gorm.DB
}
// New${entity}Repository constructs a new ${entity}RepositoryImpl.
func New${entity}Repository(db *gorm.DB) *${entity}RepositoryImpl {
\treturn &${entity}RepositoryImpl{db: db}
}
func (r *${entity}RepositoryImpl) Create(dto dtos.${entity}DTO) (uuid.UUID, error) {
\tentity := entities.${entity}{} // map DTO → entity fields
\t// (field mapping omitted for brevity — use a mapper or manual assignment)
\tif err := r.db.Create(&entity).Error; err != nil {
\t\treturn uuid.Nil, fmt.Errorf("${entity_lower} create: %w", err)
\t}
\treturn entity.ID, nil
}
func (r *${entity}RepositoryImpl) FindByID(id uuid.UUID) (*dtos.${entity}DTO, error) {
\tvar entity entities.${entity}
\tif err := r.db.First(&entity, "id = ?", id).Error; err != nil {
\t\tif err == gorm.ErrRecordNotFound {
\t\t\treturn nil, fmt.Errorf("${entity_lower} not found: %s", id)
\t\t}
\t\treturn nil, fmt.Errorf("${entity_lower} find: %w", err)
\t}
\tdto := dtos.${entity}DTO{} // map entity → DTO
\treturn &dto, nil
}
func (r *${entity}RepositoryImpl) FindBy${lookup_field}(${lookup_column} string) (*dtos.${entity}DTO, error) {
\tvar entity entities.${entity}
\tif err := r.db.Where("${lookup_column} = ?", ${lookup_column}).First(&entity).Error; err != nil {
\t\tif err == gorm.ErrRecordNotFound {
\t\t\treturn nil, fmt.Errorf("${entity_lower} with ${lookup_column}=%s not found", ${lookup_column})
\t\t}
\t\treturn nil, fmt.Errorf("${entity_lower} find by ${lookup_column}: %w", err)
\t}
\tdto := dtos.${entity}DTO{}
\treturn &dto, nil
}
func (r *${entity}RepositoryImpl) List(page, limit int) ([]dtos.${entity}DTO, error) {
\tvar entities []entities.${entity}
\toffset := (page - 1) * limit
\tif err := r.db.Offset(offset).Limit(limit).Find(&entities).Error; err != nil {
\t\treturn nil, fmt.Errorf("${entity_lower} list: %w", err)
\t}
\tdtos := make([]dtos.${entity}DTO, 0, len(entities))
\t// map each entity to DTO
\treturn dtos, nil
}
func (r *${entity}RepositoryImpl) Update(id uuid.UUID, dto dtos.${entity}DTO) error {
\tresult := r.db.Model(&entities.${entity}{}).Where("id = ?", id).Updates(dto)
\tif result.Error != nil {
\t\treturn fmt.Errorf("${entity_lower} update: %w", result.Error)
\t}
\tif result.RowsAffected == 0 {
\t\treturn fmt.Errorf("${entity_lower} not found: %s", id)
\t}
\treturn nil
}
func (r *${entity}RepositoryImpl) Delete(id uuid.UUID) error {
\t// GORM soft delete: sets deleted_at instead of removing the row
\tresult := r.db.Delete(&entities.${entity}{}, "id = ?", id)
\tif result.Error != nil {
\t\treturn fmt.Errorf("${entity_lower} delete: %w", result.Error)
\t}
\tif result.RowsAffected == 0 {
\t\treturn fmt.Errorf("${entity_lower} not found: %s", id)
\t}
\treturn nil
}
""")
_DB_INIT_TEMPLATE = Template("""\
go$go_version
initializers
// initializers/database.go
// Pattern: GORM + PostgreSQL connection with pool config + env-based DSN
// Observed in: Medical-App-Core/initializers/database.go
package initializers
import (
\t"fmt"
\t"log"
\t"os"
\t"strings"
\t"time"
\t"gorm.io/driver/postgres"
\t"gorm.io/gorm"
\t"gorm.io/gorm/logger"
)
// InitialDB opens a PostgreSQL connection using environment variables.
// Panics on failure — the app cannot run without a database.
func InitialDB() *gorm.DB {
\tdsn := buildDSN()
\tdb, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
\t\tLogger: logger.Default.LogMode(logger.Info),
\t})
\tif err != nil {
\t\tlog.Fatalf("database connection failed: %v", err)
\t}
\tsqlDB, err := db.DB()
\tif err != nil {
\t\tlog.Fatalf("failed to get underlying sql.DB: %v", err)
\t}
\tsqlDB.SetMaxOpenConns(25)
\tsqlDB.SetMaxIdleConns(10)
\tsqlDB.SetConnMaxLifetime(5 * time.Minute)
\tlog.Println("database connected:", maskDSN(dsn))
\treturn db
}
func buildDSN() string {
\treturn fmt.Sprintf(
\t\t"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=%s",
\t\tgetEnv("DB_HOST", "localhost"),
\t\tgetEnv("DB_PORT", "5432"),
\t\tgetEnv("DB_USER", "postgres"),
\t\tos.Getenv("DB_PASSWORD"),
\t\tgetEnv("DB_NAME", "appdb"),
\t\tgetEnv("DB_SSLMODE", "disable"),
\t\tgetEnv("DB_TIMEZONE", "UTC"),
\t)
}
func getEnv(key, fallback string) string {
\tif v := os.Getenv(key); v != "" {
\t\treturn v
\t}
\treturn fallback
}
func maskDSN(dsn string) string {
\tpassword := os.Getenv("DB_PASSWORD")
\tif password == "" {
\t\treturn dsn
\t}
\treturn strings.ReplaceAll(dsn, password, "*****")
}
""")
class GormPatternGenerator:
"""Generate GORM + PostgreSQL training examples."""
def all_examples(self) -> list[str]:
examples: list[str] = []
module = "medical-sas-api"
for ent in ENTITIES:
for ver in GO_VERSIONS:
examples.append(_ENTITY_TEMPLATE.substitute(go_version=ver, module=module, **ent))
examples.append(_REPO_INTERFACE_TEMPLATE.substitute(go_version=ver, module=module, **ent))
examples.append(_REPO_IMPL_TEMPLATE.substitute(go_version=ver, module=module, **ent))
for ver in GO_VERSIONS:
examples.append(_DB_INIT_TEMPLATE.substitute(go_version=ver))
return examples