func statsResetHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) cdb := complaintdb.ComplaintDB{C: c} cdb.ResetGlobalStats() w.Write([]byte(fmt.Sprintf("Stats reset\n"))) }
func profileUpdateHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) session := sessions.Get(r) if session.Values["email"] == nil { c.Errorf("profileUpdate:, session was empty; no cookie ?") http.Error(w, "session was empty; no cookie ? is this browser in privacy mode ?", http.StatusInternalServerError) return } email := session.Values["email"].(string) r.ParseForm() lat, err := strconv.ParseFloat(r.FormValue("Lat"), 64) if err != nil { c.Errorf("profileUpdate:, parse lat '%s': %v", r.FormValue("Lat"), err) http.Error(w, err.Error(), http.StatusInternalServerError) return } long, err2 := strconv.ParseFloat(r.FormValue("Long"), 64) if err2 != nil { c.Errorf("profileUpdate:, parse long '%s': %v", r.FormValue("Long"), err) http.Error(w, err2.Error(), http.StatusInternalServerError) return } // Maybe make a call to fetch the elevation ?? // https://developers.google.com/maps/documentation/elevation/intro cp := types.ComplainerProfile{ EmailAddress: email, CallerCode: r.FormValue("CallerCode"), FullName: strings.TrimSpace(r.FormValue("FullName")), Address: strings.TrimSpace(r.FormValue("AutoCompletingMagic")), StructuredAddress: types.PostalAddress{ Number: r.FormValue("AddrNumber"), Street: r.FormValue("AddrStreet"), City: r.FormValue("AddrCity"), State: r.FormValue("AddrState"), Zip: r.FormValue("AddrZip"), Country: r.FormValue("AddrCountry"), }, CcSfo: true, //FormValueCheckbox(r, "CcSfo"), Lat: lat, Long: long, } cdb := complaintdb.ComplaintDB{C: c} err = cdb.PutProfile(cp) if err != nil { c.Errorf("profileUpdate: cdb.Put: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } http.Redirect(w, r, "/", http.StatusFound) }
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 bksvSubmitUserHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) cdb := complaintdb.ComplaintDB{C: c, Memcache: true} start, end := date.WindowForYesterday() bksv_ok, bksv_not_ok := 0, 0 email := r.FormValue("user") if cp, err := cdb.GetProfileByEmailAddress(email); err != nil { c.Errorf(" /bksv/submit-user(%s): getprofile: %v", email, err) http.Error(w, err.Error(), http.StatusInternalServerError) return } else if complaints, err := cdb.GetComplaintsInSpanByEmailAddress(email, start, end); err != nil { c.Errorf(" /bksv/submit-user(%s): getcomplaints: %v", email, err) http.Error(w, err.Error(), http.StatusInternalServerError) return } else { for i, complaint := range complaints { time.Sleep(time.Millisecond * 200) if debug, err := bksv.PostComplaint(urlfetch.Client(c), *cp, complaint); err != nil { //cdb.C.Infof("pro: %v", cp) //cdb.C.Infof("comp: %#v", complaint) cdb.C.Errorf("BKSV posting error: %v", err) cdb.C.Infof("BKSV Debug\n------\n%s\n------\n", debug) bksv_not_ok++ } else { if i == 0 { cdb.C.Infof("BKSV [OK] Debug\n------\n%s\n------\n", debug) } bksv_ok++ } } } c.Infof("bksv for %s, %d/%d", email, bksv_ok, bksv_not_ok) if bksv_not_ok > 0 { c.Errorf("bksv for %s, %d/%d", email, bksv_ok, bksv_not_ok) } w.Write([]byte("OK")) }
func statsHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) cdb := complaintdb.ComplaintDB{C: c} if gs, err := cdb.LoadGlobalStats(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } else { // Sigh. Ignore. // sort.Sort(sort.Reverse(complaintdb.DailyCountDesc(gs.Counts))) var params = map[string]interface{}{ "GlobalStats": gs, } if err := templates.ExecuteTemplate(w, "stats", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } }
// Examine all users. If they had any complaints, throw them in the queue. func bksvScanYesterdayHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) cdb := complaintdb.ComplaintDB{C: c, Memcache: true} var cps = []types.ComplainerProfile{} cps, err := cdb.GetAllProfiles() if err != nil { c.Errorf(" /bksv/scan-yesterday: getallprofiles: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } start, end := date.WindowForYesterday() bksv_ok := 0 for _, cp := range cps { if cp.CcSfo == false { continue } var complaints = []types.Complaint{} complaints, err = cdb.GetComplaintsInSpanByEmailAddress(cp.EmailAddress, start, end) if err != nil { c.Errorf(" /bksv/scan-yesterday: getbyemail(%s): %v", cp.EmailAddress, err) http.Error(w, err.Error(), http.StatusInternalServerError) return } if len(complaints) > 0 { t := taskqueue.NewPOSTTask("/bksv/submit-user", map[string][]string{ "user": {cp.EmailAddress}, }) if _, err := taskqueue.Add(c, t, "submitreports"); err != nil { c.Errorf(" /bksv/scan-yesterday: enqueue: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } bksv_ok++ } } c.Infof("enqueued %d bksv", bksv_ok) w.Write([]byte(fmt.Sprintf("OK, enqueued %d", bksv_ok))) }
func profileFormHandler(w http.ResponseWriter, r *http.Request) { // https, yay if r.URL.Host == "stop.jetnoise.net" { // We're behind cloudflare, so we always see http. This is how we can tell if the user is // using https ... if r.Header.Get("Cf-Visitor") != `{"scheme":"https"}` { safeUrl := r.URL safeUrl.Scheme = "https" http.Redirect(w, r, safeUrl.String(), http.StatusFound) return } } c := appengine.NewContext(r) 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 } email := session.Values["email"].(string) cdb := complaintdb.ComplaintDB{C: c} cp, _ := cdb.GetProfileByEmailAddress(email) if cp.EmailAddress == "" { // First ever visit - empty profile ! cp.EmailAddress = email cp.CcSfo = true } var params = map[string]interface{}{ "Profile": cp, "MapsAPIKey": kGoogleMapsAPIKey, // For autocomplete & latlong goodness } params["Message"] = r.FormValue("msg") if err := templates.ExecuteTemplate(w, "profile-edit", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
func SendEmailToAllUsers(c appengine.Context, subject string) int { cdb := complaintdb.ComplaintDB{C: c} if cps, err := cdb.GetAllProfiles(); err != nil { c.Errorf("SendEmailToAllUsers/GetAllProfiles: %v", err) return 0 } else { buf := new(bytes.Buffer) params := map[string]interface{}{} if err := templates.ExecuteTemplate(buf, "email-update", params); err != nil { return 0 } n := 0 for _, cp := range cps { // This message update goes only to the opt-outers ... if cp.CcSfo == true && cp.CallerCode != "WOR005" { continue } msg := &mail.Message{ Sender: kSenderEmail, ReplyTo: kSenderEmail, To: []string{cp.EmailAddress}, Subject: subject, HTMLBody: buf.String(), } if err := mail.Send(c, msg); err != nil { c.Errorf("Could not send useremail to <%s>: %v", cp.EmailAddress, err) } n++ } return n } }
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 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 }
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 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 emailHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) session := sessions.Get(r) cdb := complaintdb.ComplaintDB{C: c} cp, err := cdb.GetProfileByEmailAddress(session.Values["email"].(string)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } start, end := date.WindowForYesterday() // end = time.Now() complaints, err2 := cdb.GetComplaintsInSpanByEmailAddress(cp.EmailAddress, start, end) if err2 != nil { http.Error(w, err2.Error(), http.StatusInternalServerError) return } var cap = types.ComplaintsAndProfile{ Profile: *cp, Complaints: complaints, } if err := templates.ExecuteTemplate(w, "email-update", map[string]interface{}{}); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else { return } msg, err3 := GenerateEmail(c, cap) if err3 != nil { http.Error(w, err3.Error(), http.StatusInternalServerError) return } cap.Profile.CcSfo = true if len(cap.Complaints) == 0 { http.Error(w, "No complaints found ?!", http.StatusInternalServerError) return } msg2, err4 := GenerateSingleComplaintEmail(c, cap.Profile, cap.Complaints[len(cap.Complaints)-1]) if err4 != nil { http.Error(w, err4.Error(), http.StatusInternalServerError) return } var params = map[string]interface{}{ "Cap": cap, "EmailBundle": msg, "EmailSingle": msg2, "EmailBundleBody": template.HTML(msg.HTMLBody), "EmailSingleBody": template.HTML(msg2.HTMLBody), } /* if err4 := sendViaHTTPGateway(c, msg); err4 != nil { http.Error(w, err4.Error(), http.StatusInternalServerError) return } */ if err := templates.ExecuteTemplate(w, "email-debug", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
func SendComplaintsWithSpan(c appengine.Context, start, end time.Time) (err error) { c.Infof("--- Emails, %s -> %s", start, end) blacklist := map[string]bool{} for _, e := range blacklistAddrs { blacklist[e] = true } cdb := complaintdb.ComplaintDB{C: c, Memcache: true} var cps = []types.ComplainerProfile{} cps, err = cdb.GetAllProfiles() if err != nil { return } complaints_private, complaints_submitted, no_data, sent_ok, sent_fail := 0, 0, 0, 0, 0 sent_single_ok, sent_single_fail := 0, 0 for _, cp := range cps { var complaints = []types.Complaint{} complaints, err = cdb.GetComplaintsInSpanByEmailAddress(cp.EmailAddress, start, end) if err != nil { c.Errorf("Could not get complaints [%v->%v] for <%s>: %v", start, end, cp.EmailAddress, err) no_data++ continue } if len(complaints) == 0 { no_data++ continue } // Emailing disabled; last run was Fri Oct 9, 4am, with data for Oct 8. BKSV is now live. if false { if cp.CcSfo == true { for _, complaint := range complaints { if msg, err := GenerateSingleComplaintEmail(c, cp, complaint); err != nil { c.Errorf("Could not generate single email to <%s>: %v", cp.EmailAddress, err) sent_single_fail++ continue } else { if blacklist[cp.EmailAddress] { sent_single_fail++ } else { if err := mail.Send(c, msg); err != nil { c.Errorf("Could not send email to <%s>: %v", cp.EmailAddress, err) sent_single_fail++ continue } else { sent_single_ok++ } } } } } } var cap = types.ComplaintsAndProfile{ Profile: cp, Complaints: complaints, } var msg *mail.Message if msg, err = GenerateEmail(c, cap); err != nil { c.Errorf("Could not generate email to <%s>: %v", cp.EmailAddress, err) sent_fail++ continue } useGateway := false if useGateway { if err = sendViaHTTPGateway(c, msg); err != nil { c.Errorf("Could not gateway email to <%s>: %v", cp.EmailAddress, err) sent_fail++ continue } } else { if blacklist[cp.EmailAddress] { sent_fail++ } else { if err = mail.Send(c, msg); err != nil { c.Errorf("Could not send email to <%s>: %v", cp.EmailAddress, err) sent_fail++ continue } } } if cap.Profile.CcSfo == true { complaints_submitted += len(cap.Complaints) } else { complaints_private += len(cap.Complaints) } sent_ok++ } subject := fmt.Sprintf("Daily report stats: users:%d/%d reports:%d/%d emails:%d:%d", sent_ok, (sent_ok + no_data), complaints_submitted, (complaints_submitted + complaints_private), sent_single_ok, sent_single_fail) SendEmailToAdmin(c, subject, "") dc := complaintdb.DailyCount{ Datestring: date.Time2Datestring(start.Add(time.Hour)), NumComplaints: complaints_submitted + complaints_private, NumComplainers: sent_ok, } cdb.AddDailyCount(dc) c.Infof("--- email wrapup: %d ok, %d fail (%d no data) : %d reports submitted (%d kept back) single[%d/%d]", sent_ok, sent_fail, no_data, complaints_submitted, complaints_private, sent_single_ok, sent_single_fail) return }
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 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 := appengine.Timeout(appengine.NewContext(r), 60*time.Second) // Default has a 5s timeout cdb := complaintdb.ComplaintDB{C: ctx, Memcache: false} 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) } }