Refactor HTML generation into shared base handler.#182
Conversation
Extracted common HTML template and post rendering logic into a new HtmlBasedAdPostsHandlerBase class and project. Updated Email and File handlers to inherit from this base, removing duplicated code. Adjusted project references and solution structure accordingly.
Summary of ChangesHello @supermartzin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly improves the maintainability and organization of the application's HTML generation capabilities. By extracting the common HTML templating and post-rendering logic into a new Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request successfully refactors the HTML generation logic into a shared base class, HtmlBasedAdPostsHandlerBase, which is a great improvement for code reuse and maintainability. The Email and File handlers are updated to use this new base class, removing significant code duplication.
My review includes a few suggestions to improve the implementation:
- A critical fix for an invalid Target Framework in the project files.
- Two high-severity bug fixes related to incorrect logic in the email and file handlers.
- Some medium-severity suggestions for performance, correctness, and code style in the new base class.
Overall, this is a solid refactoring. Addressing the identified issues will make it even better.
...EstatesWatcher.AdPostsHandlers.Base.Html/RealEstatesWatcher.AdPostsHandlers.Base.Html.csproj
Show resolved
Hide resolved
| logger?.LogDebug("Received new Real Estate Ad Post: {Post}", adPost); | ||
|
|
||
| await SendEmailAsync("🆕 New Real Estate Advert published!", CreateHtmlBody(adPost, CommonHtmlTemplateElements.TitleNewPosts), cancellationToken).ConfigureAwait(false); | ||
| await SendEmailAsync("🆕 New Real Estate Advert published!", CreateFullHtmlPage([adPost], CommonHtmlTemplateElements.TitleInitialPosts), cancellationToken).ConfigureAwait(false); |
There was a problem hiding this comment.
For a single new ad post notification, you are using TitleInitialPosts which has the text 'Current Real estate offer'. You should use TitleNewPosts which has the text 'NEW Real estate offer' to be consistent with the email subject and the event.
await SendEmailAsync("🆕 New Real Estate Advert published!", CreateFullHtmlPage([adPost], CommonHtmlTemplateElements.TitleNewPosts), cancellationToken).ConfigureAwait(false);| pageContent = pageContent.Insert(index + 8, htmlPostsElements + Environment.NewLine); | ||
| } | ||
| // write to file, keeping the <posts/> element for future appends | ||
| pageContent = pageContent.Insert(index + 8, htmlPostsElements + Environment.NewLine); |
There was a problem hiding this comment.
The comment indicates you intend to keep the <posts/> element for future appends, but the insertion logic is incorrect. pageContent.Insert(index + 8, ...) inserts the new posts after the <posts/> tag. To prepend the new posts and keep the tag for the next run, you should insert at index.
pageContent = pageContent.Insert(index, htmlPostsElements + Environment.NewLine);| postHtmlBuilder = postHtmlBuilder.Replace("{$address-links-display}", "inline-block") | ||
| .Replace("{$address-encoded}", HttpUtility.UrlEncode(post.Address)); |
There was a problem hiding this comment.
The assignment postHtmlBuilder = postHtmlBuilder.Replace(...) is redundant because StringBuilder.Replace modifies the instance in-place and returns this. You can simply chain the calls without the assignment. This applies to the entire method where this pattern is used.
postHtmlBuilder.Replace("{$address-links-display}", "inline-block")
.Replace("{$address-encoded}", HttpUtility.UrlEncode(post.Address));| // price | ||
| if (post.Price is not decimal.Zero) | ||
| { | ||
| postHtmlBuilder = postHtmlBuilder.Replace("{$price}", post.Price.ToString("N", new NumberFormatInfo { NumberGroupSeparator = " " })) |
There was a problem hiding this comment.
Creating new NumberFormatInfo on each call is inefficient. This object should be created once as a static readonly field and reused. This will improve performance, especially when processing a large number of ad posts. This also applies to formatting AdditionalFees on line 69.
You can add a field to the class:
private static readonly NumberFormatInfo SpaceSeparatedNumberFormat = new() { NumberGroupSeparator = " " };And then use it here: post.Price.ToString("N", SpaceSeparatedNumberFormat)
| postHtmlBuilder = postHtmlBuilder.Replace("{$text}", post.Text) | ||
| .Replace("{$text-display}", "table"); |
There was a problem hiding this comment.
Using display: table for a <p> element is unconventional and can lead to unexpected layout behavior. The standard and more predictable value for a paragraph is display: block. Unless there's a specific reason for using table, it's better to change it.
postHtmlBuilder = postHtmlBuilder.Replace("{$text}", post.Text)
.Replace("{$text-display}", "block");Refactored HtmlBasedAdPostsHandlerBase and its derived handlers to accept a NumberFormatInfo parameter for consistent number formatting. Updated EmailNotifyingAdPostsHandler and LocalFileAdPostsHandler to use this parameter, and modified ConsoleRunner to register handlers with a space-separated number format. This improves localization and formatting consistency for numeric values in generated HTML.
|
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request effectively refactors the HTML generation logic into a shared base class, HtmlBasedAdPostsHandlerBase, which significantly reduces code duplication in EmailNotifyingAdPostsHandler and LocalFileAdPostsHandler. The introduction of the RealEstatesWatcher.AdPostsHandlers.Base.Html project is a good structural improvement.
My review includes a few points:
- A critical issue with an invalid
TargetFrameworkin the project files. - A high-severity bug where a template placeholder is not replaced, leading to broken links.
- A medium-severity suggestion to improve the generated HTML's CSS for better predictability.
Overall, this is a solid refactoring. Once the identified issues are addressed, the code will be much cleaner and more maintainable.



Extracted common HTML template and post rendering logic into a new HtmlBasedAdPostsHandlerBase class and project. Updated Email and File handlers to inherit from this base, removing duplicated code. Adjusted project references and solution structure accordingly.