func reportHandler(w http.ResponseWriter, r *http.Request) { if r.FormValue("date") == "" { var params = map[string]interface{}{ "Yesterday": date.NowInPdt().AddDate(0, 0, -1), } if err := templates.ExecuteTemplate(w, "report-form", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } c := appengine.Timeout(appengine.NewContext(r), 60*time.Second) // Default has a 5s timeout s, e, _ := widget.FormValueDateRange(r) opt := ReportOptions{ ClassB_OnePerFlight: widget.FormValueCheckbox(r, "classb_oneperflight"), ClassB_LocalDataOnly: widget.FormValueCheckbox(r, "classb_localdataonly"), Skimmer_AltitudeTolerance: widget.FormValueFloat64(w, r, "skimmer_altitude_tolerance"), Skimmer_MinDurationNM: widget.FormValueFloat64(w, r, "skimmer_min_duration_nm"), } if fix := strings.ToUpper(r.FormValue("waypoint")); fix != "" { if _, exists := sfo.KFixes[fix]; !exists { http.Error(w, fmt.Sprintf("Waypoint '%s' not known", fix), http.StatusInternalServerError) return } opt.Waypoint = fix } reportWriter(c, w, r, s, e, opt, r.FormValue("reportname"), r.FormValue("resultformat")) }
func summaryReportHandler(w http.ResponseWriter, r *http.Request) { if r.FormValue("date") == "" { var params = map[string]interface{}{ "Title": "Summary of disturbance reports", "FormUrl": "/report/summary", "Yesterday": date.NowInPdt().AddDate(0, 0, -1), } //params["Message"] = "Please do not scrape this form. Instead, get in touch !" if err := templates.ExecuteTemplate(w, "date-report-form", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } if isBanned(r) { http.Error(w, "bad user", http.StatusUnauthorized) return } start, end, _ := widget.FormValueDateRange(r) countByUser := r.FormValue("peeps") != "" str, err := SummaryReport(r, start, end, countByUser) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/plain") w.Write([]byte(str)) }
func FixupComplaint(c *types.Complaint, key *datastore.Key) { // 0. Snag the key, so we can refer to this object later c.DatastoreKey = key.Encode() // 1. GAE datastore helpfully converts timezones to UTC upon storage; fix that c.Timestamp = date.InPdt(c.Timestamp) // 2. Compute the flight details URL, if within 24 days age := date.NowInPdt().Sub(c.Timestamp) if age < time.Hour*24 { // c.AircraftOverhead.Fr24Url = c.AircraftOverhead.PlaybackUrl() c.AircraftOverhead.Fr24Url = "http://flightaware.com/live/flight/" + c.AircraftOverhead.FlightNumber // Or: http://flightaware.com/live/flight/UAL337/history/20151215/ [0655Z/KLAX/KSFO] // date is UTC of departure time; might be tricky to guess :/ } // 3. Compute distances, if we have an aircraft if c.AircraftOverhead.FlightNumber != "" { a := c.AircraftOverhead aircraftPos := geo.Latlong{a.Lat, a.Long} observerPos := geo.Latlong{c.Profile.Lat, c.Profile.Long} c.Dist2KM = observerPos.Dist(aircraftPos) c.Dist3KM = observerPos.Dist3(aircraftPos, a.Altitude) } }
func downloadHandler(w http.ResponseWriter, r *http.Request) { session := sessions.Get(r) if session.Values["email"] == nil { http.Error(w, "session was empty; no cookie ? is this browser in privacy mode ?", http.StatusInternalServerError) return } c := appengine.Timeout(appengine.NewContext(r), 60*time.Second) cdb := complaintdb.ComplaintDB{C: c} cap, err := cdb.GetAllByEmailAddress(session.Values["email"].(string), true) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } _ = cap filename := date.NowInPdt().Format("complaints-20060102.csv") w.Header().Set("Content-Type", "application/csv") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) cols := []string{ "Date", "Time(PDT)", "Notes", "Speedbrakes", "Loudness", "Activity", "Flightnumber", "Origin", "Destination", "Speed(Knots)", "Altitude(Feet)", "Lat", "Long", "Registration", "Callsign", "VerticalSpeed(FeetPerMin)", "Dist2(km)", "Dist3(km)", } csvWriter := csv.NewWriter(w) csvWriter.Write(cols) for _, c := range cap.Complaints { a := c.AircraftOverhead speedbrakes := "" if c.HeardSpeedbreaks { speedbrakes = "y" } r := []string{ c.Timestamp.Format("2006/01/02"), c.Timestamp.Format("15:04:05"), c.Description, speedbrakes, fmt.Sprintf("%d", c.Loudness), c.Activity, a.FlightNumber, a.Origin, a.Destination, fmt.Sprintf("%.0f", a.Speed), fmt.Sprintf("%.0f", a.Altitude), fmt.Sprintf("%.5f", a.Lat), fmt.Sprintf("%.5f", a.Long), a.Registration, a.Callsign, fmt.Sprintf("%.0f", a.VerticalSpeed), fmt.Sprintf("%.1f", c.Dist2KM), fmt.Sprintf("%.1f", c.Dist3KM), } if err := csvWriter.Write(r); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } csvWriter.Flush() }
func userReportHandler(w http.ResponseWriter, r *http.Request) { ctx := req2ctx(r) cdb := complaintdb.NewDB(ctx) profiles, err := cdb.GetAllProfiles() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if true { nOK, nNotOK, nDefault := 0, 0, 0 for _, p := range profiles { switch { case p.DataSharing < 0: nNotOK++ case p.DataSharing > 0: nOK++ case p.DataSharing == 0: nDefault++ } } w.Header().Set("Content-Type", "text/plain") fmt.Fprintf(w, "nOK=%d, nDefault=%d, nNotOk=%d\n", nOK, nDefault, nNotOK) return } filename := date.NowInPdt().Format("users-as-of-20060102.csv") w.Header().Set("Content-Type", "application/csv") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) cols := []string{"EMAIL", "NAME", "CALLERCODE", "STREET", "CITY", "ZIP", "ALLINONELINE"} csvWriter := csv.NewWriter(w) csvWriter.Write(cols) for _, p := range profiles { street := p.StructuredAddress.Street if p.StructuredAddress.Number != "" { street = p.StructuredAddress.Number + " " + street } row := []string{ p.EmailAddress, p.FullName, p.CallerCode, street, p.StructuredAddress.City, p.StructuredAddress.Zip, p.Address, } csvWriter.Write(row) } csvWriter.Flush() }
func downloadHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { sesh, _ := GetUserSession(ctx) filename := date.NowInPdt().Format("complaints-20060102.csv") w.Header().Set("Content-Type", "application/csv") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) cols := []string{ "Date", "Time(PDT)", "Notes", "Speedbrakes", "Loudness", "Activity", "Flightnumber", "Origin", "Destination", "Speed(Knots)", "Altitude(Feet)", "Lat", "Long", "Registration", "Callsign", "VerticalSpeed(FeetPerMin)", "Dist2(km)", "Dist3(km)", "City", } csvWriter := csv.NewWriter(w) csvWriter.Write(cols) cdb := complaintdb.NewDB(ctx) iter := cdb.NewIter(cdb.QueryAllByEmailAddress(sesh.Email)) for { c, err := iter.NextWithErr() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if c == nil { break } a := c.AircraftOverhead speedbrakes := "" if c.HeardSpeedbreaks { speedbrakes = "y" } r := []string{ c.Timestamp.Format("2006/01/02"), c.Timestamp.Format("15:04:05"), c.Description, speedbrakes, fmt.Sprintf("%d", c.Loudness), c.Activity, a.FlightNumber, a.Origin, a.Destination, fmt.Sprintf("%.0f", a.Speed), fmt.Sprintf("%.0f", a.Altitude), fmt.Sprintf("%.5f", a.Lat), fmt.Sprintf("%.5f", a.Long), a.Registration, a.Callsign, fmt.Sprintf("%.0f", a.VerticalSpeed), fmt.Sprintf("%.1f", c.Dist2KM), fmt.Sprintf("%.1f", c.Dist3KM), c.Profile.GetStructuredAddress().City, } if err := csvWriter.Write(r); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } csvWriter.Flush() }
func (fr *Fr24) FindOverhead(observerPos geo.Latlong, overhead *Aircraft, grabAnything bool) (debug string, err error) { debug = fmt.Sprintf("*** FindOverhead for %s, at %s\n", observerPos, date.NowInPdt()) // Create a bounding box that's ~40m square, centred on the input lat,long // This is a grievous fudge http://www.movable-type.co.uk/scripts/latlong.html lat_20miles := 0.3 long_20miles := 0.35 nearby := []Aircraft{} if err = fr.ListBbox(observerPos.Lat-lat_20miles, observerPos.Long-long_20miles, observerPos.Lat+lat_20miles, observerPos.Long+long_20miles, &nearby); err != nil { debug += fmt.Sprintf("Lookup error: %s\n", err) return } for i, a := range nearby { aircraftPos := geo.Latlong{a.Lat, a.Long} nearby[i].Dist = observerPos.Dist(aircraftPos) nearby[i].Dist3 = observerPos.Dist3(aircraftPos, a.Altitude) nearby[i].BearingFromObserver = observerPos.BearingTowards(aircraftPos) } sort.Sort(byDist3(nearby)) debug += "** nearby list:-\n" + DebugFlightList(nearby) filtered := filterAircraft(nearby) if len(filtered) == 0 { debug += "** all empty after filtering\n" return } debug += "** filtered:-\n" + DebugFlightList(filtered) if grabAnything { *overhead = filtered[0] debug += "** grabbed 1st\n" } else { // closest plane has to be within 12 km to be 'overhead', and it has // to be 4km away from the next-closest if filtered[0].Dist3 < 12.0 { if (len(filtered) == 1) || (filtered[1].Dist3-filtered[0].Dist3) > 4.0 { *overhead = filtered[0] debug += "** selected 1st\n" } else { debug += "** 2nd was too close to 1st\n" } } else { debug += "** 1st was too far away\n" } } return }
func monthlySummaryTaskHandler(w http.ResponseWriter, r *http.Request) { month, year, err := FormValueMonthDefaultToPrev(r) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } countByUser := false now := date.NowInPdt() start := time.Date(int(year), time.Month(month), 1, 0, 0, 0, 0, now.Location()) end := start.AddDate(0, 1, 0).Add(-1 * time.Second) bucketname := "serfr0-reports" filename := start.Format("summary-2006-01.txt") ctx := req2ctx(r) if exists, err := gcs.Exists(ctx, bucketname, filename); err != nil { http.Error(w, fmt.Sprintf("gcs.Exists=%v for gs://%s/%s (err=%v)", exists, bucketname, filename, err), http.StatusInternalServerError) return } else if exists { w.Header().Set("Content-Type", "text/plain") w.Write([]byte(fmt.Sprintf("OK!\nGCS file %s/%s already exists\n", bucketname, filename))) return } tStart := time.Now() str, err := SummaryReport(r, start, end, countByUser) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } gcsHandle, err := gcs.OpenRW(ctx, bucketname, filename, "text/plain") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } gcsHandle.IOWriter().Write([]byte(str)) if err := gcsHandle.Close(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/plain") w.Write([]byte(fmt.Sprintf("OK!\nGCS monthly report %s/%s written, took %s", bucketname, filename, time.Since(tStart)))) }
func publishComplaintsHandler(w http.ResponseWriter, r *http.Request) { tStart := time.Now() ctx := appengine.NewContext(r) foldername := "serfr0-bigquery" datestring := r.FormValue("datestring") if datestring == "yesterday" { datestring = date.NowInPdt().AddDate(0, 0, -1).Format("2006.01.02") } filename := "anon-" + datestring + ".json" log.Infof(ctx, "Starting /backend/publish-complaints: %s", filename) n, err := writeAnonymizedGCSFile(r, datestring, foldername, filename) if err != nil { log.Errorf(ctx, "/backend/publish-complaints: %s, err: %v", filename, err) http.Error(w, err.Error(), http.StatusInternalServerError) return } log.Infof(ctx, "%d entries written to gs://%s/%s\n", n, foldername, filename) str := fmt.Sprintf("%d entries written to gs://%s/%s\n", n, foldername, filename) if r.FormValue("skipload") == "" { if err := submitLoadJob(r, foldername, filename); err != nil { http.Error(w, "submitLoadJob failed: "+err.Error(), http.StatusInternalServerError) return } str += "file submitted to BigQuery for loading\n" } w.Header().Set("Content-Type", "text/plain") w.Write([]byte(fmt.Sprintf("OK! (took %s)\n\n%s", time.Since(tStart), str))) }
// Gruesome. This pseudo-widget looks at 'year' and 'month', or defaults to the previous month. // Everything is in Pacific Time. func FormValueMonthDefaultToPrev(r *http.Request) (month, year int, err error) { // Default to the previous month oneMonthAgo := date.NowInPdt().AddDate(0, -1, 0) month = int(oneMonthAgo.Month()) year = int(oneMonthAgo.Year()) // Override with specific values, if present if r.FormValue("year") != "" { if y, err2 := strconv.ParseInt(r.FormValue("year"), 10, 64); err2 != nil { err = fmt.Errorf("need arg 'year' (2015)") return } else { year = int(y) } if m, err2 := strconv.ParseInt(r.FormValue("month"), 10, 64); err2 != nil { err = fmt.Errorf("need arg 'month' (1-12)") return } else { month = int(m) } } return }
func personalReportHandler(w http.ResponseWriter, r *http.Request) { session := sessions.Get(r) if session.Values["email"] == nil { http.Error(w, "session was empty; no cookie ?", http.StatusInternalServerError) return } email := session.Values["email"].(string) if r.FormValue("date") == "" { var params = map[string]interface{}{ "Yesterday": date.NowInPdt().AddDate(0, 0, -1), } if err := templates.ExecuteTemplate(w, "personal-report-form", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } start, end, _ := widget.FormValueDateRange(r) ctx := appengine.Timeout(appengine.NewContext(r), 60*time.Second) cdb := complaintdb.ComplaintDB{C: ctx} w.Header().Set("Content-Type", "text/plain") // w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", "sc.txt")) fmt.Fprintf(w, "Personal disturbances report for <%s>:\n From [%s]\n To [%s]\n", email, start, end) complaintStrings := []string{} var countsByHour [24]int countsByDate := map[string]int{} countsByAirline := map[string]int{} iter := cdb.NewIter(cdb.QueryInSpanByEmailAddress(start, end, email)) n := 0 for { c := iter.Next() if c == nil { break } str := fmt.Sprintf("Time: %s, Loudness:%d, Speedbrakes:%v, Flight:%6.6s, Notes:%s", c.Timestamp.Format("2006.01.02 15:04:05"), c.Loudness, c.HeardSpeedbreaks, c.AircraftOverhead.FlightNumber, c.Description) n++ complaintStrings = append(complaintStrings, str) countsByHour[c.Timestamp.Hour()]++ countsByDate[c.Timestamp.Format("2006.01.02")]++ if airline := c.AircraftOverhead.IATAAirlineCode(); airline != "" { countsByAirline[airline]++ } } fmt.Fprintf(w, "\nTotal number of disturbance reports, over %d days: %d\n", len(countsByDate), n) fmt.Fprintf(w, "\nDisturbance reports, counted by Airline (where known):\n") for _, k := range keysByIntValDesc(countsByAirline) { fmt.Fprintf(w, " %s: % 4d\n", k, countsByAirline[k]) } fmt.Fprintf(w, "\nDisturbance reports, counted by date:\n") for _, k := range keysByKeyAsc(countsByDate) { fmt.Fprintf(w, " %s: % 4d\n", k, countsByDate[k]) } fmt.Fprintf(w, "\nDisturbance reports, counted by hour of day (across all dates):\n") for i, n := range countsByHour { fmt.Fprintf(w, " %02d: % 4d\n", i, n) } fmt.Fprintf(w, "\nFull dump of all disturbance reports:\n\n") for _, s := range complaintStrings { fmt.Fprint(w, s+"\n") } }
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": "/report/", "Waypoints": sfo.ListWaypoints(), "Title": "Reports (DB v1)", } 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) metars, err := metar.LookupArchive(req2ctx(r), "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{} v1RejectByRestrict := []string{} v1RejectByReport := []string{} reportFunc := func(oldF *oldfdb.Flight) { newF, err := oldF.V2() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } newF.ComputeIndicatedAltitudes(metars) outcome, err := rep.Process(newF) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } switch outcome { case report.RejectedByGeoRestriction: v1RejectByRestrict = append(v1RejectByRestrict, oldF.UniqueIdentifier()) case report.RejectedByReport: v1RejectByReport = append(v1RejectByReport, oldF.UniqueIdentifier()) case report.Accepted: v1idspecs = append(v1idspecs, oldF.UniqueIdentifier()) v2idspecs = append(v2idspecs, newF.IdSpec().String()) } } tags := rep.Options.Tags for _, wp := range rep.Waypoints { tags = append(tags, fmt.Sprintf("%s%s", oldfdb.KWaypointTagPrefix, wp)) } db := oldfgae.NewDB(r) 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 } rep.FinishSummary() if rep.ResultsFormat == "csv" { rep.OutputAsCSV(w) return } postButtons := "" url := fmt.Sprintf("/fdb/trackset2?%s", rep.ToCGIArgs()) postButtons += maybeButtonPOST(v1RejectByRestrict, "Restriction Rejects as VectorMap", url) postButtons += maybeButtonPOST(v1RejectByReport, "Report Rejects as VectorMap", url) url = fmt.Sprintf("/fdb/descent2?%s", rep.ToCGIArgs()) postButtons += maybeButtonPOST(v1RejectByRestrict, "Restriction Rejects as DescentGraph", url) postButtons += maybeButtonPOST(v1RejectByReport, "Report Rejects DescentGraph", url) if rep.Name == "sfoclassb" { url = fmt.Sprintf("/fdb/approach2?%s", rep.ToCGIArgs()) postButtons += maybeButtonPOST(v1idspecs, "Matches as ClassB", url) postButtons += maybeButtonPOST(v1RejectByReport, "Report Rejects as ClassB", url) } // The only way to get embedded CGI args without them getting escaped is to submit a whole tag vizFormURL := "http://stop.jetnoise.net/fdb/visualize2?" + rep.ToCGIArgs() vizFormTag := "<form action=\"" + vizFormURL + "\" method=\"post\" target=\"_blank\">" 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, ",")), "VisualizationFormTag": template.HTML(vizFormTag), } if err := templates.ExecuteTemplate(w, "report3-results", params); err != nil { http.Error(w, err.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 } db := fdb.NewDB(r) db.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.KBoxSnarfingCatchment, // 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 rootHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { cdb := complaintdb.NewDB(ctx) sesh, _ := GetUserSession(ctx) cdb.Debugf("root_001", "session obtained") for _, c := range r.Cookies() { cdb.Debugf("root_002", "cookie: %s", c) } cdb.Debugf("root_002", "num cookies: %d", len(r.Cookies())) cdb.Debugf("root_002a", "Cf-Connecting-Ip: %s", r.Header.Get("Cf-Connecting-Ip")) // No session ? Get them to login if sesh.Email == "" { fb.AppId = kFacebookAppId fb.AppSecret = kFacebookAppSecret loginUrls := map[string]string{ "googlefromscratch": g.GetLoginUrl(r, true), "google": g.GetLoginUrl(r, false), "facebook": fb.GetLoginUrl(r), } if err := templates.ExecuteTemplate(w, "landing", loginUrls); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } if sesh.HasCreatedAt() { cdb.Debugf("root_003", "tstamp=%s, age=%s", sesh.CreatedAt, time.Since(sesh.CreatedAt)) } modes := map[string]bool{} // The rootHandler is the URL wildcard. Except Fragments, which are broken. if r.URL.Path == "/full" { modes["expanded"] = true } else if r.URL.Path == "/edit" { modes["edit"] = true } else if r.URL.Path == "/debug" { modes["debug"] = true } else if r.URL.Path != "/" { // This is a request for apple_icon or somesuch junk. Just say no. http.NotFound(w, r) return } cdb.Debugf("root_004", "about get cdb.GetAllByEmailAddress") cap, err := cdb.GetAllByEmailAddress(sesh.Email, modes["expanded"]) if cap == nil && err == nil { // No profile exists; daisy-chain into profile page http.Redirect(w, r, "/profile", http.StatusFound) return } else if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } cdb.Debugf("root_005", "cdb.GetAllByEmailAddress done") modes["admin"] = user.Current(ctx) != nil && user.Current(ctx).Admin modes["superuser"] = modes["admin"] // Default to "", unless we had a complaint in the past hour. lastActivity := "" if len(cap.Complaints) > 0 && time.Since(cap.Complaints[0].Timestamp) < time.Hour { lastActivity = cap.Complaints[0].Activity } var complaintDefaults = map[string]interface{}{ "ActivityList": kActivities, // lives in add-complaint "DefaultActivity": lastActivity, "DefaultLoudness": 1, "NewForm": true, } message := "" disableReporting := false if cap.Profile.FullName == "" { message += "<li>We don't have your full name</li>" disableReporting = true } if cap.Profile.StructuredAddress.Zip == "" { message += "<li>We don't have an accurate address</li>" disableReporting = true } if message != "" { message = fmt.Sprintf("<p><b>We've found some problems with your profile:</b></p><ul>%s</ul>"+ "<p> Without this data, your complaints won't be counted, so please "+ "<a href=\"/profile\"><b>update your profile</b></a> before submitting any more complaints !</p>", message) } var params = map[string]interface{}{ //"Message": template.HTML("Hi!"), "Cap": *cap, "Complaints": hintComplaints(cap.Complaints, modes["superuser"]), "Now": date.NowInPdt(), "Modes": modes, "ComplaintDefaults": complaintDefaults, "Message": template.HTML(message), //"Info": template.HTML("Hi!"), "DisableReporting": disableReporting, } if err := templates.ExecuteTemplate(w, "main", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
func zipHandler(w http.ResponseWriter, r *http.Request) { if r.FormValue("date") == "" { var params = map[string]interface{}{ "Yesterday": date.NowInPdt().AddDate(0, 0, -1), } if err := templates.ExecuteTemplate(w, "zip-report-form", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } ctx := req2ctx(r) cdb := complaintdb.NewDB(ctx) zip := r.FormValue("zip") s, e, _ := widget.FormValueDateRange(r) var countsByHour [24]int countsByDate := map[string]int{} var uniquesByHour [24]map[string]int uniquesByDate := map[string]map[string]int{} uniquesAll := map[string]int{} iter := cdb.NewIter(cdb.QueryInSpanInZip(s, e, zip)) for { c, err := iter.NextWithErr() if err != nil { http.Error(w, fmt.Sprintf("Zip iterator failed: %v", err), http.StatusInternalServerError) return } else if c == nil { break // We've hit EOF } h := c.Timestamp.Hour() countsByHour[h]++ if uniquesByHour[h] == nil { uniquesByHour[h] = map[string]int{} } uniquesByHour[h][c.Profile.EmailAddress]++ d := c.Timestamp.Format("2006.01.02") countsByDate[d]++ if uniquesByDate[d] == nil { uniquesByDate[d] = map[string]int{} } uniquesByDate[d][c.Profile.EmailAddress]++ uniquesAll[c.Profile.EmailAddress]++ } dateKeys := []string{} for k, _ := range countsByDate { dateKeys = append(dateKeys, k) } sort.Strings(dateKeys) data := [][]string{} data = append(data, []string{"Date", "NumComplaints", "UniqueComplainers"}) for _, k := range dateKeys { data = append(data, []string{ k, fmt.Sprintf("%d", countsByDate[k]), fmt.Sprintf("%d", len(uniquesByDate[k])), }) } data = append(data, []string{"------"}) data = append(data, []string{"HourAcrossAllDays", "NumComplaints", "UniqueComplainers"}) for i, v := range countsByHour { data = append(data, []string{ fmt.Sprintf("%02d:00", i), fmt.Sprintf("%d", v), fmt.Sprintf("%d", len(uniquesByHour[i])), }) } data = append(data, []string{"------"}) data = append(data, []string{"UniqueComplainersAcrossAllDays", fmt.Sprintf("%d", len(uniquesAll))}) var params = map[string]interface{}{"Data": data} if err := templates.ExecuteTemplate(w, "report", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
func monthTaskHandler(w http.ResponseWriter, r *http.Request) { //ctx,_ := context.WithTimeout(appengine.NewContext(r), 599*time.Second) ctx := appengine.NewContext(r) cdb := complaintdb.ComplaintDB{ //C: oldappengine.NewContext(r), C: oldappengine.Timeout(oldappengine.NewContext(r), 599*time.Second), } year, err := strconv.ParseInt(r.FormValue("year"), 10, 64) if err != nil { http.Error(w, "need arg 'year' (2015)", http.StatusInternalServerError) return } month, err := strconv.ParseInt(r.FormValue("month"), 10, 64) if err != nil { http.Error(w, "need arg 'month' (1-12)", http.StatusInternalServerError) return } now := date.NowInPdt() s := time.Date(int(year), time.Month(month), 1, 0, 0, 0, 0, now.Location()) e := s.AddDate(0, 1, 0).Add(-1 * time.Second) log.Infof(ctx, "Starting /be/month: %s", s) // One time, at 00:00, for each day of the given month days := date.IntermediateMidnights(s.Add(-1*time.Second), e) filename := s.Format("complaints-20060102") + e.Format("-20060102.csv") gcsHandle, err := gcs.OpenRW(ctx, "serfr0-reports", filename, "text/plain") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } csvWriter := csv.NewWriter(gcsHandle.IOWriter()) cols := []string{ "CallerCode", "Name", "Address", "Zip", "Email", "HomeLat", "HomeLong", "UnixEpoch", "Date", "Time(PDT)", "Notes", "ActivityDisturbed", "Flightnumber", "Notes", // Column names above are incorrect, but BKSV are used to them. // //"CallerCode", "Name", "Address", "Zip", "Email", "HomeLat", "HomeLong", //"UnixEpoch", "Date", "Time(PDT)", "Notes", "Flightnumber", //"ActivityDisturbed", "CcSFO", } csvWriter.Write(cols) for _, dayStart := range days { dayEnd := dayStart.AddDate(0, 0, 1).Add(-1 * time.Second) log.Infof(ctx, " /be/month: %s - %s", dayStart, dayEnd) iter := cdb.NewIter(cdb.QueryInSpan(dayStart, dayEnd)) for { c, err := iter.NextWithErr() if err != nil { http.Error(w, fmt.Sprintf("iterator failed: %v", err), http.StatusInternalServerError) return } if c == nil { break } r := []string{ c.Profile.CallerCode, c.Profile.FullName, c.Profile.Address, c.Profile.StructuredAddress.Zip, c.Profile.EmailAddress, fmt.Sprintf("%.4f", c.Profile.Lat), fmt.Sprintf("%.4f", c.Profile.Long), fmt.Sprintf("%d", c.Timestamp.UTC().Unix()), c.Timestamp.Format("2006/01/02"), c.Timestamp.Format("15:04:05"), c.Description, c.AircraftOverhead.FlightNumber, c.Activity, fmt.Sprintf("%v", c.Profile.CcSfo), } if err := csvWriter.Write(r); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } } csvWriter.Flush() if err := gcsHandle.Close(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } log.Infof(ctx, "GCS report '%s' successfully written", filename) w.Header().Set("Content-Type", "text/plain") w.Write([]byte(fmt.Sprintf("OK!\nGCS file '%s' written to bucket", filename))) }
func reportWriter(c appengine.Context, w http.ResponseWriter, r *http.Request, s, e time.Time, opt ReportOptions, rep string, format string) { var rows []ReportRow var meta ReportMetadata var err error switch rep { case "classb": rows, meta, err = classbReport(c, s, e, opt) case "adsbclassb": rows, meta, err = adsbClassbReport(c, s, e, opt) case "discrep": rows, meta, err = discrepReport(c, s, e, opt) case "serfr1": rows, meta, err = serfr1Report(c, s, e, opt) case "brixx1": rows, meta, err = brixx1Report(c, s, e, opt) case "serfr1complaints": rows, meta, err = serfr1ComplaintsReport(c, s, e, opt) case "skimmer": rows, meta, err = skimmerReport(c, s, e, opt) case "brixxviolations": rows, meta, err = brixxViolationReport(c, s, e, opt) case "serfr1at": rows, meta, err = serfr1AtReport(c, s, e, opt) } if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // Should do something better for this filename := date.NowInPdt().AddDate(0, 0, -1).Format(rep + "-20060102.csv") outFunc := func(csvWriter *csv.Writer) error { csvWriter.Write(rows[0].ToCSVHeaders()) for _, r := range rows { if err := csvWriter.Write(r.ToCSV()); err != nil { return err } } csvWriter.Flush() return nil } if format == "csv" { w.Header().Set("Content-Type", "application/csv") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) csvWriter := csv.NewWriter(w) if err := outFunc(csvWriter); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } else if format == "gcs" { newCtx := newappengine.NewContext(r) handle, err := gcs.OpenRW(newCtx, "serfr0-reports", filename, "text/plain") //?"application/csv") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } csvWriter := csv.NewWriter(handle.IOWriter()) if err := outFunc(csvWriter); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if err := handle.Close(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/plain") w.Write([]byte(fmt.Sprintf("OK!\nGCS file '%s' written to bucket", filename))) } else { var params = map[string]interface{}{ "Start": s, "End": e, "Metadata": meta, "Options": opt, } // Is there not a more elegant way to do this kind of thing ? switch rep { case "classb": out := []CBRow{} for _, r := range rows { out = append(out, r.(CBRow)) } params["Rows"] = out case "adsbclassb": out := []ACBRow{} for _, r := range rows { out = append(out, r.(ACBRow)) } params["Rows"] = out case "serfr1", "brixx1", "discrep": out := []SERFR1Row{} for _, r := range rows { out = append(out, r.(SERFR1Row)) } params["Rows"] = out rep = "serfr1" case "serfr1complaints": out := []SCRow{} for _, r := range rows { out = append(out, r.(SCRow)) } params["Rows"] = out case "skimmer": out := []SkimRow{} for _, r := range rows { out = append(out, r.(SkimRow)) } params["Rows"] = out case "brixxviolations": out := []BrixxRow{} for _, r := range rows { out = append(out, r.(BrixxRow)) } params["Rows"] = out case "serfr1at": out := []SERFR1AtRow{} for _, r := range rows { out = append(out, r.(SERFR1AtRow)) } params["Rows"] = out } if err := templates.ExecuteTemplate(w, "report-"+rep, params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } }
// Where is the version of this that does GCS via batch ? func monthHandler(w http.ResponseWriter, r *http.Request) { ctx := req2ctx(r) year, err := strconv.ParseInt(r.FormValue("year"), 10, 64) if err != nil { http.Error(w, "need arg 'year' (2015)", http.StatusInternalServerError) return } month, err := strconv.ParseInt(r.FormValue("month"), 10, 64) if err != nil { http.Error(w, "need arg 'month' (1-12)", http.StatusInternalServerError) return } day, err := strconv.ParseInt(r.FormValue("day"), 10, 64) if err != nil { // Presume we should enqueue this for batch taskUrl := fmt.Sprintf("/backend/monthdump?year=%d&month=%d", year, month) t := taskqueue.NewPOSTTask(taskUrl, map[string][]string{ "year": {r.FormValue("year")}, "month": {r.FormValue("month")}, }) if _, err := taskqueue.Add(ctx, t, "batch"); err != nil { log.Errorf(ctx, "monthHandler: enqueue: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/plain") w.Write([]byte(fmt.Sprintf("OK\nHave enqueued for batch {%s}\n", taskUrl))) return } num, err := strconv.ParseInt(r.FormValue("num"), 10, 64) if err != nil { http.Error(w, "need arg 'num' (31 - 'day')", http.StatusInternalServerError) return } now := date.NowInPdt() firstOfMonth := time.Date(int(year), time.Month(month), 1, 0, 0, 0, 0, now.Location()) s := firstOfMonth.AddDate(0, 0, int(day-1)) e := s.AddDate(0, 0, int(num)).Add(-1 * time.Second) log.Infof(ctx, "Yow: START : %s", s) log.Infof(ctx, "Yow: END : %s", e) cdb := complaintdb.NewDB(ctx) filename := s.Format("complaints-20060102") + e.Format("-20060102.csv") w.Header().Set("Content-Type", "application/csv") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) cols := []string{ "CallerCode", "Name", "Address", "Zip", "Email", "HomeLat", "HomeLong", "UnixEpoch", "Date", "Time(PDT)", "Notes", "Flightnumber", "ActivityDisturbed", "AutoSubmit", } csvWriter := csv.NewWriter(w) csvWriter.Write(cols) iter := cdb.NewIter(cdb.QueryInSpan(s, e)) for { c, err := iter.NextWithErr() if err != nil { http.Error(w, fmt.Sprintf("Zip iterator failed: %v", err), http.StatusInternalServerError) return } else if c == nil { break // We've hit EOF } r := []string{ c.Profile.CallerCode, c.Profile.FullName, c.Profile.Address, c.Profile.StructuredAddress.Zip, c.Profile.EmailAddress, fmt.Sprintf("%.4f", c.Profile.Lat), fmt.Sprintf("%.4f", c.Profile.Long), fmt.Sprintf("%d", c.Timestamp.UTC().Unix()), c.Timestamp.Format("2006/01/02"), c.Timestamp.Format("15:04:05"), c.Description, c.AircraftOverhead.FlightNumber, c.Activity, fmt.Sprintf("%v", c.Profile.CcSfo), } //r = []string{c.Timestamp.Format("15:04:05")} if err := csvWriter.Write(r); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } csvWriter.Flush() }
func communityReportHandler(w http.ResponseWriter, r *http.Request) { if r.FormValue("date") == "" { var params = map[string]interface{}{ "Title": "Community breakdown of disturbance reports", "FormUrl": "/report/community", "Yesterday": date.NowInPdt().AddDate(0, 0, -1), } if err := templates.ExecuteTemplate(w, "date-report-form", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } start, end, _ := widget.FormValueDateRange(r) ctx := req2ctx(r) cdb := complaintdb.NewDB(ctx) // Use most-recent city info for all the users, not what got cached per-complaint userCities, err := cdb.GetEmailCityMap() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } filename := start.Format("community-20060102") + end.Format("-20060102.csv") w.Header().Set("Content-Type", "application/csv") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) counts := map[string]map[string]int{} // {datestr}{city} users := map[string]map[string]int{} // {datestr}{city} // An iterator expires after 60s, no matter what; so carve up into short-lived iterators n := 0 currCounts := map[string]int{} currUsers := map[string]map[string]int{} currN := 0 for _, dayWindow := range DayWindows(start, end) { //daycounts := map[string]int{} // {city} //dayusers := map[string]map[string]int{} // {city}{email} q := cdb.QueryInSpan(dayWindow[0], dayWindow[1]) q = q.Project("Profile.StructuredAddress.City", "Profile.EmailAddress") iter := cdb.NewIter(q) for { c, err := iter.NextWithErr() if err != nil { http.Error(w, fmt.Sprintf("iterator failed at %s: %v", time.Now(), err), http.StatusInternalServerError) return } else if c == nil { break // we're all done with this iterator } n++ //email,city := c.Profile.EmailAddress,c.Profile.StructuredAddress.City email := c.Profile.EmailAddress city := userCities[email] if city == "" { city = "Unknown" } if currUsers[city] == nil { currUsers[city] = map[string]int{} } currUsers[city][email]++ currCounts[city]++ } currN++ // number of days processed since last flush. // End of a day; should we flush the counters ? flushStr := "" if true || r.FormValue("byweek") != "" { if currN == 7 { flushStr = dayWindow[0].Format("2006.01.02") } } else { flushStr = dayWindow[0].Format("2006.01.02") } if flushStr != "" { counts[flushStr] = currCounts users[flushStr] = map[string]int{} for city, _ := range currUsers { users[flushStr][city] = len(currUsers[city]) } currCounts = map[string]int{} currUsers = map[string]map[string]int{} currN = 0 } } cols := []string{"Date"} cols = append(cols, cityCols...) csvWriter := csv.NewWriter(w) csvWriter.Write(cols) for _, datestr := range keysByKeyAscNested(counts) { row := []string{datestr} for _, town := range cityCols { n := counts[datestr][town] row = append(row, fmt.Sprintf("%d", n)) } csvWriter.Write(row) } csvWriter.Write(cols) for _, datestr := range keysByKeyAscNested(users) { row := []string{datestr} for _, town := range cityCols { n := users[datestr][town] row = append(row, fmt.Sprintf("%d", n)) } csvWriter.Write(row) } csvWriter.Flush() //fmt.Fprintf(w, "(t=%s, n=%d)\n", time.Now(), n) }
func (fr *Fr24) FindAllOverhead(observerPos geo.Latlong, overhead *flightid.Aircraft, grabAnything bool) (outAll []*Aircraft, outFilt []*Aircraft, debug string, err error) { outAll = []*Aircraft{} outFilt = []*Aircraft{} debug = fmt.Sprintf("*** FindOverhead for %s, at %s\n", observerPos, date.NowInPdt()) debug += fmt.Sprintf("* url: http://%s%s?array=1&bounds=%s\n", fr.host, kListUrlPath, "...") // Create a bounding box that's ~40m square, centred on the input lat,long // This is a grievous fudge http://www.movable-type.co.uk/scripts/latlong.html lat_20miles := 0.3 long_20miles := 0.35 nearby := []Aircraft{} if err = fr.ListBbox(observerPos.Lat-lat_20miles, observerPos.Long-long_20miles, observerPos.Lat+lat_20miles, observerPos.Long+long_20miles, &nearby); err != nil { debug += fmt.Sprintf("Lookup error: %s\n", err) return } for i, a := range nearby { // Hack for Surf Air; promote callsigns into (invalid_ flightnumbers, so they don't get stripped if a.FlightNumber == "" && regexp.MustCompile("^URF\\d+$").MatchString(a.Callsign) { nearby[i].FlightNumber = a.Callsign } else if a.FlightNumber != "" { if _, _, err := fdb.ParseIata(a.FlightNumber); err != nil { debug += "** saw bad flightnumber '" + a.FlightNumber + "' from fr24\n" nearby[i].FlightNumber = "" } } aircraftPos := geo.Latlong{a.Lat, a.Long} nearby[i].Dist = observerPos.Dist(aircraftPos) nearby[i].Dist3 = observerPos.Dist3(aircraftPos, a.Altitude) nearby[i].BearingFromObserver = observerPos.BearingTowards(aircraftPos) } sort.Sort(byDist3(nearby)) debug += "** nearby list:-\n" + DebugFlightList(nearby) filtered := filterAircraft(nearby) if len(filtered) == 0 { debug += "** all empty after filtering\n" return } for i, _ := range nearby { outAll = append(outAll, &nearby[i]) } for i, _ := range filtered { outFilt = append(outFilt, &filtered[i]) } debug += "** filtered:-\n" + DebugFlightList(filtered) if grabAnything { filtered[0].IntoFlightIdAircraft(overhead) debug += "** grabbed 1st\n" } else { // closest plane has to be within 12 km to be 'overhead', and it has // to be 4km away from the next-closest if filtered[0].Dist3 < 12.0 { if (len(filtered) == 1) || (filtered[1].Dist3-filtered[0].Dist3) > 4.0 { filtered[0].IntoFlightIdAircraft(overhead) debug += "** selected 1st\n" } else { debug += "** 2nd was too close to 1st\n" } } else { debug += "** 1st was too far away\n" } } return }
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 rootHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) session := sessions.Get(r) // No session ? Get them to login if session.Values["email"] == nil { fb.AppId = kFacebookAppId fb.AppSecret = kFacebookAppSecret loginUrls := map[string]string{ "googlefromscratch": g.GetLoginUrl(r, true), "google": g.GetLoginUrl(r, false), "facebook": fb.GetLoginUrl(r), } if err := templates.ExecuteTemplate(w, "landing", loginUrls); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } modes := map[string]bool{} // The rootHandler is the URL wildcard. Except Fragments, which are broken. if r.URL.Path == "/full" { modes["expanded"] = true } else if r.URL.Path == "/edit" { modes["edit"] = true } else if r.URL.Path == "/debug" { modes["debug"] = true } else if r.URL.Path != "/" { // This is a request for apple_icon or somesuch junk. Just say no. http.NotFound(w, r) return } cdb := complaintdb.ComplaintDB{C: c} cap, err := cdb.GetAllByEmailAddress(session.Values["email"].(string), modes["expanded"]) if cap == nil && err == nil { // No profile exists; daisy-chain into profile page http.Redirect(w, r, "/profile", http.StatusFound) return } else if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } modes["admin"] = user.Current(c) != nil && user.Current(c).Admin modes["superuser"] = modes["admin"] || cap.Profile.EmailAddress == "*****@*****.**" // Default to "", unless we had a complaint in the past hour. lastActivity := "" if len(cap.Complaints) > 0 && time.Since(cap.Complaints[0].Timestamp) < time.Hour { lastActivity = cap.Complaints[0].Activity } var complaintDefaults = map[string]interface{}{ "ActivityList": kActivities, // lives in add-complaint "DefaultActivity": lastActivity, "DefaultLoudness": 1, "NewForm": true, } message := "" if cap.Profile.FullName == "" { message += "<li>We don't have your full name</li>" } if cap.Profile.StructuredAddress.Zip == "" { message += "<li>We don't have an accurate address</li>" } if message != "" { message = fmt.Sprintf("<p><b>We've found some problems with your profile:</b></p><ul>%s</ul>"+ "<p> Without this data, your complaints might be exluded, so please "+ "<a href=\"/profile\"><b>update your profile</b></a> !</p>", message) } var params = map[string]interface{}{ //"Message": template.HTML("Hi!"), "Cap": *cap, "Complaints": hintComplaints(cap.Complaints, modes["superuser"]), "Now": date.NowInPdt(), "Modes": modes, "ComplaintDefaults": complaintDefaults, "Message": template.HTML(message), } if err := templates.ExecuteTemplate(w, "main", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
func generateMonthlyCSV(cdb complaintdb.ComplaintDB, month, year int) (string, int, error) { ctx := cdb.Ctx() bucketname := "serfr0-reports" now := date.NowInPdt() s := time.Date(int(year), time.Month(month), 1, 0, 0, 0, 0, now.Location()) e := s.AddDate(0, 1, 0).Add(-1 * time.Second) log.Infof(ctx, "Starting /be/month: %s", s) // One time, at 00:00, for each day of the given month days := date.IntermediateMidnights(s.Add(-1*time.Second), e) filename := s.Format("complaints-20060102") + e.Format("-20060102.csv") gcsName := "gs://" + bucketname + "/" + filename if exists, err := gcs.Exists(ctx, bucketname, filename); err != nil { return gcsName, 0, fmt.Errorf("gcs.Exists=%v for gs://%s/%s (err=%v)", exists, bucketname, filename, err) } else if exists { return gcsName, 0, nil } gcsHandle, err := gcs.OpenRW(ctx, bucketname, filename, "text/plain") if err != nil { return gcsName, 0, err } csvWriter := csv.NewWriter(gcsHandle.IOWriter()) cols := []string{ "CallerCode", "Name", "Address", "Zip", "Email", "HomeLat", "HomeLong", "UnixEpoch", "Date", "Time(PDT)", "Notes", "ActivityDisturbed", "Flightnumber", "Notes", // Column names above are incorrect, but BKSV are used to them. // //"CallerCode", "Name", "Address", "Zip", "Email", "HomeLat", "HomeLong", //"UnixEpoch", "Date", "Time(PDT)", "Notes", "Flightnumber", //"ActivityDisturbed", "CcSFO", } csvWriter.Write(cols) tStart := time.Now() n := 0 for _, dayStart := range days { dayEnd := dayStart.AddDate(0, 0, 1).Add(-1 * time.Second) log.Infof(ctx, " /be/month: %s - %s", dayStart, dayEnd) tIter := time.Now() iter := cdb.NewLongBatchingIter(cdb.QueryInSpan(dayStart, dayEnd)) for { c, err := iter.NextWithErr() if err != nil { return gcsName, 0, fmt.Errorf("iterator failed after %s (%s): %v", err, time.Since(tIter), time.Since(tStart)) } if c == nil { break } r := []string{ c.Profile.CallerCode, c.Profile.FullName, c.Profile.Address, c.Profile.StructuredAddress.Zip, c.Profile.EmailAddress, fmt.Sprintf("%.4f", c.Profile.Lat), fmt.Sprintf("%.4f", c.Profile.Long), fmt.Sprintf("%d", c.Timestamp.UTC().Unix()), c.Timestamp.Format("2006/01/02"), c.Timestamp.Format("15:04:05"), c.Description, c.AircraftOverhead.FlightNumber, c.Activity, fmt.Sprintf("%v", c.Profile.CcSfo), } if err := csvWriter.Write(r); err != nil { return gcsName, 0, err } n++ } } csvWriter.Flush() if err := gcsHandle.Close(); err != nil { return gcsName, 0, err } log.Infof(ctx, "monthly CSV successfully written to %s, %d rows", gcsName, n) return gcsName, n, nil }