Add server wrapper, blob fetch logging, sheet cell tool; drop importblobs

This commit is contained in:
Ian Gulliver
2026-08-16 11:22:59 -07:00
parent bbff936f39
commit 366e3bb8a2
6 changed files with 280 additions and 302 deletions
+126
View File
@@ -0,0 +1,126 @@
// Command renamefamilyblobs renames family media in the drive from parent-email names to family key hashes.
package main
import (
"context"
"flag"
"fmt"
"log"
"path"
"strings"
"heliosian/internal/data"
"heliosian/internal/directory"
"google.golang.org/api/drive/v3"
"google.golang.org/api/option"
)
const folderMime = "application/vnd.google-apps.folder"
func main() {
sheet := flag.String("sheet", "", "spreadsheet id")
flag.Parse()
if *sheet == "" {
log.Fatal("[ERROR] -sheet <spreadsheet id> is required")
}
source, err := data.NewSheet(map[string]string{"directory": *sheet})
if err != nil {
log.Fatalf("[ERROR] sheet source: %v", err)
}
model, err := directory.LoadModel(source, nil)
if err != nil {
log.Fatalf("[ERROR] load model: %v", err)
}
localToHash := map[string]string{}
for _, family := range model.Families {
for _, adult := range family.AdultEmails {
local, _, _ := strings.Cut(adult, "@")
if existing, ok := localToHash[local]; ok && existing != family.Key {
log.Fatalf("[ERROR] adult local part %s maps to two families", local)
}
localToHash[local] = family.Key
}
}
svc, err := drive.NewService(context.Background(),
option.WithCredentialsFile(data.KeyFile),
option.WithScopes(drive.DriveScope))
if err != nil {
log.Fatalf("[ERROR] drive client: %v", err)
}
drives, err := svc.Drives.List().Do()
if err != nil {
log.Fatalf("[ERROR] list shared drives: %v", err)
}
if len(drives.Drives) != 1 {
log.Fatalf("[ERROR] expected one shared drive, found %d", len(drives.Drives))
}
folderList, err := svc.Files.List().
Q(fmt.Sprintf("name = 'families' and '%s' in parents and mimeType = '%s' and trashed = false", drives.Drives[0].Id, folderMime)).
SupportsAllDrives(true).IncludeItemsFromAllDrives(true).Corpora("allDrives").
Fields("files(id)").Do()
if err != nil {
log.Fatalf("[ERROR] find families folder: %v", err)
}
if len(folderList.Files) != 1 {
log.Fatalf("[ERROR] expected one families folder, found %d", len(folderList.Files))
}
folderID := folderList.Files[0].Id
renamed, kept, unknown := 0, 0, 0
token := ""
for {
call := svc.Files.List().
Q(fmt.Sprintf("'%s' in parents and trashed = false", folderID)).
SupportsAllDrives(true).IncludeItemsFromAllDrives(true).Corpora("allDrives").
Fields("nextPageToken, files(id, name, mimeType)").PageSize(1000)
if token != "" {
call = call.PageToken(token)
}
list, err := call.Do()
if err != nil {
log.Fatalf("[ERROR] list families folder: %v", err)
}
for _, f := range list.Files {
if f.MimeType == folderMime {
continue
}
ext := path.Ext(f.Name)
base := strings.TrimSuffix(f.Name, ext)
kind := ""
for _, k := range []string{"-photo", "-pronunciation"} {
if strings.HasSuffix(base, k) {
kind = k
}
}
if kind == "" {
log.Printf("[ERROR] unrecognized file name %q, leaving it", f.Name)
unknown++
continue
}
local := strings.TrimSuffix(base, kind)
hash, ok := localToHash[local]
if !ok {
if _, isCurrent := model.Families[local]; isCurrent {
kept++
continue
}
log.Printf("[ERROR] file %q matches no family adult, leaving it", f.Name)
unknown++
continue
}
newName := hash + kind + ext
_, err := svc.Files.Update(f.Id, &drive.File{Name: newName}).SupportsAllDrives(true).Do()
if err != nil {
log.Fatalf("[ERROR] rename %q to %q: %v", f.Name, newName, err)
}
log.Printf("renamed %q -> %q", f.Name, newName)
renamed++
}
if list.NextPageToken == "" {
break
}
token = list.NextPageToken
}
log.Printf("done: %d renamed, %d already keyed by hash, %d left untouched", renamed, kept, unknown)
}