Skip to content

Commit

Permalink
initial implementation of xapi (#52)
Browse files Browse the repository at this point in the history
  • Loading branch information
sei-awelle authored May 29, 2024
1 parent 6ada57c commit 6637463
Show file tree
Hide file tree
Showing 13 changed files with 634 additions and 44 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ obj/
appsettings.Development.json
.vscode/
Cite.Api.Client/node_modules
Cite.Api.Client/.config
Cite.Api.Client/.config
cite_api.db
cite_api.db*
11 changes: 6 additions & 5 deletions Cite.Api/Cite.Api.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<Version>1.6.1</Version>
<Version>1.6.2</Version>
<TargetFramework>net6.0</TargetFramework>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
<NoWarn>CS1591</NoWarn>
Expand All @@ -17,12 +17,12 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="AutoMapper" Version="10.1.1"/>
<PackageReference Include="AutoMapper" Version="10.1.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
<PackageReference Include="MediatR" Version="9.0.0"/>
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0"/>
<PackageReference Include="MediatR" Version="9.0.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.20.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.1"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand All @@ -44,6 +44,7 @@
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.4.0" />
<PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="6.13.6" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="TinCan" Version="1.3.0" />
</ItemGroup>

<ItemGroup>
Expand Down
8 changes: 6 additions & 2 deletions Cite.Api/Infrastructure/EventHandlers/SubmissionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@ public SubmissionCreatedSignalRHandler(
IMapper mapper,
ISubmissionService submissionService,
IHubContext<MainHub> mainHub,
DatabaseOptions options) : base(db, mapper, submissionService, mainHub, options) { }
DatabaseOptions options) : base(db, mapper, submissionService, mainHub, options)
{
}

public async Task Handle(EntityCreated<SubmissionEntity> notification, CancellationToken cancellationToken)
{
Expand All @@ -156,7 +158,9 @@ public SubmissionUpdatedSignalRHandler(
IMapper mapper,
ISubmissionService submissionService,
IHubContext<MainHub> mainHub,
DatabaseOptions options) : base(db, mapper, submissionService, mainHub, options) { }
DatabaseOptions options) : base(db, mapper, submissionService, mainHub, options)
{
}

public async Task Handle(EntityUpdated<SubmissionEntity> notification, CancellationToken cancellationToken)
{
Expand Down
22 changes: 0 additions & 22 deletions Cite.Api/Infrastructure/Options/VmProcessingOptions.cs

This file was deleted.

20 changes: 20 additions & 0 deletions Cite.Api/Infrastructure/Options/XApiOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2022 Carnegie Mellon University. All Rights Reserved.
// Released under a MIT (SEI)-style license, please see LICENSE.md in the project root for license information or contact [email protected] for full terms.

using System;
using System.Collections.Generic;

namespace Cite.Api.Infrastructure.Options
{
public class XApiOptions
{
public string Endpoint { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string IssuerUrl { get; set; }
public string ApiUrl { get; set; }
public string UiUrl { get; set; }
public string EmailDomain { get; set; }
public string Platform { get; set; }
}
}
57 changes: 56 additions & 1 deletion Cite.Api/Services/ActionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public interface IActionService
Task<ViewModels.Action> UpdateAsync(Guid id, ViewModels.Action action, CancellationToken ct);
Task<ViewModels.Action> SetIsCheckedAsync(Guid id, bool value, CancellationToken ct);
Task<bool> DeleteAsync(Guid id, CancellationToken ct);
Task<bool> LogXApiAsync(Uri verb, ActionEntity action, CancellationToken ct);
}

public class ActionService : IActionService
Expand All @@ -39,20 +40,23 @@ public class ActionService : IActionService
private readonly IAuthorizationService _authorizationService;
private readonly ClaimsPrincipal _user;
private readonly IMapper _mapper;
private readonly DatabaseOptions _options;
private readonly DatabaseOptions _options;
private readonly IXApiService _xApiService;

public ActionService(
CiteContext context,
IAuthorizationService authorizationService,
IPrincipal user,
IMapper mapper,
IXApiService xApiService,
DatabaseOptions options)
{
_context = context;
_authorizationService = authorizationService;
_user = user as ClaimsPrincipal;
_mapper = mapper;
_options = options;
_xApiService = xApiService;
}

public async Task<IEnumerable<ViewModels.Action>> GetByEvaluationTeamAsync(Guid evaluationId, Guid teamId, CancellationToken ct)
Expand Down Expand Up @@ -182,6 +186,14 @@ public ActionService(
actionToUpdate.ChangedBy = _user.GetId();
await _context.SaveChangesAsync(ct);

// create and send xapi statement
var verb = new Uri("https://w3id.org/xapi/dod-isd/verbs/completed");
if (!value) {
verb = new Uri("https://w3id.org/xapi/dod-isd/verbs/reset");
}

await LogXApiAsync(verb, actionToUpdate, ct);

return _mapper.Map<ViewModels.Action>(actionToUpdate);
}

Expand All @@ -204,6 +216,49 @@ public async Task<bool> DeleteAsync(Guid id, CancellationToken ct)

return true;
}
public async Task<bool> LogXApiAsync(Uri verb, ActionEntity action, CancellationToken ct)
{

if (_xApiService.IsConfigured())
{
var evaluation = await _context.Evaluations.Where(e => e.Id == action.EvaluationId).FirstAsync();
var move = await _context.Moves.Where(m => m.MoveNumber == evaluation.CurrentMoveNumber).FirstAsync();

var teamId = (await _context.TeamUsers
.SingleOrDefaultAsync(tu => tu.UserId == _user.GetId() && tu.Team.EvaluationId == action.EvaluationId)).TeamId;

var activity = new Dictionary<String,String>();
activity.Add("id", action.Id.ToString());
activity.Add("name", action.Description);
activity.Add("description", "Team-defined action or task.");
activity.Add("type", "actions");
activity.Add("activityType", "http://id.tincanapi.com/activitytype/checklist-item");

var parent = new Dictionary<String,String>();
parent.Add("id", evaluation.Id.ToString());
parent.Add("name", "Evaluation");
parent.Add("description", evaluation.Description);
parent.Add("type", "evaluations");
parent.Add("activityType", "http://adlnet.gov/expapi/activities/simulation");
parent.Add("moreInfo", "/?evaluation=" + evaluation.Id.ToString());

var category = new Dictionary<String,String>();

var grouping = new Dictionary<String,String>();
grouping.Add("id", move.Id.ToString());
grouping.Add("name", move.MoveNumber.ToString());
grouping.Add("description", move.Description);
grouping.Add("type", "moves");
grouping.Add("activityType", "http://id.tincanapi.com/activitytype/step");

var other = new Dictionary<String,String>();

return await _xApiService.CreateAsync(
verb, activity, parent, category, grouping, other, teamId, ct);

}
return false;
}

}

Expand Down
66 changes: 66 additions & 0 deletions Cite.Api/Services/RoleService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public interface IRoleService
Task<ViewModels.Role> AddUserAsync(Guid roleId, Guid userId, CancellationToken ct);
Task<ViewModels.Role> RemoveUserAsync(Guid roleId, Guid userId, CancellationToken ct);
Task<bool> DeleteAsync(Guid id, CancellationToken ct);
Task<bool> LogXApiAsync(Uri verb, RoleEntity role, UserEntity user, CancellationToken ct);

}

public class RoleService : IRoleService
Expand All @@ -40,19 +42,22 @@ public class RoleService : IRoleService
private readonly ClaimsPrincipal _user;
private readonly IMapper _mapper;
private readonly DatabaseOptions _options;
private readonly IXApiService _xApiService;

public RoleService(
CiteContext context,
IAuthorizationService authorizationService,
IPrincipal user,
IMapper mapper,
IXApiService xApiService,
DatabaseOptions options)
{
_context = context;
_authorizationService = authorizationService;
_user = user as ClaimsPrincipal;
_mapper = mapper;
_options = options;
_xApiService = xApiService;
}

public async Task<IEnumerable<ViewModels.Role>> GetByEvaluationAsync(Guid evaluationId, CancellationToken ct)
Expand Down Expand Up @@ -185,6 +190,11 @@ public RoleService(
_context.RoleUsers.Add(roleUserEntity);
await _context.SaveChangesAsync(ct);

// create and send xapi statement
var verb = new Uri("https://w3id.org/xapi/dod-isd/verbs/assigned");
// object could be the user being added
await LogXApiAsync(verb, roleToUpdate, userToAdd, ct);

return _mapper.Map<ViewModels.Role>(roleToUpdate);
}

Expand All @@ -209,6 +219,14 @@ public RoleService(
_context.RoleUsers.Remove(roleUserToRemove);
await _context.SaveChangesAsync(ct);

// create and send xapi statement
var verb = new Uri ("https://w3id.org/xapi/dod-isd/verbs/removed");
var userToRemove = await _context.Users.SingleOrDefaultAsync(v => v.Id == userId, ct);
if (userToRemove == null)
throw new EntityNotFoundException<UserEntity>();
// object could be the user being removed
await LogXApiAsync(verb, roleToUpdate, userToRemove, ct);

return _mapper.Map<ViewModels.Role>(roleToUpdate);
}

Expand All @@ -231,6 +249,54 @@ public async Task<bool> DeleteAsync(Guid id, CancellationToken ct)

return true;
}
public async Task<bool> LogXApiAsync(Uri verb, RoleEntity role, UserEntity user, CancellationToken ct)
{

if (_xApiService.IsConfigured())
{
var evaluation = await _context.Evaluations.Where(e => e.Id == role.EvaluationId).FirstAsync();
var move = await _context.Moves.Where(m => m.MoveNumber == evaluation.CurrentMoveNumber).FirstAsync();

var teamId = (await _context.TeamUsers
.SingleOrDefaultAsync(tu => tu.UserId == _user.GetId() && tu.Team.EvaluationId == role.EvaluationId)).TeamId;

var activity = new Dictionary<String,String>();
activity.Add("id", role.Id.ToString());
activity.Add("name", role.Name);
activity.Add("description", "Team-defined role.");
activity.Add("type", "roles");
activity.Add("activityType", "http://id.tincanapi.com/activitytype/resource");

var parent = new Dictionary<String,String>();
parent.Add("id", evaluation.Id.ToString());
parent.Add("name", "Evaluation");
parent.Add("description", evaluation.Description);
parent.Add("type", "evaluations");
parent.Add("activityType", "http://adlnet.gov/expapi/activities/simulation");
parent.Add("moreInfo", "/?evaluation=" + evaluation.Id.ToString());

var category = new Dictionary<String,String>();

var grouping = new Dictionary<String,String>();
grouping.Add("id", move.Id.ToString());
grouping.Add("name", move.MoveNumber.ToString());
grouping.Add("description", move.Description);
grouping.Add("type", "moves");
grouping.Add("activityType", "http://id.tincanapi.com/activitytype/step");

var other = new Dictionary<String,String>();
other.Add("id", user.Id.ToString());
other.Add("name", user.Name);
other.Add("description", "The user assigned or removed from the role.");
other.Add("type", "users");
other.Add("activityType", "http://id.tincanapi.com/activitytype/user-profile");

return await _xApiService.CreateAsync(
verb, activity, parent, category, grouping, other, teamId, ct);

}
return false;
}

}

Expand Down
Loading

0 comments on commit 6637463

Please sign in to comment.