Skip to content

Commit e93ea10

Browse files
committed
Implement missing features around kind attribute
The query parser now recognizes the supported values and accepts any prefix of them. Unknown kind values result in a generic term query. The "org" value is not transformed into "organisation" as this was some awkward special case.
1 parent 3d162a8 commit e93ea10

File tree

5 files changed

+41
-21
lines changed

5 files changed

+41
-21
lines changed

khard/carddav_object.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
which can be found here:
55
- version 3.0: https://tools.ietf.org/html/rfc2426
66
- version 4.0: https://tools.ietf.org/html/rfc6350
7+
8+
For further details about the application and device values for the kind
9+
field see:
10+
- application: https://tools.ietf.org/html/rfc6473
11+
- device: https://tools.ietf.org/html/rfc6869
712
"""
813

914
import copy
@@ -67,9 +72,13 @@ class VCardWrapper:
6772
by the vobject library are enforced here.
6873
"""
6974

70-
_default_kind = "individual"
7175
_default_version = "3.0"
7276
_supported_versions = ("3.0", "4.0")
77+
_default_kind = "individual"
78+
# FIXME we support group for the kind attribute but we do not support
79+
# member attributes in any way.
80+
_supported_kinds = ("individual", "group", "org", "location",
81+
"application", "device")
7382

7483
# vcard v3.0 supports the following type values
7584
phone_types_v3 = ("bbs", "car", "cell", "fax", "home", "isdn", "msg",
@@ -461,12 +470,16 @@ def _prepare_birthday_value(self, date: Date) -> tuple[Optional[str],
461470

462471
@property
463472
def kind(self) -> str:
464-
kind = self.get_first(self._kind_attribute_name().lower(), self._default_kind)
465-
return kind if kind != "org" else "organisation"
473+
return self.get_first(self._kind_attribute_name().lower(), self._default_kind)
466474

467475
@kind.setter
468476
def kind(self, value: str) -> None:
469-
value = value if value != "organisation" else "org"
477+
if value == "":
478+
self._delete_vcard_object(self._kind_attribute_name().lower())
479+
return
480+
value = value.lower()
481+
if not any(k.startswith(value) for k in self._supported_kinds):
482+
raise ValueError(f"Bad value for kind attribute: {value}")
470483
self.vcard.add(self._kind_attribute_name()).value = value
471484

472485
def _kind_attribute_name(self) -> str:
@@ -1272,6 +1285,7 @@ def to_yaml(self) -> str:
12721285

12731286
translation_table = {
12741287
"Formatted name": self.formatted_name,
1288+
"Kind": self.kind,
12751289
"Prefix": self._get_name_prefixes(),
12761290
"First name": self._get_first_names(),
12771291
"Additional": self._get_additional_names(),

khard/query.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,11 @@ def parse(string: str) -> Union[TermQuery, FieldQuery]:
348348
return NameQuery(term)
349349
if field == FIELD_PHONE_NUMBERS:
350350
return PhoneNumberQuery(term)
351+
if field == "kind":
352+
for kind in carddav_object.CarddavObject._supported_kinds:
353+
if kind.startswith(term.lower()):
354+
return FieldQuery(field, kind)
355+
return TermQuery(string)
351356
if field in carddav_object.CarddavObject.get_properties():
352357
return FieldQuery(field, term)
353358
return TermQuery(string)

test/test_command_line_interface.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,9 @@ def test_mixed_kinds(self):
219219
text = [line.rstrip() for line in stdout.getvalue().splitlines()]
220220
expected = [
221221
"Address book: tmp",
222-
"Index Name Phone Email Kind Uid",
223-
"1 ACME Inc. organisation 4",
224-
"2 Wile E. Coyote individual 1"]
222+
"Index Name Phone Email Kind Uid",
223+
"1 ACME Inc. org 4",
224+
"2 Wile E. Coyote individual 1"]
225225
self.assertListEqual(expected, text)
226226

227227
def test_non_individual_kind(self):
@@ -230,8 +230,8 @@ def test_non_individual_kind(self):
230230
text = [line.rstrip() for line in stdout.getvalue().splitlines()]
231231
expected = [
232232
"Address book: tmp",
233-
"Index Name Phone Email Kind Uid",
234-
"1 ACME Inc. organisation 4"]
233+
"Index Name Phone Email Kind Uid",
234+
"1 ACME Inc. org 4"]
235235
self.assertListEqual(expected, text)
236236

237237

test/test_query.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def test_individual_kind_query_without_kind_on_vcard(self):
174174

175175
def test_org_kind_query_without_kind_on_vcard(self):
176176
vcard = load_contact("minimal.vcf")
177-
query = FieldQuery("kind", "organisation")
177+
query = FieldQuery("kind", "org")
178178
self.assertFalse(query.match(vcard))
179179

180180
def test_kind_query_with_nonsensical_value(self):
@@ -183,12 +183,12 @@ def test_kind_query_with_nonsensical_value(self):
183183
self.assertFalse(query.match(vcard))
184184

185185
def test_kind_query_with_explicit_match(self):
186-
contact = TestCarddavObject(kind="organisation", version="4.0")
187-
query = FieldQuery("kind", "organisation")
186+
contact = TestCarddavObject(kind="org", version="4.0")
187+
query = FieldQuery("kind", "org")
188188
self.assertTrue(query.match(contact))
189189

190190
def test_kind_query_with_explicit_mismatch(self):
191-
contact = TestCarddavObject(kind="organisation", version="4.0")
191+
contact = TestCarddavObject(kind="org", version="4.0")
192192
query = FieldQuery("kind", "individual")
193193
self.assertFalse(query.match(contact))
194194

@@ -254,10 +254,10 @@ def test_kind_queries(self):
254254
self.assertEqual(actual, expected)
255255

256256
def test_non_sensical_kind_values_do_not_parse(self):
257-
self.assertIsNone(parse("kind:foo"))
257+
self.assertEqual(parse("kind:foo"), TermQuery("kind:foo"))
258258

259259
def test_kind_queries_only_need_a_substring_of_the_enum(self):
260260
self.assertEqual(parse("kind:ind"), FieldQuery("kind", "individual"))
261261
self.assertEqual(parse("kind:i"), FieldQuery("kind", "individual"))
262-
self.assertEqual(parse("kind:org"), FieldQuery("kind", "organisation"))
263-
self.assertEqual(parse("kind:o"), FieldQuery("kind", "organisation"))
262+
self.assertEqual(parse("kind:org"), FieldQuery("kind", "org"))
263+
self.assertEqual(parse("kind:o"), FieldQuery("kind", "org"))

test/test_yaml.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -238,24 +238,25 @@ def test_update_fn(self):
238238
def test_update_kind(self):
239239
card = create_test_card(version="4.0")
240240
self.assertEqual(card.kind, "individual")
241-
data = {"Kind": "organisation"}
241+
data = {"Kind": "org"}
242242
data = to_yaml(data)
243243
card.update(data)
244-
self.assertEqual(card.kind, "organisation")
244+
self.assertEqual(card.kind, "org")
245245

246246
def test_update_kind_on_3_0_card(self):
247247
card = create_test_card()
248248
self.assertEqual(card.kind, "individual")
249-
data = {"Kind": "organisation"}
249+
data = {"Kind": "org"}
250250
data = to_yaml(data)
251251
card.update(data)
252-
self.assertEqual(card.kind, "organisation")
252+
self.assertEqual(card.kind, "org")
253253

254254
def test_update_with_bad_kind_field_fails(self):
255255
card = create_test_card(version="4.0")
256256
self.assertEqual(card.kind, "individual")
257257
data = to_yaml({"Kind": "foo"})
258-
card.update(data)
258+
with self.assertRaises(ValueError):
259+
card.update(data)
259260
self.assertEqual(card.kind, "individual")
260261

261262
def test_parse_field(self):

0 commit comments

Comments
 (0)