Skip to content

Commit

Permalink
Merge 4a81945 into 92a5d84
Browse files Browse the repository at this point in the history
  • Loading branch information
yl-coder committed Apr 5, 2016
2 parents 92a5d84 + 4a81945 commit 7918c30
Show file tree
Hide file tree
Showing 14 changed files with 325 additions and 22 deletions.
8 changes: 7 additions & 1 deletion src/main/java/backend/Logic.java
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,13 @@ public void removeUnusedModels(Set<String> reposInUse) {
}

public ImmutablePair<Integer, Long> updateRemainingRate(ImmutablePair<Integer, Long> rateLimits) {
uiManager.updateRateLimits(rateLimits);
uiManager.updateRateLimits(rateLimits, true);
return rateLimits;
}

public ImmutablePair<Integer, Long> updateRemainingRateWithoutUpdateRefreshRate(ImmutablePair<Integer,
Long> rateLimits) {
uiManager.updateRateLimits(rateLimits, false);
return rateLimits;
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/backend/RepoIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public CompletableFuture<Model> updateModel(Model model, boolean syncOperation,
}
UI.events.triggerEvent(new UpdateProgressEvent(model.getRepoId()));
return newModel;
}
}
}).exceptionally(withResult(new Model(model.getRepoId())));
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/backend/UIManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public void update(Map<FilterExpression, List<GuiElement>> elementsToShow,
Platform.runLater(() -> ui.triggerEvent(new ModelUpdatedEvent(elementsToShow, users)));
}

public void updateRateLimits(ImmutablePair<Integer, Long> rateLimits) {
ui.triggerEvent(new UpdateRateLimitsEvent(rateLimits.left, rateLimits.right));
public void updateRateLimits(ImmutablePair<Integer, Long> rateLimits, Boolean allowUpdateOfRefreshRate) {
ui.triggerEvent(new UpdateRateLimitsEvent(rateLimits.left, rateLimits.right, allowUpdateOfRefreshRate));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/backend/UpdateController.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void processAndRefresh(List<FilterPanel> filterPanels) {
+ results.stream().filter(result -> result).count()
+ "/" + results.size() + " repos"))
.thenCompose(n -> logic.getRateLimitResetTime())
.thenApply(logic::updateRemainingRate)
.thenApply(logic::updateRemainingRateWithoutUpdateRefreshRate)
.thenRun(() -> logic.updateUI(processFilters(filterExprs))); // Then filter the second time.
});
}
Expand Down
45 changes: 38 additions & 7 deletions src/main/java/ui/GUIController.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,14 @@ public class GUIController {
private final PanelControl panelControl;
private final UI ui;
private final Label apiBox;

private int previousRemainingApiRequests = 0; //To store the previous amount of the available remaining API request.
private int lastNumberOfApiCallsUsed = 0; // To store the previous amount of api calls used.

private String defaultRepoId;

long refreshTimeInMins = 1;

public GUIController(UI ui, PanelControl panelControl, Label apiBox) {
this.ui = ui;
this.panelControl = panelControl;
Expand All @@ -41,14 +47,14 @@ public GUIController(UI ui, PanelControl panelControl, Label apiBox) {

public final void registerEvents() {
UI.events.registerEvent((ModelUpdatedEventHandler) this::modelUpdated);
UI.events.registerEvent((UpdateRateLimitsEventHandler) this::updateAPIBox);
UI.events.registerEvent((UpdateRateLimitsEventHandler) this::updateRateLimitsEvent);
UI.events.registerEvent((ShowErrorDialogEventHandler) this::showErrorDialog);
UI.events.registerEvent((PrimaryRepoChangedEventHandler) this::setDefaultRepo);
}

/**
* The handler method for a ModelUpdatedEvent.
* <p>
* <p/>
* It processes each panel in the current GUI, and checks the ModelUpdatedEvent for issues to be displayed
* that match the current panel's filter expression:
* - If not, the panel does not change its appearance.
Expand All @@ -72,7 +78,7 @@ private void modelUpdated(ModelUpdatedEvent e) {
/**
* Handler method for an applyFilterExpression call from an FilterPanel, which is in turn triggered by
* the user pressing ENTER while the cursor is on the FilterPanel's filterTextField.
* <p>
* <p/>
* Triggers a processAndRefresh call in Logic with only the given panel's filterExpression. Contrast this
* with refreshAllPanels in Logic, triggers processAndRefresh with all FilterExpressions from the GUI.
*
Expand Down Expand Up @@ -102,13 +108,38 @@ public List<FilterPanel> getAllPanels() {
.collect(Collectors.toList());
}

private void updateRateLimitsEvent(UpdateRateLimitsEvent e) {

updateAPIBox(e);

if (e.allowUpdateOfRefreshRate) {
updateSyncRefreshRate(e);
}

}

private void updateSyncRefreshRate(UpdateRateLimitsEvent e) {


int difference = previousRemainingApiRequests - e.remainingRequests;

if (difference >= 0) {
lastNumberOfApiCallsUsed = difference;
}

previousRemainingApiRequests = e.remainingRequests;
refreshTimeInMins = ui.timerManager.computeTickerTimerPeriod(e.remainingRequests,
Utility.minutesFromNow(e.nextRefreshInMillisecs), lastNumberOfApiCallsUsed);
ui.timerManager.changeTickingTimerPeriodInMins((int) refreshTimeInMins);

}

private void updateAPIBox(UpdateRateLimitsEvent e) {
Platform.runLater(() -> apiBox.setText(String.format("%s/%s",
e.remainingRequests,
Utility.minutesFromNow(e.nextRefreshInMillisecs)))
);
Platform.runLater(() -> apiBox.setText(String.format("%s/%s[x%d]", e.remainingRequests,
Utility.minutesFromNow(e.nextRefreshInMillisecs), (int) Math.ceil(refreshTimeInMins))));
}


private void showErrorDialog(ShowErrorDialogEvent e) {
Platform.runLater(() -> DialogMessage.showErrorDialog(e.header, e.message));
}
Expand Down
20 changes: 14 additions & 6 deletions src/main/java/ui/UI.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
import java.util.concurrent.TimeUnit;

import static ui.components.KeyboardShortcuts.SHOW_ISSUE_PICKER;
import static ui.components.KeyboardShortcuts.SHOW_REPO_PICKER;


public class UI extends Application implements EventDispatcher {

Expand All @@ -57,6 +57,7 @@ public class UI extends Application implements EventDispatcher {
public static final int VERSION_PATCH = 0;

private static final Logger logger = LogManager.getLogger(UI.class.getName());
public static final int APIQUOTA_BUFFER = 200;
private static HWND mainWindowHandle;
private final GlobalHotkey globalHotkey = new GlobalHotkey(this);

Expand Down Expand Up @@ -93,10 +94,10 @@ public class UI extends Application implements EventDispatcher {
public static StatusUI status;
public static EventDispatcher events;
public EventBus eventBus;
private TickingTimer refreshTimer;
public GUIController guiController;
private NotificationController notificationController;
public UndoController undoController;
public TickingTimerManager timerManager;


// Main UI elements
Expand Down Expand Up @@ -241,11 +242,17 @@ private void initPreApplicationState() {
private void initApplicationState() {
// In the future, when more arguments are passed to logic,
// we can pass them in the form of an array.

logic = new Logic(uiManager, prefs, Optional.empty(), Optional.empty());

// TODO clear cache if necessary
refreshTimer = new TickingTimer("Refresh Timer", REFRESH_PERIOD,
status::updateTimeToRefresh, logic::refresh, TimeUnit.SECONDS);
refreshTimer.start();

TickingTimer refreshTimer = new TickingTimer("Refresh Timer", REFRESH_PERIOD,
status::updateTimeToRefresh, logic::refresh, TimeUnit.SECONDS);

timerManager = new TickingTimerManager(refreshTimer);
timerManager.startTimer();

undoController = new UndoController(notificationController);
}

Expand All @@ -260,6 +267,7 @@ private void initUI(Stage stage) {
panels = new PanelControl(this, mainStage, prefs);
guiController = new GUIController(this, panels, apiBox);


Scene scene = new Scene(createRootNode());

setupMainStage(scene);
Expand Down Expand Up @@ -351,7 +359,7 @@ private void setupMainStage(Scene scene) {
if (shouldRefresh) {
logger.info("Browser view has changed; refreshing");
logic.refresh();
refreshTimer.restart();
timerManager.restartTimer();
}
}
});
Expand Down
11 changes: 10 additions & 1 deletion src/main/java/util/TickingTimer.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class TickingTimer {
private final String name;

// The period after which the timer times out.
private final int period;
private int period;

// onTick will not pause the timer when run, so it should not be a long-running task.
// Will run before onTimeout.
Expand Down Expand Up @@ -158,4 +158,13 @@ public boolean isStarted() {
return started;
}

/**
* Changes the timer's period and restart the timer based on the new period.
* @param period : the amount of time before triggering the timer.
*/
public synchronized void changePeriodInSecs(int period){
this.period = period;
this.time = period;
}

}
87 changes: 87 additions & 0 deletions src/main/java/util/TickingTimerManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package util;

/**
* This class manages the TickingTimer instance used for refreshing. It computes the refresh rate period and change
* the refresh period.
*/
public class TickingTimerManager {


public static final int BUFFER_TIME = 1; //To allow some additional time until next refresh.
private final TickingTimer timer;
public static final int REFRESH_PERIOD = 60;
//The amount that is set aside for manual refresh or creation of issues, labels and milestones.
public static final int APIQUOTA_BUFFER = 200;

public TickingTimerManager(TickingTimer timer) {
this.timer = timer;
}

public void startTimer() {
timer.start();
}

public void restartTimer() {
timer.restart();
}

/**
* Computes the TickerTimer period that are used for refreshing the issues periodically.
* @param apiQuota : The remaining allowed api request until the next api request allowance renewal.
* @param remainingTime : The remaining time left until the next api request allowance renewal.
* @param lastApiCallsUsed : The amount of api used in the last api pull.
*/
public long computeTickerTimerPeriod(int apiQuota, long remainingTime, int lastApiCallsUsed) {

assert apiQuota >= 0 && remainingTime >= 0 && lastApiCallsUsed >= 0;

long refreshTimeInMins = 1;

if (isDuringAppInitOrRemainingTimeIsZero(apiQuota, remainingTime, lastApiCallsUsed)) {
refreshTimeInMins = Utility.secsToMins(REFRESH_PERIOD);
return refreshTimeInMins;
}

if (isQuotaInsufficient(apiQuota, lastApiCallsUsed)) {
refreshTimeInMins = remainingTime + BUFFER_TIME;
return refreshTimeInMins;
}

if (isQuotaSufficient(apiQuota, lastApiCallsUsed)) {

refreshTimeInMins = (long) Math.ceil(remainingTime / computeNoOfRefreshAllowed(apiQuota, lastApiCallsUsed));

if (computeNoOfRefreshAllowed(apiQuota, lastApiCallsUsed) == 1) {
refreshTimeInMins++;
}
return refreshTimeInMins;
}

return refreshTimeInMins;
}

private double computeNoOfRefreshAllowed(int apiQuota, int lastApiCallsUsed) {
return Math.floor((apiQuota - APIQUOTA_BUFFER) / (double) lastApiCallsUsed);
}

/**
* Changes the timer refresh period.
* @param period
*/
public void changeTickingTimerPeriodInMins(int period) {
timer.changePeriodInSecs((int) Utility.minsToSecs(period));
}

private boolean isQuotaSufficient(int apiQuota, int lastApiCallsUsed) {
return apiQuota - APIQUOTA_BUFFER >= lastApiCallsUsed;
}

private boolean isQuotaInsufficient(int apiQuota, int lastApiCallsUsed) {
return apiQuota == 0 && lastApiCallsUsed == 0 || apiQuota <= APIQUOTA_BUFFER
|| apiQuota - APIQUOTA_BUFFER > 0 && apiQuota - APIQUOTA_BUFFER < lastApiCallsUsed;
}

private boolean isDuringAppInitOrRemainingTimeIsZero(int apiQuota, long remainingTime, int lastApiCallsUsed) {
return apiQuota != 0 && lastApiCallsUsed == 0 || remainingTime == 0;
}
}
10 changes: 10 additions & 0 deletions src/main/java/util/Utility.java
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,20 @@ public static long millisecToMinutes(long millisecDuration) {
return millisecDuration / 1000 / 60;
}

public static long minsToSecs(long minsDuration) {
return minsDuration * 60;
}

public static long secsToMins(long secsDuration) {
return secsDuration / 60;
}


public static long minutesFromNow(long targetTime) {
return millisecToMinutes(targetTime - new Date().getTime());
}


/**
* Parses a version number string in the format V1.2.3.
*
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/util/events/UpdateRateLimitsEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
public class UpdateRateLimitsEvent extends Event {
public final int remainingRequests;
public final long nextRefreshInMillisecs; // Epoch milliseconds
public final Boolean allowUpdateOfRefreshRate;

public UpdateRateLimitsEvent(int remainingRequests, long nextRefreshInMillisecs) {
public UpdateRateLimitsEvent(int remainingRequests, long nextRefreshInMillisecs, Boolean allowUpdateOfRefreshRate) {
this.remainingRequests = remainingRequests;
this.nextRefreshInMillisecs = nextRefreshInMillisecs;
this.allowUpdateOfRefreshRate = allowUpdateOfRefreshRate;
}
}
2 changes: 1 addition & 1 deletion src/test/java/guitests/FilterTextFieldTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void completion_validPrefixes_match() {
type("cou").push(KeyCode.TAB);
push(KeyCode.LEFT);
for (int i = 0; i < 3; i++) {
field.selectBackward();
field.selectBackward();
}
// c[oun]t
type("lo").push(KeyCode.TAB); // 'c' + 'lo' is a prefix of 'closed'
Expand Down
Loading

0 comments on commit 7918c30

Please sign in to comment.