code has been split into smaller methods, and queries on db have been optimised to make fewer, larger queries than lots of small queries. (using IN sql queries). restructured UpdateUser to be more logical which has reduced some of the complexity of the method. When deleting unassociated cards, the system will ensure all timelogs associated to those Ids are deleted also. CreateGroup will now not allow duplicate groups, but will return the ID of the existing Group found in the DB. pulled out GetUserCount method to simplify the GetUsers method. removed empty quotes in favour of string.empty #95
891 lines
33 KiB
C#
891 lines
33 KiB
C#
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.Converters;
|
|
using SQLiteRepository.Properties;
|
|
|
|
namespace SQLiteRepository
|
|
{
|
|
public class SQLiteRepository : IRepository
|
|
{
|
|
private readonly SQLiteConnection _connection;
|
|
private readonly ILogger _logger;
|
|
private readonly string _path;
|
|
private const string DATABASE_NAME = "flexitimedb.db";
|
|
private const string UPGRADE_SCRIPT_PREFIX = "SQLiteRepository.UpgradeScripts.";
|
|
|
|
public SQLiteRepository(ILogger logger)
|
|
{
|
|
_path =
|
|
new Uri(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().CodeBase), DATABASE_NAME))
|
|
.LocalPath;
|
|
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
|
_logger = logger;
|
|
|
|
_connection = new SQLiteConnection(new SQLitePlatformWin32(), _path);
|
|
|
|
_connection.CreateTable<CardUniqueId>();
|
|
_connection.CreateTable<UserIdentity>();
|
|
_connection.CreateTable<TimeLogDb>();
|
|
_connection.CreateTable<GroupDb>();
|
|
_connection.CreateTable<UserGroupJoinDb>();
|
|
_connection.CreateTable<DbVersion>();
|
|
_logger.Trace("Initialised SQLite Repository");
|
|
_logger.Trace("Checking For Upgrades");
|
|
CheckForDbUpgrade();
|
|
}
|
|
|
|
public UserList GetUsers(int pageNumber = -1, int pageSize = -1, int groupId = -1)
|
|
{
|
|
var ret = new UserList();
|
|
List<UserIdentity> users = GetUserList(groupId, pageSize, pageNumber);
|
|
|
|
var userCount = GetUserCount();
|
|
|
|
if (pageNumber == -1 && pageSize == -1)
|
|
{
|
|
ret.PageNumber = 1;
|
|
ret.PageSize = 20;
|
|
}
|
|
else
|
|
{
|
|
ret.PageNumber = pageNumber;
|
|
ret.PageSize = pageSize;
|
|
}
|
|
|
|
if (!users.Any())
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
foreach (var user in users)
|
|
{
|
|
var userObj = UserConverter.ConvertToUserDto(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);
|
|
}
|
|
|
|
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<UserIdentity>(
|
|
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 = UserConverter.ConvertToUserDto(user);
|
|
var cards = _connection.Query<CardUniqueId>(
|
|
SQLiteProcedures.GET_CARDS_BY_USER_ID,
|
|
user.Id);
|
|
|
|
foreach (var card in cards)
|
|
{
|
|
userObj.AssociatedIdentifiers.Add(IdentifierConverter.ConvertToIdentifierDto(card));
|
|
}
|
|
userObj.State = GetUserState(GetLogDirection(user.Id));
|
|
ret.Users.Add(userObj);
|
|
}
|
|
ret.PageSize = 20;
|
|
ret.PageNumber = (int)Math.Ceiling((double)ret.Users.Count/(double)ret.PageSize);
|
|
|
|
return ret;
|
|
}
|
|
|
|
public User GetUser(int id)
|
|
{
|
|
var ret = new User();
|
|
|
|
try
|
|
{
|
|
var users = _connection.Query<UserIdentity>(
|
|
SQLiteProcedures.GET_USER_BY_ID,
|
|
id);
|
|
|
|
if (!users.Any()) return ret;
|
|
|
|
var user = users.First();
|
|
ret = UserConverter.ConvertToUserDto(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<CardUniqueId>(
|
|
SQLiteProcedures.GET_CARDS_BY_USER_ID,
|
|
user.Id);
|
|
|
|
foreach (var card in cards)
|
|
{
|
|
ret.AssociatedIdentifiers.Add(IdentifierConverter.ConvertToIdentifierDto(card));
|
|
}
|
|
}
|
|
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<UserIdentity>(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<CardUniqueId>(
|
|
SQLiteProcedures.GET_UNASSIGNED_CARD_LIST,
|
|
Constants.UNASSIGNED_CARD_USER_ID);
|
|
|
|
foreach (var card in cardQuery)
|
|
{
|
|
ret.data.Add(IdentifierConverter.ConvertToIdentifierDto(card));
|
|
// 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()
|
|
{
|
|
var unassignedIdentifiers = _connection.Query<CardUniqueId>(SQLiteProcedures.GET_UNASSIGNED_CARD_LIST);
|
|
|
|
//remove the logs from the timelog db
|
|
_connection.Query<TimeLogDb>(
|
|
SQLiteProcedures.FormatInQuery(SQLiteProcedures.DELETE_TIMELOG_ENTRIES,
|
|
unassignedIdentifiers.Select(x => x.Id.ToString()).ToList()));
|
|
|
|
//now remove the unassigned identifiers/cards
|
|
_connection.Execute(
|
|
SQLiteProcedures.CLEAR_UNASSIGNED_CARDS,
|
|
Constants.UNASSIGNED_CARD_USER_ID);
|
|
}
|
|
|
|
public OperationResponse UpdateUser(User user, out int userIdResult)
|
|
{
|
|
var ret = OperationResponse.NONE;
|
|
|
|
//Get a list of current associated identifiers, convert into a list of Identifier Objects..
|
|
var currentCards =
|
|
_connection.Query<CardUniqueId>(SQLiteProcedures.GET_CARDS_BY_USER_ID, user.UserId)
|
|
.Select(IdentifierConverter.ConvertToIdentifierDto);
|
|
var cardsToRemove = currentCards.Except(user.AssociatedIdentifiers).Select(x => x.Id).ToList();
|
|
|
|
#region Update/Create User
|
|
int userId;
|
|
|
|
if (user.UserId != -1)
|
|
{
|
|
UpdateUserDetails(user.FirstName, user.LastName, user.HoursPerWeek, user.IsContractor, user.UserId);
|
|
userId = user.UserId;
|
|
}
|
|
else
|
|
{
|
|
var userInsert = UserConverter.ConvertFromUserDto(user);
|
|
_connection.Insert(userInsert);
|
|
userId = userInsert.Id;
|
|
if (ret < OperationResponse.CREATED)
|
|
ret = OperationResponse.CREATED;
|
|
}
|
|
#endregion
|
|
|
|
#region GetUnique Identifiers
|
|
|
|
var existingCards = _connection.Query<CardUniqueId>(SQLiteProcedures.FormatInQuery(
|
|
SQLiteProcedures.GET_CARDS_BY_UNIQUE_ID_LIST,
|
|
user.AssociatedIdentifiers.Select(x => x.UniqueId.ToString()).ToList()));
|
|
|
|
foreach (var card in user.AssociatedIdentifiers)
|
|
{
|
|
if (existingCards.All(x => x.CardUId != card.UniqueId))
|
|
{
|
|
//this card is not currently in the system..
|
|
var cardInsert = IdentifierConverter.ConvertFromIdentifierDto(card, user.UserId);
|
|
_connection.Insert(cardInsert);
|
|
existingCards.Add(cardInsert);
|
|
if (ret < OperationResponse.CREATED)
|
|
ret = OperationResponse.CREATED; //only change it if my status supercedes.
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Update Card/Unique Id entries/associations
|
|
//make sure all identifiers are associated to the right user
|
|
UpdateIdentifierUserId(existingCards.Select(x=>x.Id).ToList(), userId);
|
|
|
|
//make sure to remove all identifiers that have been deselected in edit are removed from user
|
|
UpdateIdentifierUserId(cardsToRemove, Constants.UNASSIGNED_CARD_USER_ID);
|
|
#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<CardUniqueId>(
|
|
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;
|
|
}
|
|
|
|
ident = cardIdQuery.First();
|
|
_logger.Warn("More than 1 Identifier returned with ID {0}, card ids returned: {2}",
|
|
ident.CardUId,
|
|
string.Join(",", cardIdQuery.Select(x => x.CardUId).ToList()));
|
|
#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<TimeLogDb>(
|
|
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;
|
|
}
|
|
|
|
public OperationResponse CreateGroup(Group group, out int groupId)
|
|
{
|
|
var query = _connection.Query<GroupDb>(SQLiteProcedures.GET_GROUP_BY_NAME, group.Name);
|
|
if (query.Any())
|
|
{
|
|
groupId = query[0].GroupId;
|
|
return OperationResponse.NONE;
|
|
}
|
|
var groupDb = new GroupDb { GroupName = group.Name };
|
|
var resp = _connection.Insert(groupDb);
|
|
groupId = groupDb.GroupId;
|
|
return OperationResponse.CREATED;
|
|
}
|
|
|
|
public List<Group> GetGroups(int userId = -1)
|
|
{
|
|
var ret = new List<Group>();
|
|
List<GroupDb> query;
|
|
if (userId == -1)
|
|
{
|
|
query = _connection.Query<GroupDb>(SQLiteProcedures.GET_GROUPS);
|
|
}
|
|
else
|
|
{
|
|
query =
|
|
_connection.Query<GroupDb>(SQLiteProcedures.GET_GROUPS_FOR_USER,
|
|
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<GroupDb>(SQLiteProcedures.GET_GROUP_BY_ID, 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)
|
|
{
|
|
_connection.Query<GroupDb>(SQLiteProcedures.UPDATE_GROUP, @group.Name, @group.Id);
|
|
|
|
return OperationResponse.UPDATED;
|
|
}
|
|
|
|
public OperationResponse DeleteGroup(int groupId)
|
|
{
|
|
_connection.Delete<GroupDb>(groupId);
|
|
return OperationResponse.DELETED;
|
|
}
|
|
|
|
public OperationResponse DeleteLog(TimeLog log)
|
|
{
|
|
var query = _connection.Query<TimeLogDb>(
|
|
SQLiteProcedures.GET_TIMELOG_ENTRY, log.Id);
|
|
|
|
if (!query.Any())
|
|
return OperationResponse.FAILED;
|
|
|
|
UpdateExistingLogDirections(log);
|
|
|
|
_connection.ExecuteScalar<TimeLogDb>(SQLiteProcedures.DELETE_TIMELOG_ENTRY, log.Id);
|
|
|
|
return OperationResponse.DELETED;
|
|
}
|
|
|
|
public OperationResponse CreateLog(TimeLog log)
|
|
{
|
|
log.CalendarWeek = GetIso8601CalendarWeek(log.EventTime.UtcDateTime);
|
|
log.Year= log.EventTime.Year;
|
|
|
|
var dbLog = TimeLogConverter.ConvertFromTimeLogDto(log);
|
|
dbLog.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<TimeLogDb>(
|
|
SQLiteProcedures.GET_TIMELOG_ENTRY, 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<TimeLogDb>(
|
|
SQLiteProcedures.UPDATE_TIMELOG_ENTRY,
|
|
log.UserId, (LogDirectionDb) (int) log.Direction, log.EventTime, log.CalendarWeek, log.Year,
|
|
(LogSourceDb) (int) log.Source, log.Id);
|
|
|
|
return OperationResponse.UPDATED;
|
|
}
|
|
|
|
private int GetUserCount()
|
|
{
|
|
return _connection.ExecuteScalar<int>(SQLiteProcedures.GET_TOTAL_USER_COUNT);
|
|
}
|
|
|
|
private List<UserIdentity> GetUserList(int groupId = -1, int pageSize=-1, int pageNumber=-1)
|
|
{
|
|
List<UserIdentity> users;
|
|
if (pageNumber != -1 && pageSize != -1)
|
|
{
|
|
users = _connection.Query<UserIdentity>(SQLiteProcedures.GET_ALL_USERS_PAGINATE,
|
|
pageSize, (pageNumber - 1) * pageSize);
|
|
}
|
|
else if (groupId != -1)
|
|
{
|
|
users =
|
|
_connection.Query<UserIdentity>(
|
|
SQLiteProcedures.GET_ALL_USERS_BY_GROUP,
|
|
groupId);
|
|
}
|
|
else
|
|
{
|
|
users = _connection.Query<UserIdentity>(SQLiteProcedures.GET_ALL_USERS);
|
|
}
|
|
|
|
return users;
|
|
}
|
|
|
|
private void CheckForDbUpgrade()
|
|
{
|
|
var data = _connection.Query<DbVersion>(SQLiteProcedures.GET_DB_VERSION);
|
|
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)
|
|
{
|
|
var instVers = new Version(installedVersion);
|
|
//so we have established that each script for upgrade will be <version>.sql, so now start at lowest version and work up!
|
|
var assembly = Assembly.GetExecutingAssembly();
|
|
var upgradeScripts = assembly.GetManifestResourceNames()
|
|
.Where(x => x.StartsWith(UPGRADE_SCRIPT_PREFIX))
|
|
.OrderBy(x =>
|
|
new Version(Path.GetFileNameWithoutExtension(x.Replace(UPGRADE_SCRIPT_PREFIX, string.Empty))))
|
|
.Where(x =>
|
|
{
|
|
var scriptVersion =
|
|
new Version(Path.GetFileNameWithoutExtension(x.Replace(UPGRADE_SCRIPT_PREFIX, string.Empty)))
|
|
.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<DbVersion>();
|
|
_connection.Insert(new DbVersion {VersionNumber = vers});
|
|
_logger.Trace("Set Database version to: {0}", vers);
|
|
}
|
|
|
|
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<Group> 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<UserGroupJoinDb>("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<string> groupNames)
|
|
{
|
|
var ret = new List<int>();
|
|
|
|
foreach (var g in groupNames)
|
|
{
|
|
var query = _connection.Query<GroupDb>("select GroupId from GroupDb where GroupName=?", g);
|
|
if (!query.Any()) continue;
|
|
var id = query.First();
|
|
ret.Add(id.GroupId);
|
|
}
|
|
return ret.ToArray();
|
|
}
|
|
|
|
private List<DailyLogs> GetTimeLogList(int userId, int calendarWeek, int year)
|
|
{
|
|
var timeLogList = _connection.Query<TimeLogDb>(
|
|
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<DayOfWeek, DailyLogs>();
|
|
var logList = new List<DailyLogs>();
|
|
|
|
//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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// determines if the number is an odd or even value
|
|
/// </summary>
|
|
/// <param name="value">number to determine is odd or even</param>
|
|
/// <returns>true - number is odd</returns>
|
|
private bool IsOdd(int value)
|
|
{
|
|
return value % 2 != 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the new direction for the user based on previous entry logs in the system.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 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")
|
|
/// </remarks>
|
|
/// <param name="userId">Id of the user to get the log direction of.</param>
|
|
/// <returns><see cref="LogDirectionDb"/> indicating what direction the new log is.</returns>
|
|
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 = LogDirectionConverter.InvertLogDirectionDb(lastEntry.Direction);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//assume its the first then!
|
|
logDirection = LogDirectionDb.IN;
|
|
}
|
|
}
|
|
return logDirection;
|
|
}
|
|
|
|
private TimeLogDb GetLastTimeLog(int userId)
|
|
{
|
|
var lastEntry = _connection.Query<TimeLogDb>(
|
|
SQLiteProcedures.GET_LAST_TIMELOG_DIRECTION,
|
|
userId);
|
|
if (lastEntry.Any())
|
|
{
|
|
return lastEntry.First();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void UpdateIdentifierLastUsed(DateTimeOffset dt, int cardId)
|
|
{
|
|
_connection.ExecuteScalar<CardUniqueId>(SQLiteProcedures.UPDATE_CARD_LAST_USED,
|
|
dt,
|
|
cardId);
|
|
}
|
|
|
|
private List<Identifier> GetAssociatedIdentifiers(int userId)
|
|
{
|
|
var cards = _connection.Query<CardUniqueId>(
|
|
SQLiteProcedures.GET_CARDS_BY_USER_ID,
|
|
userId);
|
|
var ret = new List<Identifier>();
|
|
foreach (var card in cards)
|
|
{
|
|
ret.Add(new Identifier()
|
|
{
|
|
UniqueId = card.CardUId,
|
|
IsAssociatedToUser = true,
|
|
Id = card.Id
|
|
});
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the calendar week of the year according to the ISO8601 standard (starts monday).
|
|
/// </summary>
|
|
/// <param name="date">the date to get the calendar week of.</param>
|
|
/// <returns>the calendar week of the year in integer form (1-52)</returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check whether the specified DateTime is from yesterday or older.
|
|
/// </summary>
|
|
/// <param name="dt">the DateTime object to check is yesterday or older</param>
|
|
/// <returns>true - is yesterday or older.</returns>
|
|
private bool IsLogDateTimeYesterdayOrOlder(DateTime dt)
|
|
{
|
|
return dt.Date.CompareTo(DateTime.Today.Date) < 0;
|
|
}
|
|
|
|
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 = LogDirectionConverter.InvertLogDirection(currentlogDirection);
|
|
UpdateLog(logs[i]);
|
|
currentlogDirection = logs[i].Direction;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateIdentifierUserId(List<int> cardsToUpdate, int userId)
|
|
{
|
|
foreach (var card in cardsToUpdate)
|
|
{
|
|
_connection.Query<CardUniqueId>(
|
|
SQLiteProcedures.UPDATE_CARD_USER_ID,
|
|
userId, card);
|
|
}
|
|
}
|
|
|
|
private void UpdateUserDetails(string firstName, string lastName, float hoursPerWeek, bool isContractor,
|
|
int userId)
|
|
{
|
|
_connection.Query<UserIdentity>(
|
|
SQLiteProcedures.UPDATE_USER_DETAILS,
|
|
firstName,
|
|
lastName,
|
|
hoursPerWeek,
|
|
isContractor,
|
|
userId
|
|
);
|
|
}
|
|
|
|
}
|
|
}
|