generate report for pdf and excel completed

This commit is contained in:
Qais Yousuf 2024-06-01 21:04:36 +02:00
parent 9c560a798c
commit aacc23d5ef
5 changed files with 305 additions and 143 deletions

View file

@ -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<IActionResult> UserResponsesStatus(string userEmail)
[HttpPost]
public async Task<IActionResult> 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<IActionResult> 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<Response> 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<Response> 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<Response> responses)
//{
// var responses = await _context.Responses
// .Include(r => r.Questionnaire)
// .Include(r => r.ResponseDetails)
// .ThenInclude(rd => rd.Question)
// .Include(r => r.ResponseDetails)
// var userName = responses.First().UserName;
// var userEmail = responses.First().UserEmail;
// .ThenInclude(rd => rd.ResponseAnswers)
// .Where(r => r.UserEmail == userEmail)
// .ToListAsync();
// if (responses == null || !responses.Any())
// using (var package = new ExcelPackage())
// {
// return NotFound();
// var worksheet = package.Workbook.Worksheets.Add("Report");
// 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 userName = responses.First().UserName;
// var stream = new MemoryStream();
// package.SaveAs(stream);
// stream.Position = 0;
// var viewModel = new UserResponsesViewModel
// {
// UserName = userName,
// UserEmail = userEmail,
// Responses = responses
// };
// return View(viewModel);
// return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"{userName}_report.xlsx");
// }
//}
}
}

View file

@ -1,13 +1,11 @@
@model IEnumerable<UserResponsesViewModel>
@{
ViewData["Title"] = "User Responses status";
ViewData["Title"] = "User Responses Status";
}
<div class="container-fluid mt-4 mb-5">
<div class="col-10 offset-1 ">
<div class="col-10 offset-1">
<div class="card p-4 shadow-lg rounded-2">
@ -17,20 +15,22 @@
<table class="table table-responsive w-100 d-block d-md-table table-bordered table-hover">
<thead>
<tr>
<th>
<input type="checkbox" id="selectAll" />
</th>
<th>Name</th>
<th>Email</th>
<th>Survey</th>
<th>Action</th>
<!-- Additional headers omitted for brevity -->
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
<input type="checkbox" name="selectedEmails" value="@item.UserEmail" class="selectCheckbox" />
</td>
<td>@item.UserName</td>
<td>@item.UserEmail</td>
<td>
@ -44,12 +44,11 @@
<td class="text-end">
<a asp-controller="UserResponseStatus" asp-action="UserResponsesStatus" asp-route-UserEmail="@item.UserEmail" class="btn btn-info btn-sm"><i class="bi bi-eye"></i> View Responses status</a>
</td>
</tr>
}
</tbody>
</table>
<button type="submit" class="btn btn-danger mt-2">Delete Selected</button>
</form>
</div>
@ -57,6 +56,13 @@
</div>
</div>
@section Scripts {
<script>
document.getElementById('selectAll').addEventListener('click', function (event) {
var checkboxes = document.querySelectorAll('.selectCheckbox');
checkboxes.forEach(function (checkbox) {
checkbox.checked = event.target.checked;
});
});
</script>
}

View file

@ -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 @@
}
</style>
<div class="container-fluid mt-3">
<p>
<a asp-action="Index" class="btn btn-primary btn-sm">Back to list</a>
@ -63,8 +65,17 @@
<div class="card p-5 m-3 shadow">
<div class="bd-callout bd-callout-primary">
<h4 class="text-primary">User Responses</h4>
<text>@Model.UserName (@Model.UserEmail)</text>
<h6 class="text-primary font-weight-bold"><i class="bi bi-person"></i> @Model.UserName (@Model.UserEmail)</h6>
<p class="text-info"><i class="bi bi-calculator"></i> Total responses: @Model.Responses.Count()</p>
<a asp-action="GenerateReport" asp-route-userEmail="@Model.UserEmail" asp-route-format="pdf" class="btn btn-info btn-sm">
<i class="bi bi-filetype-pdf"></i> Generate PDF Report
</a>
<a asp-action="GenerateReport" asp-route-userEmail="@Model.UserEmail" asp-route-format="excel" class="btn btn-info btn-sm">
<i class="bi bi-file-excel"></i> Generate Excel Report
</a>
</div>
<!-- Stepper -->
@ -73,14 +84,15 @@
{
<div class="stepper-item">
<div class="step-counter">
<span class="badge bg-primary p-3 shadow">@response.Questionnaire.Title</span>
<span class="badge bg-primary p-2 shadow">@response.Questionnaire.Title <i class="bi bi-arrow-right-circle-fill"></i></span>
</div>
<div class="step-content">
<div class="card p-4">
<div class="step-header">
<h6>Survey: @response.Questionnaire.Title</h6>
<p>Submitted on: @response.SubmissionDate</p>
<h6 class="text-primary font-weight-bold"><i class="bi bi-ui-checks"></i> @response.Questionnaire.Title</h6>
<h6 class="text-success"><i class="bi bi-calendar2-week"></i> Submitted on: @response.SubmissionDate</h6>
<p class="text-info"><i class="bi bi-question-square"></i> Total questions: @response.Questionnaire.Questions.Count()</p>
</div>
<!-- Collapsible button -->
@ -154,104 +166,7 @@
</div>
@* <div class="container-fluid mt-3">
<p>
<a asp-action="Index" class="btn btn-primary btn-sm">Back to list</a>
</p>
<div class="card p-5 m-3 shadow">
<div class="bd-callout bd-callout-primary">
<h4 class="text-primary">User Responses</h4>
<text>@Model.UserName (@Model.UserEmail)</text>
</div>
<div>
@foreach (var response in Model.Responses)
{
<div class="container card mt-4 p-3">
<div>
<h6>Survey: @response.Questionnaire.Title</h6>
<p>Submitted on: @response.SubmissionDate</p>
<!-- Collapsible button -->
<button class="btn btn-primary btn-sm" type="button" data-bs-toggle="collapse" data-bs-target="#collapseResponse-@response.Id" aria-expanded="false" aria-controls="collapseResponse-@response.Id">
View Responses
</button>
<!-- Collapsible content -->
<div class="collapse mt-3" id="collapseResponse-@response.Id">
<table class="table table-responsive w-100 d-block d-md-table table-hover">
<thead>
<tr>
<th>Question</th>
<th>Response</th>
</tr>
</thead>
<tbody>
@foreach (var detail in response.ResponseDetails)
{
<tr>
<td>@detail.Question.Text</td>
<td>
@if (detail.QuestionType == QuestionType.Text || detail.QuestionType == QuestionType.Slider || detail.QuestionType == QuestionType.Open_ended)
{
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between align-items-center border-1">
Question type
<span class="badge text-bg-primary rounded-pill p-1s">@detail.QuestionType</span>
</li>
</ul>
<br />
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between align-items-center border-1">
Answer
<span class="badge text-bg-primary rounded-pill p-1">@detail.TextResponse</span>
</li>
</ul>
}
else
{
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between align-items-center border-1">
Question type
<span class="badge text-bg-primary rounded-pill p-1">@detail.QuestionType</span>
</li>
</ul>
<br />
<ul class="list-group">
@foreach (var answer in detail.ResponseAnswers)
{
<li class="list-group-item d-flex justify-content-between align-items-center">
Answer
<span class="badge text-bg-primary rounded-pill p-1">@detail.Question.Answers.FirstOrDefault(a => a.Id == answer.AnswerId)?.Text</span>
</li>
}
</ul>
}
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
</div>
</div>
</div> *@
@section Scripts {
<!-- Include Bootstrap 5 JS for collapse functionality -->

View file

@ -12,7 +12,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="EPPlus" Version="7.1.3" />
<PackageReference Include="itext7" Version="8.0.4" />
<PackageReference Include="iTextSharp" Version="5.5.13.3" />
<PackageReference Include="MailJet.Api" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.3" />
@ -33,7 +35,6 @@
<ItemGroup>
<Folder Include="NewFolder\" />
<Folder Include="wwwroot\Images\" />
</ItemGroup>
</Project>

BIN
Web/wwwroot/Images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB