From 7776839c820134abb8c02e22b2b87c7622339682 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Fri, 12 Aug 2016 06:41:43 +0000 Subject: [PATCH] cmd/syncthing, gui: Improve completion calculation (fixes #3492) GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3493 --- cmd/syncthing/gui.go | 11 ++-- cmd/syncthing/mocked_model_test.go | 4 +- cmd/syncthing/summaryservice.go | 8 ++- .../syncthing/core/syncthingController.js | 64 ++++++------------- lib/model/model.go | 21 ++++-- 5 files changed, 49 insertions(+), 59 deletions(-) diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index 9324819f8..b807eff12 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -68,7 +68,7 @@ type apiService struct { type modelIntf interface { GlobalDirectoryTree(folder, prefix string, levels int, dirsonly bool) map[string]interface{} - Completion(device protocol.DeviceID, folder string) float64 + Completion(device protocol.DeviceID, folder string) model.FolderCompletion Override(folder string) NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfoTruncated, []db.FileInfoTruncated, []db.FileInfoTruncated, int) NeedSize(folder string) (nfiles int, bytes int64) @@ -583,8 +583,11 @@ func (s *apiService) getDBCompletion(w http.ResponseWriter, r *http.Request) { return } - sendJSON(w, map[string]float64{ - "completion": s.model.Completion(device, folder), + comp := s.model.Completion(device, folder) + sendJSON(w, map[string]interface{}{ + "completion": comp.CompletionPct, + "needBytes": comp.NeedBytes, + "globalBytes": comp.GlobalBytes, }) } @@ -1142,7 +1145,7 @@ func (s *apiService) getPeerCompletion(w http.ResponseWriter, r *http.Request) { for _, device := range folder.DeviceIDs() { deviceStr := device.String() if s.model.ConnectedTo(device) { - tot[deviceStr] += s.model.Completion(device, folder.ID) + tot[deviceStr] += s.model.Completion(device, folder.ID).CompletionPct } else { tot[deviceStr] = 0 } diff --git a/cmd/syncthing/mocked_model_test.go b/cmd/syncthing/mocked_model_test.go index 15d134768..72ac6f83c 100644 --- a/cmd/syncthing/mocked_model_test.go +++ b/cmd/syncthing/mocked_model_test.go @@ -21,8 +21,8 @@ func (m *mockedModel) GlobalDirectoryTree(folder, prefix string, levels int, dir return nil } -func (m *mockedModel) Completion(device protocol.DeviceID, folder string) float64 { - return 0 +func (m *mockedModel) Completion(device protocol.DeviceID, folder string) model.FolderCompletion { + return model.FolderCompletion{} } func (m *mockedModel) Override(folder string) {} diff --git a/cmd/syncthing/summaryservice.go b/cmd/syncthing/summaryservice.go index 3382a45fc..c4c498cbc 100644 --- a/cmd/syncthing/summaryservice.go +++ b/cmd/syncthing/summaryservice.go @@ -207,9 +207,11 @@ func (c *folderSummaryService) sendSummary(folder string) { // remote device. comp := c.model.Completion(devCfg.DeviceID, folder) events.Default.Log(events.FolderCompletion, map[string]interface{}{ - "folder": folder, - "device": devCfg.DeviceID.String(), - "completion": comp, + "folder": folder, + "device": devCfg.DeviceID.String(), + "completion": comp.CompletionPct, + "needBytes": comp.NeedBytes, + "globalBytes": comp.GlobalBytes, }) } } diff --git a/gui/default/syncthing/core/syncthingController.js b/gui/default/syncthing/core/syncthingController.js index 26900c61d..2ac223204 100755 --- a/gui/default/syncthing/core/syncthingController.js +++ b/gui/default/syncthing/core/syncthingController.js @@ -309,28 +309,8 @@ angular.module('syncthing.core') if (!$scope.completion[data.device]) { $scope.completion[data.device] = {}; } - $scope.completion[data.device][data.folder] = data.completion; - - var tot = 0, - cnt = 0, - isComplete = true; - for (var cmp in $scope.completion[data.device]) { - if (cmp === "_total") { - continue; - } - tot += $scope.completion[data.device][cmp] * $scope.model[cmp].globalBytes; - cnt += $scope.model[cmp].globalBytes; - if ($scope.completion[data.device][cmp] != 100) { - isComplete = false; - } - } - //To be sure that we won't get any rounding errors resulting in non-100% status when it should be - if (isComplete) { - $scope.completion[data.device]._total = 100; - } - else { - $scope.completion[data.device]._total = tot / cnt; - } + $scope.completion[data.device][data.folder] = data; + recalcCompletion(data.device); }); $scope.$on(Events.FOLDER_ERRORS, function (event, arg) { @@ -458,6 +438,20 @@ angular.module('syncthing.core') } } + function recalcCompletion(device) { + var total = 0, needed = 0; + for (var folder in $scope.completion[device]) { + if (folder === "_total") { + continue; + } + total += $scope.completion[device][folder].globalBytes; + needed += $scope.completion[device][folder].needBytes; + } + $scope.completion[device]._total = 100 * (1 - needed / total); + + console.log("recalcCompletion", device, $scope.completion[device]); + } + function refreshCompletion(device, folder) { if (device === $scope.myID) { return; @@ -467,30 +461,8 @@ angular.module('syncthing.core') if (!$scope.completion[device]) { $scope.completion[device] = {}; } - $scope.completion[device][folder] = data.completion; - - var tot = 0, - cnt = 0, - isComplete = true; - for (var cmp in $scope.completion[device]) { - if (cmp === "_total") { - continue; - } - tot += $scope.completion[device][cmp] * $scope.model[cmp].globalBytes; - cnt += $scope.model[cmp].globalBytes; - if ($scope.completion[device][cmp] != 100) { - isComplete = false; - } - } - //To be sure that we won't get any rounding errors resulting in non-100% status when it should be - if (isComplete) { - $scope.completion[device]._total = 100; - } - else { - $scope.completion[device]._total = tot / cnt; - } - - console.log("refreshCompletion", device, folder, $scope.completion[device]); + $scope.completion[device][folder] = data; + recalcCompletion(device); }).error($scope.emitHTTPError); } diff --git a/lib/model/model.go b/lib/model/model.go index fbaf18a8f..103128c7e 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -459,19 +459,28 @@ func (m *Model) FolderStatistics() map[string]stats.FolderStatistics { return res } +type FolderCompletion struct { + CompletionPct float64 + NeedBytes int64 + GlobalBytes int64 +} + // Completion returns the completion status, in percent, for the given device // and folder. -func (m *Model) Completion(device protocol.DeviceID, folder string) float64 { +func (m *Model) Completion(device protocol.DeviceID, folder string) FolderCompletion { m.fmut.RLock() rf, ok := m.folderFiles[folder] m.fmut.RUnlock() if !ok { - return 0 // Folder doesn't exist, so we hardly have any of it + return FolderCompletion{} // Folder doesn't exist, so we hardly have any of it } _, _, tot := rf.GlobalSize() if tot == 0 { - return 100 // Folder is empty, so we have all of it + // Folder is empty, so we have all of it + return FolderCompletion{ + CompletionPct: 100, + } } m.pmut.RLock() @@ -498,7 +507,11 @@ func (m *Model) Completion(device protocol.DeviceID, folder string) float64 { completionPct := 100 * (1 - needRatio) l.Debugf("%v Completion(%s, %q): %f (%d / %d = %f)", m, device, folder, completionPct, need, tot, needRatio) - return completionPct + return FolderCompletion{ + CompletionPct: completionPct, + NeedBytes: need, + GlobalBytes: tot, + } } func sizeOfFile(f db.FileIntf) (files, deleted int, bytes int64) {