<commit_before>import { pick } from 'lodash';
import { getConfig } from '../../config-helper';
import { hash } from '../utility';
import Mysql from './db/mysql';
import Schema from './db/schema';
import { UserTable } from './db/tables/user-table';
import UserClientTable from './db/tables/user-client-table';
import ClipTable, { DBClip, DBClipWithVoters } from './db/tables/clip-table';
import VoteTable from './db/tables/vote-table';

export interface Sentence {
  id: string;
  text: string;
}

export default class DB {
  clip: ClipTable;
  mysql: Mysql;
  schema: Schema;
  user: UserTable;
  userClient: UserClientTable;
  vote: VoteTable;

  constructor() {
    this.mysql = new Mysql();

    this.clip = new ClipTable(this.mysql);
    this.user = new UserTable(this.mysql);
    this.userClient = new UserClientTable(this.mysql);
    this.vote = new VoteTable(this.mysql);

    this.schema = new Schema(this.mysql);
  }

  /**
   * Normalize email address as input.
   * TODO: add validation here.
   */
  private formatEmail(email?: string): string {
    if (!email) {
      return '';
    }

    return email.toLowerCase();
  }

  private localeIds: { [name: string]: number };
  private async getLocaleId(locale: string): Promise<number> {
    if (!this.localeIds) {
      const [rows] = await this.mysql.query('SELECT id, name FROM locales');
      this.localeIds = rows.reduce(
        (obj: any, { id, name }: any) => ({
          ...obj,
          [name]: id,
        }),
        {}
      );
    }

    return this.localeIds[locale];
  }

  /**
   * Insert or update user client row.
   */
  async updateUser(client_id: string, fields: any): Promise<void> {
    let { age, accents, email, gender } = fields;
    email = this.formatEmail(email);
    await Promise.all([
      email &&
        this.user.update({
          email,
          ...pick(fields, 'send_emails', 'has_downloaded'),
        }),
      this.userClient.update({ client_id, email, age, gender }),
    ]);
    accents && (await this.saveAccents(client_id, accents));
  }

  async getOrSetUserBucket(client_id: string, locale: string, bucket: string) {
    const localeId = await this.getLocaleId(locale);
    await this.mysql.query(
      `
        INSERT IGNORE INTO user_client_locale_buckets (client_id, locale_id, bucket) VALUES (?, ?, ?)
      `,
      [client_id, localeId, bucket]
    );
    const [[row]] = await this.mysql.query(
      'SELECT bucket FROM user_client_locale_buckets WHERE client_id = ? AND locale_id = ?',
      [client_id, localeId]
    );
    return row.bucket;
  }

  /**
   * Ensure the database is setup.
   */
  async ensureSetup(): Promise<void> {
    return this.schema.ensure();
  }

  /**
   * I hope you know what you're doing.
   */
  async drop(): Promise<void> {
    if (!getConfig().PROD) {
      await this.schema.dropDatabase();
    }
  }

  async getUserCount(): Promise<number> {
    return this.user.getCount();
  }
<commit_msg>error handling for setting a user's bucket<commit_after>import { pick } from 'lodash';
import { getConfig } from '../../config-helper';
import { hash } from '../utility';
import Mysql from './db/mysql';
import Schema from './db/schema';
import { UserTable } from './db/tables/user-table';
import UserClientTable from './db/tables/user-client-table';
import ClipTable, { DBClip, DBClipWithVoters } from './db/tables/clip-table';
import VoteTable from './db/tables/vote-table';

export interface Sentence {
  id: string;
  text: string;
}

export default class DB {
  clip: ClipTable;
  mysql: Mysql;
  schema: Schema;
  user: UserTable;
  userClient: UserClientTable;
  vote: VoteTable;

  constructor() {
    this.mysql = new Mysql();

    this.clip = new ClipTable(this.mysql);
    this.user = new UserTable(this.mysql);
    this.userClient = new UserClientTable(this.mysql);
    this.vote = new VoteTable(this.mysql);

    this.schema = new Schema(this.mysql);
  }

  /**
   * Normalize email address as input.
   * TODO: add validation here.
   */
  private formatEmail(email?: string): string {
    if (!email) {
      return '';
    }

    return email.toLowerCase();
  }

  private localeIds: { [name: string]: number };
  private async getLocaleId(locale: string): Promise<number> {
    if (!this.localeIds) {
      const [rows] = await this.mysql.query('SELECT id, name FROM locales');
      this.localeIds = rows.reduce(
        (obj: any, { id, name }: any) => ({
          ...obj,
          [name]: id,
        }),
        {}
      );
    }

    return this.localeIds[locale];
  }

  /**
   * Insert or update user client row.
   */
  async updateUser(client_id: string, fields: any): Promise<void> {
    let { age, accents, email, gender } = fields;
    email = this.formatEmail(email);
    await Promise.all([
      email &&
        this.user.update({
          email,
          ...pick(fields, 'send_emails', 'has_downloaded'),
        }),
      this.userClient.update({ client_id, email, age, gender }),
    ]);
    accents && (await this.saveAccents(client_id, accents));
  }

  async getOrSetUserBucket(client_id: string, locale: string, bucket: string) {
    const localeId = await this.getLocaleId(locale);

    let userBucket = await this.getUserBucket(client_id, localeId);
    if (userBucket) return userBucket;

    try {
      await this.mysql.query(
        `
          INSERT INTO user_client_locale_buckets (client_id, locale_id, bucket) VALUES (?, ?, ?)
        `,
        [client_id, localeId, bucket]
      );
      userBucket = await this.getUserBucket(client_id, localeId);
      if (!userBucket) {
        console.error('Error: No bucket found after insert');
        return bucket;
      }
      return userBucket;
    } catch (error) {
      console.error('Error setting user bucket', error);
      return bucket;
    }
  }

  async getUserBucket(
    client_id: string,
    localeId: number
  ): Promise<string | null> {
    const [[row]] = await this.mysql.query(
      'SELECT bucket FROM user_client_locale_buckets WHERE client_id = ? AND locale_id = ?',
      [client_id, localeId]
    );
    return row ? row.bucket : null;
  }

  /**
   * Ensure the database is setup.
   */
  async ensureSetup(): Promise<void> {
    return this.schema.ensure();
  }

  /**
   * I hope you know what you're doing.
   */
  async drop(): Promise<void> {
    if (!getConfig().PROD) {
      await this.schema.dropDatabase();
    }
  }

  async getUserCount(): Promise<number> {
    return this.user.getCount();
  }

{'fraction_non_alphanumeric': 0.14065629332922508, 'fraction_numerical': 0.0, 'mean_word_length': 2.731034482758621, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4120840', 'n_tokens_mistral': 2067, 'n_tokens_neox': 2023, 'n_words': 522}