/// <summary>
        /// Meets global criteria is a criteria that must be met in order to notify
        /// no matter delivery channel
        /// </summary>
        /// <param name="feed"></param>
        /// <returns></returns>
        public bool MeetsGlobalCriteria(CityFeed feed)
        {
            bool isValid = true; // default to "valid" as if no rules are applied

            _globalRules.ForEach(rule => isValid &= rule.Invoke(feed));
            return(isValid);
        }
        // TODO: needs strong/strict validation
        public static string GetTwitterMessageSpanish(CityFeed feed)
        {
            var charactersLeft = TwitterCharLimit;
            var attributions   = from attr in feed.MaxAqiStation.Attributions
                                 let attributionMsg = attr.GetNameOrDefault()
                                                      select attributionMsg;
            var encodedText = string.Concat("Fuente:\n",
                                            attributions.Aggregate((prev, current) => string.Concat(prev, "\n", current)));
            var attrText = HttpUtility.HtmlDecode(encodedText);

            // how many characters do we have left?
            charactersLeft -= attrText.Length;
            var pascalCity = new StringBuilder().Append(char.ToUpper(feed.CityName[0]))
                             .Append(feed.CityName.Substring(1))
                             .ToString();
            var followText = $"#aireEn{pascalCity}";

            if (charactersLeft < 200)
            {
                var custom = getQuality(feed.MaxAQI) == Quality.Good ? "Es momento de respirar" : "Mantenga sus precauciones";
                return($"Calidad del aire en {pascalCity} es {getScaleSpanish(feed.MaxAQI)}\nIndice AQI:{feed.MaxAQI} segun Estacion \"{feed.MaxAqiStation.Name}\")\n\n{attrText}\n{followText}");
            }

            // attempt to send this message
            return($"La calidad del aire en {pascalCity} es {feed.MaxAQI} (estacion {feed.MaxAqiStation.Name})\n{attrText}\n{followText}");
        }
Example #3
0
        public async Task<string> AsyncExecute(string input, ILambdaContext context) {

                var waqiProxy = Waqi.create("sometoken"); // add token
                var feeds = new List<string>();
                var stationFeedsObtained = new List<WaqiCityFeed>();

                feeds.AddRange(new string[]{@"mexico/guadalajara/tlaquepaque"
                , @"mexico/guadalajara/vallarta"
                , @"mexico/guadalajara/miravalle"
                });
                foreach (var feed in feeds) {
                    System.Console.WriteLine($"Requesting feed for:{feed}");
                    string mutableString = string.Empty;
                    try {
                        mutableString = await waqiProxy.getCityFeed(feed);
                            
                        if(mutableString != string.Empty) {
                            System.Console.WriteLine("found this result:\n");
                            System.Console.WriteLine(mutableString);
                            var wcf = JsonSerializer.Deserialize<WaqiCityFeed>(mutableString);
                            stationFeedsObtained.Add(wcf);
                        }
                    } catch(Exception e) { System.Console.WriteLine(e);}
                }
                var cityFeedDto = CityFeed.From(stationFeedsObtained);

                return JsonSerializer.Serialize(cityFeedDto).ToString();
                //System.Console.WriteLine(JsonSerializer.Serialize(cityFeedDto).ToString());
        }
        public void ShouldDetectMaxAqiFromMultipleStations()
        {
            var waqiStations = GuadalajaraCityStations;

            var cityFeed = CityFeed.From(waqiStations);

            Assert.Equal(MaxAqi, cityFeed.MaxAQI);
        }
        public void ShouldDetectAndFindBestCityName()
        {
            var waqiStations = GuadalajaraCityStations;

            var cityFeed = CityFeed.From(waqiStations);

            Assert.Equal(cityFeed.CityName, GuadalajaraCity);
        }
        public static string GetSimpleMessage(CityFeed feed)
        {
            var attributionText = (from attr in feed.MaxAqiStation.Attributions
                                   let attributionMsg = attr.ToString()
                                                        select attributionMsg)
                                  .Aggregate((prev, current) => string.Concat(prev, "\n", current));

            return($"En {feed.CityName} calidad del aire es {getScaleSpanish(feed.MaxAQI)}(Indice Calidad:{feed.MaxAQI}) reportado por la estacion {feed.MaxAqiStation.Name})\n{attributionText}");
        }
        public void ShouldGetMaxAqiStation()
        {
            var waqiStations = GuadalajaraCityStations;

            var cityFeed = CityFeed.From(waqiStations);

            // max aqi station is set on constructor, currently is Tlaquepaque
            Assert.Equal(TlaquepaqueStationId, cityFeed.MaxAqiStation.Id);
        }
 public static Predicate <CityFeed> AbsoluteAqiChangedBy(CityFeed prevFeed, int absoluteDelta)
 {
     return(feed => {
         if (prevFeed == null)
         {
             return true;
         }
         return Math.Abs(prevFeed.MaxAQI - feed.MaxAQI) > absoluteDelta;
     });
 }
        public void ShouldCreateCityFeedFromExternalJson()
        {
            var waqiStations = GuadalajaraCityStations;

            var cityFeed = CityFeed.From(waqiStations);

            Assert.NotNull(cityFeed);
            Assert.NotNull(cityFeed.CityName);
            Assert.NotEmpty(cityFeed.Stations);
        }
Example #10
0
        public void ShouldContainAllStations()
        {
            var waqiStations = GuadalajaraCityStations;

            var cityFeed = CityFeed.From(waqiStations);

            Assert.NotNull(cityFeed);
            Assert.NotNull(cityFeed.CityName);
            Assert.Equal(GuadalajaraCityStations.Count, cityFeed.Stations.Count);
        }
 public static Predicate <CityFeed> MinutesApartFrom(CityFeed prevFeed, int minutes)
 {
     return(feed => {
         if (prevFeed == null)
         {
             return true;
         }
         return prevFeed.UpdatedAt.AddMinutes(minutes).CompareTo(feed) < 0;
     });
 }
Example #12
0
        public void ShouldParseCityFeedWithInvalidAqiValue()
        {
            var waqiStations = new List <WaqiCityFeed>();

            waqiStations.Add(Values.Waqi.CreateWaqiCityFeedResponseWithInvalidAqi(GuadalajaraCity, FailingStation, int.Parse(FailingStationId)));
            var cityFeed = CityFeed.From(waqiStations);

            Assert.NotNull(cityFeed);
            Assert.NotNull(cityFeed.CityName);
            Assert.NotEmpty(cityFeed.Stations);
        }
        /// <summary>
        /// Creates Document from a cityFeed but also accepts a previous Max aqui value
        /// to create a Delta
        /// delta is negative if aqi is less than previous
        /// </summary>
        /// <param name="cityFeed"></param>
        /// <param name="previousMaxAqi"></param>
        /// <returns></returns>
        public static Document From(CityFeed cityFeed, int previousMaxAqi)
        {
            var doc = new Document();

            // NOTE: default country is only MX as this is the only supported use case, however leaving room for scaling in format
            doc.Add(PartitionKeyName, $"{_defaultCountry}-{cityFeed.CityName}");
            doc.Add(FieldMaxStationName, $"{cityFeed.MaxAqiStation.Id}-{cityFeed.MaxAqiStation.Name}");
            doc.Add(FieldMaxAqi, cityFeed.MaxAQI);
            doc.Add(FieldDeltaAqi, cityFeed.MaxAQI - previousMaxAqi); // delta is null by default
            doc.Add(JsonValue, JsonSerializer.Serialize(cityFeed));
            doc.Add(FieldUpdatedTime, cityFeed.UpdatedAtText);
            return(doc);
        }
Example #14
0
        public void ShouldHaveAtLeastOneAttribution()
        {
            var waqiStations = GuadalajaraCityStations;

            var cityFeed = CityFeed.From(waqiStations);

            // max aqi station is set on constructor, currently is Tlaquepaque
            Assert.NotEmpty(cityFeed.MaxAqiStation.Attributions);
            // attributions by default contain Waqi's URL, we should expect "n = waqiStations.Length" matches
            var attributions = from station in cityFeed.Stations
                               from attribution in station.Attributions
                               where attribution.Url.Contains(Waqi.WaqiAttributionUrl)
                               select attribution;

            Assert.Equal(attributions.Count(), waqiStations.Count());
        }
Example #15
0
        public async Task <string> AsyncExecute(string input, ILambdaContext context)
        {
            var waqiProxy = Waqi.create(_token);
            var cities    = JsonSerializer.Deserialize <List <Latincoder.AirQuality.Model.Config.City> >(_citiesRaw); // default C# serialization
            // process multiple cities
            var stationFeedsByCity = from city in cities
                                     from station in city.Stations
                                     let feed = new { Uri = $"{city.Country}/{city.Name}/{station}", CityName = city.Name }
            group feed by $"{city.Country}-{city.Name}" into stationsByCity
            select stationsByCity;

            var cityFeedsDTO = new List <CityFeed>();

            foreach (var cityFeeds in stationFeedsByCity)
            {
                var feeds = from value in cityFeeds
                            select value.Uri;
                var stationFeedsObtained = new List <WaqiCityFeed>();
                foreach (var feed in feeds)
                {
                    System.Console.WriteLine($"Requesting feed for:{feed}");
                    string mutableString = string.Empty;
                    try {
                        mutableString = await waqiProxy.getCityFeed(feed);

                        if (mutableString != string.Empty)
                        {
                            System.Console.WriteLine("found this result:\n");
                            System.Console.WriteLine(mutableString);
                            var wcf = JsonSerializer.Deserialize <WaqiCityFeed>(mutableString);
                            stationFeedsObtained.Add(wcf);
                        }
                    } catch (Exception e) { System.Console.WriteLine(e); }
                }
                try {
                    cityFeedsDTO.Add(CityFeed.From(stationFeedsObtained));
                } catch (Exception e) { System.Console.WriteLine(e); }
            }
            var response = await sendCityFeed(cityFeedsDTO);

            System.Console.WriteLine(response.ToString());
            return("done");
        }
Example #16
0
        /// <summary>
        /// A simple function that takes a string and does a ToUpper
        /// </summary>
        /// <param name="input"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task <string> FunctionHandler(DynamoDBEvent dynamoDBEvent, ILambdaContext context)
        {
            snsClient = new AmazonSimpleNotificationServiceClient();

            foreach (var record in dynamoDBEvent.Records)
            {
                var      dynamoDocument = Document.FromAttributeMap(record.Dynamodb.NewImage);
                CityFeed feed           = CityFeedDocument.ToDTO(dynamoDocument);
                if (feed == null)
                {
                    System.Console.WriteLine("Streamed had no new data");
                    continue;
                }
                System.Console.WriteLine($"Feed Object:{feed}");
                System.Console.WriteLine($"Accessed stream: {feed.CityName}");
                var msg = NotificationFormatter.GetTwitterMessageSpanish(feed);
                await publishToGeneral(NotificationFormatter.GetSimpleMessage(feed));
                await publishToTwitter(msg);
            }
            return("");
        }
 public static bool AqiAboveUnhealthyForSensitiveGroupsThreshold(CityFeed feed)
 {
     return(feed.MaxAQI >= 101);
 }
 public static bool AqiAboveModerateThreshold(CityFeed feed)
 {
     return(feed.MaxAQI >= 51);
 }
 public static bool FeedWithinLast10Minutes(CityFeed feed)
 {
     return(feed.UpdatedAt.AddMinutes(10).CompareTo(DateTime.Now) > 0);
 }
Example #20
0
        public async Task <string> AsyncExecute(System.IO.Stream input, ILambdaContext context)
        {
            _ = input;                                                                                                // discard
            var waqiProxy = Waqi.create(_token);
            var cities    = JsonSerializer.Deserialize <List <Latincoder.AirQuality.Model.Config.City> >(_citiesRaw); // default C# serialization
            // process multiple cities
            var stationFeedsByCity = from city in cities
                                     from station in city.Stations
                                     let feed = new { Uri = $"{city.Country}/{city.Name}/{station}", CityName = city.Name }
            group feed by $"{city.Country}-{city.Name}" into stationsByCity
            select stationsByCity;

            var cityFeedsDTO = new List <CityFeed>();

            foreach (var cityFeeds in stationFeedsByCity)
            {
                var feeds = from value in cityFeeds
                            select value.Uri;
                var stationFeedsObtained = new List <WaqiCityFeed>();
                foreach (var feed in feeds)
                {
                    System.Console.WriteLine($"Requesting feed for:{feed}");
                    string mutableString = string.Empty;
                    try {
                        mutableString = await waqiProxy.getCityFeed(feed);

                        if (mutableString != string.Empty)
                        {
                            System.Console.WriteLine("found this result:\n");
                            System.Console.WriteLine(mutableString);
                            var wcf = JsonSerializer.Deserialize <WaqiCityFeed>(mutableString);
                            stationFeedsObtained.Add(wcf);
                        }
                    } catch (Exception e) { System.Console.WriteLine(e); }
                }
                try {
                    cityFeedsDTO.Add(CityFeed.From(stationFeedsObtained));
                } catch (Exception e) { System.Console.WriteLine(e); }
            }

            var aqiTable             = Table.LoadTable(_dynamoClient, _AqiTableName);
            var notificationIsUrgent = NotificationValidator.CreateyValidator();

            notificationIsUrgent.AddRules(Rules.AqiAboveUnhealthyThreshold);
            // store in DynamoDB if meets notification criteria
            foreach (var feed in cityFeedsDTO)
            {
                // Gather previous and new feeds
                var prevFeedDoc = await aqiTable.GetItemAsync(CityFeedDocument.PartitionKeyName);

                var newFeed = prevFeedDoc != null?
                              CityFeedDocument.From(feed, prevFeedDoc[CityFeedDocument.FieldMaxAqi].AsInt()) :
                                  CityFeedDocument.From(feed);

                // if no previous stuff
                if (prevFeedDoc == null)
                {
                    Console.WriteLine($"FIRST TIME: Store in dynamo now for {feed.CityName}");
                    await aqiTable.PutItemAsync(newFeed);

                    continue;
                }

                var prevFeed = CityFeedDocument.ToDTO(prevFeedDoc);

                // each feed will has a custom validator with it's own criteria since this is done per city
                var notificationIsNotSpam = NotificationValidator.CreateyValidator();
                // spam means max aqi changed at least by 5 points, and is at least 20 minutes apart frm last notification
                notificationIsNotSpam.AddRules(
                    Rules.MinutesApartFrom(prevFeed, 30),
                    Rules.AbsoluteAqiChangedBy(prevFeed, 5));
                // urgent or meets criteria
                if (notificationIsUrgent.MeetsGlobalCriteria(feed) && notificationIsNotSpam.MeetsGlobalCriteria(feed))
                {
                    Console.WriteLine($"Store in dynamo now for {feed.CityName}");
                    await aqiTable.PutItemAsync(newFeed);
                }
                else
                {
                    Console.WriteLine("Does not meet rules...");
                }
            }

            return("done");
        }
 public static Predicate <CityFeed> AqiBecameGoodFromModerateOrWorseWithDelta(CityFeed feed, int delta)
 {
     return(feed => feed.MaxAQI <= 50 && delta < 0);
 }
 public static bool AqiAboveHazardousThreshold(CityFeed feed)
 {
     return(feed.MaxAQI >= 151);
 }
 public static bool AqiAboveUnhealthyThreshold(CityFeed feed)
 {
     return(feed.MaxAQI >= 151);
 }