Properly save audio and image notes when uploaded

Also fix the audio processing pipeline.
This commit is contained in:
Eli Ribble 2026-01-06 22:23:59 +00:00
parent 4d02357671
commit 39d9f6d258
11 changed files with 145 additions and 138 deletions

View file

@ -16,6 +16,8 @@ import (
"github.com/Gleipnir-Technology/nidus-sync/platform"
"github.com/Gleipnir-Technology/nidus-sync/queue"
"github.com/Gleipnir-Technology/nidus-sync/userfile"
"github.com/aarondl/opt/omit"
"github.com/aarondl/opt/omitnull"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
"github.com/google/uuid"
@ -41,7 +43,18 @@ func apiAudioPost(w http.ResponseWriter, r *http.Request, u *models.User) {
http.Error(w, "Failed to decode the payload", http.StatusBadRequest)
return
}
if err := db.NoteAudioCreate(context.Background(), noteUUID, db.NoteAudio{}, u.ID); err != nil {
setter := models.NoteAudioSetter{
Created: omit.From(payload.Created),
CreatorID: omit.From(u.ID),
Deleted: omitnull.FromPtr(payload.Deleted),
DeletorID: omitnull.FromPtr(payload.DeletorID),
Duration: omit.From(payload.Duration),
Transcription: omitnull.FromPtr(payload.Transcription),
TranscriptionUserEdited: omit.From(payload.TranscriptionUserEdited),
Version: omit.From(payload.Version),
UUID: omit.From(noteUUID),
}
if err := db.NoteAudioCreate(context.Background(), u.R.Organization, u.ID, setter); err != nil {
render.Render(w, r, errRender(err))
return
}
@ -92,8 +105,15 @@ func handleClientIos(w http.ResponseWriter, r *http.Request, u *models.User) {
return
}
var since_used time.Time
if since == nil {
since_used = time.Unix(0, 0)
} else {
since_used = *since
}
response := ResponseClientIos{
Fieldseeker: toResponseFieldseeker(csync.Fieldseeker),
Since: since_used,
}
if err := render.Render(w, r, response); err != nil {
render.Render(w, r, errRender(err))
@ -101,31 +121,6 @@ func handleClientIos(w http.ResponseWriter, r *http.Request, u *models.User) {
}
}
func apiClientIosNotePut(w http.ResponseWriter, r *http.Request, u *models.User) {
id := chi.URLParam(r, "uuid")
noteUUID, err := uuid.Parse(id)
if err != nil {
http.Error(w, "Failed to decode the uuid", http.StatusBadRequest)
return
}
var payload NidusNotePayload
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read the payload", http.StatusBadRequest)
return
}
if err := json.Unmarshal(body, &payload); err != nil {
debugSaveRequest(body, err, "Note PUT JSON decode error")
http.Error(w, "Failed to decode the payload", http.StatusBadRequest)
return
}
if err := db.NoteUpdate(context.Background(), noteUUID, db.NidusNotePayload{}); err != nil {
render.Render(w, r, errRender(err))
return
}
w.WriteHeader(http.StatusAccepted)
}
func apiImagePost(w http.ResponseWriter, r *http.Request, u *models.User) {
id := chi.URLParam(r, "uuid")
noteUUID, err := uuid.Parse(id)
@ -145,7 +140,15 @@ func apiImagePost(w http.ResponseWriter, r *http.Request, u *models.User) {
http.Error(w, "Failed to decode the payload", http.StatusBadRequest)
return
}
err = db.NoteImageCreate(context.Background(), noteUUID, db.NoteImage{}, u.ID)
setter := models.NoteImageSetter{
Created: omit.From(payload.Created),
CreatorID: omit.From(u.ID),
Deleted: omitnull.FromPtr(payload.Deleted),
DeletorID: omitnull.FromPtr(payload.DeletorID),
Version: omit.From(payload.Version),
UUID: omit.From(noteUUID),
}
err = db.NoteImageCreate(context.Background(), u.R.Organization, u.ID, setter)
if err != nil {
render.Render(w, r, errRender(err))
return
@ -187,7 +190,7 @@ func apiMosquitoSource(w http.ResponseWriter, r *http.Request, u *models.User) {
}
data := []render.Renderer{}
for _, s := range *sources {
for _, s := range sources {
data = append(data, NewResponseMosquitoSource(s))
}
if err := render.RenderList(w, r, data); err != nil {
@ -212,7 +215,7 @@ func apiTrapData(w http.ResponseWriter, r *http.Request, u *models.User) {
}
data := []render.Renderer{}
for _, td := range *trap_data {
for _, td := range trap_data {
data = append(data, NewResponseTrapDatum(td))
}
if err := render.RenderList(w, r, data); err != nil {
@ -236,7 +239,7 @@ func apiServiceRequest(w http.ResponseWriter, r *http.Request, u *models.User) {
}
data := []render.Renderer{}
for _, sr := range *requests {
for _, sr := range requests {
data = append(data, NewResponseServiceRequest(sr))
}
if err := render.RenderList(w, r, data); err != nil {

View file

@ -14,7 +14,6 @@ func AddRoutes(r chi.Router) {
r.Method("GET", "/service-request", auth.NewEnsureAuth(apiServiceRequest))
r.Method("GET", "/trap-data", auth.NewEnsureAuth(apiTrapData))
r.Method("GET", "/client/ios", auth.NewEnsureAuth(handleClientIos))
r.Method("PUT", "/client/ios/note/{uuid}", auth.NewEnsureAuth(apiClientIosNotePut))
r.Method("POST", "/audio/{uuid}", auth.NewEnsureAuth(apiAudioPost))
r.Method("POST", "/audio/{uuid}/content", auth.NewEnsureAuth(apiAudioContentPost))
r.Method("POST", "/image/{uuid}", auth.NewEnsureAuth(apiImagePost))

View file

@ -32,44 +32,34 @@ func NewBounds() Bounds {
}
}
type LatLong interface {
Latitude() float64
Longitude() float64
}
/* not sure if used
type Location struct {
Latitude float64
Longitude float64
}
*/
type NoteImagePayload struct {
UUID string `json:"uuid"`
Cell H3Cell `json:"cell"`
Created time.Time `json:"created"`
}
type NoteAudio struct {
UUID string `db:"uuid"`
Breadcrumbs []NoteAudioBreadcrumbPayload
Created time.Time `db:"created"`
Creator int `db:"creator"`
Deleted *time.Time `db:"deleted"`
Duration int `db:"duration"`
IsAudioNormalized bool `db:"is_audio_normalized"`
IsTranscodedeToOgg bool `db:"is_transcoded_to_ogg"`
Transcription *string `db:"transcription"`
TranscriptionUserEdited bool `db:"transcription_user_edited"`
Version int `db:"version"`
UUID string `json:"uuid"`
Cell H3Cell `json:"cell"`
Created time.Time `json:"created"`
CreatorID int `db:"creator_id"`
Deleted *time.Time `json:"deleted"`
DeletorID *int32 `json:"deletor_id"`
Version int32 `json:"version"`
}
type NoteAudioPayload struct {
UUID string `json:"uuid"`
Breadcrumbs []NoteAudioBreadcrumbPayload `json:"breadcrumbs"`
Created time.Time `json:"created"`
Duration int `json:"duration"`
CreatorID int `json:"creator_id"`
Deleted *time.Time `json:"deleted"`
DeletorID *int32 `json:"deletor_id"`
Duration float32 `json:"duration"`
Transcription *string `json:"transcription"`
TranscriptionUserEdited bool `json:"transcriptionUserEdited"`
Version int `json:"version"`
Version int32 `json:"version"`
}
type ResponseMosquitoSource struct {
@ -97,14 +87,6 @@ type NoteAudioBreadcrumbPayload struct {
ManuallySelected bool `json:"manuallySelected"`
}
type NidusNotePayload struct {
UUID string `json:"uuid"`
Timestamp time.Time `json:"timestamp"`
H3Cell int64 `json:"h3cell"`
Images []string `json:"images"`
Text string `json:"text"`
}
type ResponseFieldseeker struct {
MosquitoSources []ResponseMosquitoSource `json:"sources"`
ServiceRequests []ResponseServiceRequest `json:"requests"`
@ -114,6 +96,7 @@ type ResponseFieldseeker struct {
// ResponseErr renderer type for handling all sorts of errors.
type ResponseClientIos struct {
Fieldseeker ResponseFieldseeker `json:"fieldseeker"`
Since time.Time `json:"since"`
}
func (i ResponseClientIos) Render(w http.ResponseWriter, r *http.Request) error {
@ -163,9 +146,9 @@ func NewResponseMosquitoInspection(i *models.FieldseekerMosquitoinspection) Resp
SiteCondition: i.Sitecond.GetOr(""),
}
}
func NewResponseMosquitoInspections(inspections *models.FieldseekerMosquitoinspectionSlice) []ResponseMosquitoInspection {
func NewResponseMosquitoInspections(inspections models.FieldseekerMosquitoinspectionSlice) []ResponseMosquitoInspection {
results := make([]ResponseMosquitoInspection, 0)
for _, i := range *inspections {
for _, i := range inspections {
results = append(results, NewResponseMosquitoInspection(i))
}
return results
@ -175,7 +158,7 @@ func (rtd ResponseMosquitoSource) Render(w http.ResponseWriter, r *http.Request)
return nil
}
func NewResponseMosquitoSource(ms *platform.MosquitoSource) ResponseMosquitoSource {
func NewResponseMosquitoSource(ms platform.MosquitoSource) ResponseMosquitoSource {
pl := ms.PointLocation
return ResponseMosquitoSource{
Active: toBool16(pl.Active),
@ -196,9 +179,9 @@ func NewResponseMosquitoSource(ms *platform.MosquitoSource) ResponseMosquitoSour
Zone: pl.Zone.GetOr(""),
}
}
func NewResponseMosquitoSources(sources *[]*platform.MosquitoSource) []ResponseMosquitoSource {
func NewResponseMosquitoSources(sources []platform.MosquitoSource) []ResponseMosquitoSource {
results := make([]ResponseMosquitoSource, 0)
for _, i := range *sources {
for _, i := range sources {
results = append(results, NewResponseMosquitoSource(i))
}
return results
@ -224,24 +207,22 @@ func (rtd ResponseMosquitoTreatment) Render(w http.ResponseWriter, r *http.Reque
}
func NewResponseMosquitoTreatment(i *models.FieldseekerTreatment) ResponseMosquitoTreatment {
return ResponseMosquitoTreatment{
/*
Comments: i.Comments(),
Created: i.Created().Format("2006-01-02T15:04:05.000Z"),
FieldTechnician: i.FieldTechnician(),
Habitat: i.Habitat(),
ID: i.ID(),
Product: i.Product(),
Quantity: i.Quantity(),
QuantityUnit: i.QuantityUnit(),
SiteCondition: i.SiteCondition(),
TreatAcres: i.TreatAcres(),
TreatHectares: i.TreatHectares(),
*/
Comments: i.Comments.GetOr(""),
Created: formatTime(i.Creationdate),
FieldTechnician: i.Fieldtech.GetOr(""),
Habitat: i.Habitat.GetOr(""),
ID: i.Globalid.String(),
Product: i.Product.GetOr(""),
Quantity: i.Qty.GetOr(0),
QuantityUnit: i.Qtyunit.GetOr(""),
SiteCondition: i.Sitecond.GetOr(""),
TreatAcres: i.Treatacres.GetOr(0.0),
TreatHectares: i.Treathectares.GetOr(0.0),
}
}
func NewResponseMosquitoTreatments(treatments *models.FieldseekerTreatmentSlice) []ResponseMosquitoTreatment {
func NewResponseMosquitoTreatments(treatments models.FieldseekerTreatmentSlice) []ResponseMosquitoTreatment {
results := make([]ResponseMosquitoTreatment, 0)
for _, i := range *treatments {
for _, i := range treatments {
results = append(results, NewResponseMosquitoTreatment(i))
}
return results
@ -298,9 +279,9 @@ func NewResponseServiceRequest(sr *models.FieldseekerServicerequest) ResponseSer
Zip: sr.Reqzip.GetOr(""),
}
}
func NewResponseServiceRequests(requests *models.FieldseekerServicerequestSlice) []ResponseServiceRequest {
func NewResponseServiceRequests(requests models.FieldseekerServicerequestSlice) []ResponseServiceRequest {
results := make([]ResponseServiceRequest, 0)
for _, i := range *requests {
for _, i := range requests {
results = append(results, NewResponseServiceRequest(i))
}
return results
@ -326,9 +307,9 @@ func NewResponseTrapDatum(td *models.FieldseekerTraplocation) ResponseTrapData {
Name: td.Name.GetOr(""),
}
}
func NewResponseTrapData(data *models.FieldseekerTraplocationSlice) []ResponseTrapData {
func NewResponseTrapData(data models.FieldseekerTraplocationSlice) []ResponseTrapData {
results := make([]ResponseTrapData, 0)
for _, i := range *data {
for _, i := range data {
results = append(results, NewResponseTrapDatum(i))
}
return results