Пример #1
0
        public async Task <IActionResult> Upload(
            [FromBody] DailyStats stats
            )
        {
            if (!ModelState.IsValid)
            {
                Logger.LogError("Failed to parse input data: {0}", ModelState);
                return(BadRequest(ModelState));
            }

            Logger.LogInformation("Receiving daily stats from device {0} for {1}", stats.InstallationId, stats.Date.ToString("d", CultureInfo.InvariantCulture));

            // Safety checks
            if (stats.Date < MinDate)
            {
                Logger.LogError("Daily statistics for unacceptable date {0}", stats.Date);
                return(UnprocessableEntity(ProblemDetailsFactory.CreateProblemDetails(HttpContext,
                                                                                      title: "Unacceptable date (out of valid range)",
                                                                                      type: "https://arianna.digit.srl/api/problems/invalid-date"
                                                                                      )));
            }
            if (stats.TotalMinutesTracked > MinutesADay)
            {
                Logger.LogError("Total minutes tracked ({0}) exceeds minutes in a day", stats.TotalMinutesTracked);
                return(UnprocessableEntity(ProblemDetailsFactory.CreateProblemDetails(HttpContext,
                                                                                      title: "Total minutes tracked exceeds minutes in a day",
                                                                                      type: "https://arianna.digit.srl/api/problems/invalid-data"
                                                                                      )));
            }
            if (stats.Date >= DateTime.UtcNow.Date)
            {
                Logger.LogError("Daily statistics for non-elapsed day {0}", stats.Date.Date);
                return(UnprocessableEntity(ProblemDetailsFactory.CreateProblemDetails(HttpContext,
                                                                                      title: "Unacceptable date (future date)",
                                                                                      type: "https://arianna.digit.srl/api/problems/invalid-date"
                                                                                      )));
            }

            GeoJsonPoint <GeoJson2DGeographicCoordinates> position;
            string geohash;

            try {
                geohash = stats.CentroidHash.Substring(0, 5);
                var decoded = Geohasher.Decode(geohash);
                position = new GeoJsonPoint <GeoJson2DGeographicCoordinates>(new GeoJson2DGeographicCoordinates(decoded.Item2, decoded.Item1));
                Logger.LogInformation("GeoHash {0} decoded as {1:F5},{2:F5}", geohash, position.Coordinates.Latitude, position.Coordinates.Longitude);
            }
            catch (Exception ex) {
                return(UnprocessableEntity(ProblemDetailsFactory.CreateProblemDetails(HttpContext,
                                                                                      title: "Cannot decode geohash",
                                                                                      type: "https://arianna.digit.srl/api/problems/invalid-data",
                                                                                      detail: ex.Message
                                                                                      )));
            }

            if (stats.LocationTracking == null)
            {
                Logger.LogError("Payload does not contain location tracking section");
                return(UnprocessableEntity(ProblemDetailsFactory.CreateProblemDetails(HttpContext,
                                                                                      title: "Payload does not contain location tracking section",
                                                                                      type: "https://arianna.digit.srl/api/problems/invalid-data"
                                                                                      )));
            }
            if (stats.LocationTracking.MinutesAtHome < 0 ||
                stats.LocationTracking.MinutesAtWork < 0 ||
                stats.LocationTracking.MinutesAtSchool < 0 ||
                stats.LocationTracking.MinutesAtOtherKnownLocations < 0 ||
                stats.LocationTracking.MinutesElsewhere < 0)
            {
                Logger.LogError("Location tracking minutes cannot be negative");
                return(UnprocessableEntity(ProblemDetailsFactory.CreateProblemDetails(HttpContext,
                                                                                      title: "Negative location tracking value",
                                                                                      type: "https://arianna.digit.srl/api/problems/invalid-data"
                                                                                      )));
            }
            if (stats.LocationTracking.MinutesAtHome > MinutesADay ||
                stats.LocationTracking.MinutesAtWork > MinutesADay ||
                stats.LocationTracking.MinutesAtSchool > MinutesADay ||
                stats.LocationTracking.MinutesAtOtherKnownLocations > MinutesADay ||
                stats.LocationTracking.MinutesElsewhere > MinutesADay)
            {
                Logger.LogError("One entry in the location tracking section exceeds minutes in a day");
                return(UnprocessableEntity(ProblemDetailsFactory.CreateProblemDetails(HttpContext,
                                                                                      title: "One entry in the location tracking section exceeds minutes in a day",
                                                                                      type: "https://arianna.digit.srl/api/problems/invalid-data"
                                                                                      )));
            }

            // Check for duplicates
            var existingStats = await Mongo.GetDailyStats(stats.InstallationId, stats.Date);

            if (existingStats != null)
            {
                Logger.LogError("Duplicate statistics from device ID {0} for date {1}", stats.InstallationId, stats.Date.ToString("d", CultureInfo.InvariantCulture));
                return(Conflict(ProblemDetailsFactory.CreateProblemDetails(HttpContext,
                                                                           title: "Duplicate statistics for date",
                                                                           type: "https://arianna.digit.srl/api/problems/duplicate"
                                                                           )));
            }

            // Compute voucher amounts
            int stayAtHomeBonus = 0;
            int womCount        = (int)Math.Ceiling(stats.TotalMinutesTracked / 60.0) + stayAtHomeBonus;

            Logger.LogInformation("Generating {0} WOM vouchers for {1} total minutes and {2} minutes at home ({3} stay at home bonus)",
                                  womCount, stats.TotalMinutesTracked, stats.LocationTracking.MinutesAtHome, stayAtHomeBonus);
            var voucherRequest = await Wom.Instrument.RequestVouchers(new VoucherCreatePayload.VoucherInfo[] {
                new VoucherCreatePayload.VoucherInfo {
                    Aim       = "P",
                    Count     = womCount,
                    Latitude  = position.Coordinates.Latitude,
                    Longitude = position.Coordinates.Longitude,
                    Timestamp = stats.Date.Date.AddHours(23.999)
                }
            });

            // OK-dokey
            await Mongo.AddDailyStats(new DataModels.DailyStats {
                InstallationId         = stats.InstallationId,
                Date                   = stats.Date.Date,
                TotalMinutesTracked    = stats.TotalMinutesTracked,
                TotalWomVouchersEarned = womCount,
                Centroid               = position,
                CentroidHash           = geohash,
                LocationCount          = stats.LocationCount,
                VehicleCount           = stats.VehicleCount,
                EventCount             = stats.EventCount,
                SampleCount            = stats.SampleCount,
                DiscardedSampleCount   = stats.DiscardedSampleCount,
                BoundingBoxDiagonal    = stats.BoundingBoxDiagonal,
                LocationTracking       = new DataModels.LocationTrackingStats {
                    MinutesAtHome   = stats.LocationTracking.MinutesAtHome,
                    MinutesAtWork   = stats.LocationTracking.MinutesAtWork,
                    MinutesAtSchool = stats.LocationTracking.MinutesAtSchool,
                    MinutesAtOtherKnownLocations = stats.LocationTracking.MinutesAtOtherKnownLocations,
                    MinutesElsewhere             = stats.LocationTracking.MinutesElsewhere
                }
            });

            return(Ok(new UploadConfirmation {
                WomLink = voucherRequest.Link,
                WomPassword = voucherRequest.Password,
                WomCount = womCount
            }));
        }