func idspecsToFlightV2s(r *http.Request) ([]*newfdb.Flight, error) { c := oldappengine.NewContext(r) db := oldfgae.FlightDB{C: c} newFlights := []*newfdb.Flight{} idspecs, err := FormValueIdSpecs(r) if err != nil { return newFlights, err } for _, idspec := range idspecs { oldF, err := db.LookupById(idspec) if err != nil { return newFlights, err } else if oldF == nil { return newFlights, fmt.Errorf("flight '%s' not found", idspec) } newF, err := oldF.V2() if err != nil { return newFlights, err } newFlights = append(newFlights, newF) } return newFlights, nil }
func idspecsToMapLines(r *http.Request) ([]newui.MapLine, error) { c := oldappengine.NewContext(r) db := oldfgae.FlightDB{C: c} lines := []newui.MapLine{} idspecs, err := FormValueIdSpecs(r) if err != nil { return lines, err } for _, idspec := range idspecs { oldF, err := db.LookupById(idspec) if err != nil { return lines, err } else if oldF == nil { return lines, fmt.Errorf("flight '%s' not found", idspec) } newF, err := oldF.V2() if err != nil { return lines, err } flightLines := newui.FlightToMapLines(newF) lines = append(lines, flightLines...) } return lines, nil }
func decodetrackHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) db := fdb.FlightDB{C: c} icao := r.FormValue("icaoid") callsign := strings.TrimSpace(r.FormValue("callsign")) if icao == "" || callsign == "" { http.Error(w, "need args {icaoid,callsign}", http.StatusInternalServerError) return } if tracks, err := db.ReadTrackFragments(icao, callsign); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else { var params = map[string]interface{}{ "Tracks": tracks, "Callsign": callsign, "Icao24": icao, } if err := templates.ExecuteTemplate(w, "fdb-decodetrack", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } }
func addtrackHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) db := fdb.FlightDB{C: c} icaoId := r.FormValue("icaoid") callsign := strings.TrimSpace(r.FormValue("callsign")) tStr := r.FormValue("track") // Validate it works before persisting t := ftype.Track{} if err := t.Base64Decode(tStr); err != nil { c.Errorf(" /mdb/addtrack: decode failed: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) } ftf := ftype.FrozenTrackFragment{ TrackBase64: tStr, Callsign: callsign, Icao24: icaoId, } if err := db.AddTrackFrgament(ftf); err != nil { c.Errorf(" /mdb/addtrack: db.AddTrackFragment failed: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) } // 3. Routine to merge track fragments ? Extra credit ? // c.Infof(" /mdb/addtrack: added %d points for [%s][%s]", len(t), icaoId, callsign) w.Write([]byte(fmt.Sprintf("Added %d for %s\n", len(t), icaoId))) }
func skimmerReport(c appengine.Context, s, e time.Time, opt ReportOptions) ([]ReportRow, ReportMetadata, error) { meta := ReportMetadata{} fdb := fdb.FlightDB{C: c} maybeMemcache(&fdb, e) tags := []string{flightdb.KTagSERFR1} if flights, err := fdb.LookupTimeRangeByTags(tags, s, e); err != nil { return nil, nil, err } else { out := []ReportRow{} for _, f := range flights { t := f.BestTrack() _, analysis := t.SkimsToSFO(opt.Skimmer_AltitudeTolerance, opt.Skimmer_MinDurationNM, 14.5, 50) // Output one row per event, in a kind of hacky way extras := fmt.Sprintf("&skim=1&alttol=%.0f&mindist=%.0f", opt.Skimmer_AltitudeTolerance, opt.Skimmer_MinDurationNM) for _, event := range analysis.Events { row := SkimRow{Url: flight2Url(f) + template.HTML(extras), F: f, Source: t.LongSource(), A: analysis} row.A.Events = []flightdb.SkimEvent{event} out = append(out, row) } } return out, meta, nil } }
func brixxViolationReport(c appengine.Context, s, e time.Time, opt ReportOptions) ([]ReportRow, ReportMetadata, error) { meta := ReportMetadata{} fdb := fdb.FlightDB{C: c} maybeMemcache(&fdb, e) tags := []string{flightdb.KTagBRIXX} if flights, err := fdb.LookupTimeRangeByTags(tags, s, e); err != nil { return nil, nil, err } else { out := []ReportRow{} // Hmm. Really need a better way to link 'violations in this report' to dots on the map for _, f := range flights { t := f.BestTrack() for i, tp := range t { if tp.Latlong.DistNM(sfo.KLatlongSJC) > 20 { continue } if tp.AltitudeFeet < 5500 && tp.Latlong.Long < sfo.KFixes["YADUT"].Long { row := BrixxRow{ Url: flight2Url(f), F: f, Source: t.LongSource(), TP: &t[i], } out = append(out, row) break } } } return out, meta, nil } }
func jobTrackTimezoneHandler(r *http.Request, f *oldfdb.Flight) (string, error) { c := appengine.NewContext(r) defaultTP := f.Track.ClosestTrackpoint(sfo.KFixes["EPICK"]) adsbTP := f.Tracks["ADSB"].ClosestTrackpoint(sfo.KFixes["EPICK"]) trackTimeDelta := defaultTP.TimestampUTC.Sub(adsbTP.TimestampUTC) str := fmt.Sprintf("OK, looked up %s\n Default: %s\n ADSB : %s\n delta: %s\n", f, defaultTP, adsbTP, trackTimeDelta) if trackTimeDelta < -4*time.Hour || trackTimeDelta > 4*time.Hour { str += fmt.Sprintf("* recoding\n* before: %s\n", f.Tracks["ADSB"]) for i, _ := range f.Tracks["ADSB"] { f.Tracks["ADSB"][i].TimestampUTC = f.Tracks["ADSB"][i].TimestampUTC.Add(time.Hour * -8) } str += fmt.Sprintf("* after : %s\n", f.Tracks["ADSB"]) db := oldfgae.FlightDB{C: oldappengine.NewContext(r)} if err := db.UpdateFlight(*f); err != nil { log.Errorf(c, "Persist Flight %s: %v", f, err) return str, err } log.Infof(c, "Updated flight %s", f) str += fmt.Sprintf("--\nFlight was updated\n") } else { log.Debugf(c, "Skipped flight %s, delta=%s", f, trackTimeDelta) str += "--\nFlight was OK, left untouched\n" } return str, nil }
func jobV2adsbHandler(r *http.Request, f *oldfdb.Flight) (string, error) { c := appengine.NewContext(r) str := "" if f.HasTrack("ADSB") { return "", nil } // Already has one err, deb := f.GetV2ADSBTrack(urlfetch.Client(c)) str += fmt.Sprintf("*getv2ADSB [%v]:-\n", err, deb) if err != nil { return str, err } if !f.HasTrack("ADSB") { return "", nil } // Didn't find one f.Analyse() // Retrigger Class-B stuff db := oldfgae.FlightDB{C: oldappengine.NewContext(r)} if err := db.UpdateFlight(*f); err != nil { log.Errorf(c, "Persist Flight %s: %v", f, err) return str, err } log.Infof(c, "Updated flight %s", f) str += fmt.Sprintf("--\nFlight was updated\n") return str, nil }
func brixx1Report(c appengine.Context, s, e time.Time, opt ReportOptions) ([]ReportRow, ReportMetadata, error) { meta := ReportMetadata{} fdb := fdb.FlightDB{C: c} maybeMemcache(&fdb, e) tags := []string{flightdb.KTagBRIXX} if flights, err := fdb.LookupTimeRangeByTags(tags, s, e); err != nil { return nil, nil, err } else { out := []ReportRow{} meta["[A] Total BRIXX flights "] = float64(len(flights)) for _, f := range flights { hasAdsb := false if _, exists := f.Tracks["ADSB"]; exists == true { meta["[B] With data from "+f.Tracks["ADSB"].LongSource()]++ } if t, exists := f.Tracks["FA"]; exists == true { meta["[B] With data from "+f.Tracks["FA"].LongSource()]++ hasAdsb = t.IsFromADSB() } else { meta["[B] With data from "+f.Track.LongSource()]++ } row := SERFR1Row{flight2Url(f), f, hasAdsb, false} out = append(out, row) } return out, meta, nil } }
// This enqueues tasks for each individual day, or flight func batchFlightScanHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) tags := []string{} //"ADSB"} // Maybe make this configurable ... n := 0 str := "" s, e, _ := widget.FormValueDateRange(r) job := r.FormValue("job") if job == "" { http.Error(w, "Missing argument: &job=foo", http.StatusInternalServerError) } days := date.IntermediateMidnights(s.Add(-1*time.Second), e) // decrement start, to include it for _, day := range days { // Get the keys for all the flights on this day. fdb := oldfgae.FlightDB{C: oldappengine.NewContext(r)} dStart, dEnd := date.WindowForTime(day) dEnd = dEnd.Add(-1 * time.Second) keys, err := fdb.KeysInTimeRangeByTags(tags, dStart, dEnd) if err != nil { log.Errorf(c, "upgradeHandler: enqueue: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } singleFlightUrl := "/backend/fdb-batch/flight" for _, key := range keys { str += fmt.Sprintf("Enqueing day=%s: %s?job=%s&key=%s\n", day.Format("2006.01.02"), singleFlightUrl, job, key.Encode()) t := taskqueue.NewPOSTTask(singleFlightUrl, map[string][]string{ "date": {day.Format("2006.01.02")}, "key": {key.Encode()}, "job": {job}, }) if _, err := taskqueue.Add(c, t, "batch"); err != nil { log.Errorf(c, "upgradeHandler: enqueue: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } n++ } } log.Infof(c, "enqueued %d batch items for '%s'", n, job) w.Header().Set("Content-Type", "text/plain") w.Write([]byte(fmt.Sprintf("OK, batch, enqueued %d tasks for %s\n%s", n, job, str))) }
// A super widget, for all the batch jobs func formValueFlightByKey(r *http.Request) (*oldfdb.Flight, error) { fdb := oldfgae.FlightDB{C: oldappengine.NewContext(r)} key, err := olddatastore.DecodeKey(r.FormValue("key")) if err != nil { return nil, fmt.Errorf("fdb-batch: %v", err) } f, err := fdb.KeyToFlight(key) if err != nil { return nil, fmt.Errorf("fdb-batch: %v", err) } return f, nil }
func adsbClassbReport(c appengine.Context, s, e time.Time, opt ReportOptions) ([]ReportRow, ReportMetadata, error) { fdb := fdb.FlightDB{C: c} maybeMemcache(&fdb, e) tags := []string{flightdb.KTagSERFR1} if flights, err := fdb.LookupTimeRangeByTags(tags, s, e); err != nil { return nil, nil, err } else { meta := ReportMetadata{} rows := []ReportRow{} for _, f := range flights { row := ACBRow{F: f, Url: flight2Url(f)} _, cbt := f.SFOClassB("FA", nil) worst := cbt.FindWorstPoint() row.FAViolation = (worst != nil) if worst != nil { row.FAAnalysis = worst.A } if f.HasTrack("ADSB") { row.HadLocalTrack = true _, cbt := f.SFOClassB("ADSB", nil) worst := cbt.FindWorstPoint() row.LocalViolation = (worst != nil) if worst != nil { row.LocalAnalysis = worst.A } } if row.LocalViolation && !row.FAViolation { row.FoundBonusViolation = true } if row.LocalViolation && row.FAViolation { hFA := row.FAAnalysis.BelowBy hLocal := row.LocalAnalysis.BelowBy row.IncreasedViolationBy = hLocal - hFA } if row.LocalViolation || row.FAViolation { rows = append(rows, row) } } return rows, meta, nil } }
func discrepReport(c appengine.Context, s, e time.Time, opt ReportOptions) ([]ReportRow, ReportMetadata, error) { meta := ReportMetadata{} fdb := fdb.FlightDB{C: c} maybeMemcache(&fdb, e) tags := []string{flightdb.KTagSERFR1} if flights, err := fdb.LookupTimeRangeByTags(tags, s, e); err != nil { return nil, nil, err } else { out := []ReportRow{} for _, f := range flights { // If only one source has ADS-B, flag it interest := false if f.HasTrack("ADSB") && !f.HasTrack("FA:TA") { interest = true meta["Missing from FlightAware ADS-B"] += 1 } if !f.HasTrack("ADSB") && f.HasTrack("FA:TA") { interest = true meta["Missing from local ADS-B"] += 1 } // If both have ADS-B, look for missed violations if f.HasTrack("ADSB") && f.HasTrack("FA:TA") { if f.HasTag("ClassB:ADSB") && !f.HasTag("ClassB:FA") { interest = true meta["Bonus violations"] += 1 } if !f.HasTag("ClassB:ADSB") && f.HasTag("ClassB:FA") { interest = true meta["Missed violations"] += 1 } } if !interest { continue } row := SERFR1Row{flight2Url(f), f, false, false} out = append(out, row) } return out, meta, nil } }
func serfr1AtReport(c appengine.Context, s, e time.Time, opt ReportOptions) ([]ReportRow, ReportMetadata, error) { meta := ReportMetadata{} fdb := fdb.FlightDB{C: c} maybeMemcache(&fdb, e) tags := []string{flightdb.KTagSERFR1} pos := sfo.KFixes[opt.Waypoint] out := []ReportRow{} iter := fdb.NewIter(fdb.QueryTimeRangeByTags(tags, s, e)) nSerfr1 := 0 for { f, err := iter.NextWithErr() if err != nil { fdb.C.Errorf("serfr1AtReport iterator failed: %v", err) return nil, nil, err } else if f == nil { break // We've hit EOF } nSerfr1++ if _, exists := f.Tracks["ADSB"]; exists == true { meta["[B] with data from "+f.Tracks["ADSB"].LongSource()]++ } if _, exists := f.Tracks["FA"]; exists == true { meta["[B] with data from "+f.Tracks["FA"].LongSource()]++ } else { meta["[B] with data from "+f.Track.LongSource()]++ } if itp, err := f.BestTrack().PointOfClosestApproach(pos); err != nil { c.Infof("Skipping flight %s: err=%v", f, err) } else { url := template.HTML(fmt.Sprintf("%s&waypoint=%s", flight2Url(*f), opt.Waypoint)) f.Tracks = nil // avoid running out of F1 RAM! row := SERFR1AtRow{url, *f, itp} out = append(out, row) } } meta["[A] Total SERFR1 flights "] = float64(nSerfr1) return out, meta, nil }
func serfr1Report(c appengine.Context, s, e time.Time, opt ReportOptions) ([]ReportRow, ReportMetadata, error) { fdb := fdb.FlightDB{C: c} maybeMemcache(&fdb, e) meta := ReportMetadata{} out := []ReportRow{} idspecs := []string{} reportFunc := func(f *flightdb.Flight) { classBViolation := f.HasTag(flightdb.KTagReliableClassBViolation) hasAdsb := false if _, exists := f.Tracks["ADSB"]; exists == true { meta["[B] with data from "+f.Tracks["ADSB"].LongSource()]++ idspecs = append(idspecs, fmt.Sprintf("%s@%d", f.Id.ModeS, f.EnterUTC.Unix())) } if t, exists := f.Tracks["FA"]; exists == true { meta["[B] with data from "+f.Tracks["FA"].LongSource()]++ hasAdsb = t.IsFromADSB() } else { meta["[B] with data from "+f.Track.LongSource()]++ } fClone := f.ShallowCopy() row := SERFR1Row{flight2Url(*fClone), *fClone, hasAdsb, classBViolation} out = append(out, row) } tags := []string{flightdb.KTagSERFR1} if err := fdb.IterWith(fdb.QueryTimeRangeByTags(tags, s, e), reportFunc); err != nil { return nil, nil, err } approachUrl := fmt.Sprintf("http://ui-dot-serfr0-fdb.appspot.com/fdb/approach?idspec=%s", strings.Join(idspecs, ",")) meta[fmt.Sprintf("[Z] %s", approachUrl)] = 1 meta["[A] Total SERFR1 flights "] = float64(len(out)) return out, meta, nil }
func jobOceanicTagHandler(r *http.Request, f *oldfdb.Flight) (string, error) { c := appengine.NewContext(r) str := "" if f.HasTag("OCEANIC") { return "", nil } if !f.IsOceanic() { return "", nil } // It's oceanic, but missing a tag ... update f.Tags[oldfdb.KTagOceanic] = true db := oldfgae.FlightDB{C: oldappengine.NewContext(r)} if err := db.UpdateFlight(*f); err != nil { log.Errorf(c, "Persist Flight %s: %v", f, err) return str, err } log.Infof(c, "Updated flight %s", f) str += fmt.Sprintf("--\nFlight was updated\n") return str, nil }
// We examine the tags CGI arg, which should be a pipe-delimited set of flight tags. func flightListHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) db := fdb.FlightDB{C: c} tags := []string{} if r.FormValue("tags") != "" { tags = append(tags, strings.Split(r.FormValue("tags"), "|")...) } timeRange := regexp.MustCompile("/fdb/(.+)$").ReplaceAllString(r.URL.Path, "$1") var flights []ftype.Flight var err error switch timeRange { case "recent": flights, err = db.LookupRecentByTags(tags, 200) case "today": s, e := date.WindowForToday() flights, err = db.LookupTimeRangeByTags(tags, s, e) case "yesterday": s, e := date.WindowForYesterday() flights, err = db.LookupTimeRangeByTags(tags, s, e) } if err != nil { c.Errorf(" %s: %v", r.URL.Path, err) http.Error(w, err.Error(), http.StatusInternalServerError) return } var params = map[string]interface{}{ "Tags": tags, "TimeRange": timeRange, "Flights": flights2params(flights), } if err := templates.ExecuteTemplate(w, "fdb-recentlist", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
func report3Handler(w http.ResponseWriter, r *http.Request) { if r.FormValue("rep") == "" { var params = map[string]interface{}{ "Yesterday": date.NowInPdt().AddDate(0, 0, -1), "Reports": report.ListReports(), "FormUrl": "/report3/", "Waypoints": sfo.ListWaypoints(), } if err := templates.ExecuteTemplate(w, "report3-form", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } rep, err := report.SetupReport(r) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if r.FormValue("debug") != "" { str := fmt.Sprintf("Report Options\n\n%s\n", rep.Options) w.Header().Set("Content-Type", "text/plain") w.Write([]byte(fmt.Sprintf("OK\n\n%s\n", str))) return } //airframes := ref.NewAirframeCache(c) client := newurlfetch.Client(newappengine.NewContext(r)) metars, err := metar.FetchFromNOAA(client, "KSFO", rep.Start.AddDate(0, 0, -1), rep.End.AddDate(0, 0, 1)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } v1idspecs := []string{} v2idspecs := []string{} v1idspecComplement := []string{} reportFunc := func(oldF *oldfdb.Flight) { newF, err := oldF.V2() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } else { newF.ComputeIndicatedAltitudes(metars) if included, err := rep.Process(newF); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } else if included { v1idspecs = append(v1idspecs, oldF.UniqueIdentifier()) v2idspecs = append(v2idspecs, newF.IdSpec()) } else { v1idspecComplement = append(v1idspecComplement, oldF.UniqueIdentifier()) } } } tags := tagList(rep.Options) db := oldfgae.FlightDB{C: oldappengine.Timeout(oldappengine.NewContext(r), 600*time.Second)} s, e := rep.Start, rep.End if err := db.LongIterWith(db.QueryTimeRangeByTags(tags, s, e), reportFunc); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } postButtons := ButtonPOST(fmt.Sprintf("%d Matches as a VectorMap", len(v1idspecs)), fmt.Sprintf("/fdb/trackset2?%s", rep.ToCGIArgs()), v1idspecs) postButtons += ButtonPOST(fmt.Sprintf("%d Non-matches as a VectorMap", len(v1idspecComplement)), fmt.Sprintf("/fdb/trackset2?%s", rep.ToCGIArgs()), v1idspecComplement) if rep.Name == "sfoclassb" { postButtons += ButtonPOST(fmt.Sprintf("%d Matches as ClassBApproaches", len(v1idspecs)), fmt.Sprintf("/fdb/approach2?%s", rep.ToCGIArgs()), v1idspecs) // This is kinda useless, as it isn't limited to SERFR1 things postButtons += ButtonPOST(fmt.Sprintf("%d Non-matches as ClassBApproaches", len(v1idspecComplement)), fmt.Sprintf("/fdb/approach2?%s", rep.ToCGIArgs()), v1idspecComplement) postButtons += ButtonPOST(fmt.Sprintf("%d Matches as ClassBApproaches (delta)", len(v1idspecs)), fmt.Sprintf("/fdb/approach2?%s&colorby=delta", rep.ToCGIArgs()), v1idspecs) // This is kinda useless, as it isn't limited to SERFR1 things postButtons += ButtonPOST(fmt.Sprintf("%d Non-matches as ClassBApproaches (delta)", len(v1idspecComplement)), fmt.Sprintf("/fdb/approach2?%s&colorby=delta", rep.ToCGIArgs()), v1idspecComplement) } var params = map[string]interface{}{ "R": rep, "Metadata": rep.MetadataTable(), "PostButtons": template.HTML(postButtons), "OptStr": template.HTML(fmt.Sprintf("<pre>%s</pre>\n", rep.Options)), "IdSpecs": template.HTML(strings.Join(v1idspecs, ",")), } if err := templates.ExecuteTemplate(w, "report3-results", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
func maybeMemcache(fdb *fdb.FlightDB, queryEnd time.Time) { if time.Now().Sub(queryEnd) > time.Hour { fdb.Memcache = true } }
func lookupHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) db := fdb.FlightDB{C: c} id := r.FormValue("id") if f, err2 := db.LookupById(id); err2 != nil { c.Errorf(" /mdb/lookup: %v", err2) http.Error(w, err2.Error(), http.StatusInternalServerError) } else if f == nil { http.Error(w, fmt.Sprintf("id=%s not found", id), http.StatusInternalServerError) } else { c.Infof("Tags: %v, Tracks: %v", f.TagList(), f.TrackList()) _, classBTrack := f.SFOClassB("", nil) f.Analyse() // Repopulate the flight tags; useful when debugging new analysis stuff // Todo: collapse all these separate tracks down into the single point/line list thing fr24TrackJSVar := classBTrack.ToJSVar() // For Flightaware tracks //faClassBTrack := fdb.ClassBTrack{} faTrackJSVar := template.JS("{}") if _, exists := f.Tracks["FA"]; exists == true { _, faClassBTrack := f.SFOClassB("FA", nil) faTrackJSVar = faClassBTrack.ToJSVar() //fr24TrackJSVar = template.JS("{}") } // For ADS-B tracks ! adsbTrackJSVar := template.JS("{}") if _, exists := f.Tracks["ADSB"]; exists == true { _, adsbClassBTrack := f.SFOClassB("ADSB", nil) adsbTrackJSVar = adsbClassBTrack.ToJSVar() } skimTrackJSVar := template.JS("{}") if r.FormValue("skim") != "" { alttol, _ := strconv.ParseFloat(r.FormValue("alttol"), 64) mindist, _ := strconv.ParseFloat(r.FormValue("mindist"), 64) skimTrack, _ := f.BestTrack().SkimsToSFO(alttol, mindist, 15.0, 40.0) skimTrackJSVar = skimTrack.ToJSVar() fr24TrackJSVar = template.JS("{}") faTrackJSVar = template.JS("{}") adsbTrackJSVar = template.JS("{}") } mapPoints := []MapPoint{} mapLines := []MapLine{} // &waypoint=EPICK if waypoint := r.FormValue("waypoint"); waypoint != "" { //pos := geo.Latlong{37.060312, -121.990814} pos := sfo.KFixes[waypoint] if itp, err := f.BestTrack().PointOfClosestApproach(pos); err != nil { c.Infof(" ** Error: %v", err) } else { mapPoints = append(mapPoints, MapPoint{Icon: "red", ITP: &itp}) mapPoints = append(mapPoints, MapPoint{Pos: &itp.Ref, Text: "** Reference point"}) mapLines = append(mapLines, MapLine{Line: &itp.Line, Color: "#ff8822"}) mapLines = append(mapLines, MapLine{Line: &itp.Perp, Color: "#ff2288"}) } } // &boxes=1 if r.FormValue("boxes") != "" { if true { // fr24 for _, box := range f.Track.AsContiguousBoxes() { mapLines = append(mapLines, LatlongTimeBoxToMapLines(box, "#118811")...) } } if t, exists := f.Tracks["FA"]; exists == true { for _, box := range t.AsContiguousBoxes() { mapLines = append(mapLines, LatlongTimeBoxToMapLines(box, "#1111aa")...) } } if t, exists := f.Tracks["ADSB"]; exists == true { for _, box := range t.AsContiguousBoxes() { mapLines = append(mapLines, LatlongTimeBoxToMapLines(box, "#aaaa11")...) } } } pointsStr := "{\n" for i, mp := range mapPoints { pointsStr += fmt.Sprintf(" %d: {%s},\n", i, mp.ToJSStr("")) } pointsJS := template.JS(pointsStr + " }\n") linesStr := "{\n" for i, ml := range mapLines { linesStr += fmt.Sprintf(" %d: {%s},\n", i, ml.ToJSStr("")) } linesJS := template.JS(linesStr + " }\n") box := sfo.KBoxSFO120K if r.FormValue("report") == "level" { box = sfo.KBoxPaloAlto20K } else if r.FormValue("report") == "stack" { box = sfo.KBoxSFO10K } var params = map[string]interface{}{ "F": f, "Legend": f.Legend(), "Oneline": f.String(), "Text": fmt.Sprintf("%#v", f.Id), "ClassB": "", "MapsAPIKey": kGoogleMapsAPIKey, "Center": sfo.KLatlongSERFR1, "Zoom": 10, "CaptureArea": box, "MapsTrack": fr24TrackJSVar, "FlightawareTrack": faTrackJSVar, "ADSBTrack": adsbTrackJSVar, "SkimTrack": skimTrackJSVar, "Points": pointsJS, "Lines": linesJS, } templateName := "fdb-lookup" if r.FormValue("map") != "" { templateName = "fdb-lookup-map" } if err7 := templates.ExecuteTemplate(w, templateName, params); err7 != nil { http.Error(w, err7.Error(), http.StatusInternalServerError) } } }
func queryHandler(w http.ResponseWriter, r *http.Request) { if r.FormValue("date") == "" && r.FormValue("epoch") == "" { var params = map[string]interface{}{ "TwoHoursAgo": date.NowInPdt().Add(-2 * time.Hour), } if err := templates.ExecuteTemplate(w, "fdb-queryform", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } c := appengine.NewContext(r) db := fdb.FlightDB{C: c, Memcache: true} var t time.Time if r.FormValue("epoch") != "" { if epoch, err := strconv.ParseInt(r.FormValue("epoch"), 10, 64); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else { t = time.Unix(epoch, 0) } } else { var err2 error t, err2 = date.ParseInPdt("2006/01/02 15:04:05", r.FormValue("date")+" "+r.FormValue("time")) if err2 != nil { http.Error(w, err2.Error(), http.StatusInternalServerError) return } } var refPoint *geo.Latlong = nil if r.FormValue("lat") != "" { refPoint = &geo.Latlong{} var err error if refPoint.Lat, err = strconv.ParseFloat(r.FormValue("lat"), 64); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if refPoint.Long, err = strconv.ParseFloat(r.FormValue("long"), 64); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } if snapshots, err := db.LookupSnapshotsAtTimestampUTC(t.UTC(), refPoint, 1000); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } else { var params = map[string]interface{}{ "Legend": buildLegend(t), "SearchTimeUTC": t.UTC(), "SearchTime": date.InPdt(t), "Flights": snapshots2params(snapshots), "FlightsJS": ftype.FlightSnapshotSet(snapshots).ToJSVar(), "MapsAPIKey": kGoogleMapsAPIKey, "Center": sfo.KLatlongSERFR1, "Zoom": 9, // "CaptureArea": fdb.KBoxSFO120K, // comment out, as we don't want it in this view } if r.FormValue("resultformat") == "json" { for i, _ := range snapshots { snapshots[i].F.Track = nil snapshots[i].F.Tracks = nil } js, err := json.Marshal(snapshots) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(js) } else { templateName := "fdb-queryresults-map" if r.FormValue("resultformat") == "list" { templateName = "fdb-queryresults-list" } if err := templates.ExecuteTemplate(w, templateName, params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } } }
func debugHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) db := fdb.FlightDB{C: c} id := r.FormValue("id") blob, f, err := db.GetBlobById(id) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else if blob == nil { http.Error(w, fmt.Sprintf("id=%s not found", id), http.StatusInternalServerError) return } blob.Flight = []byte{} // Zero this out s, e := f.Track.TimesInBox(sfo.KBoxSFO120K) log := blob.GestationLog blob.GestationLog = "" str := fmt.Sprintf("OK\n* Flight found: %s\n* Tracks: %q\n", f, f.TrackList()) str += fmt.Sprintf("* Points in default track: %d\n", len(f.Track)) str += fmt.Sprintf("* Default's start/end: %s, %s\n", s, e) str += fmt.Sprintf("\n** Gestation log:-\n%s\n", log) str += fmt.Sprintf("** Blob:-\n* Id=%s, Icao24=%s\n* Enter/Leave: %s -> %s\n* Tags: %v\n", blob.Id, blob.Icao24, blob.EnterUTC, blob.LeaveUTC, blob.Tags) v2URL := f.GetV2JsonUrl() str += "\n* V2 URL: " + v2URL + "\n" str += "\n**** Tracks Before\n\n" str += fmt.Sprintf("** %s [DEFAULT]\n", f.Track) for name, t := range f.Tracks { str += fmt.Sprintf("** %s %s\n", name, t) } err, deb := f.GetV2ADSBTrack(urlfetch.Client(c)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } str += "\n***\n" + deb str += "\n**** Tracks After\n\n" str += fmt.Sprintf("** %s [DEFAULT]\n", f.Track) for name, t := range f.Tracks { str += fmt.Sprintf("** %s %s\n", name, t) } str += "--\nPersisting ...\n" if err := db.UpdateFlight(*f); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } /* consistent,debug := f.TracksAreConsistentDebug() str += fmt.Sprintf("\n** Track consistency (%v)\n\n%s\n", consistent, debug) //str += "\n** Default track\n" //for _,tp := range f.Track { str += fmt.Sprintf(" * %s\n", tp) } */ w.Header().Set("Content-Type", "text/plain") w.Write([]byte(str)) }
func classbReport(c appengine.Context, s, e time.Time, opt ReportOptions) ([]ReportRow, ReportMetadata, error) { fdb := fdb.FlightDB{C: c} maybeMemcache(&fdb, e) tags := []string{ flightdb.KTagReliableClassBViolation, flightdb.KTagLocalADSBClassBViolation, } meta := ReportMetadata{} h := histogram.Histogram{} // Only use it for the stats rows := []ReportRow{} seen := map[string]bool{} // Because we do two passes, we might see same flight twice. metars, err := metar.FetchFromNOAA(urlfetch.Client(c), "KSFO", s.Add(-6*time.Hour), e.Add(6*time.Hour)) if err != nil { return rows, meta, err } reportFunc := func(f *flightdb.Flight) { if _, exists := seen[f.UniqueIdentifier()]; exists { return } else { seen[f.UniqueIdentifier()] = true } bestTrack := "FA" if f.HasTag(flightdb.KTagLocalADSBClassBViolation) { bestTrack = "ADSB" } _, cbt := f.SFOClassB(bestTrack, metars) tmpRows := []ReportRow{} seq := 0 for _, cbtp := range cbt { if cbtp.A.IsViolation() { fClone := f.ShallowCopy() tmpRows = append(tmpRows, CBRow{seq, flight2Url(*fClone), *fClone, cbtp.TP, cbtp.A}) seq++ } } if len(tmpRows) == 0 { return } worstCBRow := tmpRows[0].(CBRow) if seq > 0 { // Select the worst row n, belowBy := 0, 0.0 for i, row := range tmpRows { if row.(CBRow).A.BelowBy > belowBy { n, belowBy = i, row.(CBRow).A.BelowBy } } worstCBRow = tmpRows[n].(CBRow) worstCBRow.Seq = 0 // fake this out for the webpage } if opt.ClassB_LocalDataOnly && !f.HasTag(flightdb.KTagLocalADSBClassBViolation) { meta["[C] -- Skippped; not local - "+worstCBRow.TP.LongSource()]++ } else { meta["[C] -- Detected via "+worstCBRow.TP.LongSource()]++ h.Add(histogram.ScalarVal(worstCBRow.A.BelowBy)) if opt.ClassB_OnePerFlight { rows = append(rows, worstCBRow) } else { rows = append(rows, tmpRows...) } } } // Need to do multiple passes, because of tagA-or-tagB sillness // In each case, limit to SERFR1 flights for _, tag := range tags { theseTags := []string{tag, flightdb.KTagSERFR1} if err := fdb.IterWith(fdb.QueryTimeRangeByTags(theseTags, s, e), reportFunc); err != nil { return nil, nil, err } } if n, err := fdb.CountTimeRangeByTags([]string{flightdb.KTagSERFR1}, s, e); err != nil { return nil, nil, err } else { meta["[A] Total SERFR1 Flights"] = float64(n) } if stats, valid := h.Stats(); valid { meta["[B] Num violating flights"] = float64(stats.N) meta["[D] Mean violation below Class B floor"] = float64(int(stats.Mean)) meta["[D] Stddev"] = float64(int(stats.Stddev)) } else { meta["[B] Num violating flights"] = 0.0 } return rows, meta, nil }
func serfr1ComplaintsReport(c appengine.Context, s, e time.Time, opt ReportOptions) ([]ReportRow, ReportMetadata, error) { fdb := fdb.FlightDB{C: c} maybeMemcache(&fdb, e) cdb := complaintdb.ComplaintDB{C: c, Memcache: true} meta := ReportMetadata{} memKey := fmt.Sprintf("serfr1complaintsreport:%s:%d-%d", s.Unix(), e.Unix()) if rows, meta, err := reportFromMemcache(c, memKey); err == nil { return rows, meta, err } if complaints, err := cdb.GetComplaintsInSpan(s, e); err != nil { return nil, nil, err } else { meta["[B] Total disturbance reports"] = float64(len(complaints)) nFlights := 0 nFpC := map[string]int{} // num flights per carrier nCpC := map[string]int{} // num complaints per carrier // Aggregate complaints per flightnumber (as seen from complaints) nC, nSB, nWC := map[string]int{}, map[string]int{}, map[string]int{} for _, comp := range complaints { if k := comp.AircraftOverhead.FlightNumber; k != "" { nC[k] += 1 nWC[k] += comp.Loudness if comp.HeardSpeedbreaks { nSB[k] += 1 nWC[k] += 4 } } } rows := []SCRow{} reportFunc := func(f *flightdb.Flight) { nFlights++ carrier := f.Id.Designator.IATAAirlineDesignator if carrier != "" { nFpC[carrier] += 1 } if k := f.Id.Designator.String(); k != "" { if carrier != "" { nCpC[carrier] += nC[k] } fClone := f.ShallowCopy() scRow := SCRow{ NumComplaints: nC[k], NumSpeedbrakes: nSB[k], WeightedComplaints: nWC[k], F: *fClone, } rows = append(rows, scRow) meta["[B] SERFR1 disturbance reports"] += float64(nC[k]) } } tags := []string{flightdb.KTagSERFR1} if err := fdb.IterWith(fdb.QueryTimeRangeByTags(tags, s, e), reportFunc); err != nil { return nil, nil, err } meta["[A] Total flights we saw on SERFR1"] += float64(nFlights) meta["[C] Average number of reports per overflight"] = float64(int64(float64(len(complaints)) / float64(nFlights))) sort.Sort(SCRowByNumComplaints(rows)) out := []ReportRow{} for _, r := range rows { out = append(out, r) } bestCarrier, worstCarrier := "", "" bestScore, worstScore := 999, -1 for carrier, nFlights := range nFpC { if nFlights < 3 { continue } cPerFlight := nCpC[carrier] / nFlights if cPerFlight < bestScore { bestCarrier, bestScore = carrier, cPerFlight } if cPerFlight > worstScore { worstCarrier, worstScore = carrier, cPerFlight } } meta["[D] Worst airline (average reports per overflight) - "+worstCarrier] = float64(worstScore) meta["[E] Best airline (average reports per overflight) - "+bestCarrier] = float64(bestScore) // reportToMemcache(c, out, meta, memKey) return out, meta, nil } }
func addflightHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) log := fmt.Sprintf("* addFlightHandler invoked: %s\n", time.Now().UTC()) fsStr := r.FormValue("flightsnapshot") fs := ftype.FlightSnapshot{} if err := fs.Base64Decode(fsStr); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } fr24Id := fs.F.Id.ForeignKeys["fr24"] // This is the only field we take from the log += fmt.Sprintf("* fr24 key: %s\n", fr24Id) db := fdb.FlightDB{C: c} fr24db, err := fdb24.NewFlightDBFr24(urlfetch.Client(c)) if err != nil { c.Errorf(" /mdb/addflight: newdb: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } // Be idempotent - check to see if this flight has already been recorded /* Can't do this check now; the fs.F.Id we cached might be different from the * f.Id we get back from LookupPlayback(), because fr24 reuse their keys. * if exists,err := db.FlightExists(fs.F.Id.UniqueIdentifier()); err != nil { c.Errorf(" /mdb/addflight: FlightExists check failed: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } else if exists { c.Infof(" /mdb/addflight: already exists %s", fs) w.Write([]byte(fmt.Sprintf("Skipped %s\n", fs))) return } log += fmt.Sprintf("* FlightExists('%s') -> false\n", fs.F.Id.UniqueIdentifier()) */ // Now grab an initial flight (with track), from fr24. var f *ftype.Flight if f, err = fr24db.LookupPlayback(fr24Id); err != nil { // c.Errorf(" /mdb/addflight: lookup: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } log += fmt.Sprintf("* the fr24/default track has %d points\n", len(f.Track)) // Kludge: fr24 keys get reused, so the flight fr24 thinks it refers to might be // different than when we cached it. So we do the uniqueness check here, to avoid // dupes in the DB. Need a better solution to this. if exists, err := db.FlightExists(f.Id.UniqueIdentifier()); err != nil { c.Errorf(" /mdb/addflight: FlightExists check failed: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } else if exists { c.Infof(" /mdb/addflight: already exists %s", *f) w.Write([]byte(fmt.Sprintf("Skipped %s\n", *f))) return } log += fmt.Sprintf("* FlightExists('%s') -> false\n", f.Id.UniqueIdentifier()) // Fetch ADSB tracks from the new thing err, deb := f.GetV2ADSBTrack(urlfetch.Client(c)) log += "* fetchV2ADSB\n" + deb if err != nil { c.Errorf("ADSB fetch err: %v", err) } // If we have any locally received ADSB fragments for this flight, add them in //if err := db.MaybeAddTrackFragmentsToFlight(f); err != nil { // c.Errorf(" /mdb/addflight: addTrackFrags(%s): %v", f.Id, err) //} f.AnalyseFlightPath() // Takes a coarse look at the flight path log += fmt.Sprintf("* Initial tags: %v\n", f.TagList()) // For flights on the SERFR1 or BRIXX1 approaches, fetch a flightaware track if f.HasTag(ftype.KTagSERFR1) || f.HasTag(ftype.KTagBRIXX) { u, p := kFlightawareAPIUsername, kFlightawareAPIKey if err := fdbfa.AddFlightAwareTrack(urlfetch.Client(c), f, u, p); err != nil { c.Errorf(" /mdb/addflight: addflightaware: %v", err) } } f.Analyse() if err := db.PersistFlight(*f, log); err != nil { c.Errorf(" /mdb/addflight: persist: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } // Success ! w.Write([]byte(fmt.Sprintf("Added %s\n", f))) }