Skip to content

Commit 94bab3f

Browse files
committed
fix: match meetup titles with event prefixes
1 parent 3b7ec9a commit 94bab3f

2 files changed

Lines changed: 89 additions & 3 deletions

File tree

SgfDevs.Tests/SessionizeSyncPlannerTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,55 @@ public void FindMatch_MatchesNormalizedTitlesNearTheSameDate()
140140
Assert.Equal("1", match!.Id);
141141
}
142142

143+
[Fact]
144+
public void FindMatch_MatchesMeetupTitlesWithEventPrefix()
145+
{
146+
var sessionStartsAtLocal = new DateTime(2026, 5, 6, 18, 30, 0);
147+
var events = new[]
148+
{
149+
new MeetupApiEventDto
150+
{
151+
Id = "1",
152+
Title = "Dev Night - Software History 101",
153+
EventUrl = "https://meetup.example/events/1",
154+
DateTime = new DateTime(2026, 5, 6, 18, 0, 0)
155+
}
156+
};
157+
158+
var match = _matcher.FindMatch(events, "Software History 101", sessionStartsAtLocal);
159+
160+
Assert.NotNull(match);
161+
Assert.Equal("1", match!.Id);
162+
}
163+
164+
[Fact]
165+
public void FindMatch_PrefersExactTitleOverPrefixedVariant()
166+
{
167+
var sessionStartsAtLocal = new DateTime(2026, 5, 6, 18, 30, 0);
168+
var events = new[]
169+
{
170+
new MeetupApiEventDto
171+
{
172+
Id = "1",
173+
Title = "Dev Night - Software History 101",
174+
EventUrl = "https://meetup.example/events/1",
175+
DateTime = new DateTime(2026, 5, 6, 18, 0, 0)
176+
},
177+
new MeetupApiEventDto
178+
{
179+
Id = "2",
180+
Title = "Software History 101",
181+
EventUrl = "https://meetup.example/events/2",
182+
DateTime = new DateTime(2026, 5, 6, 18, 5, 0)
183+
}
184+
};
185+
186+
var match = _matcher.FindMatch(events, "Software History 101", sessionStartsAtLocal);
187+
188+
Assert.NotNull(match);
189+
Assert.Equal("2", match!.Id);
190+
}
191+
143192
[Fact]
144193
public void FindMatch_ReturnsNullWhenOnlyFarAwayDatesExist()
145194
{

SgfDevs/Dev/EventSync/MeetupEventMatcher.cs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,49 @@ public class MeetupEventMatcher
1414
var normalizedSessionTitle = Normalize(sessionTitle);
1515

1616
return events
17-
.Where(meetupEvent => string.Equals(Normalize(meetupEvent.Title), normalizedSessionTitle, StringComparison.Ordinal))
18-
.Where(meetupEvent => Math.Abs((meetupEvent.DateTime.Date - sessionStartsAtLocal.Date).TotalDays) <= 2)
19-
.OrderBy(meetupEvent => Math.Abs((meetupEvent.DateTime - sessionStartsAtLocal).TotalMinutes))
17+
.Select(meetupEvent => new
18+
{
19+
Event = meetupEvent,
20+
MatchRank = GetMatchRank(normalizedSessionTitle, Normalize(meetupEvent.Title))
21+
})
22+
.Where(item => item.MatchRank.HasValue)
23+
.Where(item => Math.Abs((item.Event.DateTime.Date - sessionStartsAtLocal.Date).TotalDays) <= 2)
24+
.OrderBy(item => item.MatchRank)
25+
.ThenBy(item => Math.Abs((item.Event.DateTime - sessionStartsAtLocal).TotalMinutes))
26+
.Select(item => item.Event)
2027
.FirstOrDefault();
2128
}
2229

30+
private static int? GetMatchRank(string normalizedSessionTitle, string normalizedMeetupTitle)
31+
{
32+
if (string.Equals(normalizedMeetupTitle, normalizedSessionTitle, StringComparison.Ordinal))
33+
{
34+
return 0;
35+
}
36+
37+
if (ContainsWholePhrase(normalizedMeetupTitle, normalizedSessionTitle))
38+
{
39+
return 1;
40+
}
41+
42+
if (ContainsWholePhrase(normalizedSessionTitle, normalizedMeetupTitle))
43+
{
44+
return 2;
45+
}
46+
47+
return null;
48+
}
49+
50+
private static bool ContainsWholePhrase(string source, string phrase)
51+
{
52+
if (string.IsNullOrWhiteSpace(source) || string.IsNullOrWhiteSpace(phrase))
53+
{
54+
return false;
55+
}
56+
57+
return $" {source} ".Contains($" {phrase} ", StringComparison.Ordinal);
58+
}
59+
2360
internal static string Normalize(string value)
2461
{
2562
var builder = new StringBuilder(value.Length);

0 commit comments

Comments
 (0)