From aacc23d5ef94ef12940a0cad863503372ca609ec Mon Sep 17 00:00:00 2001 From: Qais Yousuf Date: Sat, 1 Jun 2024 21:04:36 +0200 Subject: [PATCH] generate report for pdf and excel completed --- .../UserResponseStatusController.cs | 286 ++++++++++++++++-- .../Views/UserResponseStatus/Index.cshtml | 36 ++- .../UserResponsesStatus.cshtml | 123 ++------ Web/Web.csproj | 3 +- Web/wwwroot/Images/logo.png | Bin 0 -> 5883 bytes 5 files changed, 305 insertions(+), 143 deletions(-) create mode 100644 Web/wwwroot/Images/logo.png diff --git a/Web/Areas/Admin/Controllers/UserResponseStatusController.cs b/Web/Areas/Admin/Controllers/UserResponseStatusController.cs index eafd36b..330b23d 100644 --- a/Web/Areas/Admin/Controllers/UserResponseStatusController.cs +++ b/Web/Areas/Admin/Controllers/UserResponseStatusController.cs @@ -1,10 +1,15 @@ using Data; +using iTextSharp.text; +using iTextSharp.text.pdf; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Model; +using OfficeOpenXml; using Services.Interaces; using Web.ViewModel.QuestionnaireVM; + + namespace Web.Areas.Admin.Controllers { public class UserResponseStatusController : Controller @@ -67,34 +72,269 @@ namespace Web.Areas.Admin.Controllers } - //public async Task UserResponsesStatus(string userEmail) + + [HttpPost] + public async Task DeleteSelected(string[] selectedEmails) + { + if (selectedEmails == null || selectedEmails.Length == 0) + { + return RedirectToAction(nameof(Index)); + } + + var responsesToDelete = await _context.Responses + .Where(r => selectedEmails.Contains(r.UserEmail)) + .ToListAsync(); + + if (responsesToDelete.Any()) + { + _context.Responses.RemoveRange(responsesToDelete); + await _context.SaveChangesAsync(); + } + + return RedirectToAction(nameof(Index)); + } + + + + public async Task GenerateReport(string userEmail, string format) + { + var responses = await _context.Responses + .Include(r => r.Questionnaire) + .Include(r => r.ResponseDetails) + .ThenInclude(rd => rd.Question) + .ThenInclude(q => q.Answers) + .Include(r => r.ResponseDetails) + .ThenInclude(rd => rd.ResponseAnswers) + .Where(r => r.UserEmail == userEmail) + .ToListAsync(); + + if (responses == null || !responses.Any()) + { + return NotFound(); + } + + switch (format.ToLower()) + { + case "pdf": + return GeneratePdfReport(responses); + case "excel": + return GenerateExcelReport(responses); + default: + return BadRequest("Unsupported report format."); + } + } + + + private IActionResult GeneratePdfReport(List responses) + { + var userName = responses.First().UserName; + var userEmail = responses.First().UserEmail; + + var stream = new MemoryStream(); + var document = new Document(PageSize.A4, 50, 50, 25, 25); + var writer = PdfWriter.GetInstance(document, stream); + writer.CloseStream = false; // Prevent the stream from being closed when the document is closed + + document.Open(); + + // Add a title + var titleFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 18, BaseColor.BLACK); + var title = new Paragraph($"Report for {userName} ({userEmail})", titleFont) + { + Alignment = Element.ALIGN_CENTER, + SpacingAfter = 20 + }; + document.Add(title); + + // Add a logo + var logoPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images", "logo.png"); + if (System.IO.File.Exists(logoPath)) + { + var logo = Image.GetInstance(logoPath); + logo.ScaleToFit(100f, 100f); + logo.Alignment = Image.ALIGN_CENTER; + document.Add(logo); + } + + // Add a table for each response + var headerFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 14, BaseColor.WHITE); + var cellFont = FontFactory.GetFont(FontFactory.HELVETICA, 12, BaseColor.BLACK); + + foreach (var response in responses) + { + var table = new PdfPTable(2) + { + WidthPercentage = 100, + SpacingBefore = 20, + SpacingAfter = 20 + }; + table.SetWidths(new float[] { 1, 3 }); + + var cell = new PdfPCell(new Phrase($"Survey: {response.Questionnaire.Title}", headerFont)) + { + Colspan = 2, + BackgroundColor = new BaseColor(0, 150, 0), + HorizontalAlignment = Element.ALIGN_CENTER, + Padding = 10 + }; + table.AddCell(cell); + + table.AddCell(new PdfPCell(new Phrase("Submitted on:", cellFont)) { Padding = 5 }); + table.AddCell(new PdfPCell(new Phrase(response.SubmissionDate.ToString(), cellFont)) { Padding = 5 }); + + foreach (var detail in response.ResponseDetails) + { + table.AddCell(new PdfPCell(new Phrase("Question:", cellFont)) { Padding = 5 }); + table.AddCell(new PdfPCell(new Phrase(detail.Question.Text, cellFont)) { Padding = 5 }); + + if (detail.QuestionType == QuestionType.Text || detail.QuestionType == QuestionType.Slider || detail.QuestionType == QuestionType.Open_ended) + { + table.AddCell(new PdfPCell(new Phrase("Answer:", cellFont)) { Padding = 5 }); + table.AddCell(new PdfPCell(new Phrase(detail.TextResponse, cellFont)) { Padding = 5 }); + } + else + { + table.AddCell(new PdfPCell(new Phrase("Answers:", cellFont)) { Padding = 5 }); + var answers = string.Join(", ", detail.ResponseAnswers.Select(a => detail.Question.Answers.FirstOrDefault(ans => ans.Id == a.AnswerId)?.Text)); + table.AddCell(new PdfPCell(new Phrase(answers, cellFont)) { Padding = 5 }); + } + } + + document.Add(table); + } + + document.Close(); + writer.Close(); + + stream.Position = 0; + return File(stream, "application/pdf", $"{userName}_report.pdf"); + } + + + + private IActionResult GenerateExcelReport(List responses) + { + var userName = responses.First().UserName; + var userEmail = responses.First().UserEmail; + + using (var package = new ExcelPackage()) + { + var worksheet = package.Workbook.Worksheets.Add("Report"); + + // Add a logo + var logoPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images", "logo.png"); + if (System.IO.File.Exists(logoPath)) + { + var logo = new FileInfo(logoPath); + var picture = worksheet.Drawings.AddPicture("Logo", logo); + picture.SetPosition(0, 0, 0, 0); + picture.SetSize(300, 70); // Adjust the size as needed + } + + // Add a title + worksheet.Cells[6, 1].Value = $"Report for {userName} ({userEmail})"; + worksheet.Cells[6, 1, 6, 4].Merge = true; + worksheet.Cells[6, 1, 6, 4].Style.Font.Size = 18; + worksheet.Cells[6, 1, 6, 4].Style.Font.Bold = true; + worksheet.Cells[6, 1, 6, 4].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center; + + // Add headers + worksheet.Cells[7, 1].Value = "Survey"; + worksheet.Cells[7, 2].Value = "Submitted on"; + worksheet.Cells[7, 3].Value = "Question"; + worksheet.Cells[7, 4].Value = "Response"; + + using (var range = worksheet.Cells[7, 1, 7, 4]) + { + range.Style.Font.Bold = true; + range.Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid; + range.Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightGray); + range.Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center; + } + + // Add data + var row = 8; + foreach (var response in responses) + { + worksheet.Cells[row, 1].Value = response.Questionnaire.Title; + worksheet.Cells[row, 2].Value = response.SubmissionDate.ToString(); + row++; + + foreach (var detail in response.ResponseDetails) + { + worksheet.Cells[row, 3].Value = detail.Question.Text; + + if (detail.QuestionType == QuestionType.Text || detail.QuestionType == QuestionType.Slider || detail.QuestionType == QuestionType.Open_ended) + { + worksheet.Cells[row, 4].Value = detail.TextResponse; + } + else + { + var answers = string.Join(", ", detail.ResponseAnswers.Select(a => detail.Question.Answers.FirstOrDefault(ans => ans.Id == a.AnswerId)?.Text)); + worksheet.Cells[row, 4].Value = answers; + } + row++; + } + row++; + } + + worksheet.Cells.AutoFitColumns(); + + var stream = new MemoryStream(); + package.SaveAs(stream); + stream.Position = 0; + + return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"{userName}_report.xlsx"); + } + } + + + //private IActionResult GenerateExcelReport(List responses) //{ - // var responses = await _context.Responses - // .Include(r => r.Questionnaire) - // .Include(r => r.ResponseDetails) - // .ThenInclude(rd => rd.Question) - // .Include(r => r.ResponseDetails) - - // .ThenInclude(rd => rd.ResponseAnswers) - // .Where(r => r.UserEmail == userEmail) - // .ToListAsync(); - - // if (responses == null || !responses.Any()) - // { - // return NotFound(); - // } - // var userName = responses.First().UserName; + // var userEmail = responses.First().UserEmail; - // var viewModel = new UserResponsesViewModel + // using (var package = new ExcelPackage()) // { - // UserName = userName, - // UserEmail = userEmail, - // Responses = responses - // }; + // var worksheet = package.Workbook.Worksheets.Add("Report"); - // return View(viewModel); + // worksheet.Cells[1, 1].Value = $"Report for {userName} ({userEmail})"; + // worksheet.Cells[2, 1].Value = "Survey"; + // worksheet.Cells[2, 2].Value = "Submitted on"; + // worksheet.Cells[2, 3].Value = "Question"; + // worksheet.Cells[2, 4].Value = "Response"; + + // var row = 3; + // foreach (var response in responses) + // { + // worksheet.Cells[row, 1].Value = response.Questionnaire.Title; + // worksheet.Cells[row, 2].Value = response.SubmissionDate.ToString(); + // row++; + + // foreach (var detail in response.ResponseDetails) + // { + // worksheet.Cells[row, 3].Value = detail.Question.Text; + + // if (detail.QuestionType == QuestionType.Text || detail.QuestionType == QuestionType.Slider || detail.QuestionType == QuestionType.Open_ended) + // { + // worksheet.Cells[row, 4].Value = detail.TextResponse; + // } + // else + // { + // var answers = string.Join(", ", detail.ResponseAnswers.Select(a => detail.Question.Answers.FirstOrDefault(ans => ans.Id == a.AnswerId)?.Text)); + // worksheet.Cells[row, 4].Value = answers; + // } + // row++; + // } + // row++; + // } + + // var stream = new MemoryStream(); + // package.SaveAs(stream); + // stream.Position = 0; + + // return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"{userName}_report.xlsx"); + // } //} - } } diff --git a/Web/Areas/Admin/Views/UserResponseStatus/Index.cshtml b/Web/Areas/Admin/Views/UserResponseStatus/Index.cshtml index dfde84e..228130a 100644 --- a/Web/Areas/Admin/Views/UserResponseStatus/Index.cshtml +++ b/Web/Areas/Admin/Views/UserResponseStatus/Index.cshtml @@ -1,36 +1,36 @@ @model IEnumerable @{ - ViewData["Title"] = "User Responses status"; + ViewData["Title"] = "User Responses Status"; } - -
-
+
+ +
-
-

Response status

+ - - - @foreach (var item in Model) { - + - }
+ + Name Email SurveyAction
+ + @item.UserName @item.UserEmail @@ -44,12 +44,11 @@ View Responses status
- +
@@ -57,6 +56,13 @@
- - - +@section Scripts { + +} diff --git a/Web/Areas/Admin/Views/UserResponseStatus/UserResponsesStatus.cshtml b/Web/Areas/Admin/Views/UserResponseStatus/UserResponsesStatus.cshtml index a2dae27..4a67337 100644 --- a/Web/Areas/Admin/Views/UserResponseStatus/UserResponsesStatus.cshtml +++ b/Web/Areas/Admin/Views/UserResponseStatus/UserResponsesStatus.cshtml @@ -40,7 +40,7 @@ justify-content: center; width: 40px; height: 40px; - background-color: #007bff; + background-color: #aed5ff; color: white; border-radius: 50%; font-size: 14px; @@ -56,6 +56,8 @@ } + +

Back to list @@ -63,24 +65,34 @@

-

User Responses

- @Model.UserName (@Model.UserEmail) -
+ +
@Model.UserName (@Model.UserEmail)
+

Total responses: @Model.Responses.Count()

+ + + Generate PDF Report + + + Generate Excel Report + +
+
@foreach (var response in Model.Responses) {
- @response.Questionnaire.Title + @response.Questionnaire.Title
-
Survey: @response.Questionnaire.Title
-

Submitted on: @response.SubmissionDate

+
@response.Questionnaire.Title
+
Submitted on: @response.SubmissionDate
+

Total questions: @response.Questionnaire.Questions.Count()

@@ -154,104 +166,7 @@
-@*
-

- Back to list -

-
-
-

User Responses

- @Model.UserName (@Model.UserEmail) -
- -
- @foreach (var response in Model.Responses) - { -
-
-
Survey: @response.Questionnaire.Title
-

Submitted on: @response.SubmissionDate

- - - - - -
- - - - - - - - - @foreach (var detail in response.ResponseDetails) - { - - - - - } - -
QuestionResponse
@detail.Question.Text - @if (detail.QuestionType == QuestionType.Text || detail.QuestionType == QuestionType.Slider || detail.QuestionType == QuestionType.Open_ended) - { -
    -
  • - Question type - @detail.QuestionType -
  • - -
-
- -
    -
  • - - Answer - @detail.TextResponse -
  • -
- - } - else - { - -
    -
  • - Question type - @detail.QuestionType -
  • - -
-
- -
    - - @foreach (var answer in detail.ResponseAnswers) - { - - -
  • - Answer - @detail.Question.Answers.FirstOrDefault(a => a.Id == answer.AnswerId)?.Text -
  • - } - -
- - } -
-
-
-
- } -
-
-
*@ @section Scripts { diff --git a/Web/Web.csproj b/Web/Web.csproj index 890ab12..8733259 100644 --- a/Web/Web.csproj +++ b/Web/Web.csproj @@ -12,7 +12,9 @@ + + @@ -33,7 +35,6 @@ - diff --git a/Web/wwwroot/Images/logo.png b/Web/wwwroot/Images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5c15acc1af9649b4993ca7ef90e84b1afc52283f GIT binary patch literal 5883 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