Fix the support on grouped vCard properties (#207 )

Parse and match vCard properties that use grouping prefixes (e.g. item1.TEL) by extracting the property name correctly.

Regression caused by the removal of the vobject dependency.
This commit is contained in:
KnugiHK
2026-04-01 01:04:51 +08:00
parent 0056204d87
commit 2ebb389ad1
3 changed files with 49 additions and 9 deletions

View File

@@ -66,13 +66,18 @@ def _parse_vcard_line(line: str) -> tuple[str, dict[str, str], str] | None:
value = line[colon_index + 1:].strip()
# Split property name from parameters
parts = prop_and_params.split(';')
property_name = parts[0].upper()
property_part, *params = prop_and_params.split(';')
# We only care about property name for now, but the grouping mechanism may be
# useful in the future if we want to associate multiple properties together.
parts = property_part.split('.')
_, property_name = parts if len(parts) == 2 else (None, parts[0])
property_name = property_name.upper()
parameters = {}
for part in parts[1:]:
if '=' in part:
key, val = part.split('=', 1)
for param in params:
if '=' in param:
key, val = param.split('=', 1)
parameters[key.upper()] = val.strip('"') # Remove potential quotes from value
return property_name, parameters, value
@@ -98,8 +103,9 @@ def get_vcard_value(entry: str, field_name: str) -> list[str]:
values.append(decode_quoted_printable(cached_line + line, charset))
cached_line = ""
else:
# Skip empty lines or lines that don't start with the target field (after stripping)
if not line or not line.upper().startswith(target_name):
# Skip empty lines or lines that don't start with the target
# field (after stripping), considering potential grouping prefixes
if not line or (not line.upper().startswith(target_name) and f".{target_name}" not in line.upper().split(':')[0]):
continue
parsed = _parse_vcard_line(line)

View File

@@ -42,3 +42,12 @@ VERSION:2.1
TEL;CELL:8889990001
ORG:AAA Car Service
END:VCARD
BEGIN:VCARD
VERSION:2.1
item1.TEL;CELL:7777777778
item2.TEL;CELL:7777777779
item1.FN:Racing Team
item2.FN:Racing Team
END:VCARD

View File

@@ -1,7 +1,7 @@
# from contacts_names_from_vcards import readVCardsFile
import os
from Whatsapp_Chat_Exporter.vcards_contacts import normalize_number, read_vcards_file
from Whatsapp_Chat_Exporter.vcards_contacts import normalize_number, read_vcards_file, get_vcard_value
def test_readVCardsFile():
@@ -17,7 +17,7 @@ def test_readVCardsFile():
# Print the count and the name
print(f"{count}. {name}")
print(data)
assert len(data) == 6
assert len(data) == 8
# Test simple contact name
assert data[0][1] == "Sample Contact"
# Test complex name
@@ -30,6 +30,31 @@ def test_readVCardsFile():
assert data[4][1] == "James Peacock Elementary"
# Test business entry using ORG but not F/FN
assert data[5][1] == "AAA Car Service"
# Test grouped entry
assert data[6][1] == "Racing Team (1)"
assert data[7][1] == "Racing Team (2)"
def test_grouping_mechanism():
no_group_vcf = """
BEGIN:VCARD
VERSION:2.1
TEL;CELL:7777777778
TEL;CELL:7777777779
TEL;CELL:7777777780
ORG:Racing Team
END:VCARD"""
group_vcf = """
BEGIN:VCARD
VERSION:2.1
item1.TEL;CELL:7777777778
item2.TEL;CELL:7777777779
item3.TEL;CELL:7777777780
ORG:Racing Team
END:VCARD"""
assert get_vcard_value(no_group_vcf, "TEL") == ["7777777778", "7777777779", "7777777780"]
assert get_vcard_value(group_vcf, "TEL") == ["7777777778", "7777777779", "7777777780"]
def test_create_number_to_name_dicts():