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 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 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 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": "/fdb/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), 60*time.Second)} s, e := rep.Start, rep.End if err := db.IterWith(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) } 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) } }