I will load data via API into my ListView async - c#

I will load data via API into my ListView async.
I have a list of items (1 to 6300) and in my search box I can search for the items and then it will be displayed in the ListView.
Now I want to show the items Avg prices, which come from a JSON Api.
If you want to take a look at the tool, here is the git link: https://github.com/Triky313/AlbionOnline-StatisticsAnalysis
My current method looks like this. Once new data is loaded from the API and then again only if they are older than one hour.
public static ObservableCollection<MarketStatChartItem> MarketStatChartItemList = new ObservableCollection<MarketStatChartItem>();
public static async Task<string> GetMarketStatAvgPriceAsync(string uniqueName, Location location)
{
try
{
using (var wc = new WebClient())
{
var apiString = "https://www.albion-online-data.com/api/v1/stats/charts/" +
$"{FormattingUniqueNameForApi(uniqueName)}?date={DateTime.Now:MM-dd-yyyy}";
var itemCheck = MarketStatChartItemList?.FirstOrDefault(i => i.UniqueName == uniqueName);
if (itemCheck == null)
{
var itemString = await wc.DownloadStringTaskAsync(apiString);
var values = JsonConvert.DeserializeObject<List<MarketStatChartResponse>>(itemString);
var newItem = new MarketStatChartItem()
{
UniqueName = uniqueName,
MarketStatChartResponse = values,
LastUpdate = DateTime.Now
};
MarketStatChartItemList?.Add(newItem);
var data = newItem.MarketStatChartResponse
.FirstOrDefault(itm => itm.Location == Locations.GetName(location))?.Data;
var findIndex = data?.TimeStamps?.FindIndex(t => t == data.TimeStamps.Max());
if (findIndex != null)
return data.PricesAvg[(int) findIndex].ToString("N", LanguageController.DefaultCultureInfo);
return "-";
}
if (itemCheck.LastUpdate <= DateTime.Now.AddHours(-1))
{
var itemString = await wc.DownloadStringTaskAsync(apiString);
var values = JsonConvert.DeserializeObject<List<MarketStatChartResponse>>(itemString);
itemCheck.LastUpdate = DateTime.Now;
itemCheck.MarketStatChartResponse = values;
}
var itemCheckData = itemCheck.MarketStatChartResponse
.FirstOrDefault(itm => itm.Location == Locations.GetName(location))?.Data;
var itemCheckFindIndex =
itemCheckData?.TimeStamps?.FindIndex(t => t == itemCheckData.TimeStamps.Max());
if (itemCheckFindIndex != null)
return itemCheckData.PricesAvg[(int) itemCheckFindIndex]
.ToString("N", LanguageController.DefaultCultureInfo);
return "-";
}
}
catch (Exception ex)
{
Debug.Print(ex.StackTrace);
Debug.Print(ex.Message);
return "-";
}
}
Through the API requests everything loads very long and I can not normally use the search.
Does anyone know of a better solution for loading asnyc data without the search problems?
EDIT:
Here again visually represented...
The item list is already loaded and the search is very fast.
Now you can see some minuses on the right side, there should be numbers. These numbers are loaded later when the item is shown in the list.
The problem: The search is extremely stale if he has 50+ items in the search and then fill them with the data of the API.
For each item, an API request is made.
Can this API query be canceled if the search changes or is there another possibility?

Related

Random JSON arrays in C# streams coming from Azure Blob Storage JSON files

I have some code that uses Azure Storage as a repository for serialized JSON objects. I recently set up a method to go through and pick up every file named location.json containing a single object inside a number of directories.
When I first tested the code the deserialization failed because there were brackets around my object. However, when I downloaded the file, the array declaration was not there. I cannot for the life of me figure out where the array declaration is coming from in my code, and it's forcing me to change the way I think about using storage as a result. Has anyone else run into this?
Here's what I ended up using, which works but is also very slow.
public async Task<List<T>> GetAllAsync<T>(string delimiter)
{
var ret = new List<T>();
var blobs = client.GetBlobsByHierarchyAsync(delimiter:delimiter); // "*/location.json"
await foreach (var blobItem in blobs)
{
if (blobItem == null) continue;
log.LogInformation("{Prefix}",blobItem.Prefix);
var blob = client.GetBlobClient(blobItem.Blob.Name);
await using var stm = await blob.OpenReadAsync(new BlobOpenReadOptions(allowModifications: false)
{ Position = 0 });
if (stm == null) continue;
using var sr = new StreamReader(stm);
var s = await sr.ReadToEndAsync();
if (s.First() == '[')
{
log.LogInformation("{Type} serialized as array, taking 'first' item",typeof(T));
var list = JsonSerializer.Deserialize<List<T>>(s);
if (list == null)
{
log.LogInformation("deserialized a null array");
continue;
}
ret.Add(list[0]);
}
else
{
var l = JsonSerializer.Deserialize<T>(s);
ret.Add(l);
}
}
return ret;
}

How do I get the count of messages in the mailbox?

I have the problem with counting the messages of mailbox.
I use c# and Microsoft.Graph 1.18.0
Here is my code
public async Task<long> GetItemsCountAsync(string userId)
{
var countOption = new QueryOption("$count", "true");
var request = ServiceClient.Value.Users[userId].Messages.Request();
request.QueryOptions.Add(countOption);
var resultMessages = new List<Message>();
var count = 0L;
do
{
var messagesResult = await request.GetAsync();
if (messagesResult.AdditionalData != null && messagesResult.AdditionalData.TryGetValue("#odata.count", out var messagesCount))
{
count = (long)messagesCount;
}
resultMessages.AddRange(messagesResult);
request = messagesResult.NextPageRequest;
}
while (request != null);
return count;
}
And I have at the end count = 1417 and resultMessages.Count = 760
Did I miss something?
Thank you for any help!
Everything is fine with the provided example. It appears $count for List messages endpoint could not be trusted here since API does not return accurate count for messages from a specified search folder (refer, for example, this answer for a more details).
To get messages count List mailFolders endpoint could be utilized instead:
GET /users/{id | userPrincipalName}/mailFolders?$select=totalItemCount
where totalItemCount represents the number of items in the mail folder.
C# example
var folders = await graphClient.Users[userId].MailFolders.Request().Select(f =>f.TotalItemCount).GetAsync();
var totalMessagesCount = folders.Sum(folder => folder.TotalItemCount);

Instasharper does not return Likers and Tags list

I have this piece of code in C# using InstaSharper to return information of a media id from Instagram.
public async Task<UserSessionData> SignIn(string userName, string password)
{
userSessionData = new UserSessionData();
userSessionData.UserName = "username";
userSessionData.Password = "password";
api = InstaApiBuilder.CreateBuilder()
.SetUser(userSessionData)
.Build();
var loginRequest = await api.LoginAsync();
if (loginRequest.Succeeded && api.IsUserAuthenticated)
{
IResult<InstaUser> userSearch = await api.GetUserAsync(userName);
IResult<InstaMediaList> media = await api.GetUserMediaAsync(userName, PaginationParameters.MaxPagesToLoad(1));
IResult<InstaMedia> mediaInfo = await api.GetMediaByIdAsync("1924613027431050955");
return userSessionData;
}
else
{
return null;
}
}
GetMediaByIdAsync method correctly returns data about the requested media but the Likers and Tags collection is empty. Is there any way to include those data?
Had the same issue, I found the solution.
Try using this code:
var posts = await api.GetUserMediaAsync("username", PaginationParameters.MaxPagesToLoad(Int32.MaxValue)); // If you want to load all of the posts. If not, replace the "Int32.MaxValue" with the amount of posts you want to load
foreach (var post in posts.Value)
{
var likers = await api.GetMediaLikersAsync(post.InstaIdentifier);
foreach (var liker in likers.Value)
{
Console.WriteLine(liker.UserName);
}
}
This works fine for me, try it out, Im sure it will work for you as well.

Performing paginated search on Active Directory using System.DirectoryServices.Protocols

I am trying to perform paginated search on Active Directory using System.DirectoryServices.Protocols.PageResultRequestControl.
I do get the search results in pages, however, the searchResponse that I get does NOT have the correct TotalCount for total number of pages.
Is it not supported? Or am I missing something here?
This is sample code that I have used in order to implement above. I am using System.DirectoryServices.Protocols to query Active Directory.
When PageResultRequestControl is added with page number, everything works perfectly except for totalSize.
For example, in this code
LdapConnection connection = new LdapConnection(ldapDirectoryIdentifier, credential);
SearchRequest sr = new SearchRequest("", "(displayName=*)", System.DirectoryServices.Protocols.SearchScope.Subtree, new[] { "displayName"});
PageResultRequestControl pr = new PageResultRequestControl(50);
SearchOptionsControl so = new SearchOptionsControl(SearchOption.DomainScope);
sr.Controls.Add(pr);
sr.Controls.Add(so);
SearchResponse searchResponse;
while (true)
{
searchResponse = (SearchResponse)connection.SendRequest(sr);
if (searchResponse.Controls.Length != 1 || !(searchResponse.Controls[0] is PageResultResponseControl))
{
totalPageCount = 0;
return null;
}
PageResultResponseControl pageResponse = (PageResultResponseControl)searchResponse.Controls[0];
totalPageCount = pageResponse.TotalCount;
if (pageResponse.Cookie.Length == 0)
{
break;
}
else
{
pageRequest.Cookie = pageResponse.Cookie;
}
}
As documentation says, the TotalCount property contains the estimated result set count (https://technet.microsoft.com/en-us/library/system.directoryservices.protocols.pageresultresponsecontrol.totalcount)

How to get Bitcoin value for corresponding USD value in ASP.NET C#?

I want to get Bitcoin value for corresponding USD value and store it in table or variable. I got this URL from which I can get a Bitcoin value for USK amount. I searched on blockchain and I found this URL.
For example:
500usd = 0.76105818 btc
I tried:
https://blockchain.info/tobtc?currency=USD&value=500
at the end, its USD value which we want to convert in Bitcoin.
I want to get the result in the variable in C# (backend).
How can I accomplish this?
You need to just make call to server and parse the response.
var uri = String.Format("https://blockchain.info/tobtc?currency=USD&value={0}", 500);
WebClient client = new WebClient();
client.UseDefaultCredentials = true;
var data = client.DownloadString(uri);
var result = Convert.ToDouble(data);
Install-Package CoinMarketCapClient
using CoinMarketCap;
public static async Task<double> GetBitcoinInUsd(double usd){
//https://api.coinmarketcap.com/v1/ticker/bitcoin/
CoinMarketCapClient client = CoinMarketCapClient.GetInstance();
var entity = await client.GetTickerAsync("bitcoin");
return entity.PriceUsd * usd;
}
var uri = String.Format(#"https://blockchain.info/tobtc?currency=USD&value={0}",1);
WebClient client = new WebClient();
client.UseDefaultCredentials = true;
var data = client.DownloadString(uri);
var result = 1/Convert.ToDouble(data.Replace('.',',')); //you will receive 1 btc = result;
There are several APIs out there that will allow you to request the prices for a list of crypto currencies, and/or fiat currencies. The problem is that all the APIs do it in a disparate way. The follow on from that is that any one could be down at any given time, so you need to have some failure tolerance built in. I.e. the code should attempt to use one API, and if that fails, move to the next. Of course, this is further complicated by the fact that price is subjective and localised to a given country, exchange and so on. So, getting an accurate value is very difficult.
Here is example Crypto Compare client from CryptoCurrency.Net (https://github.com/MelbourneDeveloper/CryptoCurrency.Net/blob/master/src/CryptoCurrency.Net/APIClients/PriceEstimationClients/CryptoCompareClient.cs):
public class CryptoCompareClient : PriceEstimationClientBase, IPriceEstimationClient
{
public CryptoCompareClient(IRestClientFactory restClientFactory) : base(restClientFactory)
{
RESTClient = restClientFactory.CreateRESTClient(new Uri("https://min-api.cryptocompare.com"));
}
protected override Func<GetPricesArgs, Task<EstimatedPricesModel>> GetPricesFunc { get; } = async a =>
{
var retVal = new EstimatedPricesModel();
if (a.Currencies.ToList().Count == 0)
{
return retVal;
}
retVal.LastUpdate = DateTime.Now;
var symbolsPart = string.Join(",", a.Currencies.Select(c => c.Name));
var priceJson = await a.RESTClient.GetAsync<string>($"data/pricemultifull?fsyms={symbolsPart}&tsyms={a.FiatCurrency}");
var jObject = (JObject)JsonConvert.DeserializeObject(priceJson);
var rawNode = (JObject)jObject.First.First;
foreach (JProperty coinNode in rawNode.Children())
{
var fiatNode = (JProperty)coinNode.First().First;
var allProperties = fiatNode.First.Children().Cast<JProperty>().ToList();
var change24HourProperty = allProperties.FirstOrDefault(p => string.Compare(p.Name, "CHANGEPCT24HOUR", true) == 0);
var priceProperty = allProperties.FirstOrDefault(p => string.Compare(p.Name, "PRICE", true) == 0);
var price = (decimal)priceProperty.Value;
var change24Hour = (decimal)change24HourProperty.Value;
retVal.Result.Add(new CoinEstimate { CurrencySymbol = new CurrencySymbol(coinNode.Name), ChangePercentage24Hour = change24Hour, FiatEstimate = price, LastUpdate = DateTime.Now });
}
//Extreme hack. It's better to show zero than nothing at all and get the coins stuck
foreach (var currency in a.Currencies)
{
if (retVal.Result.FirstOrDefault(ce => ce.CurrencySymbol.Equals(currency)) == null)
{
retVal.Result.Add(new CoinEstimate { ChangePercentage24Hour = 0, CurrencySymbol = currency, FiatEstimate = 0, LastUpdate = DateTime.Now });
}
}
return retVal;
};
}
The PriceEstimationManager will flick through APIs until it finds one that works (https://github.com/MelbourneDeveloper/CryptoCurrency.Net/blob/master/src/CryptoCurrency.Net/APIClients/PriceEstimationClients/PriceEstimationManager.cs):
public class PriceEstimationManager
{
#region Fields
private readonly Collection<IPriceEstimationClient> _Clients = new Collection<IPriceEstimationClient>();
#endregion
#region Constructor
public PriceEstimationManager(IRestClientFactory restClientFactory)
{
foreach (var typeInfo in typeof(PriceEstimationManager).GetTypeInfo().Assembly.DefinedTypes)
{
var type = typeInfo.AsType();
if (typeInfo.ImplementedInterfaces.Contains(typeof(IPriceEstimationClient)))
{
_Clients.Add((IPriceEstimationClient)Activator.CreateInstance(type, restClientFactory));
}
}
}
#endregion
#region Public Methods
/// <summary>
/// TODO: This needs to be averaged. The two current clients give wildly different values. Need to include some Australian exchanges etc.
/// </summary>
public async Task<EstimatedPricesModel> GetPrices(IEnumerable<CurrencySymbol> currencySymbols, string fiatCurrency)
{
//Lets try a client that hasn't been used before if there is one
var client = _Clients.FirstOrDefault(c => c.AverageCallTimespan.TotalMilliseconds == 0);
var currencies = currencySymbols.ToList();
if (client != null)
{
try
{
return await client.GetPrices(currencies, fiatCurrency);
}
catch
{
//Do nothing
}
}
foreach (var client2 in _Clients.OrderBy(c => c.SuccessRate).ThenBy(c => c.AverageCallTimespan).ToList())
{
try
{
return await client2.GetPrices(currencies, fiatCurrency);
}
catch (Exception ex)
{
Logger.Log("Error Getting Prices", ex, nameof(PriceEstimationManager));
}
}
throw new Exception("Can't get prices");
}
#endregion
}
At a higher level, you can use the code like this (https://github.com/MelbourneDeveloper/CryptoCurrency.Net/blob/master/src/CryptoCurrency.Net.UnitTests/PricingTests.cs):
public async Task GetUSDBitcoinPrice()
{
var priceEstimationManager = new PriceEstimationManager(new RESTClientFactory());
var estimatedPrice = await priceEstimationManager.GetPrices(new List<CurrencySymbol> { CurrencySymbol.Bitcoin }, "USD");
Console.WriteLine($"Estimate: {estimatedPrice.Result.First().FiatEstimate}");
}
As more pricing clients are added, it will get more and more reliable.

Categories