diff --git a/DataCenter_Windows/WindowsDataCenter/Interfaces/IRepository.cs b/DataCenter_Windows/WindowsDataCenter/Interfaces/IRepository.cs
index e5e4f59..63402ab 100644
--- a/DataCenter_Windows/WindowsDataCenter/Interfaces/IRepository.cs
+++ b/DataCenter_Windows/WindowsDataCenter/Interfaces/IRepository.cs
@@ -1,114 +1,116 @@
-using System;
-using System.Collections.Generic;
-
-namespace Interfaces
-{
- public interface IRepository
- {
- ///
- /// Get a list of Users
- ///
- ///
- /// Returns with full list of users,
- /// plus a total user count. Pagination options are supported.
- ///
- UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1);
- ///
- /// Search the user list for the following string
- ///
- /// string to search the user store for.
- ///
- /// Returns with full list of users,
- /// plus a total user count. Pagination options are supported.
- ///
- UserList Search(string searchParam);
- ///
- /// Get details about a single user in the system base on their Id
- ///
- ///
- /// integer data type, the Id of the User to get details about.
- ///
- /// object with full details,
- /// including full
- ///
- User GetUser(int id);
- ///
- /// Get a list of the timelogs available for the specified user
- /// for the current Calendar Week
- ///
- ///
- /// integer data type, the Id of the user to get time logs for
- ///
- ///
- /// with nested objects
- /// for the current calendar week
- ///
- TimeLogList GetTimeLogs(int userId);
- ///
- /// Get a list of the timelogs available for the specified user
- /// for the specified calendar week
- ///
- ///
- /// integer data type, the Id of the user to get time logs for
- ///
- ///
- /// datetime data type, the date to receive time logs for (will scope out to calendar week).
- ///
- ///
- /// with nested objects
- /// for the current calendar week
- ///
- TimeLogList GetTimeLogs(int userId, DateTime selectedDate);
- ///
- /// Get a full list of Identifiers which are not associated to a user.
- ///
- ///
- /// with nested list
- ///
- IdentifierList GetUnassignedIdentifierList();
- ///
- /// Remove all unassigned identifiers from the system.
- ///
- void ClearUnassignedIdentifiers();
-
- ///
- /// Update a user in the system with the new values.
- ///
- ///
- /// If the user object passed does not exist, it will be created.
- ///
- ///
- /// object detailing the new properties to assign to the user.
- /// The Id Field should not be changed, or should be -1 for new users.
- ///
- ///
- /// int - ref param, value will be the UserId of the inserted or updated user
- ///
- ///
- /// to indicate procedure status.
- ///
- OperationResponse UpdateUser(User user, out int userId);
-
- ///
- /// Create a new TimeLog Event in the repository.
- ///
- ///
- /// object with the Unique Id triggering the event
- ///
- /// The resultant Id of the inserted TimeLog
- ///
- /// to indicate procedure status.
- ///
- LogEventResponse LogEventTime(Identifier identifier, out int logId);
-
- OperationResponse CreateGroup(Group group, out int groupId);
- List GetGroups(int userId = -1);
- Group GetGroup(int groupId);
- OperationResponse UpdateGroup(Group group);
- OperationResponse DeleteGroup(int groupId);
-
- OperationResponse DeleteLog(TimeLog log);
- OperationResponse CreateLog(TimeLog log);
- OperationResponse UpdateLog(TimeLog log);
- }
-}
+using System;
+using System.Collections.Generic;
+
+namespace Interfaces
+{
+ public interface IRepository
+ {
+ ///
+ /// Get a list of Users
+ ///
+ ///
+ /// Returns with full list of users,
+ /// plus a total user count. Pagination options are supported.
+ ///
+ UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1);
+ ///
+ /// Search the user list for the following string
+ ///
+ /// string to search the user store for.
+ ///
+ /// Returns with full list of users,
+ /// plus a total user count. Pagination options are supported.
+ ///
+ UserList Search(string searchParam);
+ ///
+ /// Get details about a single user in the system base on their Id
+ ///
+ ///
+ /// integer data type, the Id of the User to get details about.
+ ///
+ /// object with full details,
+ /// including full
+ ///
+ User GetUser(int id);
+ ///
+ /// Get a list of the timelogs available for the specified user
+ /// for the current Calendar Week
+ ///
+ ///
+ /// integer data type, the Id of the user to get time logs for
+ ///
+ ///
+ /// with nested objects
+ /// for the current calendar week
+ ///
+ TimeLogList GetTimeLogs(int userId);
+ ///
+ /// Get a list of the timelogs available for the specified user
+ /// for the specified calendar week
+ ///
+ ///
+ /// integer data type, the Id of the user to get time logs for
+ ///
+ ///
+ /// datetime data type, the date to receive time logs for (will scope out to calendar week).
+ ///
+ ///
+ /// with nested objects
+ /// for the current calendar week
+ ///
+ TimeLogList GetTimeLogs(int userId, DateTime selectedDate);
+ ///
+ /// Get a full list of Identifiers which are not associated to a user.
+ ///
+ ///
+ /// with nested list
+ ///
+ IdentifierList GetUnassignedIdentifierList();
+ ///
+ /// Remove all unassigned identifiers from the system.
+ ///
+ void ClearUnassignedIdentifiers();
+
+ ///
+ /// Update a user in the system with the new values.
+ ///
+ ///
+ /// If the user object passed does not exist, it will be created.
+ ///
+ ///
+ /// object detailing the new properties to assign to the user.
+ /// The Id Field should not be changed, or should be -1 for new users.
+ ///
+ ///
+ /// int - ref param, value will be the UserId of the inserted or updated user
+ ///
+ ///
+ /// to indicate procedure status.
+ ///
+ OperationResponse UpdateUser(User user, out int userId);
+
+ ///
+ /// Create a new TimeLog Event in the repository.
+ ///
+ ///
+ /// object with the Unique Id triggering the event
+ ///
+ /// The resultant Id of the inserted TimeLog
+ /// Optional - To set the log time of the swipe.
+ /// Particularly useful for buffering logs in nodes if they cannot contact server for whatever reason to minimise data loss
+ ///
+ /// to indicate procedure status.
+ ///
+ LogEventResponse LogEventTime(Identifier identifier, out int logId, DateTime logTime = default(DateTime));
+
+ OperationResponse CreateGroup(Group group, out int groupId);
+ List GetGroups(int userId = -1);
+ Group GetGroup(int groupId);
+ OperationResponse UpdateGroup(Group group);
+ OperationResponse DeleteGroup(int groupId);
+
+ OperationResponse DeleteLog(TimeLog log);
+ OperationResponse CreateLog(TimeLog log);
+ OperationResponse UpdateLog(TimeLog log);
+ }
+}
diff --git a/DataCenter_Windows/WindowsDataCenter/SQLiteRepository/SQLiteRepository.cs b/DataCenter_Windows/WindowsDataCenter/SQLiteRepository/SQLiteRepository.cs
index 89c510b..d260a81 100644
--- a/DataCenter_Windows/WindowsDataCenter/SQLiteRepository/SQLiteRepository.cs
+++ b/DataCenter_Windows/WindowsDataCenter/SQLiteRepository/SQLiteRepository.cs
@@ -1,929 +1,941 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Reflection;
-using Interfaces;
-using SQLite.Net;
-using SQLite.Net.Platform.Win32;
-using SQLiteRepository.Properties;
-
-namespace SQLiteRepository
-{
- public class SQLiteRepository : IRepository
- {
- private readonly SQLiteConnection _connection;
- private readonly ILogger _logger;
- private string _path = "flexitimedb.db";
-
- public SQLiteRepository(ILogger logger)
- {
- _path =
- new Uri(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().CodeBase), "flexitimedb.db"))
- .LocalPath;
- if (logger == null) throw new ArgumentNullException(nameof(logger));
- _logger = logger;
-
- _connection = new SQLiteConnection(new SQLitePlatformWin32(), _path);
-
- _connection.CreateTable();
- _connection.CreateTable();
- _connection.CreateTable();
- _connection.CreateTable();
- _connection.CreateTable();
- _connection.CreateTable();
- _logger.Trace("Initialised SQLite Repository");
- _logger.Trace("Checking For Upgrades");
- CheckForDbUpgrade();
- }
-
- private void CheckForDbUpgrade()
- {
- var data = _connection.Query("select * from DbVersion");
- if (!data.Any())
- {
- //Pre-Upgrade database, need upgrading
- _logger.Trace("Pre version 0.2 RC database found, performing update..");
- ExecuteUpgradeFromVersion("0.1"); //execute 0.2 upgrade scripts onwards.
- }
- else
- {
- var installedVersion = new Version(data.First().VersionNumber);
- var currentVersion = new Version(AssemblyInfo.ASSEMBLY_VERSION);
- if (currentVersion.CompareTo(installedVersion) > 0) //greater than 0 - current version is newer
- {
- _logger.Trace("Installed Database Version: {0} is older than current version {1}");
- ExecuteUpgradeFromVersion(installedVersion.ToString());
- }
- }
- }
-
- private void ExecuteUpgradeFromVersion(string installedVersion)
- {
- const string PREFIX = "SQLiteRepository.UpgradeScripts.";
- var instVers = new Version(installedVersion);
- //so we have established that each script for upgrade will be .sql, so now start at lowest version and work up!
- var assembly = Assembly.GetExecutingAssembly();
- var upgradeScripts = assembly.GetManifestResourceNames()
- .Where(x=>x.StartsWith(PREFIX))
- .OrderBy(x=>new Version(Path.GetFileNameWithoutExtension(x.Replace(PREFIX, ""))))
- .Where(x =>
- {
- var scriptVersion = new Version(Path.GetFileNameWithoutExtension(x.Replace(PREFIX, ""))).CompareTo(instVers);
- return scriptVersion > 0;
- }
- )
- .ToList();
- //now have an ordered list of upgrade script files, so execute in order!
- foreach (var upgradeScript in upgradeScripts)
- {
- using (var stream = assembly.GetManifestResourceStream(upgradeScript))
- using(var str = new StreamReader(stream))
- {
- var script = str.ReadToEnd();
- _logger.Trace("Executing upgrade script with name: {0}", upgradeScript);
- _connection.Execute(script);
- }
- }
- SetDbVersion(AssemblyInfo.ASSEMBLY_VERSION);
- }
-
- private void SetDbVersion(string vers)
- {
- _connection.DeleteAll();
- _connection.Insert(new DbVersion {VersionNumber = vers});
- _logger.Trace("Set Database version to: {0}", vers);
- }
-
- public UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1)
- {
- var ret = new UserList();
- List users;
- int userCount;
- if (pageNumber != -1 && pageSize != -1)
- {
- users = _connection.Query(SQLiteProcedures.GET_ALL_USERS_PAGINATE,
- pageSize, (pageNumber - 1) * pageSize);
- userCount = _connection.ExecuteScalar(SQLiteProcedures.GET_TOTAL_USER_COUNT);
- }
- else if (groupId != -1)
- {
- users =
- _connection.Query(
- SQLiteProcedures.GET_ALL_USERS_BY_GROUP,
- groupId);
- userCount = users.Count;
- }
- else
- {
- users = _connection.Query(SQLiteProcedures.GET_ALL_USERS);
- userCount = users.Count;
- }
-
- if (!users.Any())
- {
- if (pageNumber == -1 && pageSize == -1)
- {
- ret.PageNumber = 1;
- ret.PageSize = 20;
- }
- else
- {
- ret.PageNumber = pageNumber;
- ret.PageSize = pageSize;
- }
- return ret;
- }
-
- foreach (var user in users)
- {
- var userObj = ChangeToUserObject(user);
-
- userObj.AssociatedIdentifiers = GetAssociatedIdentifiers(user.Id);
- userObj.State = GetUserState(GetLogDirection(user.Id));
- userObj.LastEventDateTime = GetLastLogDateTime(GetLastTimeLog(user.Id));
- userObj.Groups = GetGroups(user.Id);
- ret.Users.Add(userObj);
- }
- if (pageNumber == -1 && pageSize == -1)
- {
- ret.PageSize = 10;
- ret.PageNumber = 1;
- }
- else
- {
- ret.PageSize = pageSize;
- ret.PageNumber = pageNumber;
- }
- ret.TotalUserCount = userCount;
- return ret;
- }
-
- public UserList Search(string searchParam)
- {
- _logger.Trace("Searching SQLite database for the term: {0}", searchParam);
- var ret = new UserList();
- searchParam = string.Format("%{0}%", searchParam);
- var users = _connection.Query(
- SQLiteProcedures.SEARCH_USER_LIST,
- searchParam, searchParam);
-
- _logger.Trace("Got {0} results for term: {1}", users.Count, searchParam);
- if (!users.Any())
- {
- ret.PageNumber = 1;
- ret.PageSize = 20;
- return ret;
- }
-
- foreach (var user in users)
- {
- var userObj = ChangeToUserObject(user);
- var cards = _connection.Query(
- SQLiteProcedures.GET_CARDS_BY_USER_ID,
- user.Id);
-
- foreach (var card in cards)
- {
- userObj.AssociatedIdentifiers.Add(new Identifier()
- {
- UniqueId = card.CardUId,
- IsAssociatedToUser = true,
- Id = card.Id
- });
- }
- userObj.State = GetUserState(GetLogDirection(user.Id));
- ret.Users.Add(userObj);
- }
- //TODO: figure out paging here. - should there be any?
- ret.PageSize = 20;
- ret.PageNumber = 1;
-
- return ret;
- }
-
- public User GetUser(int id)
- {
- var ret = new User();
-
- try
- {
- var users = _connection.Query(
- SQLiteProcedures.GET_USER_BY_ID,
- id);
-
- if (!users.Any()) return ret;
-
- var user = users.First();
- ret = ChangeToUserObject(user);
- ret.Groups = GetGroups();
- var usersGroups = GetGroups(user.Id);
- foreach (var group in usersGroups)
- {
- ret.Groups.First(x => x.Id == group.Id).IsAssociatedToUser = true;
- }
- var cards = _connection.Query(
- SQLiteProcedures.GET_CARDS_BY_USER_ID,
- user.Id);
-
- foreach (var card in cards)
- {
- ret.AssociatedIdentifiers.Add(new Identifier { UniqueId = card.CardUId, IsAssociatedToUser = true, Id = card.Id });
- }
- }
- catch (Exception ex)
- {
- _logger.Error(ex, "Error in GetUser with Id: {0}", id);
- ret.UserId = id;
- ret.FirstName = ret.LastName = string.Empty;
- ret.HoursPerWeek = -1.0f;
- ret.IsContractor = false;
- ret.AssociatedIdentifiers.Clear();
- }
-
- return ret;
- }
-
- public TimeLogList GetTimeLogs(int userId)
- {
- return GetTimeLogs(userId, DateTime.UtcNow);
- }
-
- public TimeLogList GetTimeLogs(int userId, DateTime selectedDate)
- {
- var ret = new TimeLogList { SelectedDate = selectedDate.Date };
- var calendarWeek = GetIso8601CalendarWeek(selectedDate);
- ret.CalendarWeek = calendarWeek;
-
- try
- {
- ret.TimeLogs = GetTimeLogList(userId, calendarWeek, selectedDate.Year);
- }
- catch (Exception ex)
- {
- _logger.Error(ex, "Error in GetTimeLogs with Id: {0} and selected date: {1}, Exception: {2}", userId, selectedDate, ex);
- }
-
- try
- {
- ret.HoursPerWeekMinutes = GetUserContractedHours(userId) * 60.0f;
- }
- catch (Exception 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;
- }
-
- private float GetUserContractedHours(int userId)
- {
- var hoursQuery = _connection.Query(SQLiteProcedures.GET_USER_CONTRACTED_HOURS, userId);
- if (hoursQuery.Any())
- {
- return hoursQuery.First().HoursPerWeek;
- }
- return -1.0f;
- }
-
- public IdentifierList GetUnassignedIdentifierList()
- {
- var ret = new IdentifierList();
- var cardQuery = _connection.Query(
- SQLiteProcedures.GET_UNASSIGNED_CARD_LIST,
- Constants.UNASSIGNED_CARD_USER_ID);
-
- foreach (var card in cardQuery)
- {
- ret.data.Add(new Identifier
- {
- Id = card.Id,
- IsAssociatedToUser = card.UserId_FK != Constants.UNASSIGNED_CARD_USER_ID,
- UniqueId = card.CardUId,
- LastUsed = card.LastUsed.DateTime
- });
- }
- return ret;
- }
-
- public void ClearUnassignedIdentifiers()
- {
- _connection.Execute(
- SQLiteProcedures.CLEAR_UNASSIGNED_CARDS,
- Constants.UNASSIGNED_CARD_USER_ID);
- }
-
- //TODO: Check time logs table on update to ensure associated cards/unique identifiers are removed/added as appropriate.
- public OperationResponse UpdateUser(User user, out int userIdResult)
- {
- //if(user.UserId <=0) return OperationResponse.FAILED;
-
- var ret = OperationResponse.NONE;
- var cardIds = new List();
-
- //Get a list of current associated identifiers, convert into a list of Identifier Objects..
- var currentCards =
- _connection.Query(SQLiteProcedures.GET_CARDS_BY_USER_ID, user.UserId)
- .Select(x => new Identifier { Id = x.Id, IsAssociatedToUser = true, UniqueId = x.CardUId });
- var cardsToRemove = currentCards.Except(user.AssociatedIdentifiers).Select(x => x.Id).ToList();
-
- #region GetUnique Identifiers
- foreach (var card in user.AssociatedIdentifiers)
- {
- var existingCard = _connection.Query(
- SQLiteProcedures.GET_CARDS_BY_UNIQUE_ID, card.UniqueId);
-
- if (!existingCard.Any())
- {
- var cardInsert = new CardUniqueId { CardUId = card.UniqueId, UserId_FK = -1 };
- _connection.Insert(cardInsert);
- cardIds.Add(cardInsert.Id);
- if (ret < OperationResponse.CREATED)
- ret = OperationResponse.CREATED; //only change it if my status supercedes.
- }
- else
- {
- cardIds.Add(existingCard.First().Id);
- if (ret < OperationResponse.UPDATED)
- ret = OperationResponse.UPDATED;
- }
- }
- #endregion
-
- #region Update/Create User
- int userId;
-
- if (user.UserId != -1)
- {
- //edit..
- _connection.Query(
- SQLiteProcedures.UPDATE_USER_DETAILS,
- user.FirstName,
- user.LastName,
- user.HoursPerWeek,
- user.IsContractor,
- user.UserId
- );
- userId = user.UserId;
- }
- else
- {
- var userInsert = new UserIdentity
- {
- FirstName = user.FirstName,
- LastName = user.LastName,
- HoursPerWeek = user.HoursPerWeek,
- IsContractor = user.IsContractor
- };
- _connection.Insert(userInsert);
- userId = userInsert.Id;
- if (ret < OperationResponse.CREATED)
- ret = OperationResponse.CREATED;
- }
- #endregion
-
- #region Update Card/Unique Id entries.
- foreach (var cardId in cardIds)
- {
- _connection.Query(
- SQLiteProcedures.UPDATE_CARD_USER_ID,
- userId, cardId);
- }
- foreach (var card in cardsToRemove)
- {
- _connection.Query(
- SQLiteProcedures.UPDATE_CARD_USER_ID,
- -1, card);
- }
- #endregion
-
- #region Update Group Associations
-
- SetUserGroups(userId, user.Groups.Where(x => x.IsAssociatedToUser).ToList());
-
- #endregion
-
- userIdResult = userId;
- return ret;
- }
-
- public LogEventResponse LogEventTime(Identifier identifier, out int logId)
- {
- var ret = new LogEventResponse();
- var cardIdQuery = _connection.Query(
- SQLiteProcedures.GET_CARDS_BY_UNIQUE_ID,
- identifier.UniqueId);
-
- #region Get/Insert the PK Id Identifier to associate the time log to.
- var ident = new CardUniqueId();
- if (!cardIdQuery.Any())
- {
- //new card, create it!
- ident.UserId_FK = -1;
- ident.CardUId = identifier.UniqueId;
- _connection.Insert(ident);
- UpdateIdentifierLastUsed(DateTimeOffset.UtcNow, ident.Id);
- logId = -1;
- //dont try to log any timelogs against this card, as it is unassigned to a user.
- ret.ProcessResponse=OperationResponse.SUCCESS;
- ret.Direction = LogDirection.UNKNOWN;
- return ret;
- }
- else
- {
- //TODO: log when more than one comes back. should NEVER happen but....
- ident = cardIdQuery.First();
- }
- #endregion
-
- // Get The User Direction (are they going in or out)?
- var logDirection = GetLogDirection(ident.UserId_FK);
-
- #region Check the user hasnt registered an event in the last few minutes..
-
- if (ident.UserId_FK != -1)
- { //only check log gap if the card is associated to a user
- var hysteresisThresholdMinutes = Convert.ToInt32(ConfigurationHandler.ConfigurationHandler.GetConfiguration("SwipeTimeGap") ?? "3");
- var threshold = DateTime.UtcNow.AddMinutes(0 - hysteresisThresholdMinutes);
- var logs = _connection.Query(
- SQLiteProcedures.GET_LOGS_IN_LAST_X_MINUTES,
- threshold.Ticks, ident.UserId_FK);
- _logger.Trace("Checking last swipe event gap");
- if (logs.Any())
- {
- _logger.Error("Not logging event for user id: {0}, logged event within TimeGap Threshold of {1}", ident.UserId_FK, threshold);
- logId = -1;
- ret.ProcessResponse = OperationResponse.FAILED;
- ret.Direction = LogDirection.UNKNOWN;
- return ret;
- }
- }
-
- #endregion
-
- #region Get the current time (for swiping). and calendar week/year to help recall the data.
- var logTime = DateTime.UtcNow;
- var calendarWeek = GetIso8601CalendarWeek(logTime);
- var year = logTime.Year;
- #endregion
-
- var timeLog = new TimeLogDb
- {
- SwipeEventDateTime = DateTime.UtcNow,
- UserId_FK = ident.UserId_FK,
- IdentifierId = ident.Id,
- Direction = logDirection,
- Year = year,
- CalendarWeek = calendarWeek,
- Source = LogSourceDb.IDENTIFIER
- };
-
- _connection.Insert(timeLog);
- UpdateIdentifierLastUsed(timeLog.SwipeEventDateTime, timeLog.IdentifierId);
-
- logId = timeLog.Id;
- ret.Direction = (LogDirection)(int)logDirection;
- ret.ProcessResponse = OperationResponse.SUCCESS;
- return ret;
- }
-
- /*Groups*/
- //TODO: check group name can only be entered once.
- public OperationResponse CreateGroup(Group group, out int groupId)
- {
- var groupDb = new GroupDb { GroupName = group.Name };
- var resp = _connection.Insert(groupDb);
- groupId = groupDb.GroupId;
- return OperationResponse.CREATED;
- }
-
- public List GetGroups(int userId = -1)
- {
- var ret = new List();
- List query;
- if (userId == -1)
- {
- query = _connection.Query("select gp.GroupId, gp.GroupName, " +
- "sum(case when gp.GroupId = ujdb.GroupId_FK then 1 else 0 end) as AssignedUserCount " +
- "from GroupDb gp " +
- "left join UserGroupJoinDb ujdb " +
- "on ujdb.GroupId_FK = gp.GroupId " +
- "group by gp.GroupId");
- }
- else
- {
- query =
- _connection.Query(
- "select gdb.GroupId, gdb.GroupName, gdb.AssignedUserCount" +
- " from GroupDb gdb" +
- " left join UserGroupJoinDb ujdb" +
- " on gdb.GroupId = ujdb.GroupId_FK" +
- " where ujdb.UserId_FK = ?",
- userId);
- }
- foreach (var group in query)
- {
- ret.Add(new Group
- {
- Id = group.GroupId,
- Name = group.GroupName,
- UserCount = int.Parse(group.AssignedUserCount ?? "0")
- });
- }
- return ret;
- }
-
- public Group GetGroup(int groupId)
- {
- var query = _connection.Query("select * from GroupDb where GroupId = ?", groupId);
- if (query.Any())
- {
- var group = query.First();
- return new Group
- {
- Id = group.GroupId,
- Name = group.GroupName,
- UserCount = 0
- };
- }
- return new Group();
- }
-
- public OperationResponse UpdateGroup(Group group)
- {
- //TODO: I would probably prefer to do this manually....
- var resp = _connection.Query("update GroupDb set GroupName=? where GroupId=?", group.Name, group.Id);
-
- return OperationResponse.UPDATED;
- }
-
- public OperationResponse DeleteGroup(int groupId)
- {
- _connection.Delete(groupId);
- return OperationResponse.DELETED;
- }
-
- public OperationResponse DeleteLog(TimeLog log)
- {
- var query = _connection.Query(
- "select * from TimeLogDb where Id=?", log.Id);
-
- if (!query.Any())
- return OperationResponse.FAILED;
-
- UpdateExistingLogDirections(log);
-
- _connection.ExecuteScalar("delete from TimeLogDb where Id=?", log.Id);
-
- return OperationResponse.DELETED;
- }
-
- public OperationResponse CreateLog(TimeLog log)
- {
- var calendarWeek = GetIso8601CalendarWeek(log.EventTime.UtcDateTime);
- var year = log.EventTime.Year;
- log.CalendarWeek = calendarWeek;
- log.Year = year;
- var dbLog = new TimeLogDb
- {
- SwipeEventDateTime = log.EventTime,
- Direction = (LogDirectionDb)(int)log.Direction,
- Year = year,
- CalendarWeek = calendarWeek,
- Source = (LogSourceDb)(int)log.Source,
- UserId_FK = log.UserId,
- IdentifierId = ConvertSourceToIdentifierId(log.Source),
- };
- #region update in/out directions for manual logs.
- UpdateExistingLogDirections(log);
- #endregion
- //and now insert the new log.
- _connection.Insert(dbLog);
- return OperationResponse.CREATED;
- }
-
- public OperationResponse UpdateLog(TimeLog log)
- {
- var query = _connection.Query(
- "select * from TimeLogDb where Id=?", log.Id);
- if(!query.Any())
- return OperationResponse.FAILED;
-
- if (log.CalendarWeek > 52 || log.CalendarWeek < 1)
- {
- log.CalendarWeek = GetIso8601CalendarWeek(log.EventTime.UtcDateTime);
- }
- if (log.Year < 2017)
- {
- log.Year = log.EventTime.Year;
- }
- _connection.ExecuteScalar(
- "update TimeLogDb set UserId_FK=?, Direction=?,SwipeEventDateTime=?,CalendarWeek=?,Year=?,Source=? where Id=?",
- log.UserId, (LogDirectionDb) (int) log.Direction, log.EventTime, log.CalendarWeek, log.Year,
- (LogSourceDb) (int) log.Source, log.Id);
-
- return OperationResponse.UPDATED;
- }
-
- private DateTime GetLastLogDateTime(TimeLogDb timeLog)
- {
- if (timeLog != null)
- {
- return timeLog.SwipeEventDateTime.DateTime;
- }
- return DateTime.MinValue;
- }
-
- private bool GetUserState(LogDirectionDb logDirection)
- {
- switch (logDirection)
- {
- case LogDirectionDb.OUT:
- return true;
- default:
- return false;
- }
- }
-
- private int ConvertSourceToIdentifierId(LogSource logSource)
- {
- switch (logSource)
- {
- case LogSource.UI:
- return -100;
- case LogSource.TRAYAPP:
- return -200;
- default:
- return -10;
- }
- }
-
- private bool SetUserGroups(int userId, List groups)
- {
- var groupIds = GetGroupIds(groups.Select(x => x.Name).ToList());
- return SetUserGroups(userId, groupIds);
- }
-
- private bool SetUserGroups(int userId, int[] groupIds)
- {
- //remove the existing user>group associations
- _connection.Query("delete from UserGroupJoinDb where UserId_FK = ?", userId);
- //add the new group associations.
- _connection.InsertAll(groupIds.Select(x => new UserGroupJoinDb { GroupId_FK = x, UserId_FK = userId }));
- return true;
- }
-
- private int[] GetGroupIds(List groupNames)
- {
- var ret = new List();
-
- foreach (var g in groupNames)
- {
- var query = _connection.Query("select GroupId from GroupDb where GroupName=?", g);
- if (!query.Any()) continue;
- var id = query.First();
- ret.Add(id.GroupId);
- }
- return ret.ToArray();
- }
-
- private List GetTimeLogList(int userId, int calendarWeek, int year)
- {
- var timeLogList = _connection.Query(
- SQLiteProcedures.GET_TIMELOGS,
- userId, calendarWeek, year);
-
- var timeLogs = timeLogList.Select(x => new TimeLog
- {
- Id = x.Id,
- CalendarWeek = x.CalendarWeek,
- Direction = (LogDirection)x.Direction,
- IdentifierId = x.IdentifierId,
- EventTime = x.SwipeEventDateTime,
- UserId = x.UserId_FK,
- Year = x.Year
- }).OrderBy(x=>x.EventTime.UtcDateTime).ToList();
-
- var dict = new Dictionary();
- var logList = new List();
-
- //make sure each day of the week is accounted for in the dictionary.
- foreach (DayOfWeek day in Enum.GetValues(typeof(DayOfWeek)))
- {
- dict.Add(day, new DailyLogs());
- }
-
- //add the logs to the respective day of the week.
- foreach (var log in timeLogs.OrderBy(x=>x.EventTime))
- {
- dict[log.EventTime.DayOfWeek].Logs.Add(log);
- }
- var logGroups = timeLogs.GroupBy(x => x.EventTime.DayOfWeek);
- foreach (var group in logGroups)
- {
- var groupLogs = group.ToList();
- var dailyLog = new DailyLogs
- {
- Logs = groupLogs,
- Day = group.Key,
- DayOfWeek = group.Key.ToString()
- };
- dailyLog.DailyTotal = CalculateDailyTotal(dailyLog);
- logList.Add(dailyLog);
- }
- foreach (DayOfWeek day in Enum.GetValues(typeof(DayOfWeek)))
- {
- if (logList.Any(x => x.Day == day)) continue;
- var dailyLog = new DailyLogs { Day = day, DayOfWeek = day.ToString() };
- logList.Add(dailyLog);
- }
-
- foreach (var dailyCollection in dict)
- {
- dailyCollection.Value.DailyTotal = CalculateDailyTotal(dailyCollection.Value);
- }
-
- return logList.OrderBy(x => ((int)x.Day + 6) % 7).ToList();
- }
-
- private double CalculateDailyTotal(DailyLogs dailyLogs)
- {
- var totalInTime = TimeSpan.FromSeconds(0);
- var logs = dailyLogs.Logs.OrderBy(x => x.EventTime.UtcDateTime).ToArray();
- var totalCalcMax = IsOdd(logs.Length) ? logs.Length - 1 : logs.Length;
- for (int i = 0; i < totalCalcMax; i += 2)
- {
- totalInTime += (logs[i + 1].EventTime - logs[i].EventTime);
- }
- return Math.Round(totalInTime.TotalMinutes, 2);
- }
-
- ///
- /// determines if the number is an odd or even value
- ///
- /// number to determine is odd or even
- /// true - number is odd
- private bool IsOdd(int value)
- {
- return value % 2 != 0;
- }
-
- ///
- /// Get the new direction for the user based on previous entry logs in the system.
- ///
- ///
- /// If the user has not logged in today, the direction will be In.
- /// If the user has logged in already today, the direction will be the opposite of the last
- /// recorded log direction. ("out" if "in", "in" if "out")
- ///
- /// Id of the user to get the log direction of.
- /// indicating what direction the new log is.
- private LogDirectionDb GetLogDirection(int userId)
- {
- var logDirection = LogDirectionDb.UNKNOWN;
- if (userId != -1)
- {
- var lastEntry = GetLastTimeLog(userId);
- if (lastEntry != null)
- {
- // See if the datetime retrieved is yesterday. If yesterday, logDirection = true (in)
- if (IsLogDateTimeYesterdayOrOlder(lastEntry.SwipeEventDateTime.DateTime))
- {
- logDirection = LogDirectionDb.IN;
- }
- else
- {
- // we have a time log from today already, so just do the opposite of what we last did!
- logDirection = InvertLogDirectionDb(lastEntry.Direction);
- }
- }
- else
- {
- //assume its the first then!
- logDirection = LogDirectionDb.IN;
- }
- }
- return logDirection;
- }
-
- private TimeLogDb GetLastTimeLog(int userId)
- {
- var lastEntry = _connection.Query(
- SQLiteProcedures.GET_LAST_TIMELOG_DIRECTION,
- userId);
- if (lastEntry.Any())
- {
- return lastEntry.First();
- }
- return null;
- }
-
- private void UpdateIdentifierLastUsed(DateTimeOffset dt, int cardId)
- {
- var res = _connection.ExecuteScalar(SQLiteProcedures.UPDATE_CARD_LAST_USED,
- dt,
- cardId);
- }
-
- private List GetAssociatedIdentifiers(int userId)
- {
- var cards = _connection.Query(
- SQLiteProcedures.GET_CARDS_BY_USER_ID,
- userId);
- var ret = new List();
- foreach (var card in cards)
- {
- ret.Add(new Identifier()
- {
- UniqueId = card.CardUId,
- IsAssociatedToUser = true,
- Id = card.Id
- });
- }
- return ret;
- }
-
- ///
- /// Get the calendar week of the year according to the ISO8601 standard (starts monday).
- ///
- /// the date to get the calendar week of.
- /// the calendar week of the year in integer form (1-52)
- private int GetIso8601CalendarWeek(DateTime date)
- {
- var day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(date);
- if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
- {
- date = date.AddDays(3);
- }
- return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek,
- DayOfWeek.Monday);
- }
-
- ///
- /// Check whether the specified DateTime is from yesterday or older.
- ///
- /// the DateTime object to check is yesterday or older
- /// true - is yesterday or older.
- private bool IsLogDateTimeYesterdayOrOlder(DateTime dt)
- {
- return dt.Date.CompareTo(DateTime.Today.Date) < 0;
- }
-
- private User ChangeToUserObject(UserIdentity user)
- {
- return new User
- {
- UserId = user.Id,
- FirstName = user.FirstName,
- LastName = user.LastName,
- HoursPerWeek = user.HoursPerWeek,
- IsContractor = user.IsContractor
- };
- }
-
- private void UpdateExistingLogDirections(TimeLog log)
- {
- //need to make this generic so that both create and delete will update the log directions.. but ARGH.
- //if you want to use this as a delete, you would delete and call this, if you want to create, call this then create.
- //should look at how to improve this method so that it will compensate for if the log hasnt been deleted yet, but ott?
- var weekLogs = GetTimeLogList(log.UserId, log.CalendarWeek, log.Year);
-
- //Get the same logs of the day that the log has been entered for
- var todaysLogs = weekLogs.FirstOrDefault(x => x.Day == log.EventTime.DayOfWeek);
- if (todaysLogs != null)
- {
- //Get the days logs that are after the manually created date
- var logs = todaysLogs.Logs.Where(x => x.EventTime.CompareTo(log.EventTime) >= 0).OrderBy(x => x.EventTime).ToList();
- //Update each log with the inverse progressively
- var currentlogDirection = log.Direction;
- for (var i = 0; i < logs.Count; i++)
- {
- logs[i].Direction = InvertLogDirection(currentlogDirection);
- UpdateLog(logs[i]);
- currentlogDirection = logs[i].Direction;
- }
- }
- }
-
- private LogDirectionDb InvertLogDirectionDb(LogDirectionDb direction)
- {
- return (LogDirectionDb) (int) InvertLogDirection((LogDirection) (int) direction);
- }
-
- private LogDirection InvertLogDirection(LogDirection direction)
- {
- switch (direction)
- {
- case LogDirection.IN:
- return LogDirection.OUT;
- case LogDirection.OUT:
- return LogDirection.IN;
- default:
- return LogDirection.UNKNOWN;
- }
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Reflection;
+using Interfaces;
+using SQLite.Net;
+using SQLite.Net.Platform.Win32;
+using SQLiteRepository.Properties;
+
+namespace SQLiteRepository
+{
+ public class SQLiteRepository : IRepository
+ {
+ private readonly SQLiteConnection _connection;
+ private readonly ILogger _logger;
+ private string _path = "flexitimedb.db";
+
+ public SQLiteRepository(ILogger logger)
+ {
+ _path =
+ new Uri(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().CodeBase), "flexitimedb.db"))
+ .LocalPath;
+ if (logger == null) throw new ArgumentNullException(nameof(logger));
+ _logger = logger;
+
+ _connection = new SQLiteConnection(new SQLitePlatformWin32(), _path);
+
+ _connection.CreateTable();
+ _connection.CreateTable();
+ _connection.CreateTable();
+ _connection.CreateTable();
+ _connection.CreateTable();
+ _connection.CreateTable();
+ _logger.Trace("Initialised SQLite Repository");
+ _logger.Trace("Checking For Upgrades");
+ CheckForDbUpgrade();
+ }
+
+ private void CheckForDbUpgrade()
+ {
+ var data = _connection.Query("select * from DbVersion");
+ if (!data.Any())
+ {
+ //Pre-Upgrade database, need upgrading
+ _logger.Trace("Pre version 0.2 RC database found, performing update..");
+ ExecuteUpgradeFromVersion("0.1"); //execute 0.2 upgrade scripts onwards.
+ }
+ else
+ {
+ var installedVersion = new Version(data.First().VersionNumber);
+ var currentVersion = new Version(AssemblyInfo.ASSEMBLY_VERSION);
+ if (currentVersion.CompareTo(installedVersion) > 0) //greater than 0 - current version is newer
+ {
+ _logger.Trace("Installed Database Version: {0} is older than current version {1}");
+ ExecuteUpgradeFromVersion(installedVersion.ToString());
+ }
+ }
+ }
+
+ private void ExecuteUpgradeFromVersion(string installedVersion)
+ {
+ const string PREFIX = "SQLiteRepository.UpgradeScripts.";
+ var instVers = new Version(installedVersion);
+ //so we have established that each script for upgrade will be .sql, so now start at lowest version and work up!
+ var assembly = Assembly.GetExecutingAssembly();
+ var upgradeScripts = assembly.GetManifestResourceNames()
+ .Where(x=>x.StartsWith(PREFIX))
+ .OrderBy(x=>new Version(Path.GetFileNameWithoutExtension(x.Replace(PREFIX, ""))))
+ .Where(x =>
+ {
+ var scriptVersion = new Version(Path.GetFileNameWithoutExtension(x.Replace(PREFIX, ""))).CompareTo(instVers);
+ return scriptVersion > 0;
+ }
+ )
+ .ToList();
+ //now have an ordered list of upgrade script files, so execute in order!
+ foreach (var upgradeScript in upgradeScripts)
+ {
+ using (var stream = assembly.GetManifestResourceStream(upgradeScript))
+ using(var str = new StreamReader(stream))
+ {
+ var script = str.ReadToEnd();
+ _logger.Trace("Executing upgrade script with name: {0}", upgradeScript);
+ _connection.Execute(script);
+ }
+ }
+ SetDbVersion(AssemblyInfo.ASSEMBLY_VERSION);
+ }
+
+ private void SetDbVersion(string vers)
+ {
+ _connection.DeleteAll();
+ _connection.Insert(new DbVersion {VersionNumber = vers});
+ _logger.Trace("Set Database version to: {0}", vers);
+ }
+
+ public UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1)
+ {
+ var ret = new UserList();
+ List users;
+ int userCount;
+ if (pageNumber != -1 && pageSize != -1)
+ {
+ users = _connection.Query(SQLiteProcedures.GET_ALL_USERS_PAGINATE,
+ pageSize, (pageNumber - 1) * pageSize);
+ userCount = _connection.ExecuteScalar(SQLiteProcedures.GET_TOTAL_USER_COUNT);
+ }
+ else if (groupId != -1)
+ {
+ users =
+ _connection.Query(
+ SQLiteProcedures.GET_ALL_USERS_BY_GROUP,
+ groupId);
+ userCount = users.Count;
+ }
+ else
+ {
+ users = _connection.Query(SQLiteProcedures.GET_ALL_USERS);
+ userCount = users.Count;
+ }
+
+ if (!users.Any())
+ {
+ if (pageNumber == -1 && pageSize == -1)
+ {
+ ret.PageNumber = 1;
+ ret.PageSize = 20;
+ }
+ else
+ {
+ ret.PageNumber = pageNumber;
+ ret.PageSize = pageSize;
+ }
+ return ret;
+ }
+
+ foreach (var user in users)
+ {
+ var userObj = ChangeToUserObject(user);
+
+ userObj.AssociatedIdentifiers = GetAssociatedIdentifiers(user.Id);
+ userObj.State = GetUserState(GetLogDirection(user.Id));
+ userObj.LastEventDateTime = GetLastLogDateTime(GetLastTimeLog(user.Id));
+ userObj.Groups = GetGroups(user.Id);
+ ret.Users.Add(userObj);
+ }
+ if (pageNumber == -1 && pageSize == -1)
+ {
+ ret.PageSize = 10;
+ ret.PageNumber = 1;
+ }
+ else
+ {
+ ret.PageSize = pageSize;
+ ret.PageNumber = pageNumber;
+ }
+ ret.TotalUserCount = userCount;
+ return ret;
+ }
+
+ public UserList Search(string searchParam)
+ {
+ _logger.Trace("Searching SQLite database for the term: {0}", searchParam);
+ var ret = new UserList();
+ searchParam = string.Format("%{0}%", searchParam);
+ var users = _connection.Query(
+ SQLiteProcedures.SEARCH_USER_LIST,
+ searchParam, searchParam);
+
+ _logger.Trace("Got {0} results for term: {1}", users.Count, searchParam);
+ if (!users.Any())
+ {
+ ret.PageNumber = 1;
+ ret.PageSize = 20;
+ return ret;
+ }
+
+ foreach (var user in users)
+ {
+ var userObj = ChangeToUserObject(user);
+ var cards = _connection.Query(
+ SQLiteProcedures.GET_CARDS_BY_USER_ID,
+ user.Id);
+
+ foreach (var card in cards)
+ {
+ userObj.AssociatedIdentifiers.Add(new Identifier()
+ {
+ UniqueId = card.CardUId,
+ IsAssociatedToUser = true,
+ Id = card.Id
+ });
+ }
+ userObj.State = GetUserState(GetLogDirection(user.Id));
+ ret.Users.Add(userObj);
+ }
+ //TODO: figure out paging here. - should there be any?
+ ret.PageSize = 20;
+ ret.PageNumber = 1;
+
+ return ret;
+ }
+
+ public User GetUser(int id)
+ {
+ var ret = new User();
+
+ try
+ {
+ var users = _connection.Query(
+ SQLiteProcedures.GET_USER_BY_ID,
+ id);
+
+ if (!users.Any()) return ret;
+
+ var user = users.First();
+ ret = ChangeToUserObject(user);
+ ret.Groups = GetGroups();
+ var usersGroups = GetGroups(user.Id);
+ foreach (var group in usersGroups)
+ {
+ ret.Groups.First(x => x.Id == group.Id).IsAssociatedToUser = true;
+ }
+ var cards = _connection.Query(
+ SQLiteProcedures.GET_CARDS_BY_USER_ID,
+ user.Id);
+
+ foreach (var card in cards)
+ {
+ ret.AssociatedIdentifiers.Add(new Identifier { UniqueId = card.CardUId, IsAssociatedToUser = true, Id = card.Id });
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "Error in GetUser with Id: {0}", id);
+ ret.UserId = id;
+ ret.FirstName = ret.LastName = string.Empty;
+ ret.HoursPerWeek = -1.0f;
+ ret.IsContractor = false;
+ ret.AssociatedIdentifiers.Clear();
+ }
+
+ return ret;
+ }
+
+ public TimeLogList GetTimeLogs(int userId)
+ {
+ return GetTimeLogs(userId, DateTime.UtcNow);
+ }
+
+ public TimeLogList GetTimeLogs(int userId, DateTime selectedDate)
+ {
+ var ret = new TimeLogList { SelectedDate = selectedDate.Date };
+ var calendarWeek = GetIso8601CalendarWeek(selectedDate);
+ ret.CalendarWeek = calendarWeek;
+
+ try
+ {
+ ret.TimeLogs = GetTimeLogList(userId, calendarWeek, selectedDate.Year);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "Error in GetTimeLogs with Id: {0} and selected date: {1}, Exception: {2}", userId, selectedDate, ex);
+ }
+
+ try
+ {
+ ret.HoursPerWeekMinutes = GetUserContractedHours(userId) * 60.0f;
+ }
+ catch (Exception 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;
+ }
+
+ private float GetUserContractedHours(int userId)
+ {
+ var hoursQuery = _connection.Query(SQLiteProcedures.GET_USER_CONTRACTED_HOURS, userId);
+ if (hoursQuery.Any())
+ {
+ return hoursQuery.First().HoursPerWeek;
+ }
+ return -1.0f;
+ }
+
+ public IdentifierList GetUnassignedIdentifierList()
+ {
+ var ret = new IdentifierList();
+ var cardQuery = _connection.Query(
+ SQLiteProcedures.GET_UNASSIGNED_CARD_LIST,
+ Constants.UNASSIGNED_CARD_USER_ID);
+
+ foreach (var card in cardQuery)
+ {
+ ret.data.Add(new Identifier
+ {
+ Id = card.Id,
+ IsAssociatedToUser = card.UserId_FK != Constants.UNASSIGNED_CARD_USER_ID,
+ UniqueId = card.CardUId,
+ LastUsed = card.LastUsed.DateTime
+ });
+ }
+ return ret;
+ }
+
+ public void ClearUnassignedIdentifiers()
+ {
+ _connection.Execute(
+ SQLiteProcedures.CLEAR_UNASSIGNED_CARDS,
+ Constants.UNASSIGNED_CARD_USER_ID);
+ }
+
+ //TODO: Check time logs table on update to ensure associated cards/unique identifiers are removed/added as appropriate.
+ public OperationResponse UpdateUser(User user, out int userIdResult)
+ {
+ //if(user.UserId <=0) return OperationResponse.FAILED;
+
+ var ret = OperationResponse.NONE;
+ var cardIds = new List();
+
+ //Get a list of current associated identifiers, convert into a list of Identifier Objects..
+ var currentCards =
+ _connection.Query(SQLiteProcedures.GET_CARDS_BY_USER_ID, user.UserId)
+ .Select(x => new Identifier { Id = x.Id, IsAssociatedToUser = true, UniqueId = x.CardUId });
+ var cardsToRemove = currentCards.Except(user.AssociatedIdentifiers).Select(x => x.Id).ToList();
+
+ #region GetUnique Identifiers
+ foreach (var card in user.AssociatedIdentifiers)
+ {
+ var existingCard = _connection.Query(
+ SQLiteProcedures.GET_CARDS_BY_UNIQUE_ID, card.UniqueId);
+
+ if (!existingCard.Any())
+ {
+ var cardInsert = new CardUniqueId { CardUId = card.UniqueId, UserId_FK = -1 };
+ _connection.Insert(cardInsert);
+ cardIds.Add(cardInsert.Id);
+ if (ret < OperationResponse.CREATED)
+ ret = OperationResponse.CREATED; //only change it if my status supercedes.
+ }
+ else
+ {
+ cardIds.Add(existingCard.First().Id);
+ if (ret < OperationResponse.UPDATED)
+ ret = OperationResponse.UPDATED;
+ }
+ }
+ #endregion
+
+ #region Update/Create User
+ int userId;
+
+ if (user.UserId != -1)
+ {
+ //edit..
+ _connection.Query(
+ SQLiteProcedures.UPDATE_USER_DETAILS,
+ user.FirstName,
+ user.LastName,
+ user.HoursPerWeek,
+ user.IsContractor,
+ user.UserId
+ );
+ userId = user.UserId;
+ }
+ else
+ {
+ var userInsert = new UserIdentity
+ {
+ FirstName = user.FirstName,
+ LastName = user.LastName,
+ HoursPerWeek = user.HoursPerWeek,
+ IsContractor = user.IsContractor
+ };
+ _connection.Insert(userInsert);
+ userId = userInsert.Id;
+ if (ret < OperationResponse.CREATED)
+ ret = OperationResponse.CREATED;
+ }
+ #endregion
+
+ #region Update Card/Unique Id entries.
+ foreach (var cardId in cardIds)
+ {
+ _connection.Query(
+ SQLiteProcedures.UPDATE_CARD_USER_ID,
+ userId, cardId);
+ }
+ foreach (var card in cardsToRemove)
+ {
+ _connection.Query(
+ SQLiteProcedures.UPDATE_CARD_USER_ID,
+ -1, card);
+ }
+ #endregion
+
+ #region Update Group Associations
+
+ SetUserGroups(userId, user.Groups.Where(x => x.IsAssociatedToUser).ToList());
+
+ #endregion
+
+ userIdResult = userId;
+ return ret;
+ }
+
+ public LogEventResponse LogEventTime(Identifier identifier, out int logId, DateTime logTime = default(DateTime))
+ {
+ #region Set the LogTime before we start querying anything.
+ if (logTime == default(DateTime))
+ {
+ logTime = DateTime.UtcNow;
+ _logger.Debug("Using own log time: {0}", logTime.ToString("o"));
+ }
+ else
+ {
+ _logger.Debug("Using supplied log time: {0}", logTime.ToString("o"));
+ }
+ #endregion
+
+ var ret = new LogEventResponse();
+ var cardIdQuery = _connection.Query(
+ SQLiteProcedures.GET_CARDS_BY_UNIQUE_ID,
+ identifier.UniqueId);
+
+ #region Get/Insert the PK Id Identifier to associate the time log to.
+ var ident = new CardUniqueId();
+ if (!cardIdQuery.Any())
+ {
+ //new card, create it!
+ ident.UserId_FK = -1;
+ ident.CardUId = identifier.UniqueId;
+ _connection.Insert(ident);
+ UpdateIdentifierLastUsed(DateTimeOffset.UtcNow, ident.Id);
+ logId = -1;
+ //dont try to log any timelogs against this card, as it is unassigned to a user.
+ ret.ProcessResponse=OperationResponse.SUCCESS;
+ ret.Direction = LogDirection.UNKNOWN;
+ return ret;
+ }
+ else
+ {
+ //TODO: log when more than one comes back. should NEVER happen but....
+ ident = cardIdQuery.First();
+ }
+ #endregion
+
+ // Get The User Direction (are they going in or out)?
+ var logDirection = GetLogDirection(ident.UserId_FK);
+
+ #region Check the user hasnt registered an event in the last few minutes..
+
+ if (ident.UserId_FK != -1)
+ { //only check log gap if the card is associated to a user
+ var hysteresisThresholdMinutes = Convert.ToInt32(ConfigurationHandler.ConfigurationHandler.GetConfiguration("SwipeTimeGap") ?? "3");
+ var threshold = DateTime.UtcNow.AddMinutes(0 - hysteresisThresholdMinutes);
+ var logs = _connection.Query(
+ SQLiteProcedures.GET_LOGS_IN_LAST_X_MINUTES,
+ threshold.Ticks, ident.UserId_FK);
+ _logger.Trace("Checking last swipe event gap");
+ if (logs.Any())
+ {
+ _logger.Error("Not logging event for user id: {0}, logged event within TimeGap Threshold of {1}", ident.UserId_FK, threshold);
+ logId = -1;
+ ret.ProcessResponse = OperationResponse.FAILED;
+ ret.Direction = LogDirection.UNKNOWN;
+ return ret;
+ }
+ }
+
+ #endregion
+
+ #region Get the current time (for swiping). and calendar week/year to help recall the data.
+
+ var calendarWeek = GetIso8601CalendarWeek(logTime);
+ var year = logTime.Year;
+ #endregion
+
+ var timeLog = new TimeLogDb
+ {
+ SwipeEventDateTime = DateTime.UtcNow,
+ UserId_FK = ident.UserId_FK,
+ IdentifierId = ident.Id,
+ Direction = logDirection,
+ Year = year,
+ CalendarWeek = calendarWeek,
+ Source = LogSourceDb.IDENTIFIER
+ };
+
+ _connection.Insert(timeLog);
+ UpdateIdentifierLastUsed(timeLog.SwipeEventDateTime, timeLog.IdentifierId);
+
+ logId = timeLog.Id;
+ ret.Direction = (LogDirection)(int)logDirection;
+ ret.ProcessResponse = OperationResponse.SUCCESS;
+ return ret;
+ }
+
+ /*Groups*/
+ //TODO: check group name can only be entered once.
+ public OperationResponse CreateGroup(Group group, out int groupId)
+ {
+ var groupDb = new GroupDb { GroupName = group.Name };
+ var resp = _connection.Insert(groupDb);
+ groupId = groupDb.GroupId;
+ return OperationResponse.CREATED;
+ }
+
+ public List GetGroups(int userId = -1)
+ {
+ var ret = new List();
+ List query;
+ if (userId == -1)
+ {
+ query = _connection.Query("select gp.GroupId, gp.GroupName, " +
+ "sum(case when gp.GroupId = ujdb.GroupId_FK then 1 else 0 end) as AssignedUserCount " +
+ "from GroupDb gp " +
+ "left join UserGroupJoinDb ujdb " +
+ "on ujdb.GroupId_FK = gp.GroupId " +
+ "group by gp.GroupId");
+ }
+ else
+ {
+ query =
+ _connection.Query(
+ "select gdb.GroupId, gdb.GroupName, gdb.AssignedUserCount" +
+ " from GroupDb gdb" +
+ " left join UserGroupJoinDb ujdb" +
+ " on gdb.GroupId = ujdb.GroupId_FK" +
+ " where ujdb.UserId_FK = ?",
+ userId);
+ }
+ foreach (var group in query)
+ {
+ ret.Add(new Group
+ {
+ Id = group.GroupId,
+ Name = group.GroupName,
+ UserCount = int.Parse(group.AssignedUserCount ?? "0")
+ });
+ }
+ return ret;
+ }
+
+ public Group GetGroup(int groupId)
+ {
+ var query = _connection.Query("select * from GroupDb where GroupId = ?", groupId);
+ if (query.Any())
+ {
+ var group = query.First();
+ return new Group
+ {
+ Id = group.GroupId,
+ Name = group.GroupName,
+ UserCount = 0
+ };
+ }
+ return new Group();
+ }
+
+ public OperationResponse UpdateGroup(Group group)
+ {
+ //TODO: I would probably prefer to do this manually....
+ var resp = _connection.Query("update GroupDb set GroupName=? where GroupId=?", group.Name, group.Id);
+
+ return OperationResponse.UPDATED;
+ }
+
+ public OperationResponse DeleteGroup(int groupId)
+ {
+ _connection.Delete(groupId);
+ return OperationResponse.DELETED;
+ }
+
+ public OperationResponse DeleteLog(TimeLog log)
+ {
+ var query = _connection.Query(
+ "select * from TimeLogDb where Id=?", log.Id);
+
+ if (!query.Any())
+ return OperationResponse.FAILED;
+
+ UpdateExistingLogDirections(log);
+
+ _connection.ExecuteScalar("delete from TimeLogDb where Id=?", log.Id);
+
+ return OperationResponse.DELETED;
+ }
+
+ public OperationResponse CreateLog(TimeLog log)
+ {
+ var calendarWeek = GetIso8601CalendarWeek(log.EventTime.UtcDateTime);
+ var year = log.EventTime.Year;
+ log.CalendarWeek = calendarWeek;
+ log.Year = year;
+ var dbLog = new TimeLogDb
+ {
+ SwipeEventDateTime = log.EventTime,
+ Direction = (LogDirectionDb)(int)log.Direction,
+ Year = year,
+ CalendarWeek = calendarWeek,
+ Source = (LogSourceDb)(int)log.Source,
+ UserId_FK = log.UserId,
+ IdentifierId = ConvertSourceToIdentifierId(log.Source),
+ };
+ #region update in/out directions for manual logs.
+ UpdateExistingLogDirections(log);
+ #endregion
+ //and now insert the new log.
+ _connection.Insert(dbLog);
+ return OperationResponse.CREATED;
+ }
+
+ public OperationResponse UpdateLog(TimeLog log)
+ {
+ var query = _connection.Query(
+ "select * from TimeLogDb where Id=?", log.Id);
+ if(!query.Any())
+ return OperationResponse.FAILED;
+
+ if (log.CalendarWeek > 52 || log.CalendarWeek < 1)
+ {
+ log.CalendarWeek = GetIso8601CalendarWeek(log.EventTime.UtcDateTime);
+ }
+ if (log.Year < 2017)
+ {
+ log.Year = log.EventTime.Year;
+ }
+ _connection.ExecuteScalar(
+ "update TimeLogDb set UserId_FK=?, Direction=?,SwipeEventDateTime=?,CalendarWeek=?,Year=?,Source=? where Id=?",
+ log.UserId, (LogDirectionDb) (int) log.Direction, log.EventTime, log.CalendarWeek, log.Year,
+ (LogSourceDb) (int) log.Source, log.Id);
+
+ return OperationResponse.UPDATED;
+ }
+
+ private DateTime GetLastLogDateTime(TimeLogDb timeLog)
+ {
+ if (timeLog != null)
+ {
+ return timeLog.SwipeEventDateTime.DateTime;
+ }
+ return DateTime.MinValue;
+ }
+
+ private bool GetUserState(LogDirectionDb logDirection)
+ {
+ switch (logDirection)
+ {
+ case LogDirectionDb.OUT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private int ConvertSourceToIdentifierId(LogSource logSource)
+ {
+ switch (logSource)
+ {
+ case LogSource.UI:
+ return -100;
+ case LogSource.TRAYAPP:
+ return -200;
+ default:
+ return -10;
+ }
+ }
+
+ private bool SetUserGroups(int userId, List groups)
+ {
+ var groupIds = GetGroupIds(groups.Select(x => x.Name).ToList());
+ return SetUserGroups(userId, groupIds);
+ }
+
+ private bool SetUserGroups(int userId, int[] groupIds)
+ {
+ //remove the existing user>group associations
+ _connection.Query("delete from UserGroupJoinDb where UserId_FK = ?", userId);
+ //add the new group associations.
+ _connection.InsertAll(groupIds.Select(x => new UserGroupJoinDb { GroupId_FK = x, UserId_FK = userId }));
+ return true;
+ }
+
+ private int[] GetGroupIds(List groupNames)
+ {
+ var ret = new List();
+
+ foreach (var g in groupNames)
+ {
+ var query = _connection.Query("select GroupId from GroupDb where GroupName=?", g);
+ if (!query.Any()) continue;
+ var id = query.First();
+ ret.Add(id.GroupId);
+ }
+ return ret.ToArray();
+ }
+
+ private List GetTimeLogList(int userId, int calendarWeek, int year)
+ {
+ var timeLogList = _connection.Query(
+ SQLiteProcedures.GET_TIMELOGS,
+ userId, calendarWeek, year);
+
+ var timeLogs = timeLogList.Select(x => new TimeLog
+ {
+ Id = x.Id,
+ CalendarWeek = x.CalendarWeek,
+ Direction = (LogDirection)x.Direction,
+ IdentifierId = x.IdentifierId,
+ EventTime = x.SwipeEventDateTime,
+ UserId = x.UserId_FK,
+ Year = x.Year
+ }).OrderBy(x=>x.EventTime.UtcDateTime).ToList();
+
+ var dict = new Dictionary();
+ var logList = new List();
+
+ //make sure each day of the week is accounted for in the dictionary.
+ foreach (DayOfWeek day in Enum.GetValues(typeof(DayOfWeek)))
+ {
+ dict.Add(day, new DailyLogs());
+ }
+
+ //add the logs to the respective day of the week.
+ foreach (var log in timeLogs.OrderBy(x=>x.EventTime))
+ {
+ dict[log.EventTime.DayOfWeek].Logs.Add(log);
+ }
+ var logGroups = timeLogs.GroupBy(x => x.EventTime.DayOfWeek);
+ foreach (var group in logGroups)
+ {
+ var groupLogs = group.ToList();
+ var dailyLog = new DailyLogs
+ {
+ Logs = groupLogs,
+ Day = group.Key,
+ DayOfWeek = group.Key.ToString()
+ };
+ dailyLog.DailyTotal = CalculateDailyTotal(dailyLog);
+ logList.Add(dailyLog);
+ }
+ foreach (DayOfWeek day in Enum.GetValues(typeof(DayOfWeek)))
+ {
+ if (logList.Any(x => x.Day == day)) continue;
+ var dailyLog = new DailyLogs { Day = day, DayOfWeek = day.ToString() };
+ logList.Add(dailyLog);
+ }
+
+ foreach (var dailyCollection in dict)
+ {
+ dailyCollection.Value.DailyTotal = CalculateDailyTotal(dailyCollection.Value);
+ }
+
+ return logList.OrderBy(x => ((int)x.Day + 6) % 7).ToList();
+ }
+
+ private double CalculateDailyTotal(DailyLogs dailyLogs)
+ {
+ var totalInTime = TimeSpan.FromSeconds(0);
+ var logs = dailyLogs.Logs.OrderBy(x => x.EventTime.UtcDateTime).ToArray();
+ var totalCalcMax = IsOdd(logs.Length) ? logs.Length - 1 : logs.Length;
+ for (int i = 0; i < totalCalcMax; i += 2)
+ {
+ totalInTime += (logs[i + 1].EventTime - logs[i].EventTime);
+ }
+ return Math.Round(totalInTime.TotalMinutes, 2);
+ }
+
+ ///
+ /// determines if the number is an odd or even value
+ ///
+ /// number to determine is odd or even
+ /// true - number is odd
+ private bool IsOdd(int value)
+ {
+ return value % 2 != 0;
+ }
+
+ ///
+ /// Get the new direction for the user based on previous entry logs in the system.
+ ///
+ ///
+ /// If the user has not logged in today, the direction will be In.
+ /// If the user has logged in already today, the direction will be the opposite of the last
+ /// recorded log direction. ("out" if "in", "in" if "out")
+ ///
+ /// Id of the user to get the log direction of.
+ /// indicating what direction the new log is.
+ private LogDirectionDb GetLogDirection(int userId)
+ {
+ var logDirection = LogDirectionDb.UNKNOWN;
+ if (userId != -1)
+ {
+ var lastEntry = GetLastTimeLog(userId);
+ if (lastEntry != null)
+ {
+ // See if the datetime retrieved is yesterday. If yesterday, logDirection = true (in)
+ if (IsLogDateTimeYesterdayOrOlder(lastEntry.SwipeEventDateTime.DateTime))
+ {
+ logDirection = LogDirectionDb.IN;
+ }
+ else
+ {
+ // we have a time log from today already, so just do the opposite of what we last did!
+ logDirection = InvertLogDirectionDb(lastEntry.Direction);
+ }
+ }
+ else
+ {
+ //assume its the first then!
+ logDirection = LogDirectionDb.IN;
+ }
+ }
+ return logDirection;
+ }
+
+ private TimeLogDb GetLastTimeLog(int userId)
+ {
+ var lastEntry = _connection.Query(
+ SQLiteProcedures.GET_LAST_TIMELOG_DIRECTION,
+ userId);
+ if (lastEntry.Any())
+ {
+ return lastEntry.First();
+ }
+ return null;
+ }
+
+ private void UpdateIdentifierLastUsed(DateTimeOffset dt, int cardId)
+ {
+ var res = _connection.ExecuteScalar(SQLiteProcedures.UPDATE_CARD_LAST_USED,
+ dt,
+ cardId);
+ }
+
+ private List GetAssociatedIdentifiers(int userId)
+ {
+ var cards = _connection.Query(
+ SQLiteProcedures.GET_CARDS_BY_USER_ID,
+ userId);
+ var ret = new List();
+ foreach (var card in cards)
+ {
+ ret.Add(new Identifier()
+ {
+ UniqueId = card.CardUId,
+ IsAssociatedToUser = true,
+ Id = card.Id
+ });
+ }
+ return ret;
+ }
+
+ ///
+ /// Get the calendar week of the year according to the ISO8601 standard (starts monday).
+ ///
+ /// the date to get the calendar week of.
+ /// the calendar week of the year in integer form (1-52)
+ private int GetIso8601CalendarWeek(DateTime date)
+ {
+ var day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(date);
+ if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
+ {
+ date = date.AddDays(3);
+ }
+ return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek,
+ DayOfWeek.Monday);
+ }
+
+ ///
+ /// Check whether the specified DateTime is from yesterday or older.
+ ///
+ /// the DateTime object to check is yesterday or older
+ /// true - is yesterday or older.
+ private bool IsLogDateTimeYesterdayOrOlder(DateTime dt)
+ {
+ return dt.Date.CompareTo(DateTime.Today.Date) < 0;
+ }
+
+ private User ChangeToUserObject(UserIdentity user)
+ {
+ return new User
+ {
+ UserId = user.Id,
+ FirstName = user.FirstName,
+ LastName = user.LastName,
+ HoursPerWeek = user.HoursPerWeek,
+ IsContractor = user.IsContractor
+ };
+ }
+
+ private void UpdateExistingLogDirections(TimeLog log)
+ {
+ //need to make this generic so that both create and delete will update the log directions.. but ARGH.
+ //if you want to use this as a delete, you would delete and call this, if you want to create, call this then create.
+ //should look at how to improve this method so that it will compensate for if the log hasnt been deleted yet, but ott?
+ var weekLogs = GetTimeLogList(log.UserId, log.CalendarWeek, log.Year);
+
+ //Get the same logs of the day that the log has been entered for
+ var todaysLogs = weekLogs.FirstOrDefault(x => x.Day == log.EventTime.DayOfWeek);
+ if (todaysLogs != null)
+ {
+ //Get the days logs that are after the manually created date
+ var logs = todaysLogs.Logs.Where(x => x.EventTime.CompareTo(log.EventTime) >= 0).OrderBy(x => x.EventTime).ToList();
+ //Update each log with the inverse progressively
+ var currentlogDirection = log.Direction;
+ for (var i = 0; i < logs.Count; i++)
+ {
+ logs[i].Direction = InvertLogDirection(currentlogDirection);
+ UpdateLog(logs[i]);
+ currentlogDirection = logs[i].Direction;
+ }
+ }
+ }
+
+ private LogDirectionDb InvertLogDirectionDb(LogDirectionDb direction)
+ {
+ return (LogDirectionDb) (int) InvertLogDirection((LogDirection) (int) direction);
+ }
+
+ private LogDirection InvertLogDirection(LogDirection direction)
+ {
+ switch (direction)
+ {
+ case LogDirection.IN:
+ return LogDirection.OUT;
+ case LogDirection.OUT:
+ return LogDirection.IN;
+ default:
+ return LogDirection.UNKNOWN;
+ }
+ }
+ }
+}
diff --git a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/CardData.cs b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/CardData.cs
index eee79f0..a019bea 100644
--- a/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/CardData.cs
+++ b/DataCenter_Windows/WindowsDataCenter/WindowsDataCenter/CardData.cs
@@ -1,7 +1,10 @@
-namespace WindowsDataCenter
-{
- public class CardData
- {
- public string CardUId { get; set; }
- }
+using System;
+
+namespace WindowsDataCenter
+{
+ public class CardData
+ {
+ public DateTime? UtcTimeStamp { get; set; }
+ public string CardUId { get; set; }
+ }
}
\ No newline at end of file