Skip to content

Commit

Permalink
handle nullable for activity to be more swift friendly and start loca…
Browse files Browse the repository at this point in the history
…tion/country for #78
  • Loading branch information
roznet committed Feb 21, 2021
1 parent 3833dc9 commit e2edeb5
Show file tree
Hide file tree
Showing 15 changed files with 136 additions and 47 deletions.
18 changes: 17 additions & 1 deletion ConnectStats.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@
4248ED9D1DCFFD2400254216 /* GCFieldsForCategory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4212088F1C6E860700E0470D /* GCFieldsForCategory.m */; };
424B7F2A16CB049A000B38AD /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 424B7F2916CB049A000B38AD /* Security.framework */; };
424B7F4816CFBE30000B38AD /* GCSettingsHelpViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 424B7F4716CFBE2F000B38AD /* GCSettingsHelpViewController.m */; };
424BDAB925E2DA7C00EC7DBE /* GCActivity+Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 424BDAB825E2DA7C00EC7DBE /* GCActivity+Location.swift */; };
424BDABA25E2DA7C00EC7DBE /* GCActivity+Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 424BDAB825E2DA7C00EC7DBE /* GCActivity+Location.swift */; };
424BDABB25E2DA7C00EC7DBE /* GCActivity+Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 424BDAB825E2DA7C00EC7DBE /* GCActivity+Location.swift */; };
424BDAD425E2E25400EC7DBE /* RZExternalUniversal in Frameworks */ = {isa = PBXBuildFile; productRef = 424BDAD325E2E25400EC7DBE /* RZExternalUniversal */; };
424D08C01DC5DA4E000B4620 /* GCAppPasswordManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 424D08BF1DC5DA4E000B4620 /* GCAppPasswordManager.swift */; };
424F1A7B21E20477009DD368 /* FITAppGlobal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 424F1A7A21E20477009DD368 /* FITAppGlobal.swift */; };
424F92D61B1072FD0075C5F1 /* GCDebugActionsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 422637A41AFF6C5F005461E3 /* GCDebugActionsTableViewController.m */; };
Expand Down Expand Up @@ -1478,6 +1482,7 @@
424B7F2916CB049A000B38AD /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
424B7F4616CFBE2F000B38AD /* GCSettingsHelpViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GCSettingsHelpViewController.h; path = src/GCSettingsHelpViewController.h; sourceTree = "<group>"; };
424B7F4716CFBE2F000B38AD /* GCSettingsHelpViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GCSettingsHelpViewController.m; path = src/GCSettingsHelpViewController.m; sourceTree = "<group>"; };
424BDAB825E2DA7C00EC7DBE /* GCActivity+Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "GCActivity+Location.swift"; path = "src/GCActivity+Location.swift"; sourceTree = "<group>"; };
424D08BF1DC5DA4E000B4620 /* GCAppPasswordManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GCAppPasswordManager.swift; path = src/GCAppPasswordManager.swift; sourceTree = "<group>"; };
424F1A7A21E20477009DD368 /* FITAppGlobal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FITAppGlobal.swift; path = src/FITAppGlobal.swift; sourceTree = "<group>"; };
424F9DB817C35BDD001C73B1 /* GCHealthZoneCalculator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GCHealthZoneCalculator.h; path = src/GCHealthZoneCalculator.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2224,6 +2229,7 @@
42200E9D17EEF0EC0078B45A /* StoreKit.framework in Frameworks */,
42DD59E117E60B6E00227A36 /* libc++.dylib in Frameworks */,
422DCB6F258A2779000DEE4B /* RZUtilsSwift in Frameworks */,
424BDAD425E2E25400EC7DBE /* RZExternalUniversal in Frameworks */,
42AB66D316DACF8A00C633BD /* CoreText.framework in Frameworks */,
4211D576259A0BDA00967874 /* RZExternal in Frameworks */,
42A66BDB160D0A0800DA29A5 /* QuartzCore.framework in Frameworks */,
Expand Down Expand Up @@ -3032,6 +3038,7 @@
42EB51A718AC33DB00B85FAB /* GCActivity+Import.m */,
421A514A1C82FA6C00671587 /* GCActivity+Fields.h */,
421A514B1C82FA6C00671587 /* GCActivity+Fields.m */,
424BDAB825E2DA7C00EC7DBE /* GCActivity+Location.swift */,
42959CD618D5F5F100C0B841 /* GCActivity+Database.h */,
42959CD718D5F5F100C0B841 /* GCActivity+Database.m */,
427208611DE7A35300924C90 /* GCActivity+Import.swift */,
Expand Down Expand Up @@ -4409,6 +4416,7 @@
4211D4702597832D00967874 /* OAuthSwift */,
4211D575259A0BDA00967874 /* RZExternal */,
42DB6CB625D56107003AC195 /* ZIPFoundation */,
424BDAD325E2E25400EC7DBE /* RZExternalUniversal */,
);
productName = GarminConnect;
productReference = 428FF70915F36888007A88FA /* ConnectStats.app */;
Expand Down Expand Up @@ -5316,6 +5324,7 @@
422FAEF519A1DD6000D09773 /* GCDerivedDataSerie.m in Sources */,
423F9FCA19AD2ACA0057DB5A /* GCStatsHistGraphConfig.m in Sources */,
42F0A4F41B60620200901CFF /* GCDerivedRequest.m in Sources */,
424BDABB25E2DA7C00EC7DBE /* GCActivity+Location.swift in Sources */,
42E04E5A25398099005FFD5E /* GCCellRoundedPatternView.swift in Sources */,
422FAEF819A1DD6000D09773 /* GCHistoryFieldSummaryStats.m in Sources */,
4239E4CC19ABDA65000E67A2 /* GCStatsOneFieldConfig.m in Sources */,
Expand Down Expand Up @@ -5649,6 +5658,7 @@
42AA288F1637F6AB0054F84A /* GCActivitySearchElement.m in Sources */,
422549571EE7361F004F4AE5 /* FITFitStatisticsWeight.swift in Sources */,
422C70AE1642B677003779E0 /* GCHistoryAggregatedActivityStats.m in Sources */,
424BDAB925E2DA7C00EC7DBE /* GCActivity+Location.swift in Sources */,
421A514F1C82FA9400671587 /* GCCalendarDataDateMarkers.m in Sources */,
42453BAA1DD8B5940073ACCA /* GCMapGradientPathOverlay.m in Sources */,
42684EF41B2DB2A6001A90D2 /* GCSettingsSourceTableViewController.m in Sources */,
Expand Down Expand Up @@ -5974,6 +5984,7 @@
42B5E2C01BA5B216000FAE4E /* GCActivityTypes.m in Sources */,
42B5E2C11BA5B216000FAE4E /* GCActivitiesOrganizer.m in Sources */,
4209E19B1DA8E15700B80735 /* GCGarminRequestModernSearch.m in Sources */,
424BDABA25E2DA7C00EC7DBE /* GCActivity+Location.swift in Sources */,
422CC8761E6ACA1700195414 /* GCUnit+FIT.m in Sources */,
421A51511C82FB1000671587 /* GCActivity+Fields.m in Sources */,
42B5E2C21BA5B216000FAE4E /* GCActivitiesCacheManagement.m in Sources */,
Expand Down Expand Up @@ -6974,7 +6985,7 @@
repositoryURL = "https://github.com/roznet/rzexternal";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.0.0;
minimumVersion = 1.0.1;
};
};
422DCB6D258A2779000DEE4B /* XCRemoteSwiftPackageReference "rzutils" */ = {
Expand Down Expand Up @@ -7185,6 +7196,11 @@
package = 422DCD72258A74DF000DEE4B /* XCRemoteSwiftPackageReference "rzutils-macos" */;
productName = RZUtilsMacOS;
};
424BDAD325E2E25400EC7DBE /* RZExternalUniversal */ = {
isa = XCSwiftPackageProductDependency;
package = 4211D572259A0BDA00967874 /* XCRemoteSwiftPackageReference "rzexternal" */;
productName = RZExternalUniversal;
};
425D249C259E08DC0003574F /* KeychainSwift */ = {
isa = XCSwiftPackageProductDependency;
package = 425D249B259E08DC0003574F /* XCRemoteSwiftPackageReference "keychain-swift" */;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@
"repositoryURL": "https://github.com/roznet/rzexternal",
"state": {
"branch": null,
"revision": "5739045c1f6ebbbfec939bc64d233357c87273e3",
"version": "1.0.0"
"revision": "0508fcb2d0e16326f149e263ef4d573b32ace205",
"version": "1.0.1"
}
},
{
Expand Down
Binary file added ConnectStats/shapefiles/TM_WORLD_BORDERS-0.2.dbf
Binary file not shown.
1 change: 1 addition & 0 deletions ConnectStats/shapefiles/TM_WORLD_BORDERS-0.2.prj
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
Binary file added ConnectStats/shapefiles/TM_WORLD_BORDERS-0.2.shp
Binary file not shown.
Binary file added ConnectStats/shapefiles/TM_WORLD_BORDERS-0.2.shx
Binary file not shown.
18 changes: 10 additions & 8 deletions ConnectStats/src/GCActivitiesOrganizer.m
Original file line number Diff line number Diff line change
Expand Up @@ -1407,15 +1407,17 @@ -(NSDictionary*)fieldsSeries:(NSArray*)fields
}else if ([one isKindOfClass:[NSNumber class]]){
field = [GCField fieldForFlag:(gcFieldFlag)[one integerValue] andActivityType:act.activityType];
}
nu = [act numberWithUnitForField:field];
if (nu) {
nu = [nu convertToGlobalSystem];
GCStatsDataSerieWithUnit * serie = rv[one];
if (serie == nil) {
serie = [GCStatsDataSerieWithUnit dataSerieWithUnit:nu.unit];
rv[one] = serie;
if( field ){
nu = [act numberWithUnitForField:field];
if (nu) {
nu = [nu convertToGlobalSystem];
GCStatsDataSerieWithUnit * serie = rv[one];
if (serie == nil) {
serie = [GCStatsDataSerieWithUnit dataSerieWithUnit:nu.unit];
rv[one] = serie;
}
[serie addNumberWithUnit:nu forDate:act.date];
}
[serie addNumberWithUnit:nu forDate:act.date];
}
}
}
Expand Down
48 changes: 48 additions & 0 deletions ConnectStats/src/GCActivity+Location.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// MIT License
//
// Created on 21/02/2021 for ConnectStats
//
// Copyright (c) 2021 Brice Rosenzweig
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//



import Foundation

extension GCActivity {


var country : String {
if let locationCountry = self.location.components(separatedBy: ", ").last {
return locationCountry
}else{
let gps = self.beginCoordinate

if( CLLocationCoordinate2DIsValid(gps) ){

}


}
return "Unknown"
}

}
39 changes: 19 additions & 20 deletions ConnectStats/src/GCActivity.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
@class GCCalculatedCachedTrackInfo;
@class GCCalculatedCachedTrackKey;

NS_ASSUME_NONNULL_BEGIN

typedef BOOL (^GCActivityMatchBlock)(GCActivity*act);

#define GC_ALL_LAPS -1
Expand Down Expand Up @@ -95,7 +97,7 @@ typedef NS_ENUM(NSUInteger, gcIgnoreMode) {
/**
All implementation detail should be hidden in GCActivity+CachedTracked.h
*/
@property (nonatomic,retain) NSDictionary<GCField*,GCCalculatedCachedTrackInfo*> * cachedCalculatedTracks;
@property (nullable,nonatomic,retain) NSDictionary<GCField*,GCCalculatedCachedTrackInfo*> * cachedCalculatedTracks;


@property (nonatomic,readonly) NSString * activityType;// DEPRECATED_MSG_ATTRIBUTE("use GCActivityType.");
Expand All @@ -117,7 +119,7 @@ typedef NS_ENUM(NSUInteger, gcIgnoreMode) {
@property (nonatomic,assign) gcDownloadMethod downloadMethod;

@property (nonatomic,retain) NSString * calculatedLapName;
@property (nonatomic,retain) GCWeather * weather;
@property (nullable,nonatomic,retain) GCWeather * weather;

@property (nonatomic,retain) FMDatabase * db;
@property (nonatomic,retain) FMDatabase * trackdb;
Expand All @@ -143,26 +145,26 @@ typedef NS_ENUM(NSUInteger, gcIgnoreMode) {
can return nil, but asking for this will trigger attempt to load from db
or download from web if applicable
*/
@property (nonatomic,retain) NSArray<GCTrackPoint*> * trackpoints;
@property (nullable,nonatomic,retain) NSArray<GCTrackPoint*> * trackpoints;

/**
@brief For multi-sport activities, the parent activityId
*/
@property (nonatomic,retain) NSString * parentId;
@property (nullable,nonatomic,retain) NSString * parentId;

/**
@brief For multi-sport activities, Arrays of child activityIds
*/
@property (nonatomic,retain) NSArray<NSString*> * childIds;
@property (nullable,nonatomic,retain) NSArray<NSString*> * childIds;
/**
@brief To maintain an activityId in an external system. For example an activity from ConnectStats Service should have a activityID on the garmin service
*/
@property (nonatomic,retain) NSString*externalServiceActivityId;
@property (nullable,nonatomic,retain) NSString*externalServiceActivityId;

/**
Avoid to use directly, should be used via GCActivity+Assets functions
*/
@property (nonatomic,retain) NSDictionary * assetsInfo;
@property (nullable,nonatomic,retain) NSDictionary * assetsInfo;

/**
@brief Display name, if none, will use location.
Expand All @@ -181,8 +183,8 @@ typedef NS_ENUM(NSUInteger, gcIgnoreMode) {
-(GCActivity*)initWithId:(NSString*)aId NS_DESIGNATED_INITIALIZER;
-(GCActivity*)initWithResultSet:(FMResultSet*)res NS_DESIGNATED_INITIALIZER;

-(BOOL)updateWithTrackpoints:(NSArray<GCTrackPoint*>*)trackpoints andLaps:(NSArray<GCLap*>*)laps;
-(BOOL)saveTrackpoints:(NSArray*)aTrack andLaps:(NSArray*)laps;
-(BOOL)updateWithTrackpoints:(NSArray<GCTrackPoint*>*)trackpoints andLaps:(nullable NSArray<GCLap*>*)laps;
-(BOOL)saveTrackpoints:(NSArray<GCTrackPoint*>*)aTrack andLaps:(nullable NSArray<GCLap*>*)laps;

/// Logic to change activity type
/// @param newActivityType if nil will do nothing
Expand Down Expand Up @@ -234,7 +236,7 @@ typedef NS_ENUM(NSUInteger, gcIgnoreMode) {
Return the next Field which has a track serie.
If one is nil or not a valid field, the first available field will be returned
*/
-(GCField*)nextAvailableTrackField:(GCField*)one;
-(nullable GCField*)nextAvailableTrackField:(nullable GCField*)one;

/**
Return list of available fields with Track Points. Will include calculated tracks
Expand Down Expand Up @@ -267,7 +269,7 @@ typedef NS_ENUM(NSUInteger, gcIgnoreMode) {
/**
return numberWithUnit for field. This is the main access function for values
*/
-(GCNumberWithUnit*)numberWithUnitForField:(GCField*)field;
-(nullable GCNumberWithUnit*)numberWithUnitForField:(GCField*)field;
/**
@brief Set number for field
@return true if changed or new, false if unchanged
Expand All @@ -282,12 +284,7 @@ typedef NS_ENUM(NSUInteger, gcIgnoreMode) {

-(double)summaryFieldValueInStoreUnit:(gcFieldFlag)fieldFlag;
-(void)setSummaryField:(gcFieldFlag)fieldFlag inStoreUnitValue:(double)value;
-(GCNumberWithUnit*)numberWithForFieldInStoreUnit:(GCField *)field;

/**
Format a value with unit similar to a given field
*/
-(NSString*)formatValue:(double)aval forField:(GCField*)which DEPRECATED_MSG_ATTRIBUTE("use numberWithUnitForField");
-(nullable GCNumberWithUnit*)numberWithUnitForFieldInStoreUnit:(GCField *)field;

/**
Format a value with unit similar to a given field, unit name not displayed
Expand All @@ -312,7 +309,7 @@ typedef NS_ENUM(NSUInteger, gcIgnoreMode) {
*/
-(NSArray<GCField*>*)allFields;

-(GCActivityMetaValue*)metaValueForField:(NSString*)field;
-(nullable GCActivityMetaValue*)metaValueForField:(NSString*)field;
/**
@brief method to update the dictionary of meta data
*/
Expand All @@ -330,9 +327,9 @@ typedef NS_ENUM(NSUInteger, gcIgnoreMode) {

#pragma mark - Laps

-(NSArray<GCLap*>*)laps;
-(nullable NSArray<GCLap*>*)laps;
-(NSUInteger)lapCount;
-(GCLap*)lapNumber:(NSUInteger)idx;
-(nullable GCLap*)lapNumber:(NSUInteger)idx;
-(void)registerLaps:(NSArray<GCLap*>*)laps forName:(NSString*)name;
-(BOOL)useLaps:(NSString*)name;
-(void)clearCalculatedLaps;
Expand All @@ -355,3 +352,5 @@ typedef NS_ENUM(NSUInteger, gcIgnoreMode) {
-(BOOL)isSkiActivity;

@end

NS_ASSUME_NONNULL_END
2 changes: 1 addition & 1 deletion ConnectStats/src/GCActivity.m
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ -(BOOL)setSummaryField:(gcFieldFlag)which with:(GCNumberWithUnit*)nu{
return rv;
}

-(GCNumberWithUnit*)numberWithForFieldInStoreUnit:(GCField *)field{
-(GCNumberWithUnit*)numberWithUnitForFieldInStoreUnit:(GCField *)field{
switch (field.fieldFlag) {
case gcFieldFlagWeightedMeanSpeed:
return [[self numberWithUnitForField:field] convertToUnit:[GCUnit unitForKey:STOREUNIT_SPEED]];
Expand Down
3 changes: 2 additions & 1 deletion ConnectStats/src/GCActivityOrganizedFields.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ class GCActivityOrganizedFields : NSObject, NSSecureCoding {
self.geometry.timeAlignment = .center
for fields in self.groupedPrimaryFields {
for field in fields {
self.geometry.adjust(for: activity.numberWithUnit(for: field),
guard let nu = activity.numberWithUnit(for: field) else { continue }
self.geometry.adjust(for: nu,
numberAttribute: GCViewConfig.attribute(rzAttribute.value),
unitAttribute: GCViewConfig.attribute(rzAttribute.unit))
}
Expand Down
27 changes: 13 additions & 14 deletions ConnectStats/src/GCHistoryStatistics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -250,20 +250,19 @@ class IndexData {
}

func update(activity : GCActivity){
if let fields = self.fields ?? activity.allFields() {

let distance = activity.numberWithForField(inStoreUnit: GCField(for: .sumDistance, andActivityType: activity.activityType))?.value ?? 1.0
let duration = activity.numberWithForField(inStoreUnit: GCField(for: .sumDuration, andActivityType: activity.activityType))?.value ?? 1.0

for field in fields {
if let nu = activity.numberWithUnit(for: field) {
guard nu.value != 0.0 || field.isZeroValid else { continue }

if let stats = self.data[field] {
stats.add(numberWithUnit: nu, timeweight: duration, distweight: distance)
}else{
self.data[field] = SummaryStatistics(numberWithUnit: nu, timeweight: duration, distweight: distance)
}
let fields = self.fields ?? activity.allFields()

let distance = activity.numberWithUnitForField(inStoreUnit: GCField(for: .sumDistance, andActivityType: activity.activityType))?.value ?? 1.0
let duration = activity.numberWithUnitForField(inStoreUnit: GCField(for: .sumDuration, andActivityType: activity.activityType))?.value ?? 1.0

for field in fields {
if let nu = activity.numberWithUnit(for: field) {
guard nu.value != 0.0 || field.isZeroValid else { continue }

if let stats = self.data[field] {
stats.add(numberWithUnit: nu, timeweight: duration, distweight: distance)
}else{
self.data[field] = SummaryStatistics(numberWithUnit: nu, timeweight: duration, distweight: distance)
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions ConnectStats/src/GCStatsMultiFieldViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,11 @@ -(UITableViewCell*)tableView:(UITableView *)tableView fieldSummaryCell:(NSIndexP
GCCellGrid * cell = [GCCellGrid cellGrid:tableView];
GCField * field =[self fieldsForSection:indexPath.section][indexPath.row];

if( field == nil ){
[cell setupForRows:1 andCols:1];
[cell labelForRow:0 andCol:0].text = NSLocalizedString(@"Empty",@"fieldSummaryCell");
return cell;
}
static NSDictionary * doGraph = nil;
if (doGraph==nil) {
doGraph = @{@"SumDistance": @1,
Expand Down
2 changes: 2 additions & 0 deletions ConnectStats/src/GCWebReverseGeocode.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,6 @@
-(void)start;
-(void)next;

+(NSString*)countryISOFromCoordinate:(CLLocationCoordinate2D)coord;

@end
Loading

0 comments on commit e2edeb5

Please sign in to comment.