feat(calendar): fuzzy Polish name matching + richer ICS/Google Calendar export
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Name linking now handles Polish declensions (Iwonę/Iwoną/Iwony → Iwona) using stem-based regex matching. ICS and Google Calendar exports now include full event description, speaker name, and properly formatted newlines. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
08a499ac40
commit
c456ab49af
@ -144,10 +144,25 @@ def _enrich_event_description(db, html):
|
||||
).all()
|
||||
members = sorted(members, key=lambda m: len(m.name), reverse=True)
|
||||
|
||||
# person_map: exact name → url
|
||||
# person_fuzzy: list of (pattern, display_name, url) for declined Polish names
|
||||
person_map = {}
|
||||
person_fuzzy = []
|
||||
for m in members:
|
||||
if m.name not in person_map: # first match wins (longest name already sorted)
|
||||
person_map[m.name] = flask_url_for('public.user_profile', user_id=m.id)
|
||||
if m.name in person_map:
|
||||
continue
|
||||
url = flask_url_for('public.user_profile', user_id=m.id)
|
||||
person_map[m.name] = url
|
||||
# Build fuzzy pattern for Polish name declensions: "Iwona Spaleniak" → "Iwon\w+ Spaleniak"
|
||||
parts = m.name.split()
|
||||
if len(parts) >= 2:
|
||||
first = parts[0]
|
||||
rest = ' '.join(parts[1:])
|
||||
# Stem: keep at least 3 chars, cut last 1-2 chars depending on length
|
||||
stem_len = max(3, len(first) - 2)
|
||||
stem = re.escape(first[:stem_len])
|
||||
fuzzy_pattern = r'\b' + stem + r'\w*\s+' + re.escape(rest) + r'\b'
|
||||
person_fuzzy.append((fuzzy_pattern, m.name, url))
|
||||
|
||||
# Build replacement maps — companies
|
||||
from database import Company
|
||||
@ -173,10 +188,16 @@ def _enrich_event_description(db, html):
|
||||
text = re.sub(url_pattern, url_replacer, text)
|
||||
|
||||
# 2. Link person names (pill badge — green)
|
||||
# First: exact matches
|
||||
for name, url in person_map.items():
|
||||
pattern = r'\b' + re.escape(name) + r'\b'
|
||||
link = f'<a href="{url}" class="person-link" title="Zobacz profil">{name}</a>'
|
||||
text = re.sub(pattern, link, text)
|
||||
# Then: fuzzy matches for Polish declensions (Iwonę → Iwona, etc.)
|
||||
for fuzzy_pattern, display_name, url in person_fuzzy:
|
||||
def fuzzy_replacer(m, _url=url, _display=display_name):
|
||||
return f'<a href="{_url}" class="person-link" title="Zobacz profil">{m.group(0)}</a>'
|
||||
text = re.sub(fuzzy_pattern, fuzzy_replacer, text)
|
||||
|
||||
# 3. Link company names (pill badge — orange)
|
||||
for name, url in company_map.items():
|
||||
|
||||
@ -640,18 +640,26 @@ const _evt = {
|
||||
start: '{{ event.time_start.strftime("%H%M") if event.time_start else "0000" }}',
|
||||
end: '{{ event.time_end.strftime("%H%M") if event.time_end else "" }}',
|
||||
location: {{ (event.location or '')|tojson }},
|
||||
speaker: {{ (event.speaker_name or '')|tojson }},
|
||||
description: {{ (event.description or '')|tojson }},
|
||||
url: window.location.href,
|
||||
};
|
||||
|
||||
function addToGoogleCalendar() {
|
||||
const start = _evt.date + 'T' + _evt.start + '00';
|
||||
const end = _evt.end ? (_evt.date + 'T' + _evt.end + '00') : '';
|
||||
const details = [
|
||||
_evt.speaker ? 'Prowadzący: ' + _evt.speaker : '',
|
||||
_evt.description,
|
||||
'',
|
||||
'Szczegóły: ' + _evt.url,
|
||||
].filter(Boolean).join('\n');
|
||||
const params = new URLSearchParams({
|
||||
action: 'TEMPLATE',
|
||||
text: _evt.title,
|
||||
dates: start + '/' + (end || start),
|
||||
location: _evt.location,
|
||||
details: 'Szczegóły: ' + _evt.url,
|
||||
details: details,
|
||||
ctz: 'Europe/Warsaw',
|
||||
});
|
||||
window.open('https://calendar.google.com/calendar/render?' + params, '_blank');
|
||||
@ -662,8 +670,13 @@ function downloadICS() {
|
||||
const end = _evt.end ? (_evt.date + 'T' + _evt.end + '00') : (_evt.date + 'T' + _evt.start + '00');
|
||||
const uid = 'nordabiz-event-{{ event.id }}@nordabiznes.pl';
|
||||
const now = new Date().toISOString().replace(/[-:]/g,'').split('.')[0] + 'Z';
|
||||
const speaker = {{ (event.speaker_name or '')|tojson }};
|
||||
const desc = (speaker ? 'Prowadzący: ' + speaker + '\\n' : '') + 'Szczegóły: ' + _evt.url;
|
||||
const descParts = [
|
||||
_evt.speaker ? 'Prowadzący: ' + _evt.speaker : '',
|
||||
_evt.description,
|
||||
'',
|
||||
'Szczegóły: ' + _evt.url,
|
||||
].filter(Boolean);
|
||||
const desc = descParts.join('\\n');
|
||||
const ics = [
|
||||
'BEGIN:VCALENDAR',
|
||||
'VERSION:2.0',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user