Merge branch 'CreateAbout-#48#45' into 'master'

Release0.1.4

See merge request !6
This commit is contained in:
Chris Watts 2017-02-28 20:55:40 +00:00
commit 7f531b7baf
17 changed files with 215 additions and 68 deletions

View File

@ -0,0 +1,11 @@
namespace Interfaces
{
public class AppDetails
{
public string ApplicationName { get; set; }
public string Version { get; set; }
public string DataBaseProvider { get; set; }
public string LoggerProvider { get; set; }
public string ErrorEmailAddress { get; set; }
}
}

View File

@ -40,6 +40,7 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AppDetails.cs" />
<Compile Include="DailyLogs.cs" /> <Compile Include="DailyLogs.cs" />
<Compile Include="ILogger.cs" /> <Compile Include="ILogger.cs" />
<Compile Include="IRepository.cs" /> <Compile Include="IRepository.cs" />

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.1.2.0")] [assembly: AssemblyVersion("0.1.4.0")]
[assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyFileVersion("0.1.4.0")]

View File

@ -19,5 +19,6 @@ namespace Interfaces
get { return Math.Round(TimeLogs.Sum(x => x.DailyTotal), 2); } get { return Math.Round(TimeLogs.Sum(x => x.DailyTotal), 2); }
} }
public float HoursPerWeekMinutes { get; set; } public float HoursPerWeekMinutes { get; set; }
public User UserInformation { get; set; }
} }
} }

View File

@ -21,5 +21,7 @@ namespace Interfaces
} }
public List<Identifier> AssociatedIdentifiers { get; set; } public List<Identifier> AssociatedIdentifiers { get; set; }
public bool State { get; set; }
} }
} }

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.1.2.0")] [assembly: AssemblyVersion("0.1.4.0")]
[assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyFileVersion("0.1.4.0")]

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.1.2.0")] [assembly: AssemblyVersion("0.1.4.0")]
[assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyFileVersion("0.1.2.0")]

View File

@ -2,28 +2,50 @@ namespace SQLiteRepository
{ {
internal static class SQLiteProcedures internal static class SQLiteProcedures
{ {
public const string GET_LOGS_IN_LAST_X_MINUTES= "select * from TimeLogDb where "+nameof(TimeLogDb.SwipeEventDateTime)+" > ? AND "+nameof(TimeLogDb.UserId_FK)+"=?"; public const string GET_LOGS_IN_LAST_X_MINUTES =
public const string GET_TIMELOGS = "select * from "+nameof(TimeLogDb)+ " where (" + nameof(TimeLogDb.UserId_FK) + "=? AND " + nameof(TimeLogDb.CalendarWeek) + "=? and " + nameof(TimeLogDb.Year) + "=?)"; "select * from TimeLogDb where " + nameof(TimeLogDb.SwipeEventDateTime) + " > ? AND " +
public const string GET_ALL_USERS = "select * from " + nameof(UserIdentity); nameof(TimeLogDb.UserId_FK) + "=?";
public const string GET_USER_BY_ID = "select * from " + nameof(UserIdentity) + " where " + nameof(UserIdentity.Id) + "=?";
public const string GET_CARDS_BY_USER_ID = "select * from " + nameof(CardUniqueId) + " where " + nameof(CardUniqueId.UserId_FK) + "=?"; public const string GET_TIMELOGS =
public const string GET_CARDS_BY_UNIQUE_ID = "select * from " + nameof(CardUniqueId) + " where " + nameof(CardUniqueId.CardUId) + "=?"; "select * from " + nameof(TimeLogDb) + " where (" + nameof(TimeLogDb.UserId_FK) + "=? AND " +
public const string GET_UNASSIGNED_CARD_LIST = "select * from " + nameof(CardUniqueId) + " where " + nameof(CardUniqueId.UserId_FK) + "=?"; nameof(TimeLogDb.CalendarWeek) + "=? and " + nameof(TimeLogDb.Year) + "=?)";
public const string GET_USER_BY_FIRST_AND_LAST = public const string GET_ALL_USERS =
"select * from " + nameof(UserIdentity) + " where " + nameof(UserIdentity.FirstName) + " = ? AND " + nameof(UserIdentity.LastName) + " = ?"; "select * from " + nameof(UserIdentity) + " order by " + nameof(UserIdentity.LastName) + " collate nocase, " +
nameof(UserIdentity.FirstName) + " collate nocase";
public const string UPDATE_CARD_USER_ID = "update " + nameof(CardUniqueId) + " set " + nameof(CardUniqueId.UserId_FK) + "=? where " + nameof(CardUniqueId.Id) + "=?"; public const string GET_ALL_USERS_PAGINATE =
"select * from " + nameof(UserIdentity) + " order by " + nameof(UserIdentity.LastName) + " collate nocase, " +
nameof(UserIdentity.FirstName) + " collate nocase limit ? offset ?";
public const string SEARCH_USER_LIST = "SELECT * FROM " + nameof(UserIdentity) + " where(" + nameof(UserIdentity.FirstName) + " Like ? OR " + nameof(UserIdentity.LastName) + " Like ?)"; public const string GET_USER_BY_ID =
"select * from " + nameof(UserIdentity) + " where " + nameof(UserIdentity.Id) + "=?";
public const string GET_LAST_TIMELOG_DIRECTION = "SELECT * FROM " + nameof(TimeLogDb) + " where " + nameof(TimeLogDb.UserId_FK) + " = ? order by " + nameof(TimeLogDb.SwipeEventDateTime) + " desc LIMIT 1"; public const string GET_CARDS_BY_USER_ID =
"select * from " + nameof(CardUniqueId) + " where " + nameof(CardUniqueId.UserId_FK) + "=?";
public const string GET_ALL_USERS_PAGINATE = "select * from "+ nameof(UserIdentity)+" limit ? offset ?"; public const string GET_CARDS_BY_UNIQUE_ID =
"select * from " + nameof(CardUniqueId) + " where " + nameof(CardUniqueId.CardUId) + "=?";
public const string GET_TOTAL_USER_COUNT = "select Max("+nameof(UserIdentity.Id)+") from " + nameof(UserIdentity); public const string GET_UNASSIGNED_CARD_LIST =
"select * from " + nameof(CardUniqueId) + " where " + nameof(CardUniqueId.UserId_FK) + "=?";
public const string GET_USER_CONTRACTED_HOURS = "select "+nameof(UserIdentity.HoursPerWeek)+ " From UserIdentity where " + nameof(UserIdentity.Id) + "=?"; public const string UPDATE_CARD_USER_ID =
"update " + nameof(CardUniqueId) + " set " + nameof(CardUniqueId.UserId_FK) + "=? where " +
nameof(CardUniqueId.Id) + "=?";
public const string SEARCH_USER_LIST =
"SELECT * FROM " + nameof(UserIdentity) + " where(" + nameof(UserIdentity.FirstName) + " Like ? OR " +
nameof(UserIdentity.LastName) + " Like ?)";
public const string GET_LAST_TIMELOG_DIRECTION =
"SELECT * FROM " + nameof(TimeLogDb) + " where " + nameof(TimeLogDb.UserId_FK) + " = ? order by " +
nameof(TimeLogDb.SwipeEventDateTime) + " desc LIMIT 1";
public const string GET_TOTAL_USER_COUNT =
"select Max(" + nameof(UserIdentity.Id) + ") from " + nameof(UserIdentity);
public const string GET_USER_CONTRACTED_HOURS =
"select " + nameof(UserIdentity.HoursPerWeek) + " From UserIdentity where " + nameof(UserIdentity.Id) + "=?";
} }
} }

View File

@ -82,6 +82,8 @@ namespace SQLiteRepository
Id = card.Id Id = card.Id
}); });
} }
userObj.State = GetUserState(GetLogDirection(user.Id));
ret.Users.Add(userObj); ret.Users.Add(userObj);
} }
if (pageNumber == -1 && pageSize == -1) if (pageNumber == -1 && pageSize == -1)
@ -98,6 +100,17 @@ namespace SQLiteRepository
return ret; return ret;
} }
private bool GetUserState(LogDirectionDb logDirection)
{
switch (logDirection)
{
case LogDirectionDb.OUT:
return true;
default:
return false;
}
}
public UserList Search(string searchParam) public UserList Search(string searchParam)
{ {
_logger.Trace("Searching SQLite database for the term: {0}", searchParam); _logger.Trace("Searching SQLite database for the term: {0}", searchParam);
@ -205,6 +218,7 @@ namespace SQLiteRepository
_logger.Error(ex, "Error in GetUserContracterHours with Id: {0} and selected date: {1}, Exception: {2}", userId, selectedDate, ex); _logger.Error(ex, "Error in GetUserContracterHours with Id: {0} and selected date: {1}, Exception: {2}", userId, selectedDate, ex);
} }
ret.UserInformation = GetUser(userId);
return ret; return ret;
} }

View File

@ -3,7 +3,8 @@
<appSettings> <appSettings>
<add key="NLogConfigFilePath" value="Configs/NLogConfig.xml" /> <add key="NLogConfigFilePath" value="Configs/NLogConfig.xml" />
<add key="DefaultPageSize" value="20" /> <add key="DefaultPageSize" value="20" />
<add key="SwipeTimeGap" value="20" /> <add key="SwipeTimeGap" value="3" />
<add key="BugSubmissionEmailAddress" value="incoming+WattsC/FlexiTimeTrackerTool+24qrefn8e1urhl4iqct7we2jl@gitlab.com"/>
</appSettings> </appSettings>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />

View File

@ -0,0 +1,27 @@
using System.Configuration;
using System.Reflection;
using System.Web.Http;
using Interfaces;
namespace WindowsDataCenter
{
[RoutePrefix("api/app")]
public class ApplicationController:ApiController
{
[Route("")]
public IHttpActionResult GetAppDetails()
{
var ninjectHelper = NinjectHelper.GetInstance();
var appDetails = new AppDetails
{
ApplicationName = "Flexitime Tracker",
DataBaseProvider = ninjectHelper.Get<IRepository>().GetType().ToString(),
LoggerProvider = ninjectHelper.Get<ILogger>().GetType().ToString(),
Version = Assembly.GetEntryAssembly().GetName().Version.ToString(),
ErrorEmailAddress = ConfigurationManager.AppSettings["BugSubmissionEmailAddress"] ?? "NONE"
};
return Ok(appDetails);
}
}
}

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.1.2.0")] [assembly: AssemblyVersion("0.1.4.0")]
[assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyFileVersion("0.1.4.0")]

View File

@ -147,6 +147,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="CardData.cs" /> <Compile Include="CardData.cs" />
<Compile Include="Controllers\ApplicationController.cs" />
<Compile Include="Controllers\CardsController.cs" /> <Compile Include="Controllers\CardsController.cs" />
<Compile Include="Configuration.cs" /> <Compile Include="Configuration.cs" />
<Compile Include="Controllers\TimelogController.cs" /> <Compile Include="Controllers\TimelogController.cs" />

View File

@ -37,6 +37,9 @@
<a class="indent-nav-xs" data-bind="text: $data"></a> <a class="indent-nav-xs" data-bind="text: $data"></a>
</li> </li>
<!-- /ko --> <!-- /ko -->
<li class="hidden-xs" >
<a data-toggle="modal" data-target="#aboutDialog">About</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
@ -61,15 +64,24 @@
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
<th class="col-md-1"></th>
<th class="col-md-3">First Name</th> <th class="col-md-3">First Name</th>
<th class="col-md-3">Last Name</th> <th class="col-md-3">Last Name</th>
<th class="col-md-1 text-center">Contractor</th> <th class="col-md-1 text-center">Contractor</th>
<th /> <th/>
<th/> <th/>
</tr> </tr>
</thead> </thead>
<tbody data-bind="foreach: Users"> <tbody data-bind="foreach: Users">
<tr> <tr>
<td class="valign text-center">
<!-- ko if: State -->
<span class="label label-success" style="display: block">IN</span>
<!-- /ko -->
<!-- ko if: !State -->
<span class="label label-danger" style="display: block">OUT</span>
<!-- /ko -->
</td>
<td class="valign" data-bind="text: FirstName"></td> <td class="valign" data-bind="text: FirstName"></td>
<td class="valign" data-bind="text: LastName"></td> <td class="valign" data-bind="text: LastName"></td>
<td class="valign text-center"><span data-bind="css:{ 'glyphicon glyphicon-ok': IsContractor}"></span></td> <td class="valign text-center"><span data-bind="css:{ 'glyphicon glyphicon-ok': IsContractor}"></span></td>
@ -116,6 +128,7 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="container" data-bind="with: chosenUserDetails"> <div class="container" data-bind="with: chosenUserDetails">
@ -186,54 +199,92 @@
</div> </div>
</div> </div>
<div class="container" data-bind="with: userTimeLogData"> <div class="container" data-bind="with: userTimeLogData">
<button pageDestination="Users" data-bind="click: $root.returnButtonClick" id="returnButton" class="btn btn-default"> <div class="row">
<span class="glyphicon glyphicon-chevron-left"></span>Users <div class="col-md-2 text-center" style="margin-top: 15px;">
</button> <button pageDestination="Users" data-bind="click: $root.returnButtonClick" id="returnButton" class="btn btn-default">
<br /> <span class="glyphicon glyphicon-chevron-left"></span>Users
<br /> </button>
<div class="row"> </div>
<div class="col-md-3 col-xs-12"> <h3 class="col-md-offset-1 col-md-9 pull-right">
<div id="datePickerContainer"> Logs for: <span data-bind="text: UserInformation.FirstName"></span> <span data-bind="text: UserInformation.LastName"></span>
<div id="weeklyDatePicker"></div> </h3>
</div> </div>
<br/>
<br/>
<!--<div class="row">
</div>-->
<div class="row">
<div class="col-md-3 col-xs-12">
<div id="datePickerContainer">
<div id="weeklyDatePicker"></div>
</div> </div>
<div class="col-md-9 col-xs-12 well"> </div>
<!-- main content panel. --> <div class="col-md-9 col-xs-12 well">
<table class="table table-striped"> <!-- main content panel. -->
<thead> <table class="table table-striped">
<tr> <thead>
<th>Day Of Week</th> <tr>
<!-- ko foreach: new Array($root.getTimeLogEntryArrayLength(MaxDailyLogCount)) --> <th>Day Of Week</th>
<th>In</th> <!-- ko foreach: new Array($root.getTimeLogEntryArrayLength(MaxDailyLogCount)) -->
<th>Out</th> <th>In</th>
<!-- /ko --> <th>Out</th>
<th>Sub-Total</th>
</tr>
</thead>
<tbody>
<!-- ko foreach: TimeLogs-->
<tr>
<td class="valign" data-bind="text: DayOfWeek"></td>
<!-- ko foreach: Logs -->
<td class="valign" data-bind="text: $root.convertToDisplayTime(EventTime)"></td>
<!-- /ko -->
<!-- ko foreach: new Array($root.correctLogOffset($parent.MaxDailyLogCount)-LogCount)-->
<td class="valign"></td>
<!-- /ko -->
<td class="valign"data-bind="text: $root.convertToHours(DailyTotal)"></td>
</tr>
<!-- /ko --> <!-- /ko -->
<th>Sub-Total</th>
</tr>
</thead>
<tbody>
<!-- ko foreach: TimeLogs-->
<tr>
<td class="valign" data-bind="text: DayOfWeek"></td>
<!-- ko foreach: Logs -->
<td class="valign" data-bind="text: $root.convertToDisplayTime(EventTime)"></td>
<!-- /ko -->
<!-- ko foreach: new Array($root.correctLogOffset($parent.MaxDailyLogCount)-LogCount)-->
<td class="valign"></td>
<!-- /ko -->
<td class="valign"data-bind="text: $root.convertToHours(DailyTotal)"></td>
</tr>
<!-- /ko -->
<tr>
<td class="valign" data-bind="attr:{colspan: $root.correctLogOffset(MaxDailyLogCount)+1}">Weekly Total</td>
<td class="valign" for="dailyHrsTotal" data-bind="text: $root.convertToHours(WeeklyTotal)"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div id="aboutDialog" class="modal fade" role="dialog" data-bind="with: appDetails">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 data-bind="text: ApplicationName"></h4>
</div>
<div class="modal-body">
<table class="table">
<tr> <tr>
<td class="valign" data-bind="attr:{colspan: $root.correctLogOffset(MaxDailyLogCount)+1}">Weekly Total</td> <th>Version</th>
<td class="valign" for="dailyHrsTotal" data-bind="text: $root.convertToHours(WeeklyTotal)"></td> <td data-bind="text: Version"></td>
</tr>
<tr>
<th>Database Provider</th>
<td data-bind="text: DataBaseProvider"></td>
</tr> </tr>
</tbody>
</table> </table>
<br />
<a class="btn btn-default" data-bind="attr:{href:'mailto:'+ErrorEmailAddress}">Submit Error Report</a>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div> </div>
</div> </div>
</div> </div>
</div>
<footer data-bind="with: errorData, css: {footer: $root.errorData()!==null}"> <footer data-bind="with: errorData, css: {footer: $root.errorData()!==null}">
<div class="container"> <div class="container">
<!-- Alert/Error banner--> <!-- Alert/Error banner-->

View File

@ -3,6 +3,7 @@
var self = this; var self = this;
self.menuOptions = ["Home"]; self.menuOptions = ["Home"];
self.chosenMenuItemId = ko.observable(); self.chosenMenuItemId = ko.observable();
self.appDetails = ko.observable(null);
self.userList = ko.observable(null); self.userList = ko.observable(null);
self.chosenUserDetails = ko.observable(null); self.chosenUserDetails = ko.observable(null);
self.userTimeLogData = ko.observable(null); self.userTimeLogData = ko.observable(null);
@ -16,7 +17,8 @@
getUserDetails: "/api/users", getUserDetails: "/api/users",
editUser: "/api/users/edit", editUser: "/api/users/edit",
getTimeLogs: "/api/timelogs", getTimeLogs: "/api/timelogs",
getUnassignedCards: "/api/cards/unassigned" getUnassignedCards: "/api/cards/unassigned",
getAppDetails: "/api/app"
}; };
self.uiPages = { self.uiPages = {
users: "users", users: "users",
@ -231,6 +233,16 @@
self.assignErrorObject(errObj.errorCode, errObj.errorMessage, "getUserList"); self.assignErrorObject(errObj.errorCode, errObj.errorMessage, "getUserList");
}); });
}; };
self.getAppDetails = function() {
var url = self.createRequestUrl(self.apiEndpoints.getAppDetails, null, false, false);
$.getJSON(url, function (res) {
self.appDetails(res);
}).fail(function (response, status, error) {
console.log("error - getusers");
var errObj = self.processRequestFailure(response, status, error);
self.assignErrorObject(errObj.errorCode, errObj.errorMessage, "getUserList");
});
};
self.searchUsers = function(query) { self.searchUsers = function(query) {
var url = self.createRequestUrl(self.apiEndpoints.getUserList, var url = self.createRequestUrl(self.apiEndpoints.getUserList,
[{ key: "query", value: query }], false, false); [{ key: "query", value: query }], false, false);
@ -308,6 +320,9 @@
self.chosenUserDetails(null); self.chosenUserDetails(null);
self.userList(null); self.userList(null);
self.userTimeLogData(null); self.userTimeLogData(null);
if (self.appDetails() === null) {
self.getAppDetails();
}
if (query) if (query)
self.searchUsers(query); self.searchUsers(query);
else else

View File

@ -3,7 +3,8 @@
<appSettings> <appSettings>
<add key="NLogConfigFilePath" value="Configs/NLogConfig.xml" /> <add key="NLogConfigFilePath" value="Configs/NLogConfig.xml" />
<add key="DefaultPageSize" value="20" /> <add key="DefaultPageSize" value="20" />
<add key="SwipeTimeGap" value="20" /> <add key="SwipeTimeGap" value="3" />
<add key="BugSubmissionEmailAddress" value="incoming+WattsC/FlexiTimeTrackerTool+24qrefn8e1urhl4iqct7we2jl@gitlab.com"/>
</appSettings> </appSettings>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyVersion("0.1.4.0")]
[assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyFileVersion("0.1.4.0")]