<commit_before>package models

import (
	"database/sql"
	"encoding/json"
	"errors"
	"io"
	"local/mistify-operator-admin/db"
	"net/mail"

	"code.google.com/p/go-uuid/uuid"
)

type User struct {
	ID       string            `json:"id"`
	Username string            `json:"username"`
	Email    string            `json:"email"`
	Metadata map[string]string `json:"metadata"`
	Projects []*Project        `json:"-"`
}

func (user *User) Validate() error {
	if user.ID == "" {
		return errors.New("missing id")
	}
	if uuid.Parse(user.ID) == nil {
		return errors.New("invalid id. must be uuid")
	}
	if user.Username == "" {
		return errors.New("missing username")
	}
	if user.Email == "" {
		return errors.New("missing email")
	}
	if _, err := mail.ParseAddress(user.Email); err != nil {
		return err
	}
	return nil
}

func (user *User) Save() error {
	err := user.Validate()
	if err != nil {
		return err
	}

	d, err := db.Connect(nil)
	if err != nil {
		return err
	}

	// Writable CTE for an Upsert
	// See: http://stackoverflow.com/a/8702291
	// And: http://dba.stackexchange.com/a/78535
	sql := `
	WITH new_values (user_id, username, email, metadata) as (
		VALUES ($1::uuid, $2, $3, $4::json)
	),
	upsert as (
		UPDATE users u SET
			username = nv.username,
			email = nv.email,
			metadata = nv.metadata
		FROM new_values nv
		WHERE u.user_id = nv.user_id
		RETURNING nv.user_id
	)
	INSERT INTO users
		(user_id, username, email, metadata)
	SELECT user_id, username, email, metadata
	FROM new_values nv
	WHERE NOT EXISTS (SELECT 1 FROM upsert u WHERE nv.user_id = u.user_id)
	`
	metadata, err := json.Marshal(user.Metadata)
	if err != nil {
		return err
	}
	_, err = d.Exec(sql,
		user.ID,
		user.Username,
		user.Email,
		string(metadata),
	)
	return err
}

/*
func (user *User) Apply(update *User) {
	if update.Username != "" {
		user.Username = update.Username
	}
	if _, err := mail.ParseAddress(update.Email); err == nil {
		user.Email = update.Email
	}
}
*/

func (user *User) Delete() error {
	d, err := db.Connect(nil)
	if err != nil {
		return err
	}
	sql := "DELETE FROM users WHERE user_id = $1"
	_, err = d.Exec(sql, user.ID)
	return err
}

func (user *User) Load() error {
	d, err := db.Connect(nil)
	if err != nil {
		return err
	}
	sql := `
	SELECT user_id, username, email, metadata
	FROM users
	WHERE user_id = $1
	`
	rows, err := d.Query(sql, user.ID)
	if err != nil {
		return err
	}
	rows.Next()
	return user.fromRows(rows)
}<commit_msg>MIST-227 Remove no longer used Apply func from User<commit_after>package models

import (
	"database/sql"
	"encoding/json"
	"errors"
	"io"
	"local/mistify-operator-admin/db"
	"net/mail"

	"code.google.com/p/go-uuid/uuid"
)

type User struct {
	ID       string            `json:"id"`
	Username string            `json:"username"`
	Email    string            `json:"email"`
	Metadata map[string]string `json:"metadata"`
	Projects []*Project        `json:"-"`
}

func (user *User) Validate() error {
	if user.ID == "" {
		return errors.New("missing id")
	}
	if uuid.Parse(user.ID) == nil {
		return errors.New("invalid id. must be uuid")
	}
	if user.Username == "" {
		return errors.New("missing username")
	}
	if user.Email == "" {
		return errors.New("missing email")
	}
	if _, err := mail.ParseAddress(user.Email); err != nil {
		return err
	}
	return nil
}

func (user *User) Save() error {
	err := user.Validate()
	if err != nil {
		return err
	}

	d, err := db.Connect(nil)
	if err != nil {
		return err
	}

	// Writable CTE for an Upsert
	// See: http://stackoverflow.com/a/8702291
	// And: http://dba.stackexchange.com/a/78535
	sql := `
	WITH new_values (user_id, username, email, metadata) as (
		VALUES ($1::uuid, $2, $3, $4::json)
	),
	upsert as (
		UPDATE users u SET
			username = nv.username,
			email = nv.email,
			metadata = nv.metadata
		FROM new_values nv
		WHERE u.user_id = nv.user_id
		RETURNING nv.user_id
	)
	INSERT INTO users
		(user_id, username, email, metadata)
	SELECT user_id, username, email, metadata
	FROM new_values nv
	WHERE NOT EXISTS (SELECT 1 FROM upsert u WHERE nv.user_id = u.user_id)
	`
	metadata, err := json.Marshal(user.Metadata)
	if err != nil {
		return err
	}
	_, err = d.Exec(sql,
		user.ID,
		user.Username,
		user.Email,
		string(metadata),
	)
	return err
}

func (user *User) Delete() error {
	d, err := db.Connect(nil)
	if err != nil {
		return err
	}
	sql := "DELETE FROM users WHERE user_id = $1"
	_, err = d.Exec(sql, user.ID)
	return err
}

func (user *User) Load() error {
	d, err := db.Connect(nil)
	if err != nil {
		return err
	}
	sql := `
	SELECT user_id, username, email, metadata
	FROM users
	WHERE user_id = $1
	`
	rows, err := d.Query(sql, user.ID)
	if err != nil {
		return err
	}
	rows.Next()
	return user.fromRows(rows)
}
{'fraction_non_alphanumeric': 0.14855454737286347, 'fraction_numerical': 0.008651614264612787, 'mean_word_length': 3.054747647562019, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7340831', 'n_tokens_mistral': 1861, 'n_tokens_neox': 1777, 'n_words': 486}