• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bergercookie / syncall / 10336325820

11 Aug 2024 02:27AM UTC coverage: 56.661%. First build
10336325820

Pull #112

github

web-flow
Merge 16924bf31 into 437635fb1
Pull Request #112: New CalDAV description format

6 of 7 new or added lines in 2 files covered. (85.71%)

1663 of 2935 relevant lines covered (56.66%)

0.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

45.9
/syncall/caldav/caldav_utils.py
1
import traceback
1✔
2
from typing import Dict, List, Optional, Sequence, Tuple
1✔
3
from uuid import UUID
1✔
4

5
import caldav
1✔
6
from bubop import logger
1✔
7
from icalendar.prop import vCategory
1✔
8
from item_synchronizer.resolution_strategy import Item
1✔
9

10

11
def icalendar_component(obj: caldav.CalendarObjectResource):
1✔
12
    """The .icalendar_component isn't picked up by linters
13

14
    Ignore the warning when accessing it.
15
    """
16

17
    return obj.icalendar_component  # type: ignore
×
18

19

20
def calendar_todos(calendar: caldav.Calendar) -> Sequence[caldav.CalendarObjectResource]:
1✔
21
    return calendar.todos(include_completed=True)
×
22

23

24
def _parse_vcategory(vcategory: vCategory) -> Sequence[str]:
1✔
25
    return [str(category) for category in vcategory.cats]
×
26

27

28
def map_ics_to_item(vtodo) -> Dict:
1✔
29
    """
30
    Utility function that extracts the relevant info from an icalendar_component into a python
31
    dict
32
    """
33
    todo_item = {}
×
34
    todo_item["id"] = str(vtodo.get("uid"))
×
35

36
    def _convert_one(name: str) -> str:
×
37
        item = vtodo.get(name)
×
38
        if item:
×
39
            return str(item)
×
40

41
        return ""
×
42

43
    for name in ["status", "priority"]:
×
44
        todo_item[name] = _convert_one(name).lower()
×
45
    for name in ["description", "summary"]:
×
46
        todo_item[name] = _convert_one(name)
×
NEW
47
    todo_item["uuid"] = _convert_one("x-syncall-tw-uuid")
×
48

49
    for date_field in ("due", "created", "completed", "last-modified"):
×
50
        if vtodo.get(date_field):
×
51
            todo_item[date_field] = vtodo[date_field].dt
×
52

53
    vcategories = vtodo.get("categories")
×
54
    if vcategories is not None:
×
55
        # categories might be a vCategory containing the category names (strings) or might
56
        # return a List[vCategory], each vCategory with a single name
57
        # Option 1:
58
        #
59
        # CATEGORIES:bugwarrior
60
        # CATEGORIES:github_working_on_it
61
        # CATEGORIES:programming
62
        # CATEGORIES:remindme
63
        #
64
        # Option 2:
65
        # CATEGORIES:bugwarrior,github_bug,github_help_wanted,github_tw_gcal_sync,pro
66
        all_categories = []
×
67
        if isinstance(vcategories, Sequence):
×
68
            for vcategory in vcategories:
×
69
                all_categories.extend(_parse_vcategory(vcategory))
×
70
        else:
71
            all_categories = [str(category) for category in vtodo["categories"].cats]
×
72
        todo_item["categories"] = all_categories
×
73

74
    return todo_item
×
75

76

77
def parse_caldav_item_desc(
1✔
78
    caldav_item: Item,
79
) -> Tuple[List[str], Optional[UUID]]:
80
    """
81
    Parse and return the necessary TW fields off a caldav Item.
82

83
    Pretty much directly copied from tw_gcal_utils, however we handle status differently, so only return annotations/uuid
84
    """
85
    annotations: List[str] = []
1✔
86
    uuid = None
1✔
87

88
    if "description" not in caldav_item.keys():
1✔
89
        return annotations, uuid
1✔
90

91
    caldav_desc = caldav_item["description"]
1✔
92
    # strip whitespaces, empty lines
93
    lines = [line.strip() for line in caldav_desc.split("\n") if line][1:]
1✔
94

95
    # annotations
96
    i = 0
1✔
97
    for i, line in enumerate(lines):
1✔
98
        parts = line.split(":", maxsplit=1)
1✔
99
        if len(parts) == 2 and parts[0].lower().startswith("* annotation"):
1✔
100
            annotations.append(parts[1].strip())
×
101
        else:
102
            break
1✔
103

104
    if i == len(lines):
1✔
105
        return annotations, uuid
×
106

107
    # Iterate through rest of lines, find only the uuid
108
    for line in lines[i:]:
1✔
109
        parts = line.split(":", maxsplit=1)
1✔
110
        if len(parts) == 2 and parts[0].lower().startswith("* uuid"):
1✔
111
            try:
×
112
                uuid = UUID(parts[1].strip())
×
113
            except ValueError as err:
×
114
                logger.error(
×
115
                    f'Invalid UUID "{err}" provided during caldav -> TW conversion,'
116
                    f" Using None...\n\n{traceback.format_exc()}"
117
                )
118

119
    return annotations, uuid
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc