gauri's software engineering portfolio

CSV Generation Closest Date Algorithm Example Report Engine Standardizing Error Messages Text Message Notifications Download as Markdown

code sample 3

Sample Report Engine
Click here to view the source on GitHub.

about this sample

This sample of code takes data gathered by hitting the Invoice endpoint and calculates the date of the next invoice in it. It is used in two places: first, in the registration summary page, as shown below. It is also shown on the Update Card Information page on the payment user accounts.

    public class TotalProfitReportEngine : ITotalProfitReportEngine
    {
        private readonly IInvoiceAccessor _invoiceAccessor;
        private readonly IInvoiceItemAccessor _invoiceItemAccessor;
        private readonly IStudentAccessor _studentAccessor;

        public TotalProfitReportEngine(IInvoiceAccessor ia, IInvoiceItemAccessor it, IStudentAccessor sa)
        {
            _invoiceAccessor = ia;
            _invoiceItemAccessor = it;
            _studentAccessor = sa;
        }


        public byte[] CreateReport(ReportPreferences preferences)
        {
            var recordsToWrite = CreateReportItemsFromDatabase(preferences);
            return CsvGenerator.GenerateCsv(recordsToWrite, new TotalProfitClassMap());
        }

        public IList<TotalProfitReportItem> CreateReportItemsFromDatabase(ReportPreferences preferences)
        {
            var invoiceItems = _invoiceItemAccessor.Get();
            IList<TotalProfitReportItem> reportItems = new List<TotalProfitReportItem>();

            var startDate = DateTime.Parse(preferences.StartTime);
            var endDate = DateTime.Parse(preferences.EndTime);

            foreach (var invoiceItem in invoiceItems)
            {
                LoadInvoiceItem(invoiceItem);
                if (invoiceItem.Type == InvoiceItemType.BaseTuition || invoiceItem.Type == InvoiceItemType.LateFee)
                {
                    if (startDate <= invoiceItem.Invoice.InvoiceDate && endDate >= invoiceItem.Invoice.InvoiceDate)
                    {
                        var type = invoiceItem.Type;
                        type = StringUtils.ConvertConstantToTitleCase(type);

                        reportItems.Add(new TotalProfitReportItem
                        {
                            StudentFirstName = invoiceItem.Invoice.Student.FirstName,
                            StudentLastName = invoiceItem.Invoice.Student.LastName,
                            FeeType = type,
                            Amount = Math.Round(invoiceItem.Amount, 2)
                        });
                    }
                }
            }

            return reportItems;
        }

        public ReportTotal GetTotal(ReportPreferences preferences)
        {
            var totalProfitEarned = CreateReportItemsFromDatabase(preferences).Sum(profitItem => profitItem.Amount);
            return new ReportTotal
            {
                Amount = Math.Round(totalProfitEarned, 2),
                Description = "Total Profit Earned In This Period"
            };
        }
    }

where the heavy lifting happens.

This code chunk demonstrates good use of IDesign Architecture and dependency injection. As seen in the constructor of the TotalProfitReportEngine, because this is an engine, it is only communicating with the Accessors (the layer below it). Additionally, this code follows the dependency inversion principle by using constructor injection for all of the Accessors it is using. One large roadblock I ran into while writing all of the report engines was standardizing the way different languages represent dates. C# and JavaScript each use different ways of representing dates as strings. I fixed this by creating a C#-formatted date string on the JavaScript side, and allowing the report engines to parse it into DateTime objects. Because that is a piece of business logic, I put this code in the Engine layer. This sample demonstrates high cohesion because all methods relating to the TotalProfitReportEngine are encapsulated in one class, allowing for maximum code reuse. Efficiency and clean code was taken into account while writing this method - I took care to only create report column objects for those invoices that fall in the desired date range. This class implements the ITotalProfitReportEngine interface, which implements the IReportEngine interface. The IReportEngine interface contains methods for all of the bare-bones necessities of a revenue report - CreateReportItemsFromDatabase, the method that takes persisted data and constructs rows based on user preferences. There is the ReportTotal, which calculates any kind of aggregate amount the user wants, and finally, CreateReport, which creates the CSV to send up to the web layer. Finally, this Engine utilizes LINQ to help make the C# code clean and readable.

Report Summary

example report