Skip to content

Commit

Permalink
Merge pull request #11 from surgeforward/MeazureChanges
Browse files Browse the repository at this point in the history
Add Meazure Remote Repository Support
  • Loading branch information
jeremysawesome committed Jun 8, 2016
2 parents d218f8b + a3b3221 commit 5064b36
Show file tree
Hide file tree
Showing 57 changed files with 1,098 additions and 303 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# README #

### What is this repository for? ###
Nagger is meant to be a time tracking tool. The tool asks you a simple question every 15 minutes, "What are you doing?". From here you can select a story/task you are working on. Nagger also remembers the choice you made last time and has that prepopulated. So if you are working on the same thing you can simply push the "Confirmation" button and keep doing what you are doing.
Nagger is meant to be a time tracking tool. The tool asks you a simple question every 15 minutes, "What are you doing?". From here you can select a story/task you are working on. Nagger also remembers the choice you made last time and has that prepopulated. So if you are working on the same thing you can simply type "y" for Yes, push Enter, and move on.

Nagger uses an internal database to track tasks and time. This means that you can track your time (for known tasks) even when you are not connected to the internet. At the beginning of every day, Nagger will attempt to log your time in your chosen remote repository. The only remote repository that is planned to be added at the moment is for JIRA. If Nagger does not have an internet connection it will continue to try to log the time every 15 minutes (the same interval it asks questions).
Nagger uses an internal database to track tasks and time. This means that you can track your time (for known tasks) even when you are not connected to the internet. At the beginning of every day, Nagger will attempt to log your time in your chosen remote repository. If Nagger does not have an internet connection it will continue to try to log the time every 15 minutes (the same interval it asks questions).

Nagger currently supports two repositories, JIRA and a propietary time-tracking site called Meazure.

The idea behind Nagger is to make it as painless as possible to track time. By proactively asking you what you are working on it removes the problems caused by having to remember to stop/resume timers. It also makes it so you do not have to remember to insert time every day. Finally, by asking you at a given interval, it assures that you track time against those one off tasks that might get forgotten at the end of the day (or at the end of the month).

Expand All @@ -24,6 +26,10 @@ Note: Nagger makes use of `%localappdata%\Nagger`.

Nagger is still in development. Setting it up should be as simple as downloading the repo and opening the solution in Visual Studio. Nagger makes use of NuGet for some packages, so package restore should be enabled.

### Nagger Commands ###

By default Nagger will attempt to log time to the remote repository (i.e. JIRA) at the beginning of every day. However, using the "-push" command line argument you can manually tell Nagger to push all un-logged time entries to your remote repository. Example, running "Nagger.exe -push" will push all unlogged entries to your chosen remote repository.

### Contribution guidelines ###

* Writing tests
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
namespace Nagger.Data.JIRA.API
namespace Nagger.Data.API
{
using System;
using System.Net;
using System.Security.Authentication;
using Models;
using RestSharp;

public class JiraBaseApi
public class BaseApi
{
readonly string _apiUrl;
readonly User _user;
protected readonly string ApiUrl;
protected readonly User User;

public JiraBaseApi(User user, string apiBaseUrl, string apiPath)
public BaseApi(User user, string apiBaseUrl, string apiPath)
{
_user = user;
_apiUrl = apiBaseUrl.TrimEnd('/') + apiPath;
User = user;
ApiUrl = apiBaseUrl.TrimEnd('/') + apiPath;
}

public T Execute<T>(RestRequest request) where T : new()
public T Execute<T>(RestRequest request, IRestClient client = null) where T : new()
{
var client = GetClient();
if(client == null) client = GetClient();
var response = client.Execute<T>(request);

// we will deal with this later. When InvalidCredentials are provided then we need to clear out the saved creds
if(response.StatusCode == HttpStatusCode.Unauthorized) throw new InvalidCredentialException("User provided is not authorized to access this JIRA API: "+_apiUrl);
if(response.StatusCode == HttpStatusCode.Unauthorized) throw new InvalidCredentialException("User provided is not authorized to access this API: "+ApiUrl);

if (response.ErrorException == null) return response.Data;

throw new ApplicationException("Error retrieving data from Jira", response.ErrorException);
throw new ApplicationException("Error retrieving data from API", response.ErrorException);
}

RestClient GetClient()
{
return new RestClient
{
BaseUrl = new Uri(_apiUrl),
Authenticator = new HttpBasicAuthenticator(_user.Username, _user.Password)
BaseUrl = new Uri(ApiUrl),
Authenticator = new HttpBasicAuthenticator(User.Username, User.Password)
};
}
}
Expand Down
50 changes: 50 additions & 0 deletions src/Nagger.Data/BaseRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
namespace Nagger.Data
{
using Interfaces;
using Models;

public class BaseRepository
{
readonly ISettingsService _settingsService;
readonly IInputService _inputService;

public BaseRepository(ISettingsService settingsService, IInputService inputService)
{
_settingsService = settingsService;
_inputService = inputService;
}
public User GetUser(string apiName, string usernameKey, string passwordKey)
{
var username = _settingsService.GetSetting<string>(usernameKey);
if (string.IsNullOrEmpty(username))
{
username = _inputService.AskForInput($"Please provide your username for {apiName}");
_settingsService.SaveSetting(usernameKey, username);
}

var password = _settingsService.GetSetting<string>(passwordKey);
if (string.IsNullOrEmpty(password))
{
password = _inputService.AskForPassword($"Please provide your password for {apiName}");
_settingsService.SaveSetting(passwordKey, password);
}

return new User
{
Username = username,
Password = password
};
}

public string GetApiBaseUrl(string apiName, string apiBaseUrlKey)
{
var baseUrl = _settingsService.GetSetting<string>(apiBaseUrlKey);
if (string.IsNullOrEmpty(baseUrl))
{
baseUrl = _inputService.AskForInput($"Please provide your {apiName} base url");
_settingsService.SaveSetting(apiBaseUrlKey, baseUrl);
}
return baseUrl;
}
}
}
3 changes: 2 additions & 1 deletion src/Nagger.Data/JIRA/API/JiraApi.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
namespace Nagger.Data.JIRA.API
{
using Data.API;
using Models;

/**
* Based on the example here: https://github.com/restsharp/RestSharp/wiki/Recommended-Usage
**/

public class JiraApi : JiraBaseApi
public class JiraApi : BaseApi
{
// todo: move the ApiUrl out into a setting so this can be used by whomever
const string ApiUrlPath = "/rest/api/latest";
Expand Down
6 changes: 3 additions & 3 deletions src/Nagger.Data/JIRA/API/JiraSprintApi.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace Nagger.Data.JIRA
namespace Nagger.Data.JIRA.API
{
using API;
using Data.API;
using Models;

public class JiraSprintApi : JiraBaseApi
public class JiraSprintApi : BaseApi
{
//todo: move the url out to a setting so this can be used by whomever
const string ApiUrlPath = "/rest/greenhopper/latest";
Expand Down
57 changes: 6 additions & 51 deletions src/Nagger.Data/JIRA/BaseJiraRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,25 @@

public class BaseJiraRepository
{
const string ApiName = "JIRA";
const string UsernameKey = "JiraUsername";
const string PasswordKey = "JiraPassword";
const string ApiBaseUrlKey = "JiraApi";
readonly ISettingsService _settingsService;
readonly IInputService _inputService;
readonly BaseRepository _baseRepository;

User _user;
string _apiBaseUrl;

public BaseJiraRepository(ISettingsService settingsService, IInputService inputService)
{
_settingsService = settingsService;
_inputService = inputService;
_baseRepository = new BaseRepository(settingsService, inputService);
}

public string ApiBaseUrl
{
get
{
_apiBaseUrl = _apiBaseUrl ?? GetApiBaseUrl();
_apiBaseUrl = _apiBaseUrl ?? _baseRepository.GetApiBaseUrl(ApiName, ApiBaseUrlKey);
return _apiBaseUrl;
}
}
Expand All @@ -33,55 +32,11 @@ public User JiraUser
{
get
{
_user = _user ?? GetUser();
_user = _user ?? _baseRepository.GetUser(ApiName, UsernameKey, PasswordKey);
return _user;
}
}

public bool UserExists
{
get { return JiraUser != null; }
}

User GetUser()
{
var username = _settingsService.GetSetting<string>(UsernameKey);
if (string.IsNullOrEmpty(username))
{
username = _inputService.AskForInput("Please provide your username for JIRA");
_settingsService.SaveSetting(UsernameKey, username);
}

var password = _settingsService.GetSetting<string>(PasswordKey);
if (string.IsNullOrEmpty(password))
{
password = _inputService.AskForPassword("Please provide your password for JIRA");
_settingsService.SaveSetting(PasswordKey, password);
}

return new User
{
Username = username,
Password = password
};
}

string GetApiBaseUrl()
{
var baseUrl = _settingsService.GetSetting<string>(ApiBaseUrlKey);
if (string.IsNullOrEmpty(baseUrl))
{
baseUrl = _inputService.AskForInput("Please provide your JIRA base url");
_settingsService.SaveSetting(ApiBaseUrlKey, baseUrl);
}
return baseUrl;
}

public void SaveUser(User user)
{
if (user == null) return;
_settingsService.SaveSetting(UsernameKey, user.Username);
_settingsService.SaveSetting(PasswordKey, user.Password);
}
public bool UserExists => JiraUser != null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
using Models;
using RestSharp;

public class JiraRemoteProjectRepository : BaseJiraRepository, IRemoteProjectRepository
public class JiraProjectRepository : BaseJiraRepository, IRemoteProjectRepository
{
readonly JiraApi _api;

public JiraRemoteProjectRepository(ISettingsService settingsService, IInputService inputService)
public JiraProjectRepository(ISettingsService settingsService, IInputService inputService)
: base(settingsService, inputService)
{
// the project and time repositories use different API's but the same user and password
Expand All @@ -27,9 +27,7 @@ public IEnumerable<Project> GetProjects()

var apiResult = _api.Execute<List<DTO.Project>>(request);

if (apiResult == null) return null;

return apiResult.Select(x => new Project
return apiResult?.Select(x => new Project
{
Id = x.id,
Name = x.name,
Expand Down
Loading

0 comments on commit 5064b36

Please sign in to comment.