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 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")) }
// Writes them all into a batch queue func publishAllComplaintsHandler(w http.ResponseWriter, r *http.Request) { ctx := appengine.NewContext(r) str := "" s, e, _ := widget.FormValueDateRange(r) days := date.IntermediateMidnights(s.Add(-1*time.Second), e) // decrement start, to include it url := "/backend/publish-complaints" for i, day := range days { dayStr := day.Format("2006.01.02") thisUrl := fmt.Sprintf("%s?datestring=%s", url, dayStr) if r.FormValue("skipload") != "" { thisUrl += "&skipload=" + r.FormValue("skipload") } t := taskqueue.NewPOSTTask(thisUrl, map[string][]string{}) // Give ourselves time to get all these tasks posted, and stagger them out a bit t.Delay = time.Minute + time.Duration(i)*15*time.Second if _, err := taskqueue.Add(ctx, t, "batch"); err != nil { log.Errorf(ctx, "publishAllComplaintsHandler: enqueue: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } str += " * posting for " + thisUrl + "\n" } w.Header().Set("Content-Type", "text/plain") w.Write([]byte(fmt.Sprintf("OK, enqueued %d\n--\n%s", len(days), str))) }
// 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))) }
// Enqueues one 'day' task per day in the range func batchFlightDateRangeHandler(w http.ResponseWriter, r *http.Request) { ctx := req2ctx(r) n := 0 str := "" s, e, _ := widget.FormValueDateRange(r) job := r.FormValue("job") if job == "" { http.Error(w, "Missing argument: &job=foo", http.StatusInternalServerError) return } str += fmt.Sprintf("** s: %s\n** e: %s\n", s, e) days := date.IntermediateMidnights(s.Add(-1*time.Second), e) // decrement start, to include it for _, day := range days { dayUrl := "/backend/fdb-batch/day" dayStr := day.Format("2006/01/02") str += fmt.Sprintf(" * adding %s, %s via %s\n", job, dayStr, dayUrl) if r.FormValue("dryrun") == "" { t := taskqueue.NewPOSTTask(dayUrl, map[string][]string{ "day": {dayStr}, "job": {job}, }) if _, err := taskqueue.Add(ctx, t, "batch"); err != nil { log.Errorf(ctx, "upgradeHandler: enqueue: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } } n++ } log.Infof(ctx, "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))) }
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 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 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) } }