-
Notifications
You must be signed in to change notification settings - Fork 51
Submitted for review #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| { | ||
| "cSpell.words": [ | ||
| "ackages", | ||
| "aclocal", | ||
| "appxbundle", | ||
| "appxupload", | ||
| "buildtransitive", | ||
| "contentfiles", | ||
| "dlldata", | ||
| "docstates", | ||
| "ebug", | ||
| "ehthumbs", | ||
| "esktop", | ||
| "esult", | ||
| "Fody", | ||
| "Fractors", | ||
| "fseventsd", | ||
| "healthcheck", | ||
| "healthchecksdb", | ||
| "Nsight", | ||
| "ntvs", | ||
| "userosscache", | ||
| "userprefs", | ||
| "wwwroot" | ||
| ] | ||
| } |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Again, this needs to be in a project submission directory. i.e., |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| # Water Drinking Logger | ||
|
|
||
| A web-based CRUD application for tracking daily water intake, built with | ||
| ASP.NET Core Razor Pages and SQLite. Submitted as a solution to the | ||
| [C# Academy — Project #24: Water Drinking Logger](https://thecsharpacademy.com/project/24/water-drinking-logger). | ||
|
|
||
| --- | ||
|
|
||
| ## Features | ||
|
|
||
| - Log water intake entries with a date, quantity, and unit of measure | ||
| - View all records in a paginated table | ||
| - Edit and delete individual records | ||
| - Calculate the total quantity of water logged | ||
| - Export all records to an Excel spreadsheet (`.xlsx`) | ||
|
|
||
| --- | ||
|
|
||
| ## Project Requirements & How They Are Met | ||
|
|
||
| | Requirement | Implementation | | ||
| | --- | --- | | ||
| | This is a web application | ASP.NET Core Razor Pages web app | | ||
| | Users should be able to insert, delete, update and view their drinking water | Full CRUD via Create, Delete, Update, Index Razor Pages | | ||
| | You should use Entity Framework Core as your ORM | `WaterLoggerContext` extends `DbContext`; all DB operations use EF Core LINQ methods | | ||
| | Your database should be SQLite | `Microsoft.EntityFrameworkCore.Sqlite` provider; DB file stored at `DB/drinking_water` | | ||
| | You should have a model to represent the data | `DrinkingWaterModel` with `Id`, `Date`, `Quantity`, and `Measure` fields | | ||
|
|
||
| ### Challenge — Export to Excel | ||
|
|
||
| The application includes an **Export to Spreadsheet** button on the Index page. | ||
| Clicking it downloads a `WaterLog.xlsx` file containing all logged records, with | ||
| a bold header row and auto-fitted column widths. This is implemented via the | ||
| `OnGetExport()` handler in `Index.cshtml.cs` using the **ClosedXML** library. | ||
|
|
||
| --- | ||
|
|
||
| ## Tech Stack | ||
|
|
||
| ### Framework & Runtime | ||
|
|
||
| | Tool | Version | Purpose | | ||
| | --- | --- | --- | | ||
| | [.NET](https://dotnet.microsoft.com/) | 10.0 | Runtime and SDK | | ||
| | [ASP.NET Core Razor Pages](https://learn.microsoft.com/aspnet/core/razor-pages/) | 10.0 | Web framework and page routing | | ||
|
|
||
| ### NuGet Packages | ||
|
|
||
| | Package | Version | Purpose | | ||
| | --- | --- | --- | | ||
| | [Microsoft.EntityFrameworkCore.Sqlite](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite) | 9.0.3 | EF Core provider for SQLite | | ||
| | [Microsoft.EntityFrameworkCore.Design](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Design) | 9.0.3 | Design-time EF Core tools (migrations) | | ||
| | [ClosedXML](https://www.nuget.org/packages/ClosedXML) | 0.105.0 | Generate `.xlsx` Excel files | | ||
|
|
||
| ### Frontend Libraries (bundled via `wwwroot/lib/`) | ||
|
|
||
| | Library | Purpose | | ||
| | --- | --- | | ||
| | [Bootstrap 5](https://getbootstrap.com/) | Responsive layout and button styles | | ||
| | [jQuery](https://jquery.com/) | DOM manipulation | | ||
| | [jQuery Validation](https://jqueryvalidation.org/) | Client-side form validation | | ||
| | [jQuery Validation Unobtrusive](https://github.com/aspnet/jquery-validation-unobtrusive) | ASP.NET Core's unobtrusive validation adapter | | ||
|
|
||
| ### Database | ||
|
|
||
| | Tool | Purpose | | ||
| | --- | --- | | ||
| | [SQLite](https://www.sqlite.org/) | Embedded, file-based relational database (no server required) | | ||
|
|
||
| ### VS Code Extensions (recommended) | ||
|
|
||
| | Extension | Purpose | | ||
| | --- | --- | | ||
| | [C# Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit) | C# language support, IntelliSense, debugging | | ||
| | [SQLite Viewer](https://marketplace.visualstudio.com/items?itemName=qwtel.sqlite-viewer) | Browse the SQLite database file directly in VS Code | | ||
|
|
||
| --- | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| ### Both Platforms | ||
|
|
||
| - [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) | ||
| - Git | ||
|
|
||
| ### Windows | ||
|
|
||
| - Windows 10 / 11 (x64) | ||
| - [Visual Studio 2022](https://visualstudio.microsoft.com/) (with **ASP.NET and | ||
| web development** workload) **or** [VS Code](https://code.visualstudio.com/) | ||
| with the C# Dev Kit extension | ||
|
|
||
| ### Linux | ||
|
|
||
| - Any modern distro (Ubuntu 20.04+, Fedora 36+, Arch, etc.) | ||
| - [VS Code](https://code.visualstudio.com/) with the C# Dev Kit extension | ||
| (recommended) | ||
| - `libssl` and `libicu` — usually pre-installed; if missing, install via your | ||
| package manager | ||
|
|
||
| --- | ||
|
|
||
| ## Installation & Running | ||
|
|
||
| ### 1. Clone the repository | ||
|
|
||
| ```bash | ||
| git clone https://github.com/RyanW84/WaterLoggerPractice.git | ||
| cd WaterLoggerPractice | ||
| ``` | ||
|
|
||
| ### 2. Restore dependencies | ||
|
|
||
| ```bash | ||
| dotnet restore WaterLogger.Ryanw84/WaterLogger.Ryanw84.csproj | ||
| ``` | ||
|
|
||
| ### 3. Run the application | ||
|
|
||
| ```bash | ||
| dotnet run --project WaterLogger.Ryanw84/WaterLogger.Ryanw84.csproj | ||
| ``` | ||
|
|
||
| The app will start and print the local URL (usually `http://localhost:5000`). | ||
| Open it in your browser. | ||
|
|
||
| > **Note:** The SQLite database file is created automatically at | ||
| > `WaterLogger.Ryanw84/DB/drinking_water` on first run — no manual setup | ||
| > required. | ||
|
|
||
| ### Windows — running via Visual Studio | ||
|
|
||
| 1. Open the solution or `WaterLogger.Ryanw84.csproj` in Visual Studio 2022. | ||
| 2. Press **F5** to build and launch with the built-in browser. | ||
|
|
||
| ### Linux — common issues | ||
|
|
||
| | Issue | Fix | | ||
| | --- | --- | | ||
| | `dotnet: command not found` | Install the .NET 10 SDK from [dotnet.microsoft.com](https://dotnet.microsoft.com/download) and ensure `~/.dotnet/tools` or `/usr/share/dotnet` is on your `PATH` | | ||
| | `libssl` errors on older distros | `sudo apt install libssl-dev libicu-dev` (Debian/Ubuntu) | | ||
| | Port already in use | Change the port: `dotnet run --urls "http://localhost:5001"` | | ||
|
|
||
| --- | ||
|
|
||
| ## Project Structure | ||
|
|
||
| ```text | ||
| WaterLoggerPractice/ | ||
| └── WaterLogger.Ryanw84/ | ||
| ├── Data/ | ||
| │ └── WaterLoggerContext.cs # EF Core DbContext | ||
| ├── DB/ | ||
| │ └── drinking_water # SQLite database file (auto-created) | ||
| ├── Models/ | ||
| │ └── DrinkingWaterModel.cs # Entity model (maps to the DB table) | ||
| ├── Pages/ | ||
| │ ├── Index.cshtml(.cs) # List all records + export | ||
| │ ├── Create.cshtml(.cs) # Add a new record | ||
| │ ├── Update.cshtml(.cs) # Edit an existing record | ||
| │ ├── Delete.cshtml(.cs) # Delete a record | ||
| │ └── Shared/ | ||
| │ └── _Layout.cshtml # Shared Bootstrap layout | ||
| ├── wwwroot/ # Static assets (CSS, JS, lib) | ||
| ├── appsettings.json # App config (connection string) | ||
| └── Program.cs # App bootstrap and DI setup | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## Usage | ||
|
|
||
| | Action | How | | ||
| | --- | --- | | ||
| | Add a record | Click **Add Record**, fill in the date, quantity, and measure, then submit | | ||
| | Edit a record | Click the **pencil icon** on any row | | ||
| | Delete a record | Click the **trash icon** on any row and confirm | | ||
| | Calculate total | Click **Calculate Total** to sum all quantities | | ||
| | Export to Excel | Click **Export to Spreadsheet** to download `WaterLog.xlsx` | |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟠 Database File in Source ❓ Why is the database is your source control? |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| using Microsoft.EntityFrameworkCore; | ||
| using WaterLogger.Ryanw84.Models; | ||
|
|
||
| namespace WaterLogger.Ryanw84.Data | ||
| { | ||
| // DbContext is the EF Core equivalent of manually opening a SqliteConnection. | ||
| // It manages the connection, translates LINQ to SQL, and tracks changes. | ||
| public class WaterLoggerContext(DbContextOptions<WaterLoggerContext> options) : DbContext(options) | ||
| { | ||
| // DbSet<T> maps to a table. You query it with LINQ instead of writing SQL strings. | ||
| public DbSet<DrinkingWaterModel> DrinkingWater { get; set; } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| using System.ComponentModel.DataAnnotations; | ||
| using System.ComponentModel.DataAnnotations.Schema; | ||
|
|
||
| namespace WaterLogger.Ryanw84.Models | ||
| { | ||
| [Table("drinking_water")] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟢 Good Use of DataAnnotations 💡 Proper use of |
||
| public class DrinkingWaterModel | ||
| { | ||
| public int Id { get; set; } | ||
|
|
||
| [DisplayFormat(DataFormatString = "{0:dd-MM-yy}", ApplyFormatInEditMode = true)] | ||
| public DateTime Date { get; set; } | ||
|
|
||
| [Range(0, float.MaxValue, ErrorMessage = "Value for {0} must be a positive.")] | ||
| public float Quantity { get; set; } | ||
|
|
||
| [Required] | ||
| public string? Measure { get; set; } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| @page | ||
| @model WaterLogger.Ryanw84.Pages.Create | ||
| @{ | ||
| } | ||
|
|
||
| <h2>Add New Record</h2> | ||
|
|
||
| <h4>Add Record</h4> | ||
|
Comment on lines
+6
to
+8
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Redundant 💡 Having both of these are probably overkill. |
||
| <hr /> | ||
| <div class="row"> | ||
| <div class="col-md-4"> | ||
| <form method="post"> | ||
| <dl class="row g-3"> | ||
| <dt class="col-sm-3pe-4"><label asp-for="DrinkingWater.Date" class="control-label"></label></dt> | ||
| <dd class="col-sm-9-pe2"> | ||
| <input type="date" asp-for="DrinkingWater.Date" class="form-control" /> | ||
| <span asp-validation-for="DrinkingWater.Date" class="text-danger"></span> | ||
| </dd> | ||
| <dt class="col-sm-3-pe2"><label asp-for="DrinkingWater.Quantity" class="control-label"></label></dt> | ||
| <dd class="col-sm-9-pe2"> | ||
| <input type="number" asp-for="DrinkingWater.Quantity" class="form-control" /> | ||
| <span asp-validation-for="DrinkingWater.Quantity" class="text-danger"></span> | ||
| </dd> | ||
| <dt class="col-sm-3-pe2"><label asp-for="DrinkingWater.Measure" class="control-label"></label></dt> | ||
| <dd class="col-sm-9"> | ||
| <select asp-for="DrinkingWater.Measure" class="form-control"> | ||
| <option value="">Select Measure </option> | ||
| <option value="Bottle">Bottle</option> | ||
| <option value="Big Bottle">Big Bottle</option> | ||
| <option value="Glass">Glass</option> | ||
| </select> | ||
| <span asp-validation-for="DrinkingWater.Measure" class="text-danger"></span> | ||
| </dd> | ||
| <dd class="col-sm-9 offset-sm-3"> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟠 Offset ❓ Why is this offset on screens larger than small? It doesn't match the other pages. |
||
| <input type="submit" value="create" class="btn btn-success" /> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Lowercase 💡 You have |
||
| </dd> | ||
| </dl> | ||
| </form> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div> | ||
| <a asp-page="Index">Back To List</a> | ||
| </div> | ||
|
|
||
| @section Scripts{ | ||
| @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| using Microsoft.AspNetCore.Mvc; | ||
| using Microsoft.AspNetCore.Mvc.RazorPages; | ||
| using WaterLogger.Ryanw84.Data; | ||
| using WaterLogger.Ryanw84.Models; | ||
|
|
||
| namespace WaterLogger.Ryanw84.Pages | ||
| { | ||
|
Comment on lines
+6
to
+7
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟠 Namespaces 💡 As per the academy's code-conventions, use file-scoped namespaces. ➡️ From C# 10, we are able to remove the block/braces and save on level of indentation, optimising space and improving readability. |
||
| public class Create(WaterLoggerContext db) : PageModel | ||
| { | ||
| private readonly WaterLoggerContext _db = db; | ||
|
|
||
| [BindProperty] | ||
| public DrinkingWaterModel? DrinkingWater { get; set; } | ||
|
|
||
| public IActionResult OnGet() | ||
| { | ||
| return Page(); | ||
| } | ||
|
|
||
| public async Task<IActionResult> OnPostAsync() | ||
| { | ||
| if (!ModelState.IsValid || DrinkingWater is null) | ||
| { | ||
| return Page(); | ||
| } | ||
|
|
||
| // Add() stages the new record in EF's change tracker. | ||
| // SaveChangesAsync() translates that to: INSERT INTO drinking_water (...) VALUES (...) | ||
| _db.DrinkingWater.Add(DrinkingWater); | ||
| await _db.SaveChangesAsync(); | ||
|
|
||
| return RedirectToPage("./Index"); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| @page | ||
| @model WaterLogger.Ryanw84.Pages.DeleteModel | ||
| @{ | ||
| } | ||
|
|
||
| <h3>Are you sure you want to delete this record?</h3> | ||
|
|
||
| <div> | ||
| @if (Model?.DrinkingWater == null) | ||
| { | ||
| <p>Record not found.</p> | ||
| <a asp-page="./Index">Back to List</a> | ||
| } | ||
| else | ||
| { | ||
| <dl class="row"> | ||
| <dt class="col-sm-2"> | ||
| Date | ||
| </dt> | ||
| <dd class="col-sm-10"> | ||
| @Model.DrinkingWater?.Date | ||
| </dd> | ||
| <dt class="col-sm-2"> | ||
| Quantity | ||
| </dt> | ||
| <dd class="col-sm-10"> | ||
| @Model.DrinkingWater?.Quantity | ||
| </dd> | ||
| </dl> | ||
|
|
||
| <form method="post"> | ||
| <input type="hidden" asp-for="DrinkingWater.Id" /> | ||
| <input type="submit" value="Delete" class="btn btn-danger" /><br /> | ||
| <a asp-page="./Index">Back to List</a> | ||
| </form> | ||
| } | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 IDE
💡 Normally IDE settings should not be in source control.