Bnei-Baruch/mdb

View on GitHub
importer/likutim/create_unit.go

Summary

Maintainability
B
5 hrs
Test Coverage
package likutim

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "os"
    "path"
    "strings"
    "time"

    log "github.com/Sirupsen/logrus"
    "github.com/pkg/errors"
    "github.com/spf13/viper"
    "github.com/volatiletech/null/v8"
    "github.com/volatiletech/sqlboiler/v4/boil"
    "github.com/volatiletech/sqlboiler/v4/queries/qm"

    "github.com/Bnei-Baruch/mdb/common"
    "github.com/Bnei-Baruch/mdb/models"
    "github.com/Bnei-Baruch/mdb/utils"
)

type CreateUnits struct {
    duplicates []*Double
    mdb        *sql.DB
}

func (c *CreateUnits) Run() {
    err := c.duplicatesFromJSON()
    //if no json file with data of duplicated doc files run compare
    if err != nil {
        log.Errorf("Error on read. Error: %s", err)
        compare := new(Compare)
        compare.Run()
        c.duplicates = compare.result
    }

    c.openDB()
    defer c.mdb.Close()

    utils.Must(os.MkdirAll(viper.GetString("likutim.os-dir"), os.ModePerm))
    c.updateUnits()
}

func (c *CreateUnits) updateUnits() {
    for _, d := range c.duplicates {
        log.Debugf("\n\nStart create new unit type LIKUTIM, file: %s all files: %v", d.Save, d.Doubles)

        tx, err := c.mdb.Begin()
        if err != nil {
            log.Error(err)
            continue
        }
        derived := make([]int64, len(d.Doubles))
        cu, err := c.createUnit(tx, d.Save, derived)
        if err != nil {
            log.Error(err)
            if err = tx.Rollback(); err != nil {
                log.Errorf("problem rollback transaction. Error: %s", err)
            }
            continue
        }

        // Derivation unit type LIKITIM to origins of doubles
        for i, uid := range d.Doubles {
            if uid == d.Save {
                continue
            }

            if err = moveData(tx, uid, cu, derived, i); err != nil {
                log.Error(err)
                if err = tx.Rollback(); err != nil {
                    log.Errorf("problem rollback transaction. Error: %s", err)
                }
                break
            }
        }

        if err = tx.Commit(); err != nil {
            log.Errorf("problem commit transaction. Error: %s", err)
        }
        log.Debugf("End create new unit type LIKUTIM id: %d", cu.ID)
    }
}

func (c *CreateUnits) createUnit(tx *sql.Tx, uid string, derived []int64) (*models.ContentUnit, error) {
    //fetch files and get origin unit
    f, err := models.Files(
        qm.Where("uid = ?", uid),
        qm.Load("ContentUnit"),
        qm.Load("ContentUnit.Files"),
        qm.Load("ContentUnit.DerivedContentUnitDerivations"),
        qm.Load("ContentUnit.DerivedContentUnitDerivations.Source"),
    ).One(tx)
    if err != nil {
        return nil, errors.Wrapf(err, "Can't find base file by uid: %s.", uid)
    }
    if f.R.ContentUnit == nil {
        return nil, errors.Wrapf(err, "Can't find base unit by file: %v.", f)
    }

    if f.R.ContentUnit.TypeID == common.CONTENT_TYPE_REGISTRY.ByName[common.CT_LIKUTIM].ID {
        return nil, errors.New(fmt.Sprintf("this file alredy have Unit type LIKUTIM : %v.", f))
    }

    if len(f.R.ContentUnit.R.DerivedContentUnitDerivations) == 0 {
        return nil, errors.Wrapf(err, "Can't find origin unit by unit: %v.", f.R.ContentUnit)
    }

    cuo, err := models.ContentUnits(
        qm.Where("id = ?", f.R.ContentUnit.R.DerivedContentUnitDerivations[0].SourceID),
        qm.Load("ContentUnitI18ns"),
        qm.Load("Tags"),
    ).One(tx)
    if err != nil {
        return nil, errors.Wrapf(err, "Can't find origin unit by unit: %v.", f.R.ContentUnit)
    }
    var props map[string]interface{}
    var filmDate string
    if f.R.ContentUnit.Properties.Valid {
        if err = json.Unmarshal(f.R.ContentUnit.Properties.JSON, &props); err != nil {
            return nil, errors.Wrapf(err, "Can't unmarshal properties of unit: %d.", f.R.ContentUnit.ID)
        }
        filmDate = fmt.Sprintf("%v", props["film_date"])
    } else {
        filmDate = time.Now().Format("2006-01-02")
    }
    //create unit type LIKUTIM
    cu, err := c.createCU(tx, cuo, filmDate, f)
    if err != nil {
        return nil, errors.Wrapf(err, "Can't create unit %v to unit: %v.", cu, cuo)
    }
    if err := c.moveFiles(tx, f.R.ContentUnit, &cu); err != nil {
        return nil, err
    }
    //derivation to origin
    derived[0] = cuo.ID
    cud := &models.ContentUnitDerivation{
        SourceID:  cuo.ID,
        DerivedID: cu.ID,
    }
    err = cuo.AddSourceContentUnitDerivations(tx, true, cud)
    if err != nil {
        return nil, errors.Wrapf(err, "Can't derive unit %v to unit: %v.", cu, cuo)
    }
    return &cu, nil
}

func moveData(tx *sql.Tx, uid string, cu *models.ContentUnit, derived []int64, pos int) error {

    f, err := models.Files(qm.Where("uid = ?", uid),
        qm.Load("ContentUnit"),
        qm.Load("ContentUnit.DerivedContentUnitDerivations"),
        qm.Load("ContentUnit.DerivedContentUnitDerivations.Source")).
        One(tx)
    if err != nil {
        return errors.Wrapf(err, "Can't find file by uid: %s.", uid)
    }

    if f.R.ContentUnit == nil {
        return errors.Wrapf(err, "Can't find unit by file: %v.", f)
    }

    if len(f.R.ContentUnit.R.DerivedContentUnitDerivations) == 0 {
        return errors.Wrapf(err, "Can't find origin unit by unit: %v.", f.R.ContentUnit)
    }
    //check if new unit was already derived (origins can be same)
    for _, duid := range derived {
        if duid == f.R.ContentUnit.R.DerivedContentUnitDerivations[0].SourceID {
            return nil
        }
    }
    derived[pos] = f.R.ContentUnit.R.DerivedContentUnitDerivations[0].SourceID
    d := &models.ContentUnitDerivation{
        SourceID:  f.R.ContentUnit.R.DerivedContentUnitDerivations[0].SourceID,
        DerivedID: cu.ID,
    }
    err = cu.AddDerivedContentUnitDerivations(tx, true, d)
    if err != nil {
        return errors.Wrapf(err, "Can't derive unit %v to unit id: %d.", cu, f.R.ContentUnit.R.DerivedContentUnitDerivations[0].SourceID)
    }
    return nil
}

func (c *CreateUnits) moveFiles(tx *sql.Tx, cukm, newu *models.ContentUnit) error {
    for _, f := range cukm.R.Files {
        if !strings.Contains(f.Name, ".doc") {
            continue
        }
        f.ContentUnitID = null.Int64{Int64: newu.ID, Valid: true}
        if _, err := f.Update(tx, boil.Whitelist("content_unit_id")); err != nil {
            return errors.Wrapf(err, "Can't insert files from kitvei makor unit: %s  to new CU: %s", cukm.UID, newu.UID)
        }
    }
    return nil
}

func (c *CreateUnits) createCU(tx *sql.Tx, cuo *models.ContentUnit, filmDate string, f *models.File) (models.ContentUnit, error) {
    props, _ := json.Marshal(map[string]string{"film_date": filmDate, "pattern": patternByFileName(f.Name), "original_language": common.LANG_HEBREW})
    cu := models.ContentUnit{
        UID:        utils.GenerateUID(8),
        TypeID:     common.CONTENT_TYPE_REGISTRY.ByName[common.CT_LIKUTIM].ID,
        Secure:     0,
        Published:  true,
        Properties: null.JSONFrom(props),
    }
    err := cu.Insert(tx, boil.Infer())
    if err != nil {
        log.Errorf("Can't add tags for CU id %d. Error: %s", cu.ID, err)
        return cu, err
    }
    log.Debugf("Unit was inserted CU %v", cu)

    //take data from origin for new unit
    err = cu.AddTags(tx, false, cuo.R.Tags...)
    if err != nil {
        log.Errorf("Can't add tags for CU id %d. Error: %s", cu.ID, err)
        return cu, err
    }

    var i18n models.ContentUnitI18nSlice
    for _, i := range cuo.R.ContentUnitI18ns {
        n := &models.ContentUnitI18n{
            ContentUnitID: cu.ID,
            Language:      i.Language,
            Name:          i.Name,
        }
        i18n = append(i18n, n)
    }
    err = cu.AddContentUnitI18ns(tx, true, i18n...)
    if err != nil {
        log.Errorf("Can't add i18n for CU id %d. Error: %s", cu.ID, err)
        return cu, err
    }
    return cu, nil
}
func patternByFileName(name string) string {
    spl := strings.Split(strings.ToLower(name), "kitei-makor")
    spl = strings.Split(spl[1], ".")
    spl = strings.Split(spl[0], "_")
    if spl[0] != "" || len(spl) < 2 {
        return spl[0]
    }
    return spl[1]
}

func (c *CreateUnits) duplicatesFromJSON() error {
    p := path.Join(viper.GetString("likutim.os-dir"), "kitvei-makor-duplicates.json")
    j, err := ioutil.ReadFile(p)
    if err != nil {
        return err
    }
    var r []*Double
    err = json.Unmarshal(j, &r)
    if err != nil {
        return err
    }
    c.duplicates = r
    return nil
}

func (c *CreateUnits) openDB() {
    mdb, err := sql.Open("postgres", viper.GetString("mdb.url"))
    utils.Must(err)
    utils.Must(mdb.Ping())
    boil.SetDB(mdb)
    boil.DebugMode = true
    utils.Must(common.InitTypeRegistries(mdb))
    c.mdb = mdb
}