feat: expand cockpit integrations and support workflows
Add Phoenix remote desktop, action connector, documentation engine, and gamification support. Refactor support-case processing and search/action UI, and update installer assets and dependencies.
This commit is contained in:
13
F4SD-Gamification/CockpitAction.cs
Normal file
13
F4SD-Gamification/CockpitAction.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace F4SD.Gamification
|
||||
{
|
||||
public enum CockpitAction
|
||||
{
|
||||
CaseOpened,
|
||||
CaseClosed,
|
||||
QuickActionExecuted,
|
||||
CopyTemplateClicked,
|
||||
BuiltDirectConnection,
|
||||
AddNotes,
|
||||
StartRemoteConnection,
|
||||
}
|
||||
}
|
||||
12
F4SD-Gamification/F4SD-Gamification.csproj
Normal file
12
F4SD-Gamification/F4SD-Gamification.csproj
Normal file
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<RootNamespace>F4SD.Gamification</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
10
F4SD-Gamification/LevelEventArgs.cs
Normal file
10
F4SD-Gamification/LevelEventArgs.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace F4SD.Gamification
|
||||
{
|
||||
public class LevelEventArgs
|
||||
{
|
||||
public int CurrentLevel { get; set; }
|
||||
public string LevelTitle { get; set; }
|
||||
public int CurrentXp { get; set; }
|
||||
public int XpRequiredForLevelUp { get; set; }
|
||||
}
|
||||
}
|
||||
25
F4SD-Gamification/Services/GamificationService.cs
Normal file
25
F4SD-Gamification/Services/GamificationService.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace F4SD.Gamification.Services
|
||||
{
|
||||
public static class GamificationService
|
||||
{
|
||||
private static readonly LevelService _levelService = new();
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
PersistenceService.Initialize();
|
||||
|
||||
foreach (var action in PersistenceService.GetActions())
|
||||
{
|
||||
_levelService.InitializeAction(action.Action);
|
||||
}
|
||||
}
|
||||
|
||||
public static void TrackAction(CockpitAction action, params object[] args)
|
||||
{
|
||||
PersistenceService.Persist(action);
|
||||
_levelService.TrackAction(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
178
F4SD-Gamification/Services/LevelService.cs
Normal file
178
F4SD-Gamification/Services/LevelService.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace F4SD.Gamification.Services
|
||||
{
|
||||
public class LevelService
|
||||
{
|
||||
private static readonly List<(int MinimumTotalExperiencePoints, int Level)> _experienceToLevelMap = new()
|
||||
{
|
||||
(0,1),
|
||||
(1128,2),
|
||||
(2400,3),
|
||||
(3832,4),
|
||||
(5440,5),
|
||||
(7240,6),
|
||||
(9248,7),
|
||||
(11480,8),
|
||||
(13952,9),
|
||||
(16680,10),
|
||||
(19680,11),
|
||||
(22968,12),
|
||||
(26560,13),
|
||||
(30472,14),
|
||||
(34720,15),
|
||||
(39320,16),
|
||||
(44288,17),
|
||||
(49640,18),
|
||||
(55392,19),
|
||||
(61560,20),
|
||||
(68160,21),
|
||||
(75208,22),
|
||||
(82720,23),
|
||||
(90712,24),
|
||||
(99200,25),
|
||||
(108200,26),
|
||||
(117728,27),
|
||||
(127800,28),
|
||||
(138432,29),
|
||||
(149640,30),
|
||||
(161440,31),
|
||||
(173848,32),
|
||||
(186880,33),
|
||||
(200552,34),
|
||||
(214880,35),
|
||||
(229880,36),
|
||||
(245568,37),
|
||||
(261960,38),
|
||||
(279072,39),
|
||||
(296920,40),
|
||||
(315520,41),
|
||||
(334888,42),
|
||||
(355040,43),
|
||||
(375992,44),
|
||||
(397760,45),
|
||||
(420360,46),
|
||||
(443808,47),
|
||||
(468120,48),
|
||||
(493312,49),
|
||||
(519400,50),
|
||||
};
|
||||
|
||||
private int _totalExperiencePoints = 0;
|
||||
private int _currentExperiencePoints;
|
||||
|
||||
private int _level;
|
||||
|
||||
internal void InitializeAction(CockpitAction action)
|
||||
{
|
||||
AddExperiencePoints(GetExperiencePointsFor(action), true);
|
||||
}
|
||||
|
||||
internal void TrackAction(CockpitAction type, params object[] args)
|
||||
{
|
||||
AddExperiencePoints(GetExperiencePointsFor(type));
|
||||
}
|
||||
|
||||
private void AddExperiencePoints(int points, bool isInitialization = false)
|
||||
{
|
||||
_totalExperiencePoints += points;
|
||||
UpdateLevel(isInitialization);
|
||||
UpdateCurrentExperiencePoints();
|
||||
|
||||
if (isInitialization)
|
||||
return;
|
||||
|
||||
ExperiencePointsChanged?.Invoke(null, new LevelEventArgs()
|
||||
{
|
||||
CurrentLevel = _level,
|
||||
LevelTitle = GetTitle(_level),
|
||||
CurrentXp = _currentExperiencePoints,
|
||||
XpRequiredForLevelUp = GetXpRequiredForNextLevel()
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateCurrentExperiencePoints()
|
||||
{
|
||||
int xpForCurrentLevel = _experienceToLevelMap.FirstOrDefault(mapEntry => mapEntry.Level == _level).MinimumTotalExperiencePoints;
|
||||
_currentExperiencePoints = _totalExperiencePoints - xpForCurrentLevel;
|
||||
}
|
||||
|
||||
private void UpdateLevel(bool isInitialization)
|
||||
{
|
||||
int previousLevel = _level;
|
||||
_level = _experienceToLevelMap.FirstOrDefault(mapEntry => _totalExperiencePoints < mapEntry.MinimumTotalExperiencePoints).Level - 1;
|
||||
|
||||
if (!isInitialization && previousLevel < _level)
|
||||
LevelChanged?.Invoke(null, new LevelEventArgs() { CurrentLevel = _level, LevelTitle = GetTitle(_level) });
|
||||
}
|
||||
|
||||
private int GetXpRequiredForNextLevel()
|
||||
{
|
||||
int totalXpForNextLevel = _experienceToLevelMap.FirstOrDefault(mapEntry => mapEntry.Level == _level + 1).MinimumTotalExperiencePoints;
|
||||
int totalXpForCurrentLevel = _experienceToLevelMap.FirstOrDefault(mapEntry => mapEntry.Level == _level).MinimumTotalExperiencePoints;
|
||||
return totalXpForNextLevel - totalXpForCurrentLevel;
|
||||
}
|
||||
|
||||
private int GetExperiencePointsFor(CockpitAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CockpitAction.CaseOpened:
|
||||
break;
|
||||
case CockpitAction.CaseClosed:
|
||||
return 10;
|
||||
case CockpitAction.QuickActionExecuted:
|
||||
return 20;
|
||||
case CockpitAction.CopyTemplateClicked:
|
||||
return 5;
|
||||
case CockpitAction.BuiltDirectConnection:
|
||||
break;
|
||||
case CockpitAction.AddNotes:
|
||||
break;
|
||||
case CockpitAction.StartRemoteConnection:
|
||||
return 50;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static string GetTitle(int level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case <= 5:
|
||||
return "Support Trainee";
|
||||
case <= 10:
|
||||
return "Response Assistant";
|
||||
case <= 15:
|
||||
return "Service Aider";
|
||||
case <= 20:
|
||||
return "Ticket Resolver";
|
||||
case <= 25:
|
||||
return "Customer Navigator";
|
||||
case <= 30:
|
||||
return "Incident Coordinator";
|
||||
case <= 35:
|
||||
return "IT Guy";
|
||||
case <= 40:
|
||||
return "Support Specialist";
|
||||
case <= 45:
|
||||
return "Service Mentor";
|
||||
case <= 49:
|
||||
return "Operations Lead";
|
||||
case > 49:
|
||||
return "Ticket Titan";
|
||||
}
|
||||
}
|
||||
|
||||
public static event EventHandler<LevelEventArgs> ExperiencePointsChanged;
|
||||
public static event EventHandler<LevelEventArgs> LevelChanged;
|
||||
}
|
||||
}
|
||||
70
F4SD-Gamification/Services/PersistenceService.cs
Normal file
70
F4SD-Gamification/Services/PersistenceService.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace F4SD.Gamification.Services
|
||||
{
|
||||
internal static class PersistenceService
|
||||
{
|
||||
private static readonly string _databaseName = "F4SD-gmfc.txt";
|
||||
private static readonly string _directory = $@"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}\Consulting4IT GmbH\C4IT First Aid Service Desk";
|
||||
|
||||
private static string GetConnectionString()
|
||||
{
|
||||
return Path.Combine(_directory, _databaseName);
|
||||
}
|
||||
|
||||
internal static void Persist(CockpitAction action)
|
||||
{
|
||||
Initialize();
|
||||
|
||||
using var writer = File.AppendText(GetConnectionString());
|
||||
string actionLine = $"{DateTime.UtcNow},{action}";
|
||||
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(actionLine));
|
||||
writer.WriteLine(base64);
|
||||
}
|
||||
|
||||
internal static void Initialize()
|
||||
{
|
||||
Directory.CreateDirectory(_directory);
|
||||
|
||||
if (!File.Exists(GetConnectionString()))
|
||||
{
|
||||
FileStream createdFile = File.Create(GetConnectionString());
|
||||
File.SetAttributes(GetConnectionString(), FileAttributes.Hidden);
|
||||
createdFile.Close();
|
||||
}
|
||||
}
|
||||
|
||||
internal static IEnumerable<(DateTime Time, CockpitAction Action)> GetActions()
|
||||
{
|
||||
using StreamReader reader = new(GetConnectionString());
|
||||
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
string encodedLine = string.Empty;
|
||||
|
||||
try { encodedLine = Encoding.UTF8.GetString(Convert.FromBase64String(line)); }
|
||||
catch { continue; }
|
||||
|
||||
if (string.IsNullOrWhiteSpace(encodedLine))
|
||||
continue;
|
||||
|
||||
string[] splittedLine = encodedLine.Split(',');
|
||||
|
||||
if (splittedLine.Length < 2)
|
||||
continue;
|
||||
|
||||
if (!DateTime.TryParse(splittedLine[0], out var date))
|
||||
continue;
|
||||
|
||||
if (!(Enum.TryParse(splittedLine[1], out CockpitAction action)))
|
||||
continue;
|
||||
|
||||
yield return new(date, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user