infoalloggi-monorepo/apps/backend/update.go

359 lines
8.1 KiB
Go
Raw Normal View History

2026-05-22 15:49:28 +02:00
package main
import (
"backend/config"
models "backend/gen/postgres/postgres/public/model"
"backend/parser"
"backend/queries"
"backend/typesdefs"
"backend/utils"
2026-05-26 12:04:53 +02:00
"context"
2026-05-22 15:49:28 +02:00
"fmt"
"log"
"slices"
"strings"
"time"
)
2026-05-26 12:04:53 +02:00
type UpdateParams struct {
2026-05-22 15:49:28 +02:00
// Dry run flag to simulate the update process without making changes.
isDryRun bool
// Filter by codice filter for specific updates. If empty, all records are updated.
codFilter string
// Force media refresh flag to re-download and upload media
forceMediaRefresh bool
}
type Updater struct {
isDryRun bool
codFilter string
forceMediaRefresh bool
2026-05-26 12:04:53 +02:00
caratterisiche typesdefs.ListaCaratteristiche
categorie []typesdefs.Categoria
2026-05-22 15:49:28 +02:00
2026-05-26 12:04:53 +02:00
imgPool []typesdefs.MediaToProcess
videoPool []typesdefs.MediaToProcess
annunci typesdefs.AnnunciParsed
}
func NewUpdater() *Updater {
u := Updater{}
2026-05-22 15:49:28 +02:00
u.init_Updater()
return &u
}
func (u *Updater) init_Updater() {
var err error
u.caratterisiche, err = genCaratteristiche()
if err != nil {
log.Fatalf("Error generating carateristiche: %v", err)
}
u.categorie, err = genCategorie()
if err != nil {
log.Fatalf("Error generating categorie: %v", err)
}
}
func genCaratteristiche() (typesdefs.ListaCaratteristiche, error) {
var values typesdefs.ListaCaratteristiche
//https://www.miogest.com/apps/revo.aspx?tipo=schede
var result, err = utils.GetXMLFromFile[typesdefs.DefaultCaratteristiche]("./caratteristiche.xml")
if err != nil {
return values, err
}
for _, tag := range result.Scheda {
var c = typesdefs.Caratteristica{Id: tag.Id, Tagxml: tag.Tagxml}
values.Caratteristiche = append(values.Caratteristiche, c)
}
return values, nil
}
func genCategorie() ([]typesdefs.Categoria, error) {
var values []typesdefs.Categoria
//https://www.miogest.com/apps/revo.aspx?tipo=categorie
var result, err = utils.GetXMLFromFile[typesdefs.DefaultCategories]("./categorie.xml")
if err != nil {
return values, err
}
for _, elem := range result.Cat {
var c = typesdefs.Categoria{Id: elem.ID, Nome: elem.Nome}
values = append(values, c)
}
return values, nil
}
2026-05-26 12:04:53 +02:00
func (u *Updater) Update(ctx context.Context, params UpdateParams) error {
u.isDryRun = params.isDryRun
u.forceMediaRefresh = params.forceMediaRefresh
u.codFilter = params.codFilter
u.annunci = typesdefs.AnnunciParsed{}
u.imgPool = []typesdefs.MediaToProcess{}
u.videoPool = []typesdefs.MediaToProcess{}
2026-05-22 15:49:28 +02:00
var err error
err = u.parseAnnunci()
if err != nil {
return err
}
err = queries.AnnunciInsert(u.annunci, u.codFilter == "", u.isDryRun)
if err != nil {
return err
}
err = ProcessImagePool(&u.imgPool, u.isDryRun)
if err != nil {
return err
}
err = ProcessVideoPool(&u.videoPool, u.isDryRun)
if err != nil {
return err
}
log.Println("Update completed")
return nil
}
func (u *Updater) parseAnnunci() error {
var err error
xml, err := getAnnunciMiogest()
if err != nil {
return err
}
// filter by codFilter if provided
if u.codFilter != "" {
idx := slices.IndexFunc(xml.Annuncio, func(a typesdefs.AnnuncioXML) bool {
return *a.Codice == u.codFilter
})
if idx == -1 {
return fmt.Errorf("Codice Filtering: Annuncio con codice %s not found", u.codFilter)
}
xml.Annuncio = []typesdefs.AnnuncioXML{xml.Annuncio[idx]}
}
u.setupMediaWorkload(&xml)
for i := range xml.Annuncio {
u.annunci.Annuncio = append(u.annunci.Annuncio, u.extractAnnuncioData(&xml.Annuncio[i]))
}
2026-05-22 20:31:58 +02:00
return nil
2026-05-22 15:49:28 +02:00
}
func getAnnunciMiogest() (typesdefs.AnnunciXML, error) {
var result typesdefs.AnnunciXML
var err error
result, err = utils.GetDataXML[typesdefs.AnnunciXML]("http://partner.miogest.com/agenzie/infoalloggi.xml")
if err != nil {
return result, fmt.Errorf("Error fetching data from Miogest: %w", err)
}
if len(result.Annuncio) == 0 {
return result, fmt.Errorf("No annunci found in the XML data")
}
for i, d := range result.Annuncio {
sanified := strings.ReplaceAll(*d.Codice, " ", "")
result.Annuncio[i].Codice = &sanified
}
return result, nil
}
func (u *Updater) setupMediaWorkload(xml *typesdefs.AnnunciXML) {
if u.forceMediaRefresh && (config.Cfg.Images.Enabled || config.Cfg.Videos.Enabled) {
for i := range xml.Annuncio {
xml.Annuncio[i].ProcessingImages = config.Cfg.Images.Enabled
xml.Annuncio[i].ProcessingVideos = config.Cfg.Videos.Enabled
}
return
}
db_media, err := queries.GetMediaFromDB()
if err != nil {
log.Fatalf("failed to get media from db: %v", err)
return
}
for i, a := range xml.Annuncio {
if a.Codice == nil {
continue
}
var in_db []models.MediaRefs
for _, v := range db_media {
if v.Codice == *a.Codice {
in_db = append(in_db, v)
}
}
if len(in_db) == 0 {
// not found in db
if config.Cfg.Images.Enabled {
xml.Annuncio[i].ProcessingImages = true
}
if config.Cfg.Videos.Enabled {
xml.Annuncio[i].ProcessingVideos = true
}
continue
}
if config.Cfg.Images.Enabled {
img_process := false
for _, fotoUrl := range *a.Foto {
found_img := false
found_thumb := false
for _, ref := range in_db {
if found_img && found_thumb {
break
}
if fotoUrl == ref.OgURL {
if ref.IsThumbnail {
found_thumb = true
continue
}
found_img = true
}
}
if !found_img || !found_thumb {
img_process = true
break
}
}
xml.Annuncio[i].ProcessingImages = img_process
}
if config.Cfg.Videos.Enabled {
vid_process := false
for _, media := range a.AMMedias.AMVideo {
if media.Url == nil {
continue
}
if !strings.Contains(*media.Url, "video.miogest") {
continue
}
found_vid := false
found_thumb := false
for _, ref := range in_db {
if found_vid && found_thumb {
break
}
if *media.Url == ref.OgURL {
if ref.IsThumbnail {
found_thumb = true
continue
}
found_vid = true
}
}
if !found_vid || !found_thumb {
vid_process = true
break
}
}
xml.Annuncio[i].ProcessingImages = vid_process
}
}
if u.codFilter != "" {
for i := range xml.Annuncio {
if config.Cfg.Images.Enabled {
xml.Annuncio[i].ProcessingImages = true
}
if config.Cfg.Videos.Enabled {
xml.Annuncio[i].ProcessingImages = true
}
}
}
}
func (u *Updater) extractAnnuncioData(annuncio *typesdefs.AnnuncioXML) typesdefs.AnnuncioParsed {
tmp := typesdefs.AnnuncioParsed{}
tmp.Codice = utils.RemoveWhitespace(utils.GetStringValue(annuncio.Codice))
processLocatore(&tmp, annuncio)
processIndirizzo(&tmp, annuncio)
tmp.Tipo = parser.ParseTipologia(annuncio.Tipologia, annuncio.Tipologia2)
tmp.Consegna = parser.ParseConsegna(annuncio.Consegna)
processCategorie(&tmp, annuncio, &u.categorie)
processDatiImmobile(&tmp, annuncio)
tmp.Stato = annuncio.Stato
tmp.Homepage = boolUpd(false)
if annuncio.HomePage != nil {
if *annuncio.HomePage == "si" {
tmp.Homepage = boolUpd(true)
}
}
layout := "20060102150405"
if annuncio.Creato != nil {
t, err := time.Parse(layout, *annuncio.Creato)
if err != nil {
tmp.Creato_il = nil
} else {
tmp.Creato_il = &t
}
}
if annuncio.Modifica != nil {
t, err := time.Parse(layout, *annuncio.Modifica)
if err != nil {
tmp.Modificato_il = nil
} else {
tmp.Modificato_il = &t
}
}
tmp.Titolo_it = annuncio.Titolo
tmp.Titolo_en = annuncio.TitoloEn
tmp.Desc_it = annuncio.Descrizione
tmp.Desc_en = annuncio.DescrizioneEn
if annuncio.Dettaglio != nil {
disp, parm, pers, err := parser.ParseDettaglio(*annuncio.Dettaglio, tmp.Consegna)
if err == nil {
tmp.Disponibile_da = disp
tmp.Permanenza = parm
tmp.Persone = pers
}
}
tmp.Web = boolUpd(true)
if annuncio.ProcessingVideos {
AddToVideoProcessing(&tmp, annuncio, &u.videoPool)
}
if annuncio.Video != nil {
ext_videos := []string{}
for _, v := range *annuncio.Video {
if strings.Contains(v, "youtu") {
ext_videos = append(ext_videos, v)
}
tmp.External_videos = &ext_videos
}
} else {
tmp.External_videos = &[]string{}
}
if annuncio.Accessori != nil {
tmp.Accessori = annuncio.Accessori
}
tmpcaratt := parseCaratteristiche(annuncio, &u.caratterisiche)
tmp.Caratteristiche = &tmpcaratt
if annuncio.ProcessingImages {
AddToImageProcessing(&tmp, annuncio, &u.imgPool)
}
return tmp
}