Versions Compared
Key
- This line was added.
- This line was removed.
- Formatting was changed.
Understanding the requirements
Based on the requirements we are going to be adding extensions to the ODS and to the DDS to include Transportation information to the dashboards. One of the extensions includes a new tab and view that will give us transportation information on the student.
Step 1. Integrating the "Transportation" tab to the menu
The plugin architecture follows many conventions to provide an easy to build upon framework. One of the conventions we will leverage is the way to register a menu link in the student tab area. To do this we will create an implementation of the IPluginManifest in the root of the EdFi.Dashboards.Plugins.Transportation.Web project. The code should look like the following code block:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
using System.Collections.Generic;
using EdFi.Dashboards.Core.Providers.Context;
using EdFi.Dashboards.Resource.Models.Common;
using EdFi.Dashboards.Resources.Models.Plugins;
using EdFi.Dashboards.Resources.Navigation;
using EdFi.Dashboards.Resources.Plugins;
namespace EdFi.Dashboards.Plugins.Transportation.Web
{
public class PluginManifest : IPluginManifest
{
private readonly IStudentSchoolAreaLinks areaLinks;
public PluginManifest(IStudentSchoolAreaLinks areaLinks)
{
this.areaLinks = areaLinks;
}
public string Name { get { return GetType().Assembly.GetName().Name; } }
public string Version { get { return GetType().Assembly.GetName().Version.ToString(); } }
public IEnumerable<PluginMenu> PluginMenus {
get
{
var requestContext = EdFiDashboardContext.Current;
if (requestContext.SchoolId.HasValue && requestContext.StudentUSI.HasValue)
return new List<PluginMenu>
{
new PluginMenu
{
Area = "StudentSchool",
ResourceModels = new List<ResourceModel>
{
new ResourceModel
{
Text = "Transportation",
Url = areaLinks.Resource(requestContext.SchoolId.Value, requestContext.StudentUSI.Value, "Transportation")
}
}
}
};
return new List<PluginMenu>();
}
}
}
} |
Notice that we are using dependency injection to resolve the IStudentSchoolAreaLinks dependency. This dependency provides the ability to create valid Urls for the application. Look at line 33 that depicts the usage of this component.
Now we need to let the DI container know that we want to wire up a implementation of the IPluginManifest. Lets modify the previously added Installer to include the manifest registration. Code should look like this:
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
using EdFi.Dashboards.Presentation.Core.Plugins.Utilities.CastleWindsor;
using EdFi.Dashboards.Resources.Plugins;
namespace EdFi.Dashboards.Plugins.Transportation.Web.Utilities.CastleWindsor
{
public class Installer : WebDefaultConventionInstaller<Marker_EdFi_Dasboards_Plugins_Transportation_Web>
{
public override void Install(IWindsorContainer container, IConfigurationStore store)
{
base.Install(container, store);
// Register plugin manifest for menu integration.
container.Register(Component.For<IPluginManifest>().ImplementedBy<PluginManifest>());
}
}
} |
At this point if we compile, bring up the application and navigate to a student we should see a Transportation tab that is clickable and that will take us to the desired view.
Image Modified
We now have a bare bones plugin that works and that we can build upon.
Step 2. Creating the extension database table and a EdFi.Dashboards.Plugins.Transportation.Data project.
Based on the requirements we are going to need a repository so that we can store the data for the student's (transportation method, AM bus number and PM bus number).
Step 2.1 Create the required database objects
Create a new schema that will group all of the "Transportation plugin" needs in one place. We will name the schema "transportation" and grant the edfiPService user read access to it.
Create a new table that will live in the newly created schema and name it: "transportation.StudentSchoolTransportationInformation"
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
/*Create the schema for the transportation plugin*/
CREATE SCHEMA [transportation] AUTHORIZATION [dbo]
/*Grant the needed access*/
GRANT SELECT ON SCHEMA::[transportation] TO [edfiPService]
/*Create the table*/
/****** Object: Table [transportation].[StudentSchoolTransportationInformation] Script Date: 9/28/2015 12:08:06 PM ******/
CREATE TABLE [transportation].[StudentSchoolTransportationInformation](
[StudentUSI] [int] NOT NULL,
[SchoolId] [int] NOT NULL,
[TransportationMethod] [nvarchar](50) NOT NULL,
[AMBusNumber] [nvarchar](50) NULL,
[PMBusNumber] [nvarchar](50) NULL,
CONSTRAINT [PK_StudentSchoolTransportationInformation] PRIMARY KEY CLUSTERED
(
[StudentUSI] ASC,
[SchoolId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO |
Once we have the design for this table and all of the surrounding objects, make sure to place the script in the ~\Ed-Fi-Plugins\Plugins\Transportation\Database\Structure\Dashboard\ path. The script should follow the naming convention: [scriptNumber]-[Description of the content]. So for this example we will name it "0001-Create Dashboard Transportation Extension.sql"
Step 2.2 Create the EdFi.Dashboards.Plugins.Transportation.Data project
In visual studio add a new project named EdFi.Dashboards.Plugins.Transportation.Data. For the project type select class library. For the location select the location where we are developing the plugin ~\Ed-Fi-Plugins\Plugins\Transportation\Application\.
Image Modified
Note: Make sure you select the right version of the .Net Framework or your plugin will not load.
Once the project has been created follow the steps below:
- Add the following references: System.Data.Linq, Subsonic, Castle Windsor, EdFi.Dashboards.Presentation.Core
- Copy the "Entities" folder including all of its content and the app.config from the "EdFi.Dashboards.Extensions.Data" project.
- Modify the app.config connection string to point to your local EdFi_Dashboard database.
Modify the Settings.ttinclude:
- The Namespace string needs to be updated to reflect the plugin we are working on ("EdFi.Dashboards.Plugins.Transportation.Data.Entities").
The IncludedSchemas array needs to be modified to only have the schema we created ("transportation").
Run the T4 transformation.
Add a marker interface to the Data project.
Code Block language c# title Marker interface for the Data project linenumbers true namespace EdFi.Dashboards.Plugins.Transportation.Data { public interface Marker_EdFi_Dashboards_Plugins_Transportation_Data { } }
Create an Installer.cs class in the "Utilities\CastleWindsor" path and inherit from RepositoryDefaultConventionInstaller closing the generic with the marker we just created.
Code Block language c# title Installer linenumbers true using EdFi.Dashboards.Presentation.Core.Plugins.Utilities.CastleWindsor; namespace EdFi.Dashboards.Plugins.Transportation.Data.Utilities.CastleWindsor { public class Installer : RepositoryDefaultConventionInstaller<Marker_EdFi_Dashboards_Plugins_Transportation_Data> { } }
Make sure everything compiles.
Step 3. Creating the Tests, Resources and Models
Now that we have the basic plugin artifacts and the data project in place we will continue by developing the services that will return the resources needed for our plugin.
Step 3.1 Create the Models
Follow the next instructions for the completion of the Resources.Models project:
- Within visual studio create a new class library project named EdFi.Dashboards.Plugins.Transportation.Resources.Models. (Be ware you might run into a path to long exception. If this occurs use Trans for short instead of Transportation.)
Following the conventions for file locations create a StudentSchool folder and a TransportationModel.cs class within it. The model should reflect the requirements regarding the data that will be needed.
Code Block language c# title Transportation Model linenumbers true using System; namespace EdFi.Dashboards.Plugins.Trans.Resources.Models.StudentSchool { [Serializable] public class TransportationModel { public long StudentUSI { get; set; } public int SchoolId { get; set; } public string MethodOfTransportation { get; set; } public string AMBusNumber { get; set; } public string PMBusNumber { get; set; } } }
Step 3.2 Create the Resources/Services and accompanying Tests
Follow the next instructions for the completion of the Resources and Tests project:
- Within visual studio create a new class library project named EdFi.Dashboards.Plugins.Transportation.Resources. (Be ware you might run into a path to long exception. If this occurs use Trans for short instead of Transportation.)
- Add the following references:
- EdFi.Dashboards.Plugins.Trans.Models
- EdFi.Dashboards.Plugins.Transportation.Data
- EdFi.Dashboards.Common
- EdFi.Dashboards.Data
- EdFi.Dashboards.Resources.Security.Common
- EdFi.Dashboards.SecurityTokenService.Authentication
Following the conventions for file locations create a StudentSchool folder and a TransportationService.cs class within it. The TransportationService.cs file will contain the TransportationRequest class and the TransportationService classes.
Code Block language c# title Transportation Service file linenumbers true using System.Linq; using EdFi.Dashboards.Common; using EdFi.Dashboards.Data.Repository; using EdFi.Dashboards.Plugins.Trans.Resources.Models.StudentSchool; using EdFi.Dashboards.Plugins.Transportation.Data.Entities; using EdFi.Dashboards.Resources.Security.Common; using EdFi.Dashboards.SecurityTokenService.Authentication; namespace EdFi.Dashboards.Plugins.Transportation.Resources.StudentSchool { public class TransportationRequest { public long StudentUSI { get; set; } public int SchoolId { get; set; } } public interface ITransportationService : IService<TransportationRequest, TransportationModel> { } public class TransportationService : ITransportationService { private readonly IRepository<StudentSchoolTransportationInformation> transportationInformationRepository; public TransportationService(IRepository<StudentSchoolTransportationInformation> transportationInformationRepository) { this.transportationInformationRepository = transportationInformationRepository; } [CanBeAuthorizedBy(EdFiClaimTypes.ViewAllStudents, EdFiClaimTypes.ViewMyStudents)] public TransportationModel Get(TransportationRequest request) { return new TransportationModel(); } } }
Note: Make sure to attribute the Get method with the appropriate security. "[CanBeAuthorizedBy(EdFiClaimTypes.ViewAllStudents, EdFiClaimTypes.ViewMyStudents)]"
- Within visual studio create a new class library project named EdFi.Dashboards.Plugins.Trans.Resources.Tests. (Be aware you might run into a path to long exception. If this occurs use Trans for short instead of Transportation.)
- Add the following references:
- EdFi.Dashboards.Testing
- EdFi.Dashboards.Plugins.Transportation.Data
- EdFi.Dashboards.Plugins.Transportation.Resources
- EdFi.Dashboards.Common
- EdFi.Dashboards.Data
- Rhino.Mocks
- EdFi.Dashboards.Plugins.Trans.Resources.Models
- NUnit
- EdFi.Dashboards.Presentation.Core
- Castle Windsor
- EdFi.Dashboards.Resources.Security.Common
Following the conventions for file locations create a StudentSchool folder and a TransportationServiceFixture.cs class within it. The TransportationServiceFixture.cs file will contain the tests for the TransportationService class.
Code Block language c# title Unit test for Transportation Service linenumbers true using System.Collections.Generic; using System.Linq; using EdFi.Dashboards.Data.Repository; using EdFi.Dashboards.Plugins.Trans.Resources.Models.StudentSchool; using EdFi.Dashboards.Plugins.Transportation.Data.Entities; using EdFi.Dashboards.Plugins.Transportation.Resources.StudentSchool; using EdFi.Dashboards.Testing; using NUnit.Framework; using Rhino.Mocks; namespace EdFi.Dashboards.Plugins.Trans.Resources.Tests { public class TransportationServiceFixture { [TestFixture] public class When_requesting_student_trasnportation_information : TestFixtureBase { private const int suppliedStudentUSI =1; private const int suppliedSchoolId =10; private IQueryable<StudentSchoolTransportationInformation> suppliedStudentSchoolTransportationInformationData; // The service under test private TransportationService service; private TransportationModel actualModel; protected override void EstablishContext() { suppliedStudentSchoolTransportationInformationData = GetSuppliedStudentSchoolTransportationInformationData(); // Set up the mocks var suppliedStudentSchoolTransportationRepository = mocks.StrictMock<IRepository<StudentSchoolTransportationInformation>>(); Expect.Call(suppliedStudentSchoolTransportationRepository.GetAll()).Return(suppliedStudentSchoolTransportationInformationData); service = new TransportationService(suppliedStudentSchoolTransportationRepository); } protected IQueryable<StudentSchoolTransportationInformation> GetSuppliedStudentSchoolTransportationInformationData() { return new List<StudentSchoolTransportationInformation> { new StudentSchoolTransportationInformation{ StudentUSI = suppliedStudentUSI, SchoolId = suppliedSchoolId, TransportationMethod = "Bus", AMBusNumber = "123", PMBusNumber = "456"}, new StudentSchoolTransportationInformation{ StudentUSI = 99, SchoolId = suppliedSchoolId, TransportationMethod = "Should be filtered out becuse different StudentUSI", AMBusNumber = "123", PMBusNumber = "456"}, new StudentSchoolTransportationInformation{ StudentUSI = suppliedStudentUSI, SchoolId = 99, TransportationMethod = "Should be filtered out becuse different SchooId", AMBusNumber = "123", PMBusNumber = "456"}, }.AsQueryable(); } protected override void ExecuteTest() { actualModel = service.Get(new TransportationRequest{SchoolId = suppliedSchoolId, StudentUSI = suppliedStudentUSI}); } [Test] public void Should_assign_all_properties_on_student_transportation_model() { var suppliedDataToBind = suppliedStudentSchoolTransportationInformationData.Single(x=>x.SchoolId==suppliedSchoolId && x.StudentUSI==suppliedStudentUSI); Assert.That(actualModel.SchoolId, Is.EqualTo(suppliedDataToBind.SchoolId)); Assert.That(actualModel.StudentUSI, Is.EqualTo(suppliedDataToBind.StudentUSI)); Assert.That(actualModel.MethodOfTransportation, Is.EqualTo(suppliedDataToBind.TransportationMethod)); Assert.That(actualModel.AMBusNumber, Is.EqualTo(suppliedDataToBind.AMBusNumber)); Assert.That(actualModel.PMBusNumber, Is.EqualTo(suppliedDataToBind.PMBusNumber)); } } } }
- Run the test. (The expectation is that it fails.)
Image Modified Now that we have our test setup with the expected requirement lets continue to implement the service. The final implementation of the service should look as follows:
Code Block language c# title Implemented Transportation Request linenumbers true using System.Linq; using EdFi.Dashboards.Common; using EdFi.Dashboards.Data.Repository; using EdFi.Dashboards.Plugins.Trans.Resources.Models.StudentSchool; using EdFi.Dashboards.Plugins.Transportation.Data.Entities; using EdFi.Dashboards.Resources.Security.Common; using EdFi.Dashboards.SecurityTokenService.Authentication; namespace EdFi.Dashboards.Plugins.Transportation.Resources.StudentSchool { public class TransportationRequest { public long StudentUSI { get; set; } public int SchoolId { get; set; } } public interface ITransportationService : IService<TransportationRequest, TransportationModel> { } public class TransportationService : ITransportationService { private readonly IRepository<StudentSchoolTransportationInformation> transportationInformationRepository; public TransportationService(IRepository<StudentSchoolTransportationInformation> transportationInformationRepository) { this.transportationInformationRepository = transportationInformationRepository; } [CanBeAuthorizedBy(EdFiClaimTypes.ViewAllStudents, EdFiClaimTypes.ViewMyStudents)] public TransportationModel Get(TransportationRequest request) { var data = (from t in transportationInformationRepository.GetAll() where t.SchoolId == request.SchoolId && t.StudentUSI==request.StudentUSI select t).SingleOrDefault(); if(data==null) return new TransportationModel(); var response = new TransportationModel { StudentUSI = data.StudentUSI, SchoolId = data.SchoolId, MethodOfTransportation = data.TransportationMethod, AMBusNumber = data.AMBusNumber, PMBusNumber = data.PMBusNumber, }; return response; } } }
Note: Make sure to attribute the Get method with the appropriate security authorization claims. In This case users who have the "View All Students" and the "View My Students" claims should be allowed.
- Once the service has been fully implemented the test should pass.
Image Modified Create the marker interface for the Resources project.
Code Block language c# title Marker Class for the Resources project namespace EdFi.Dashboards.Plugins.Transportation.Resources { public interface Marker_EdFi_Dashboards_Plugins_Transportation_Resources { } }
Create the IoC instaler so that it picks up our interfaces and services.
Code Block language c# title IoC Installer linenumbers true using EdFi.Dashboards.Presentation.Core.Plugins.Utilities.CastleWindsor; namespace EdFi.Dashboards.Plugins.Transportation.Resources.Utilities.CastleWindsor { public class Installer : ResourceDefaultConventionInstaller<Marker_EdFi_Dashboards_Plugins_Transportation_Resources> { } }
- Done
Step 4. Sewing it all together
Now that we have plugin integration, data structures, services and models we need to make them all interact. For this we are going to add a constructor to the controller and take a dependency on the service.
Add the following references to the EdFi.Dashboards.Plugins.Transportation.Web project:
- EdFi.Dashboards.Plugins.Transportation.Resources
- EdFi.Dashboards.Plugins.Transportation.Resources.Models
- EdFi.Dashboards.Common
Open the controller and modify it so it looks like this:
Code Block language c# title Controller Updated linenumbers true using System.Web.Mvc; using EdFi.Dashboards.Plugins.Transportation.Resources.StudentSchool; namespace EdFi.Dashboards.Plugins.Transportation.Web.Areas.StudentSchool.Controllers { public class TransportationController : Controller { private readonly ITransportationService service; public TransportationController(ITransportationService service) { this.service = service; } public ActionResult Get(TransportationRequest request) { var model = service.Get(request); return View(model); } } }
Update the view so that we have the model and a way of displaying it.
Code Block language xml title Updated View linenumbers true @model EdFi.Dashboards.Plugins.Trans.Resources.Models.StudentSchool.TransportationModel @section ContentPlaceHolder1{ <style> .round-container { background-color: #d2e2ef; border-radius: 5px; padding: 10px; margin-right: 20px; margin-bottom: 20px; display: -moz-inline-block; display: inline-block; zoom: 1; vertical-align: top; width: 740px; } </style> <div class="l-information-wrapper"> <h2 class="title">Transportation Information</h2> <div class="round-container"> <ul> <li> <span class="label">Method of Transportation:</span> <span>@(Model.MethodOfTransportation)</span> </li> <li> <span class="label">AM Bus Number:</span> <span>@(Model.AMBusNumber)</span> </li> <li> <span class="label">PM Bus Number:</span> <span>@(Model.PMBusNumber)</span> </li> </ul> </div> </div> }
Add some fake data
Code Block language sql title Fake data linenumbers true INSERT INTO [EdFi_Dashboard].[transportation].[StudentSchoolTransportationInformation] (StudentUSI, SchoolId ,TransportationMethod, AMBusNumber, PMBusNumber) VALUES (605319, 255901001, 'Bus','A1024','B7245'); UPDATE [EdFi_Dashboard].[domain].[StudentInformation] set AddressLine1='106 sunfish St', City='Lakeway', State='TX' where [StudentUSI]=605319; UPDATE [EdFi_Dashboard].[domain].[SchoolInformation] set AddressLine1='3301 Serene Hills Dr', City='Lakeway', State='TX' where SchoolId=255901001; INSERT INTO [EdFi_Dashboard].[transportation].[StudentSchoolTransportationInformation] (StudentUSI, SchoolId ,TransportationMethod) VALUES (605486, 255901001, 'Car'); UPDATE [EdFi_Dashboard].[domain].[StudentInformation] set AddressLine1='106 applegreen ln', City='Lakeway', State='TX' where [StudentUSI]=605486;
- Run the code and navigate to a student and click on the Transportation tab. If you are running on your local machine you should be able to click the following link to get to the student faster: https://localhost/EdFiDashboardDev/Districts/GrandBendISD/Schools/Grand-Bend-High-School/Students/Tara-D--Abbott-605319/Transportation
Image Modified
Reach Goal "Adding a map that shows student address and the route to school"
Based on the original requirements we want to include a map that shows the route of the student from his house to the school. To do this we are going to update the model, unit test, service and view. The student address is on the domain.StudentInformation table and the school address in in the domain.SchoolInformation one.
Lets start by following these steps:
Update the model to contain the StudentAddress and the SchoolAddress as strings. Then continue to make appropriate changes to the unit test expectations.
Code Block language c# title Model updates linenumbers true using System; namespace EdFi.Dashboards.Plugins.Trans.Resources.Models.StudentSchool { [Serializable] public class TransportationModel { public long StudentUSI { get; set; } public int SchoolId { get; set; } public string MethodOfTransportation { get; set; } public string AMBusNumber { get; set; } public string PMBusNumber { get; set; } public string StudentAddress { get; set; } public string SchoolAddress { get; set; } } }
Update the service so it has the repository dependencies that are needed.
Code Block language c# title Service with Dependencies linenumbers true using System.Linq; using System.Xml; using EdFi.Dashboards.Common; using EdFi.Dashboards.Data.Entities; using EdFi.Dashboards.Data.Repository; using EdFi.Dashboards.Plugins.Trans.Resources.Models.StudentSchool; using EdFi.Dashboards.Plugins.Transportation.Data.Entities; using EdFi.Dashboards.Resources.Security.Common; using EdFi.Dashboards.SecurityTokenService.Authentication; namespace EdFi.Dashboards.Plugins.Transportation.Resources.StudentSchool { public class TransportationRequest { public long StudentUSI { get; set; } public int SchoolId { get; set; } } public interface ITransportationService : IService<TransportationRequest, TransportationModel> { } public class TransportationService : ITransportationService { private readonly IRepository<StudentSchoolTransportationInformation> transportationInformationRepository; private readonly IRepository<StudentInformation> studentInformationRepository; private readonly IRepository<SchoolInformation> schoolInformationRepository; public TransportationService(IRepository<StudentSchoolTransportationInformation> transportationInformationRepository, IRepository<StudentInformation> studentInformationRepository, IRepository<SchoolInformation> schoolInformationRepository) { this.transportationInformationRepository = transportationInformationRepository; this.studentInformationRepository = studentInformationRepository; this.schoolInformationRepository = schoolInformationRepository; } [CanBeAuthorizedBy(EdFiClaimTypes.ViewAllStudents, EdFiClaimTypes.ViewMyStudents)] public TransportationModel Get(TransportationRequest request) { var data = (from t in transportationInformationRepository.GetAll() where t.SchoolId == request.SchoolId && t.StudentUSI==request.StudentUSI select t).SingleOrDefault(); if(data==null) return new TransportationModel(); var response = new TransportationModel { StudentUSI = data.StudentUSI, SchoolId = data.SchoolId, MethodOfTransportation = data.TransportationMethod, AMBusNumber = data.AMBusNumber, PMBusNumber = data.PMBusNumber, }; return response; } } }
Update the unit test to comply with the new logic and run the unit test
Code Block language c# title Updated Test linenumbers true using System; using System.Collections.Generic; using System.Linq; using EdFi.Dashboards.Data.Entities; using EdFi.Dashboards.Data.Repository; using EdFi.Dashboards.Plugins.Trans.Resources.Models.StudentSchool; using EdFi.Dashboards.Plugins.Transportation.Data.Entities; using EdFi.Dashboards.Plugins.Transportation.Resources.StudentSchool; using EdFi.Dashboards.Testing; using NUnit.Framework; using Rhino.Mocks; namespace EdFi.Dashboards.Plugins.Trans.Resources.Tests { public class TransportationServiceFixture { [TestFixture] public class When_requesting_student_trasnportation_information : TestFixtureBase { private const int suppliedStudentUSI =1; private const int suppliedSchoolId =10; private IQueryable<StudentSchoolTransportationInformation> suppliedStudentSchoolTransportationInformationData; private IQueryable<StudentInformation> suppliedStudentInformationData; private IQueryable<SchoolInformation> suppliedSchoolInformationData; // The service under test private TransportationService service; private TransportationModel actualModel; protected override void EstablishContext() { suppliedStudentSchoolTransportationInformationData = GetSuppliedStudentSchoolTransportationInformationData(); suppliedStudentInformationData = GetSuppliedStudentInformationData(); suppliedSchoolInformationData = GetSuppliedSchoolInformationData(); // Set up the mocks var suppliedStudentSchoolTransportationRepository = mocks.StrictMock<IRepository<StudentSchoolTransportationInformation>>(); var suppliedStudentInformationRepository = mocks.StrictMock<IRepository<StudentInformation>>(); var suppliedSchoolInformationRepository = mocks.StrictMock<IRepository<SchoolInformation>>(); Expect.Call(suppliedStudentSchoolTransportationRepository.GetAll()).Return(suppliedStudentSchoolTransportationInformationData); Expect.Call(suppliedStudentInformationRepository.GetAll()).Return(suppliedStudentInformationData); Expect.Call(suppliedSchoolInformationRepository.GetAll()).Return(suppliedSchoolInformationData); service = new TransportationService(suppliedStudentSchoolTransportationRepository, suppliedStudentInformationRepository, suppliedSchoolInformationRepository); } protected IQueryable<StudentSchoolTransportationInformation> GetSuppliedStudentSchoolTransportationInformationData() { return new List<StudentSchoolTransportationInformation> { new StudentSchoolTransportationInformation{ StudentUSI = suppliedStudentUSI, SchoolId = suppliedSchoolId, TransportationMethod = "Bus", AMBusNumber = "123", PMBusNumber = "456"}, new StudentSchoolTransportationInformation{ StudentUSI = 99, SchoolId = suppliedSchoolId, TransportationMethod = "Should be filtered out becuse different StudentUSI", AMBusNumber = "123", PMBusNumber = "456"}, new StudentSchoolTransportationInformation{ StudentUSI = suppliedStudentUSI, SchoolId = 99, TransportationMethod = "Should be filtered out becuse different SchooId", AMBusNumber = "123", PMBusNumber = "456"}, }.AsQueryable(); } protected IQueryable<StudentInformation> GetSuppliedStudentInformationData() { return new List<StudentInformation> { new StudentInformation{ StudentUSI = suppliedStudentUSI, AddressLine1 = "123 My Street", City = "City", State = "State", ZipCode = "12345"}, new StudentInformation{ StudentUSI = 99, AddressLine1 = "Should be filtered out becuse different StudentUSI"}, }.AsQueryable(); } protected IQueryable<SchoolInformation> GetSuppliedSchoolInformationData() { return new List<SchoolInformation> { new SchoolInformation{ SchoolId = suppliedSchoolId, AddressLine1 = "456 My Street", City = "City", State = "State", ZipCode = "12345"}, new SchoolInformation{ SchoolId = 99, AddressLine1 = "Should be filtered out becuse different SchoolId"}, }.AsQueryable(); } protected override void ExecuteTest() { actualModel = service.Get(new TransportationRequest{SchoolId = suppliedSchoolId, StudentUSI = suppliedStudentUSI}); } [Test] public void Should_assign_all_properties_on_student_transportation_model_correctly() { var suppliedDataToBind = suppliedStudentSchoolTransportationInformationData.Single(x=>x.SchoolId==suppliedSchoolId && x.StudentUSI==suppliedStudentUSI); Assert.That(actualModel.SchoolId, Is.EqualTo(suppliedDataToBind.SchoolId)); Assert.That(actualModel.StudentUSI, Is.EqualTo(suppliedDataToBind.StudentUSI)); Assert.That(actualModel.MethodOfTransportation, Is.EqualTo(suppliedDataToBind.TransportationMethod)); Assert.That(actualModel.AMBusNumber, Is.EqualTo(suppliedDataToBind.AMBusNumber)); Assert.That(actualModel.PMBusNumber, Is.EqualTo(suppliedDataToBind.PMBusNumber)); } [Test] public void Should_assign_student_address_on_student_transportation_model() { var suppliedDataToBind = suppliedStudentInformationData.Single(x => x.StudentUSI == suppliedStudentUSI); var suppliedAddress = String.Format("{0}, {1}, {2}", suppliedDataToBind.AddressLine1, suppliedDataToBind.City, suppliedDataToBind.State); Assert.That(actualModel.StudentAddress, Is.EqualTo(suppliedAddress)); } [Test] public void Should_assign_school_address_on_student_transportation_model() { var suppliedDataToBind = suppliedSchoolInformationData.Single(x => x.SchoolId == suppliedSchoolId); var suppliedAddress = String.Format("{0}, {1}, {2}", suppliedDataToBind.AddressLine1, suppliedDataToBind.City, suppliedDataToBind.State); Assert.That(actualModel.SchoolAddress, Is.EqualTo(suppliedAddress)); } } } }
Image Modified
Continue to fully implement the service updates to get the data from the repositories and bind to the model.
Code Block language c# title Service fully implemented linenumbers true using System; using System.Linq; using System.Xml; using Castle.MicroKernel.ModelBuilder.Descriptors; using Castle.Windsor.Installer; using EdFi.Dashboards.Common; using EdFi.Dashboards.Data.Entities; using EdFi.Dashboards.Data.Repository; using EdFi.Dashboards.Plugins.Trans.Resources.Models.StudentSchool; using EdFi.Dashboards.Plugins.Transportation.Data.Entities; using EdFi.Dashboards.Resources.Security.Common; using EdFi.Dashboards.SecurityTokenService.Authentication; namespace EdFi.Dashboards.Plugins.Transportation.Resources.StudentSchool { public class TransportationRequest { public long StudentUSI { get; set; } public int SchoolId { get; set; } } public interface ITransportationService : IService<TransportationRequest, TransportationModel> { } public class TransportationService : ITransportationService { private readonly IRepository<StudentSchoolTransportationInformation> transportationInformationRepository; private readonly IRepository<StudentInformation> studentInformationRepository; private readonly IRepository<SchoolInformation> schoolInformationRepository; public TransportationService(IRepository<StudentSchoolTransportationInformation> transportationInformationRepository, IRepository<StudentInformation> studentInformationRepository, IRepository<SchoolInformation> schoolInformationRepository) { this.transportationInformationRepository = transportationInformationRepository; this.studentInformationRepository = studentInformationRepository; this.schoolInformationRepository = schoolInformationRepository; } [CanBeAuthorizedBy(EdFiClaimTypes.ViewAllStudents, EdFiClaimTypes.ViewMyStudents)] public TransportationModel Get(TransportationRequest request) { var transportationData = (from t in transportationInformationRepository.GetAll() where t.SchoolId == request.SchoolId && t.StudentUSI == request.StudentUSI select t).SingleOrDefault(); var studentData = (from s in studentInformationRepository.GetAll() where s.StudentUSI == request.StudentUSI select s).SingleOrDefault(); var schoolData = (from s in schoolInformationRepository.GetAll() where s.SchoolId == request.SchoolId select s).SingleOrDefault(); if(transportationData==null) return new TransportationModel(); var response = new TransportationModel { StudentUSI = transportationData.StudentUSI, SchoolId = transportationData.SchoolId, MethodOfTransportation = transportationData.TransportationMethod, AMBusNumber = transportationData.AMBusNumber, PMBusNumber = transportationData.PMBusNumber, }; if (studentData != null) response.StudentAddress = String.Format("{0}, {1}, {2}", studentData.AddressLine1, studentData.City, studentData.State); if (schoolData != null) response.SchoolAddress = String.Format("{0}, {1}, {2}", schoolData.AddressLine1, schoolData.City, schoolData.State); return response; } } }
Run the unit tests again
Image ModifiedUpdate the view to include the necessary java scripts and html.
Code Block language c# title Updated View linenumbers true @model EdFi.Dashboards.Plugins.Trans.Resources.Models.StudentSchool.TransportationModel @section ContentPlaceHolderHead{ <script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script> <script> $(function() { var directionsDisplay = new google.maps.DirectionsRenderer(); var directionsService = new google.maps.DirectionsService(); var center = new google.maps.LatLng(0, 0); var myOptions = { zoom: 7, mapTypeId: google.maps.MapTypeId.ROADMAP, center: center } var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions); directionsDisplay.setMap(map); var start = "@Model.StudentAddress"; var end = "@Model.SchoolAddress"; var request = { origin: start, destination: end, travelMode: google.maps.DirectionsTravelMode.DRIVING }; directionsService.route(request, function(response, status) { if (status == google.maps.DirectionsStatus.OK) { directionsDisplay.setDirections(response); } }); }); </script> } @section ContentPlaceHolder1{ <style> .round-container { background-color: #d2e2ef; border-radius: 5px; padding: 10px; margin-right: 20px; margin-bottom: 20px; display: -moz-inline-block; display: inline-block; zoom: 1; vertical-align: top; width: 740px; } </style> <div class="l-information-wrapper"> <h2 class="title">Transportation Information</h2> <div class="round-container"> <ul> <li> <span class="label">Method of Transportation:</span> <span>@(Model.MethodOfTransportation)</span> </li> <li> <span class="label">AM Bus Number:</span> <span>@(Model.AMBusNumber)</span> </li> <li> <span class="label">PM Bus Number:</span> <span>@(Model.PMBusNumber)</span> </li> </ul> </div> <div id="map_canvas" style="height: 400px; width: 740px"></div> </div> }
- Build the code and run the application.
Image Modified