Skip to content

Commit 95ab2f9

Browse files
authored
Merge pull request #19 from supermartzin/sq-refactorings
Code cleanup and improvements based on SonarQube suggestions.
2 parents 43a7755 + b05a602 commit 95ab2f9

File tree

17 files changed

+416
-401
lines changed

17 files changed

+416
-401
lines changed

Handlers/RealEstatesWatcher.AdPostsHandlers.Email/EmailNotifyingAdPostsHandler.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010
namespace RealEstatesWatcher.AdPostsHandlers.Email;
1111

1212
public class EmailNotifyingAdPostsHandler(EmailNotifyingAdPostsHandlerSettings settings,
13-
ILogger<EmailNotifyingAdPostsHandler>? logger = default) : IRealEstateAdPostsHandler
13+
ILogger<EmailNotifyingAdPostsHandler>? logger = null) : IRealEstateAdPostsHandler
1414
{
1515
private static class HtmlTemplates
1616
{
1717
public const string FullPage = """
1818
<!DOCTYPE html>
19-
<html>
19+
<html lang="en">
2020
<head>
2121
<meta charset="utf-8">
2222
<title>Real Estate Advertisements</title>
@@ -136,7 +136,7 @@ private static string CreateSingleHtmlPost(RealEstateAdPost post)
136136
postHtml = postHtml.Replace("{$layout}", post.Layout is not Layout.NotSpecified ? post.Layout.ToDisplayString() : "-");
137137

138138
// floor area
139-
postHtml = post.FloorArea is not decimal.Zero ? postHtml.Replace("{$floor-area}", post.FloorArea + " m²") : postHtml.Replace("{$floor-area}", " -");
139+
postHtml = post.FloorArea is not null and not decimal.Zero ? postHtml.Replace("{$floor-area}", post.FloorArea + " m²") : postHtml.Replace("{$floor-area}", " -");
140140

141141
// image
142142
if (post.ImageUrl is not null)
@@ -165,9 +165,9 @@ private static string CreateSingleHtmlPost(RealEstateAdPost post)
165165
}
166166

167167
// additional fees
168-
if (post.AdditionalFees is not decimal.Zero)
168+
if (post.AdditionalFees is not null and not decimal.Zero)
169169
{
170-
postHtml = postHtml.Replace("{$additional-fees}", post.AdditionalFees.ToString("N", new NumberFormatInfo { NumberGroupSeparator = " " }))
170+
postHtml = postHtml.Replace("{$additional-fees}", post.AdditionalFees.Value.ToString("N", new NumberFormatInfo { NumberGroupSeparator = " " }))
171171
.Replace("{$additional-fees-display}", "inline-block");
172172
}
173173
else

Handlers/RealEstatesWatcher.AdPostsHandlers.File/LocalFileAdPostsHandler.cs

Lines changed: 141 additions & 155 deletions
Large diffs are not rendered by default.

Portals/RealEstatesWatcher.AdsPortals.BazosCz/BazosCzAdsPortal.cs

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,65 +8,69 @@
88

99
namespace RealEstatesWatcher.AdsPortals.BazosCz;
1010

11-
public class BazosCzAdsPortal : RealEstateAdsPortalBase
11+
public partial class BazosCzAdsPortal : RealEstateAdsPortalBase
1212
{
13+
[GeneratedRegex(@"\[([0-9.\s]+)\]")]
14+
private static partial Regex DateTimeParseRegex();
15+
1316
public override string Name => "Bazoš.cz";
1417

1518
public BazosCzAdsPortal(string watchedUrl,
16-
ILogger<BazosCzAdsPortal>? logger = default) : base(watchedUrl, logger)
19+
ILogger<BazosCzAdsPortal>? logger = null) : base(watchedUrl, logger)
1720
{
1821
}
1922

2023
protected override string GetPathToAdsElements() => @"//div[@class=""maincontent""]/div[contains(@class,""inzeraty inzeratyflex"")]";
2124

22-
protected override RealEstateAdPost ParseRealEstateAdPost(HtmlNode node) => new(Name,
23-
ParseTitle(node),
24-
ParseText(node),
25-
ParsePrice(node),
26-
Currency.CZK,
27-
ParseLayout(node),
28-
ParseAddress(node),
29-
ParseWebUrl(node, RootHost),
30-
decimal.Zero,
31-
ParseFloorArea(node),
32-
imageUrl: ParseImageUrl(node),
33-
publishTime: ParsePublishDate(node),
34-
priceComment: ParsePriceComment(node));
35-
36-
private static string ParseTitle(HtmlNode node) => node.SelectSingleNode(@".//*[@class=""nadpis""]").InnerText;
37-
38-
private static string ParseText(HtmlNode node) => node.SelectSingleNode(@".//div[@class=""popis""]").InnerText;
39-
40-
private static string ParseAddress(HtmlNode node) => node.SelectSingleNode(@"./div[@class=""inzeratylok""]").InnerHtml.Replace("<br>", " ");
25+
protected override RealEstateAdPost ParseRealEstateAdPost(HtmlNode node) => new()
26+
{
27+
AdsPortalName = Name,
28+
Title = ParseTitle(node),
29+
Text = ParseText(node),
30+
Price = ParsePrice(node),
31+
PriceComment = ParsePriceComment(node),
32+
Currency = Currency.CZK,
33+
Layout = ParseLayout(node),
34+
Address = ParseAddress(node),
35+
WebUrl = ParseWebUrl(node, RootHost),
36+
PublishTime = ParsePublishDate(node),
37+
ImageUrl = ParseImageUrl(node),
38+
FloorArea = ParseFloorArea(node)
39+
};
40+
41+
private static string ParseTitle(HtmlNode node) => node.SelectSingleNode(""".//*[@class="nadpis"]""").InnerText;
42+
43+
private static string ParseText(HtmlNode node) => node.SelectSingleNode(""".//div[@class="popis"]""").InnerText;
44+
45+
private static string ParseAddress(HtmlNode node) => node.SelectSingleNode("""./div[@class="inzeratylok"]""").InnerHtml.Replace("<br>", " ");
4146

4247
private static Uri ParseWebUrl(HtmlNode node, string rootHost)
4348
{
44-
var relativePath = node.SelectSingleNode(@".//*[@class=""nadpis""]").FirstChild.GetAttributeValue("href", string.Empty);
49+
var relativePath = node.SelectSingleNode(""".//*[@class="nadpis"]""").FirstChild.GetAttributeValue("href", string.Empty);
4550

4651
return new Uri(rootHost + relativePath);
4752
}
4853

4954
private static Uri? ParseImageUrl(HtmlNode node)
5055
{
51-
var path = node.SelectSingleNode(@".//img[@class=""obrazek""]")?.GetAttributeValue("src", null);
56+
var path = node.SelectSingleNode(""".//img[@class="obrazek"]""")?.GetAttributeValue("src", null);
5257

5358
return path is not null
5459
? new Uri(path)
55-
: default;
60+
: null;
5661
}
5762

5863
private static DateTime? ParsePublishDate(HtmlNode node)
5964
{
6065
const string dateTimeFormat = "d.M.yyyy";
61-
const string dateTimeParseRegex = @"\[([0-9.\s]+)\]";
6266

63-
var value = node.SelectSingleNode(@".//span[@class=""velikost10""]")?.InnerText;
67+
var value = node.SelectSingleNode(""".//span[@class="velikost10"]""")?.InnerText;
6468
if (value is null)
65-
return default;
69+
return null;
6670

67-
var result = Regex.Match(value, dateTimeParseRegex);
71+
var result = DateTimeParseRegex().Match(value);
6872
if (!result.Success)
69-
return default;
73+
return null;
7074

7175
var dateTimeValue = result.Groups[1].Value.Replace(" ", "");
7276

Portals/RealEstatesWatcher.AdsPortals.BezrealitkyCz/BezrealitkyCzAdsPortal.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,26 @@ namespace RealEstatesWatcher.AdsPortals.BezrealitkyCz;
1010

1111
public class BezrealitkyCzAdsPortal(string watchedUrl,
1212
IWebScraper webScraper,
13-
ILogger<BezrealitkyCzAdsPortal>? logger = default) : RealEstateAdsPortalBase(watchedUrl, webScraper, logger)
13+
ILogger<BezrealitkyCzAdsPortal>? logger = null) : RealEstateAdsPortalBase(watchedUrl, webScraper, logger)
1414
{
1515
public override string Name => "Bezrealitky.cz";
1616

1717
protected override string GetPathToAdsElements() => "//article[contains(@class,\"product\")]";
1818

19-
protected override RealEstateAdPost ParseRealEstateAdPost(HtmlNode node) => new(Name,
20-
ParseTitle(node),
21-
string.Empty,
22-
ParsePrice(node),
23-
Currency.CZK,
24-
ParseLayout(node),
25-
ParseAddress(node),
26-
ParseWebUrl(node),
27-
ParseAdditionalFees(node),
28-
ParseFloorArea(node),
29-
imageUrl: ParseImageUrl(node, RootHost));
19+
protected override RealEstateAdPost ParseRealEstateAdPost(HtmlNode node) => new()
20+
{
21+
AdsPortalName = Name,
22+
Title = ParseTitle(node),
23+
Text = string.Empty,
24+
Price = ParsePrice(node),
25+
Currency = Currency.CZK,
26+
Layout = ParseLayout(node),
27+
Address = ParseAddress(node),
28+
WebUrl = ParseWebUrl(node),
29+
AdditionalFees = ParseAdditionalFees(node),
30+
FloorArea = ParseFloorArea(node),
31+
ImageUrl = ParseImageUrl(node, RootHost)
32+
};
3033

3134
private static string ParseTitle(HtmlNode node) => node.SelectSingleNode(".//p[@class=\"product__note\"]").InnerText;
3235

Portals/RealEstatesWatcher.AdsPortals.BidliCz/BidliCzAdsPortal.cs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class BidliCzAdsPortal : RealEstateAdsPortalBase
1313
public override string Name => "Bidli.cz";
1414

1515
public BidliCzAdsPortal(string watchedUrl,
16-
ILogger<BidliCzAdsPortal>? logger = default) : base(watchedUrl, logger)
16+
ILogger<BidliCzAdsPortal>? logger = null) : base(watchedUrl, logger)
1717
{
1818
PageEncoding = Encoding.GetEncoding("iso-8859-2");
1919
}
@@ -30,19 +30,22 @@ protected override RealEstateAdPost ParseRealEstateAdPost(HtmlNode node)
3030
priceComment = ParsePriceComment(node);
3131
currency = Currency.Other;
3232
}
33-
34-
return new RealEstateAdPost(Name,
35-
ParseTitle(node),
36-
string.Empty,
37-
price,
38-
currency,
39-
ParseLayout(node),
40-
ParseAddress(node),
41-
ParseWebUrl(node, RootHost),
42-
decimal.Zero,
43-
ParseFloorArea(node),
44-
priceComment,
45-
ParseImageUrl(node, RootHost));
33+
34+
return new RealEstateAdPost
35+
{
36+
AdsPortalName = Name,
37+
Title = ParseTitle(node),
38+
Text = string.Empty,
39+
Price = price,
40+
Currency = currency,
41+
Layout = ParseLayout(node),
42+
Address = ParseAddress(node),
43+
WebUrl = ParseWebUrl(node, RootHost),
44+
AdditionalFees = decimal.Zero,
45+
FloorArea = ParseFloorArea(node),
46+
PriceComment = priceComment,
47+
ImageUrl = ParseImageUrl(node, RootHost)
48+
};
4649
}
4750

4851
private static string ParseTitle(HtmlNode node) => node.SelectSingleNode(".//span[@class=\"kategorie\"]").InnerText;
@@ -64,8 +67,8 @@ private static decimal ParsePrice(HtmlNode node)
6467
{
6568
var value = node.SelectSingleNode(".//span[@class=\"cena\"]").InnerText;
6669

67-
return Regex.IsMatch(value, @"\d")
68-
? default
70+
return RegexMatchers.AtLeastOneDigitValue().IsMatch(value)
71+
? null
6972
: value.Trim();
7073
}
7174

Portals/RealEstatesWatcher.AdsPortals.BravisCz/BravisCzAdsPortal.cs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,27 @@
99
namespace RealEstatesWatcher.AdsPortals.BravisCz;
1010

1111
public class BravisCzAdsPortal(string watchedUrl,
12-
ILogger<BravisCzAdsPortal>? logger = default) : RealEstateAdsPortalBase(watchedUrl, logger)
12+
ILogger<BravisCzAdsPortal>? logger = null) : RealEstateAdsPortalBase(watchedUrl, logger)
1313
{
1414
public override string Name => "Bravis.cz";
1515

1616
protected override string GetPathToAdsElements() => "//ul[@class=\"itemslist\"]/li[not(@class)]";
1717

18-
protected override RealEstateAdPost ParseRealEstateAdPost(HtmlNode node) => new(Name,
19-
ParseTitle(node),
20-
string.Empty,
21-
ParsePrice(node),
22-
Currency.CZK,
23-
ParseLayout(node),
24-
ParseAddress(node),
25-
ParseWebUrl(node, RootHost),
26-
ParseAdditionalFees(node),
27-
ParseFloorArea(node),
28-
ParsePriceComment(node),
29-
ParseImageUrl(node, RootHost));
18+
protected override RealEstateAdPost ParseRealEstateAdPost(HtmlNode node) => new()
19+
{
20+
AdsPortalName = Name,
21+
Title = ParseTitle(node),
22+
Text = string.Empty,
23+
Price = ParsePrice(node),
24+
Currency = Currency.CZK,
25+
Layout = ParseLayout(node),
26+
Address = ParseAddress(node),
27+
WebUrl = ParseWebUrl(node, RootHost),
28+
AdditionalFees = ParseAdditionalFees(node),
29+
FloorArea = ParseFloorArea(node),
30+
PriceComment = ParsePriceComment(node),
31+
ImageUrl = ParseImageUrl(node, RootHost)
32+
};
3033

3134
private static string ParseTitle(HtmlNode node) => node.SelectSingleNode(".//h1").InnerText.Trim();
3235

@@ -114,6 +117,6 @@ private static decimal ParseFloorArea(HtmlNode node)
114117

115118
return relativePath is not null
116119
? new Uri(rootHost + relativePath)
117-
: default;
120+
: null;
118121
}
119122
}

Portals/RealEstatesWatcher.AdsPortals.CeskeRealityCz/CeskeRealityCzAdsPortal.cs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,19 @@ public CeskeRealityCzAdsPortal(string watchedUrl,
2121

2222
protected override string GetPathToAdsElements() => "//div[@class=\"g-estates\"]/article";
2323

24-
protected override RealEstateAdPost ParseRealEstateAdPost(HtmlNode node) => new(Name,
25-
ParseTitle(node),
26-
ParseText(node),
27-
ParsePrice(node),
28-
Currency.CZK,
29-
ParseLayout(node),
30-
ParseAddress(node),
31-
ParseWebUrl(node, RootHost),
32-
decimal.Zero,
33-
ParseFloorArea(node),
34-
imageUrl: ParseImageUrl(node));
24+
protected override RealEstateAdPost ParseRealEstateAdPost(HtmlNode node) => new()
25+
{
26+
AdsPortalName = Name,
27+
Title = ParseTitle(node),
28+
Text = ParseText(node),
29+
Price = ParsePrice(node),
30+
Currency = Currency.CZK,
31+
Layout = ParseLayout(node),
32+
Address = ParseAddress(node),
33+
WebUrl = ParseWebUrl(node, RootHost),
34+
FloorArea = ParseFloorArea(node),
35+
ImageUrl = ParseImageUrl(node)
36+
};
3537

3638
private static string ParseTitle(HtmlNode node) => node.SelectSingleNode(".//h2[@class=\"i-estate__header-title\"]").InnerText;
3739

Portals/RealEstatesWatcher.AdsPortals.FlatZoneCz/FlatZoneCzAdsPortal.cs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace RealEstatesWatcher.AdsPortals.FlatZoneCz;
1111

1212
public class FlatZoneCzAdsPortal(string watchedUrl,
1313
IWebScraper webScraper,
14-
ILogger<FlatZoneCzAdsPortal>? logger = default) : RealEstateAdsPortalBase(watchedUrl, webScraper, logger)
14+
ILogger<FlatZoneCzAdsPortal>? logger = null) : RealEstateAdsPortalBase(watchedUrl, webScraper, logger)
1515
{
1616
public override string Name => "FlatZone.cz";
1717

@@ -28,7 +28,7 @@ public override async Task<IList<RealEstateAdPost>> GetLatestRealEstateAdsAsync(
2828
var htmlDoc = new HtmlDocument();
2929
htmlDoc.LoadHtml(pageContent);
3030

31-
Logger?.LogDebug($"({Name}): Downloaded page with ads.");
31+
Logger?.LogDebug("({Name}): Downloaded page with ads.", Name);
3232

3333
// get HTML elements
3434
var elements = htmlDoc.DocumentNode.SelectNodes(GetPathToAdsElements());
@@ -43,7 +43,7 @@ public override async Task<IList<RealEstateAdPost>> GetLatestRealEstateAdsAsync(
4343
// parse posts
4444
var posts = elements.Select(ParseRealEstateAdPost).ToList();
4545

46-
Logger?.LogDebug($"({Name}): Successfully parsed {posts.Count} ads from page.");
46+
Logger?.LogDebug("({Name}): Successfully parsed {PostsCount} ads from page.", Name, posts.Count);
4747

4848
return posts;
4949
}
@@ -63,15 +63,18 @@ public override async Task<IList<RealEstateAdPost>> GetLatestRealEstateAdsAsync(
6363

6464
protected override string GetPathToAdsElements() => "//div[contains(@class,\"project-apartment-card\")]";
6565

66-
protected override RealEstateAdPost ParseRealEstateAdPost(HtmlNode node) => new(Name,
67-
ParseTitle(node),
68-
string.Empty,
69-
ParsePrice(node),
70-
Currency.CZK,
71-
Layout.NotSpecified,
72-
ParseAddress(node),
73-
ParseWebUrl(node),
74-
imageUrl: ParseImageUrl(node));
66+
protected override RealEstateAdPost ParseRealEstateAdPost(HtmlNode node) => new()
67+
{
68+
AdsPortalName = Name,
69+
Title = ParseTitle(node),
70+
Text = string.Empty,
71+
Price = ParsePrice(node),
72+
Currency = Currency.CZK,
73+
Layout = Layout.NotSpecified,
74+
Address = ParseAddress(node),
75+
WebUrl = ParseWebUrl(node),
76+
ImageUrl = ParseImageUrl(node)
77+
};
7578

7679
private static string ParseTitle(HtmlNode node)
7780
{
@@ -105,6 +108,6 @@ private static decimal ParsePrice(HtmlNode node)
105108

106109
return path is not null
107110
? new Uri(path)
108-
: default;
111+
: null;
109112
}
110113
}

0 commit comments

Comments
 (0)