Skip to content

Add collector for London Borough of Newham#134

Open
moley-bot[bot] wants to merge 2 commits intomainfrom
collector/LondonBoroughOfNewham-issue-128-1769769447
Open

Add collector for London Borough of Newham#134
moley-bot[bot] wants to merge 2 commits intomainfrom
collector/LondonBoroughOfNewham-issue-128-1769769447

Conversation

@moley-bot
Copy link

@moley-bot moley-bot bot commented Jan 30, 2026

Summary

This PR adds a new bin collection data collector for London Borough of Newham.

  • Implements ICollector interface
  • Adds integration tests
  • Successfully tested with example postcode from issue

Closes #128

Test Summary

 ==================== Test Summary ====================
 
 --------------------- Collector ----------------------
 
 London Borough of Newham
 
 ------------------- Addresses (66) -------------------
 
 - 135A, LEYTONSTONE ROAD, E15 1LH, 010009008472
 - 141, LEYTONSTONE ROAD, E15 1LH, 000046045316
 - 143, LEYTONSTONE ROAD, E15 1LH, 000046045317
 - 143A, LEYTONSTONE ROAD, E15 1LH, 010093475570
 - 145A, LEYTONSTONE ROAD, E15 1LH, 010012838837
 - ...
 
 --------------------- Bin Types ----------------------
 
 - General Waste (Black)
 - Mixed Recycling (Green Box)
 
 -------------------- Bin Days (1) --------------------
 
 - 02/02/2026 (2 bins):
   - General Waste (Black)
   - Mixed Recycling (Green Box)
 
 ======================================================

Generated automatically by Moley-Bot using Codex CLI

Closes #128

Generated with Codex CLI by Moley-Bot
@moley-bot moley-bot bot mentioned this pull request Jan 30, 2026
  Formatted by Moley-Bot
@BadgerHobbs
Copy link
Owner

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new collector for the London Borough of Newham, complete with integration tests. The implementation is well-structured and correctly follows the multi-step request pattern. My review includes several minor suggestions to better align the code with the repository's style guide, focusing on removing an unused using statement, adding missing trailing commas, simplifying a regular expression, and ensuring addresses are returned in their original API order.

@@ -0,0 +1,275 @@
namespace BinDays.Api.Collectors.Collectors.Councils;

using BinDays.Api.Collectors.Collectors.Vendors;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

This using statement for BinDays.Api.Collectors.Collectors.Vendors is not used within the file. Removing it will help keep the code clean and reduce clutter.

References
  1. The style guide advises removing unused using statements to keep code clean and reduce clutter. (link)

Comment on lines +70 to +71
[GeneratedRegex(@"<a[^>]+/Details/Index/(?<uid>[^""]+)""[^>]*>Select</a></td>\s*<td>(?<line1>[^<]*)</td>\s*<td>(?<line2>[^<]*)</td>\s*<td>(?<postcode>[^<]+)</td>", RegexOptions.IgnoreCase | RegexOptions.Singleline)]
private static partial Regex AddressRegex();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The style guide recommends using the postcode parameter directly rather than extracting it from other data. This regex includes a capture group for the postcode (?<postcode>) which is not used. Removing this unnecessary capture group simplifies the expression.

	[GeneratedRegex(@"<a[^>]+/Details/Index/(?<uid>[^"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'d.Index/(?<uid>[^\"\"]+)\"[^>]*>Select</a></td>\s*<td>(?<line1>[^<]*)</td>\s*<td>(?<line2>[^<]*)</td>\s*<td>[^<]+</td>", RegexOptions.IgnoreCase | RegexOptions.Singleline)]
References
  1. The style guide recommends using the postcode parameter directly instead of parsing it from address data. It also advises removing unused regex methods, which can be extended to removing unused capture groups within a regex. (link)

Comment on lines +112 to +118
var requestBody = ProcessingUtilities.ConvertDictionaryToFormData(new()
{
{ "Address", postcode },
{ "btnSearch", "Search" },
{ "as_sfid", asSfid },
{ "as_fid", asFid },
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The project's style guide requires trailing commas in multi-line initializers to improve diff clarity. Please add a trailing comma to the dictionary being created here.

			var requestBody = ProcessingUtilities.ConvertDictionaryToFormData(new()
			{
				{ "Address", postcode },
				{ "btnSearch", "Search" },
				{ "as_sfid", asSfid },
				{ "as_fid", asFid },
			});
References
  1. The style guide mandates the use of trailing commas in all multi-line initializers to produce cleaner version control diffs. (link)

Comment on lines +120 to +125
Dictionary<string, string> requestHeaders = new()
{
{ "user-agent", Constants.UserAgent },
{ "content-type", "application/x-www-form-urlencoded" },
{ "cookie", cookie },
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

To align with the project's style guide, please add a trailing comma to this multi-line dictionary initializer. This practice helps maintain clean diffs in version control.

			Dictionary<string, string> requestHeaders = new()
			{
				{ "user-agent", Constants.UserAgent },
				{ "content-type", "application/x-www-form-urlencoded" },
				{ "cookie", cookie },
			};
References
  1. The style guide mandates the use of trailing commas in all multi-line initializers to produce cleaner version control diffs. (link)

Comment on lines +149 to +182
var numericAddresses = new List<Address>();
var otherAddresses = new List<Address>();
foreach (Match rawAddress in rawAddresses)
{
var line1 = rawAddress.Groups["line1"].Value.Trim();
var line2 = rawAddress.Groups["line2"].Value.Trim();
var property = string.Join(
", ",
new[] { line1, line2 }.Where(value => !string.IsNullOrWhiteSpace(value))
);

var address = new Address
{
Property = property,
Postcode = postcode,
Uid = rawAddress.Groups["uid"].Value,
};

var hasLeadingDigit = !string.IsNullOrWhiteSpace(line1) && char.IsDigit(line1[0]);

if (hasLeadingDigit)
{
numericAddresses.Add(address);
}
else
{
otherAddresses.Add(address);
}
}

var getAddressesResponse = new GetAddressesResponse
{
Addresses = [.. numericAddresses, .. otherAddresses],
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The style guide specifies that addresses should be returned in the order received from the API, without any sorting. This logic separates addresses into numeric and non-numeric lists, altering the original order. The implementation should be simplified to process and return addresses in the sequence they are parsed.

			// Iterate through each address, and create a new address object
			var addresses = new List<Address>();
			foreach (Match rawAddress in rawAddresses)
			{
				var line1 = rawAddress.Groups["line1"].Value.Trim();
				var line2 = rawAddress.Groups["line2"].Value.Trim();
				var property = string.Join(
					", ",
					new[] { line1, line2 }.Where(value => !string.IsNullOrWhiteSpace(value))
				);

				var address = new Address
				{
					Property = property,
					Postcode = postcode,
					Uid = rawAddress.Groups["uid"].Value,
				};

				addresses.Add(address);
			}

			var getAddressesResponse = new GetAddressesResponse
			{
				Addresses = [.. addresses],
			};
References
  1. The style guide explicitly states not to sort addresses and to return them in the order they are received from the API. (link)

},
];

private const string _baseUrl = "https://bincollection.newham.gov.uk/";
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing summary docstring.

// Prepare client-side request for searching addresses
else if (clientSideResponse.RequestId == 1)
{
clientSideResponse.Headers.TryGetValue("set-cookie", out var setCookieHeader);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't use try/get, either expect the value or not.

var line2 = rawAddress.Groups["line2"].Value.Trim();
var property = string.Join(
", ",
new[] { line1, line2 }.Where(value => !string.IsNullOrWhiteSpace(value))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably don't need to join line1 and line 2, just set the to address property and street.


binDays.Add(nextBinDay);

if (!string.IsNullOrWhiteSpace(previousDateText))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could simplify with loop or something instead.

@BadgerHobbs BadgerHobbs added the new collector Request for a new collector to be supported label Feb 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new collector Request for a new collector to be supported

Projects

None yet

Development

Successfully merging this pull request may close these issues.

London Borough of Newham

1 participant