Version 3.0.364

This commit is contained in:
Anja 2023-05-09 08:47:52 +02:00
parent 91d42552c7
commit 0b9196a78d
91 changed files with 3452 additions and 555 deletions

View file

@ -7,6 +7,7 @@ using TINK.Model.Connector.Filter;
using TINK.Model.Services.CopriApi;
using TINK.Model.Stations;
using TINK.Model.Stations.StationNS;
using TINK.Model.Stations.StationNS.Operator;
using BikeInfo = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfo;
namespace TINK.Model.Connector
@ -92,11 +93,18 @@ namespace TINK.Model.Connector
var providerBikesAndStations = await m_oInnerQuery.GetBikesAndStationsAsync();
// Do filtering.
var filteredStationsDictionary = new StationDictionary(providerBikesAndStations.Response.StationsAll.CopriVersion, DoFilter(providerBikesAndStations.Response.StationsAll, Filter));
var filteredBikesDictionary = new BikeCollection(DoFilter(providerBikesAndStations.Response.Bikes, Filter));
var filteredStationsDictionary = new StationDictionary(
providerBikesAndStations.Response.StationsAll.CopriVersion,
DoFilter(providerBikesAndStations.Response.StationsAll, Filter));
var filteredBikesOccupiedDictionary = new BikeCollection(
DoFilter(providerBikesAndStations.Response.BikesOccupied, Filter));
var filteredBikesAndStations = new Result<StationsAndBikesContainer>(
providerBikesAndStations.Source,
new StationsAndBikesContainer(filteredStationsDictionary, filteredBikesDictionary),
new StationsAndBikesContainer(
filteredStationsDictionary,
filteredBikesOccupiedDictionary),
providerBikesAndStations.GeneralData,
providerBikesAndStations.Exception);
@ -106,17 +114,25 @@ namespace TINK.Model.Connector
/// <summary> Filter bikes by group. </summary>
/// <param name="bikes">Bikes to filter.</param>
/// <returns>Filtered bikes.</returns>
private static Dictionary<string, BikeInfo> DoFilter(BikeCollection bikes, IGroupFilter filter)
{
return bikes.Where(x => filter.DoFilter(x.Group).Count() > 0).ToDictionary(x => x.Id);
}
private static Dictionary<string, BikeInfo> DoFilter(BikeCollection bikes, IGroupFilter filter) =>
bikes
.Where(x => filter.DoFilter(x.Group).Count() > 0)
.ToDictionary(x => x.Id);
/// <summary> Filter stations by group. </summary>
/// <returns></returns>
private static Dictionary<string, IStation> DoFilter(StationDictionary stations, IGroupFilter filter)
{
return stations.Where(x => filter.DoFilter(x.Group).Count() > 0).ToDictionary(x => x.Id);
}
/// <summary> Filter stations by group and removes bike group collection entries which do not match group filter. </summary>
/// <returns>Matching stations.</returns>
private static Dictionary<string, IStation> DoFilter(StationDictionary stations, IGroupFilter filter) =>
stations
.Where(station => filter.DoFilter(station.Group).Count() > 0)
.Select(station => new Station(
station.Id,
station.Group,
station.Position,
station.StationName,
station.OperatorData,
new BikeGroupCol(station.BikeGroups
.Where(group => filter.DoFilter(new List<string> { group.Group }).Count() > 0))) as IStation)
.ToDictionary(x => x.Id);
}
}
}

View file

@ -86,8 +86,11 @@ namespace TINK.Model.Connector
return new Result<StationsAndBikesContainer>(
result.Source,
new StationsAndBikesContainer(
new StationDictionary(result.Response.StationsAll.CopriVersion, result.Response.StationsAll.ToDictionary(x => x.Id)),
new BikeCollection(result.Response.Bikes.ToDictionary(x => x.Id))),
new StationDictionary(
result.Response.StationsAll.CopriVersion,
result.Response.StationsAll.ToDictionary(x => x.Id)),
new BikeCollection(
result.Response.BikesOccupied?.ToDictionary(x => x.Id) ?? new Dictionary<string, BikeInfo>())),
result.GeneralData,
result.Exception);
}

View file

@ -41,31 +41,19 @@ namespace TINK.Model.Connector
resultStations.Source,
new StationsAndBikesContainer(
resultStations.Response.GetStationsAllMutable(),
(await server.GetBikesAvailable(true)).Response.GetBikesAvailable(Bikes.BikeInfoNS.BC.DataSource.Cache)),
new BikeCollection() /* There are no bikes occupied because user is not logged in. */),
resultStations.GeneralData,
resultStations.Exception);
}
var resultBikes = await server.GetBikesAvailable();
if (resultBikes.Source == typeof(CopriCallsMonkeyStore))
{
// Communication with copri in order to get bikes failed.
return new Result<StationsAndBikesContainer>(
resultBikes.Source,
new StationsAndBikesContainer(
(await server.GetStations(true)).Response.GetStationsAllMutable(),
resultBikes.Response.GetBikesAvailable(Bikes.BikeInfoNS.BC.DataSource.Cache)),
resultBikes.GeneralData,
resultBikes.Exception);
}
// Communicatin with copri succeeded.
// Communication with copri succeeded.
server.AddToCache(resultStations);
server.AddToCache(resultBikes);
return new Result<StationsAndBikesContainer>(
resultStations.Source,
new StationsAndBikesContainer(resultStations.Response.GetStationsAllMutable(), resultBikes.Response.GetBikesAvailable(Bikes.BikeInfoNS.BC.DataSource.Copri)),
new StationsAndBikesContainer(
resultStations.Response.GetStationsAllMutable(),
new BikeCollection()),
resultStations.GeneralData);
}

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Serilog;
@ -6,6 +7,7 @@ using TINK.Model.Bikes;
using TINK.Model.Connector.Updater;
using TINK.Model.Services.CopriApi;
using TINK.Repository;
using TINK.Repository.Response;
namespace TINK.Model.Connector
{
@ -25,90 +27,47 @@ namespace TINK.Model.Connector
Server = copriServer as ICachedCopriServer;
if (Server == null)
{
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {copriServer.GetType()}.");
throw new ArgumentException($"Copri server is not of expected type. Type detected is {copriServer.GetType()}.");
}
}
/// <summary> Gets all stations including positions.</summary>
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
{
BikeCollection GetBikeCollection(IEnumerable<BikeInfoReservedOrBooked> bikeInfoEnumerable, Bikes.BikeInfoNS.BC.DataSource dataSource) =>
BikeCollectionFactory.GetBikesAll(
null, // Bikes available are no more of interest because count of available bikes at each given station is was added to station object.
bikeInfoEnumerable ?? new Dictionary<string, BikeInfoReservedOrBooked>().Values,
Mail,
DateTimeProvider,
dataSource);
var stationsResponse = await Server.GetStations();
if (stationsResponse.Source == typeof(CopriCallsMonkeyStore)
|| stationsResponse.Exception != null)
{
// Stations were read from cache ==> get bikes availbalbe and occupied from cache as well to avoid inconsistencies
// Stations were read from cache ==> get bikes available and occupied from cache as well to avoid inconsistencies
return new Result<StationsAndBikesContainer>(
stationsResponse.Source,
new StationsAndBikesContainer(
stationsResponse.Response.GetStationsAllMutable(),
BikeCollectionFactory.GetBikesAll(
(await Server.GetBikesAvailable(true)).Response?.bikes?.Values,
(await Server.GetBikesOccupied(true)).Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache)),
GetBikeCollection(stationsResponse.Response.bikes_occupied?.Values, Bikes.BikeInfoNS.BC.DataSource.Cache)),
stationsResponse.GeneralData,
stationsResponse.Exception);
}
var bikesAvailableResponse = await Server.GetBikesAvailable();
if (bikesAvailableResponse.Source == typeof(CopriCallsMonkeyStore)
|| bikesAvailableResponse.Exception != null)
{
// Bikes avilable were read from cache ==> get bikes occupied from cache as well to avoid inconsistencies
return new Result<StationsAndBikesContainer>(
bikesAvailableResponse.Source,
new StationsAndBikesContainer(
(await Server.GetStations(true)).Response.GetStationsAllMutable(),
BikeCollectionFactory.GetBikesAll(bikesAvailableResponse.Response?.bikes?.Values,
(await Server.GetBikesOccupied(true)).Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache)),
bikesAvailableResponse.GeneralData,
bikesAvailableResponse.Exception);
}
var bikesOccupiedResponse = await Server.GetBikesOccupied();
if (bikesOccupiedResponse.Source == typeof(CopriCallsMonkeyStore)
|| bikesOccupiedResponse.Exception != null)
{
// Bikes occupied were read from cache ==> get bikes available from cache as well to avoid inconsistencies
return new Result<StationsAndBikesContainer>(
bikesOccupiedResponse.Source,
new StationsAndBikesContainer(
(await Server.GetStations(true)).Response.GetStationsAllMutable(),
BikeCollectionFactory.GetBikesAll(
(await Server.GetBikesAvailable(true)).Response?.bikes?.Values,
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache)),
bikesOccupiedResponse.GeneralData,
bikesOccupiedResponse.Exception);
}
// Both types bikes could read from copri => update cache
Server.AddToCache(stationsResponse);
Server.AddToCache(bikesAvailableResponse);
Server.AddToCache(bikesOccupiedResponse);
var exceptions = new[] { stationsResponse?.Exception, bikesAvailableResponse?.Exception, bikesOccupiedResponse?.Exception }.Where(x => x != null).ToArray();
var stationsMutable = stationsResponse.Response.GetStationsAllMutable();
var bikesMutable = BikeCollectionFactory.GetBikesAll(
bikesAvailableResponse.Response?.bikes?.Values,
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Copri);
return new Result<StationsAndBikesContainer>(
stationsResponse.Source,
new StationsAndBikesContainer(stationsMutable, bikesMutable),
new StationsAndBikesContainer(
stationsResponse.Response.GetStationsAllMutable(),
GetBikeCollection(stationsResponse.Response.bikes_occupied?.Values, Bikes.BikeInfoNS.BC.DataSource.Copri)),
stationsResponse.GeneralData,
exceptions.Length > 0 ? new AggregateException(exceptions) : null);
stationsResponse?.Exception);
}
/// <summary> Gets bikes occupied. </summary>

View file

@ -32,11 +32,12 @@ namespace TINK.Model.Connector
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
{
var stationsAllResponse = await server.GetStationsAsync();
var bikesAvailableResponse = await server.GetBikesAvailableAsync();
return new Result<StationsAndBikesContainer>(
typeof(CopriCallsMonkeyStore),
new StationsAndBikesContainer(stationsAllResponse.GetStationsAllMutable(), bikesAvailableResponse.GetBikesAvailable(Bikes.BikeInfoNS.BC.DataSource.Cache)),
new StationsAndBikesContainer(
stationsAllResponse.GetStationsAllMutable(),
new BikeCollection() /* There are no bikes occupied because user is not logged in. */),
stationsAllResponse.GetGeneralData());
}

View file

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TINK.Model.Bikes;
using TINK.Model.Connector.Updater;
using TINK.Model.Services.CopriApi;
using TINK.Repository;
using TINK.Repository.Response;
namespace TINK.Model.Connector
{
@ -34,16 +36,14 @@ namespace TINK.Model.Connector
public async Task<Result<StationsAndBikesContainer>> GetBikesAndStationsAsync()
{
var stationResponse = await server.GetStationsAsync();
var bikesAvailableResponse = await server.GetBikesAvailableAsync();
var bikesOccupiedResponse = await server.GetBikesOccupiedAsync();
return new Result<StationsAndBikesContainer>(
typeof(CopriCallsMonkeyStore),
new StationsAndBikesContainer(
stationResponse.GetStationsAllMutable(),
BikeCollectionFactory.GetBikesAll(
bikesAvailableResponse?.bikes?.Values,
bikesOccupiedResponse?.bikes_occupied?.Values,
null, // Bikes available are no more of interest because count of available bikes at each given station is was added to station object.
stationResponse.bikes_occupied?.Values ?? new Dictionary<string, BikeInfoReservedOrBooked>().Values,
Mail,
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache)),

View file

@ -7,9 +7,12 @@ using Serilog;
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
using TINK.Model.Services.CopriApi.ServerUris;
using TINK.Model.State;
using TINK.Model.Stations.StationNS;
using TINK.Model.Stations.StationNS.Operator;
using TINK.Repository.Exception;
using TINK.Repository.Response;
using TINK.Repository.Response.Stations.Station;
using Xamarin.Forms;
namespace TINK.Model.Connector
{
@ -388,5 +391,52 @@ namespace TINK.Model.Connector
/// <param name="bike">Bike get to state from.</param>
public static bool GetIsFeedbackPending(this BikeInfoAvailable bike)
=> bike.co2saving != null;
/// <summary>
/// Gets the count of bikes available at station.
/// </summary>
/// <param name="stationInfo">Object to get information from.</param>
/// <returns>Count of bikes available or null if information is unknown.</returns>
public static int? GetBikesAvailableCount(this StationInfo stationInfo)
=> !string.IsNullOrWhiteSpace(stationInfo?.bike_count)
&& int.TryParse(stationInfo?.bike_count, out int bikeCount)
? bikeCount
: (int?)null;
/// <summary>
/// Gets station object from response object.
/// </summary>
/// <param name="station">Response object to get station object from.</param>
/// <returns>Station object.</returns>
public static Station GetStation(this StationInfo station) =>
new Station(
station.station,
station.GetGroup(),
station.GetPosition(),
station.description,
new Data(station.operator_data?.operator_name,
station.operator_data?.operator_phone,
station.operator_data?.operator_hours,
station.operator_data?.operator_email,
!string.IsNullOrEmpty(station.operator_data?.operator_color)
? Color.FromHex(station.operator_data?.operator_color)
: (Color?)null),
new BikeGroupCol(station.station_type?.Select(x => new BikeGroupCol.Entry(
x.Key,
int.TryParse(x.Value.bike_count, out int count) ? count : 0,
x.Value.bike_group)) ?? new List<BikeGroupCol.Entry>()
));
/// <summary>
/// Gets the bike group object from response object.
/// </summary>
/// <param name="bikeGroup">Response object to get station from.</param>
/// <param name="name">Name of the bike group.</param>
/// <returns></returns>
public static BikeGroupCol.Entry GetBikeGroup(this BikeGroup bikeGroup, string name) =>
new BikeGroupCol.Entry(
name,
int.TryParse(bikeGroup?.bike_count ?? "0", out var countCity) ? countCity : 0,
bikeGroup?.bike_group ?? string.Empty);
}
}

View file

@ -30,7 +30,7 @@ namespace TINK.Model.Connector.Updater
/// <summary>
/// Gets all station for station provider and add them into station list.
/// </summary>
/// <param name="p_oStationList">List of stations to update.</param>
/// <param name="stationsAllResponse">List of stations to update.</param>
public static StationDictionary GetStationsAllMutable(this StationsAvailableResponse stationsAllResponse)
{
// Get stations from Copri/ file/ memory, ....
@ -43,7 +43,7 @@ namespace TINK.Model.Connector.Updater
Version.TryParse(stationsAllResponse.copri_version, out Version copriVersion);
var stations = new StationDictionary(p_oVersion: copriVersion);
var stations = new StationDictionary(version: copriVersion);
foreach (var station in stationsAllResponse.stations)
{
@ -54,18 +54,7 @@ namespace TINK.Model.Connector.Updater
string.Format("Station id {0} is not unique.", station.Value.station), stationsAllResponse);
}
stations.Add(new Stations.StationNS.Station(
station.Value.station,
station.Value.GetGroup(),
station.Value.GetPosition(),
station.Value.description,
new Data(station.Value.operator_data?.operator_name,
station.Value.operator_data?.operator_phone,
station.Value.operator_data?.operator_hours,
station.Value.operator_data?.operator_email,
!string.IsNullOrEmpty(station.Value.operator_data?.operator_color)
? Color.FromHex(station.Value.operator_data?.operator_color)
: (Color?)null)));
stations.Add(station.Value.GetStation());
}
return stations;