예제 #1
0
파일: stats.go 프로젝트: hugoh/complaints
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")))
}
예제 #2
0
파일: profile.go 프로젝트: hugoh/complaints
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)
}
예제 #3
0
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()
}
예제 #4
0
파일: email.go 프로젝트: hugoh/complaints
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"))
}
예제 #5
0
파일: stats.go 프로젝트: hugoh/complaints
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)
		}
	}

}
예제 #6
0
파일: email.go 프로젝트: hugoh/complaints
// 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)))
}
예제 #7
0
파일: profile.go 프로젝트: hugoh/complaints
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)
	}
}
예제 #8
0
파일: email.go 프로젝트: hugoh/complaints
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
	}
}
예제 #9
0
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")
	}
}
예제 #10
0
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
}
예제 #11
0
파일: reports.go 프로젝트: hugoh/complaints
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
	}
}
예제 #12
0
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)))
}
예제 #13
0
파일: email.go 프로젝트: hugoh/complaints
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)
	}
}
예제 #14
0
파일: email.go 프로젝트: hugoh/complaints
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
}
예제 #15
0
파일: main.go 프로젝트: hugoh/complaints
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)
	}
}
예제 #16
0
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)
	}
}