-
-
Notifications
You must be signed in to change notification settings - Fork 27
/
locations.go
148 lines (128 loc) · 4.52 KB
/
locations.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package cmd
import (
"database/sql"
"errors"
"strconv"
"time"
"github.com/google/uuid"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
modelsutils "atomys.codes/stud42/internal/models"
modelgen "atomys.codes/stud42/internal/models/generated"
"atomys.codes/stud42/internal/models/generated/campus"
"atomys.codes/stud42/internal/models/generated/location"
"atomys.codes/stud42/internal/models/generated/user"
"atomys.codes/stud42/internal/pkg/searchengine"
"atomys.codes/stud42/pkg/duoapi"
)
// locationsCmd represents the locations command
var locationsCmd = &cobra.Command{
Use: "locations",
Short: "Crawl all active locations of specific campus and update the database",
Long: `Crawl all active locations of specific campus and update the database.
For any closed locations, the location will be marked as inactive in the database.`,
PreRun: func(cmd *cobra.Command, args []string) {
if err := modelsutils.Connect(); err != nil {
log.Fatal().Err(err).Msg("failed to connect to database")
}
if err := modelsutils.Migrate(); err != nil {
log.Fatal().Err(err).Msg("failed to migrate database")
}
searchengine.Initizialize()
},
Run: func(cmd *cobra.Command, args []string) {
var campusID = cmd.Flag("campus_id").Value.String()
db := modelsutils.Client()
// Retrieve the campus
cID, _ := strconv.Atoi(campusID)
campus, err := db.Campus.Query().Where(campus.DuoID(cID)).Only(cmd.Context())
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
log.Fatal().Msgf("Campus %s not found", campusID)
}
log.Fatal().Err(err).Msg("Failed to get campus")
}
log.Info().Msgf("Start the crawling of active locations of campus %s (%s)", campus.Name, campusID)
// Fetch all active locations of the campus
locations, err := duoapi.LocationsActive(cmd.Context(), campusID)
if err != nil {
log.Fatal().Err(err).Msg("Failed to get duoapi response")
}
log.Debug().Msgf("Found %d locations", len(locations))
// Create the new locations in the database
duoLocationIDs := []int{}
for _, l := range locations {
log.Debug().Msgf("Crawling location %d", l.ID)
duoLocationIDs = append(duoLocationIDs, l.ID)
u, err := modelsutils.UserFirstOrCreateFromComplexLocation(cmd.Context(), l)
if err != nil {
log.Fatal().Err(err).Msg("Failed to create user")
}
if err = modelsutils.WithTx(cmd.Context(), db, func(tx *modelgen.Tx) error {
createdLocationID, err := db.Location.Create().
SetCampus(campus).
SetUser(u).
SetDuoID(l.ID).
SetBeginAt(l.BeginAt.Time()).
SetNillableEndAt(l.EndAt.NillableTime()).
SetIdentifier(l.Host).
SetUserDuoID(l.User.ID).
SetUserDuoLogin(l.User.Login).
OnConflictColumns(location.FieldDuoID).UpdateNewValues().
ID(cmd.Context())
if err != nil {
log.Error().Err(err).Msg("Failed to create location")
return err
}
if err := db.User.UpdateOne(u).
SetCurrentLocationID(createdLocationID).
SetLastLocationID(createdLocationID).
Exec(cmd.Context()); err != nil {
log.Error().Err(err).Msg("Failed to update user")
return err
}
return nil
}); err != nil {
log.Fatal().Err(err).Msg("Failed to process location")
}
}
// Mark all locations as inactive that are not in the list anymore
err = modelsutils.WithTx(cmd.Context(), db, func(tx *modelgen.Tx) error {
locationUUIDsToClose, err := db.Location.Query().Where(
location.DuoIDNotIn(duoLocationIDs...),
location.CampusID(campus.ID),
location.EndAtIsNil(),
).IDs(cmd.Context())
if err != nil {
return err
}
// Close location
if err := db.Location.Update().
Where(location.IDIn(locationUUIDsToClose...)).
SetEndAt(time.Now().UTC()).
Exec(cmd.Context()); err != nil {
return err
}
// Unassign current location from user
var uuids []uuid.UUID
if uuids, err = db.User.Query().Where(user.CurrentLocationIDIn(locationUUIDsToClose...)).IDs(cmd.Context()); err != nil {
return err
}
for _, uuid := range uuids {
if err := db.User.UpdateOneID(uuid).ClearCurrentLocationID().Exec(cmd.Context()); err != nil {
return err
}
}
log.Info().Msgf("Successfully close %d inactive locations", len(locationUUIDsToClose))
return nil
})
if err != nil {
log.Fatal().Err(err).Msg("Failed to close inactive locations")
}
log.Info().Msgf("Successfully sync the campus of %s with %d locations active", campus.Name, len(locations))
},
}
func init() {
crawlerCmd.AddCommand(locationsCmd)
locationsCmd.Flags().StringP("campus_id", "c", "1", "Campus to crawl")
}