add proxy for the first part and try to develop the second part
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import warnings
|
||||
|
||||
from ics.attendee import Attendee
|
||||
from ics.parsers.parser import Parser, option
|
||||
from ics.utils import iso_to_arrow, parse_duration, unescape_string
|
||||
|
||||
|
||||
class BaseAlarmParser(Parser):
|
||||
@option(required=True)
|
||||
def parse_trigger(alarm, line):
|
||||
if line.params.get("VALUE", [""])[0] == "DATE-TIME":
|
||||
alarm.trigger = iso_to_arrow(line)
|
||||
elif line.params.get("VALUE", ["DURATION"])[0] == "DURATION":
|
||||
alarm.trigger = parse_duration(line.value)
|
||||
else:
|
||||
warnings.warn(
|
||||
"ics.py encountered a TRIGGER of unknown type '%s'. It has been ignored."
|
||||
% line.params["VALUE"][0]
|
||||
)
|
||||
|
||||
def parse_duration(alarm, line):
|
||||
if line:
|
||||
alarm._duration = parse_duration(line.value)
|
||||
|
||||
def parse_repeat(alarm, line):
|
||||
if line:
|
||||
alarm._repeat = int(line.value)
|
||||
|
||||
|
||||
class CustomAlarmParser(BaseAlarmParser):
|
||||
def parse_action(alarm, line, required=True):
|
||||
print(line)
|
||||
if line:
|
||||
alarm._action = line.value
|
||||
|
||||
|
||||
class AudioAlarmParser(BaseAlarmParser):
|
||||
def parse_attach(alarm, line):
|
||||
if line:
|
||||
alarm._sound = line
|
||||
|
||||
|
||||
class DisplayAlarmParser(BaseAlarmParser):
|
||||
@option(required=True)
|
||||
def parse_description(alarm, line):
|
||||
alarm.display_text = unescape_string(line.value) if line else None
|
||||
|
||||
|
||||
class EmailAlarmParser(BaseAlarmParser):
|
||||
@option(required=True)
|
||||
def parse_description(alarm, line):
|
||||
alarm.body = unescape_string(line.value) if line else None
|
||||
|
||||
@option(required=True)
|
||||
def parse_summary(alarm, line):
|
||||
alarm.subject = unescape_string(line.value) if line else None
|
||||
|
||||
@option(required=True, multiple=True)
|
||||
def parse_attendee(alarm, lines):
|
||||
for line in lines:
|
||||
alarm.recipients.append(Attendee.parse(line))
|
||||
|
||||
|
||||
class NoneAlarmParser(BaseAlarmParser):
|
||||
pass
|
||||
@@ -0,0 +1,34 @@
|
||||
from ics.parsers.parser import Parser
|
||||
from ics.utils import unescape_string
|
||||
|
||||
|
||||
class PersonParser(Parser):
|
||||
def parse_cn(person: "Person", value):
|
||||
if value:
|
||||
person.common_name = unescape_string(value)
|
||||
|
||||
def parse_dir(person: "Person", value):
|
||||
if value:
|
||||
person.dir = unescape_string(value)
|
||||
|
||||
def parse_sent_by(person: "Person", value):
|
||||
if value:
|
||||
person.sent_by = unescape_string(value)
|
||||
|
||||
|
||||
class AttendeeParser(PersonParser):
|
||||
def parse_rsvp(attendee: "Attendee", value):
|
||||
if value:
|
||||
attendee.rsvp = bool(value)
|
||||
|
||||
def parse_role(attendee: "Attendee", value):
|
||||
if value:
|
||||
attendee.role = unescape_string(value)
|
||||
|
||||
def parse_partstat(attendee: "Attendee", value):
|
||||
if value:
|
||||
attendee.partstat = unescape_string(value)
|
||||
|
||||
def parse_cutype(attendee: "Attendee", value):
|
||||
if value:
|
||||
attendee.cutype = unescape_string(value)
|
||||
@@ -0,0 +1,98 @@
|
||||
import re
|
||||
|
||||
from ics import Organizer, Attendee
|
||||
from ics.alarm.utils import get_type_from_container
|
||||
from ics.parsers.parser import Parser, option
|
||||
from ics.utils import (iso_precision, iso_to_arrow, parse_duration,
|
||||
unescape_string)
|
||||
|
||||
|
||||
class EventParser(Parser):
|
||||
def parse_dtstamp(event, line):
|
||||
if line:
|
||||
# get the dict of vtimezones passed to the classmethod
|
||||
tz_dict = event._classmethod_kwargs["tz"]
|
||||
event.created = iso_to_arrow(line, tz_dict)
|
||||
|
||||
def parse_last_modified(event, line):
|
||||
if line:
|
||||
tz_dict = event._classmethod_kwargs["tz"]
|
||||
event.last_modified = iso_to_arrow(line, tz_dict)
|
||||
|
||||
def parse_dtstart(event, line):
|
||||
if line:
|
||||
# get the dict of vtimezones passed to the classmethod
|
||||
tz_dict = event._classmethod_kwargs["tz"]
|
||||
event.begin = iso_to_arrow(line, tz_dict)
|
||||
event._begin_precision = iso_precision(line.value)
|
||||
|
||||
def parse_duration(event, line):
|
||||
if line:
|
||||
# TODO: DRY [1]
|
||||
if event._end_time: # pragma: no cover
|
||||
raise ValueError("An event can't have both DTEND and DURATION")
|
||||
event._duration = parse_duration(line.value)
|
||||
|
||||
def parse_dtend(event, line):
|
||||
if line:
|
||||
# TODO: DRY [1]
|
||||
if event._duration:
|
||||
raise ValueError("An event can't have both DTEND and DURATION")
|
||||
# get the dict of vtimezones passed to the classmethod
|
||||
tz_dict = event._classmethod_kwargs["tz"]
|
||||
event._end_time = iso_to_arrow(line, tz_dict)
|
||||
# one could also save the end_precision to check that if begin_precision is day, end_precision also is
|
||||
|
||||
def parse_summary(event, line):
|
||||
event.name = unescape_string(line.value) if line else None
|
||||
|
||||
def parse_organizer(event, line):
|
||||
event.organizer = Organizer.parse(line) if line else None
|
||||
|
||||
@option(multiple=True)
|
||||
def parse_attendee(event, lines):
|
||||
for line in lines:
|
||||
event.attendees.add(Attendee.parse(line))
|
||||
|
||||
def parse_description(event, line):
|
||||
event.description = unescape_string(line.value) if line else None
|
||||
|
||||
def parse_location(event, line):
|
||||
event.location = unescape_string(line.value) if line else None
|
||||
|
||||
def parse_geo(event, line):
|
||||
if line:
|
||||
latitude, _, longitude = unescape_string(line.value).partition(";")
|
||||
event.geo = float(latitude), float(longitude)
|
||||
|
||||
def parse_url(event, line):
|
||||
event.url = unescape_string(line.value) if line else None
|
||||
|
||||
def parse_transp(event, line):
|
||||
if line and line.value in ["TRANSPARENT", "OPAQUE"]:
|
||||
event.transparent = line.value == "TRANSPARENT"
|
||||
|
||||
# TODO : make uid required ?
|
||||
# TODO : add option somewhere to ignore some errors
|
||||
def parse_uid(event, line):
|
||||
if line:
|
||||
event.uid = line.value
|
||||
|
||||
@option(multiple=True)
|
||||
def parse_valarm(event, lines):
|
||||
event.alarms = [get_type_from_container(x)._from_container(x) for x in lines]
|
||||
|
||||
def parse_status(event, line):
|
||||
if line:
|
||||
event.status = line.value
|
||||
|
||||
def parse_class(event, line):
|
||||
if line:
|
||||
event.classification = line.value
|
||||
|
||||
def parse_categories(event, line):
|
||||
event.categories = set()
|
||||
if line:
|
||||
# In the regular expression: Only match unquoted commas.
|
||||
for cat in re.split("(?<!\\\\),", line.value):
|
||||
event.categories.update({unescape_string(cat)})
|
||||
@@ -0,0 +1,80 @@
|
||||
from dateutil.tz import tzical
|
||||
from six import StringIO
|
||||
|
||||
from ics.event import Event
|
||||
from ics.grammar.parse import ContentLine
|
||||
from ics.parsers.parser import Parser, option
|
||||
from ics.todo import Todo
|
||||
from ics.utils import remove_sequence, remove_x
|
||||
|
||||
|
||||
class CalendarParser(Parser):
|
||||
@option(required=True)
|
||||
def parse_prodid(calendar, prodid):
|
||||
calendar._creator = prodid.value
|
||||
|
||||
_version_default = [ContentLine(name="VERSION", value="2.0")]
|
||||
|
||||
@option(required=True, default=_version_default)
|
||||
def parse_version(calendar, line):
|
||||
version = line
|
||||
# TODO : should take care of minver/maxver
|
||||
if ";" in version.value:
|
||||
_, calendar.version = version.value.split(";")
|
||||
else:
|
||||
calendar.version = version.value
|
||||
|
||||
def parse_calscale(calendar, line):
|
||||
calscale = line
|
||||
if calscale:
|
||||
calendar.scale = calscale.value.lower()
|
||||
calendar.scale_params = calscale.params
|
||||
else:
|
||||
calendar.scale = "georgian"
|
||||
calendar.scale_params = {}
|
||||
|
||||
def parse_method(calendar, line):
|
||||
method = line
|
||||
if method:
|
||||
calendar.method = method.value
|
||||
calendar.method_params = method.params
|
||||
else:
|
||||
calendar.method = None
|
||||
calendar.method_params = {}
|
||||
|
||||
@option(multiple=True)
|
||||
def parse_vtimezone(calendar, vtimezones):
|
||||
"""Receives a list of VTIMEZONE blocks.
|
||||
|
||||
Parses them and adds them to calendar._timezones.
|
||||
"""
|
||||
for vtimezone in vtimezones:
|
||||
remove_x(vtimezone) # Remove non standard lines from the block
|
||||
remove_sequence(
|
||||
vtimezone
|
||||
) # Remove SEQUENCE lines because tzical does not understand them
|
||||
fake_file = StringIO()
|
||||
fake_file.write(str(vtimezone)) # Represent the block as a string
|
||||
fake_file.seek(0)
|
||||
timezones = tzical(fake_file) # tzical does not like strings
|
||||
# timezones is a tzical object and could contain multiple timezones
|
||||
for key in timezones.keys():
|
||||
calendar._timezones[key] = timezones.get(key)
|
||||
|
||||
@option(multiple=True)
|
||||
def parse_vevent(calendar, lines):
|
||||
# tz=calendar._timezones gives access to the event factory to the
|
||||
# timezones list
|
||||
def event_factory(x):
|
||||
return Event._from_container(x, tz=calendar._timezones)
|
||||
|
||||
calendar.events = set(map(event_factory, lines))
|
||||
|
||||
@option(multiple=True)
|
||||
def parse_vtodo(calendar, lines):
|
||||
# tz=calendar._timezones gives access to the event factory to the
|
||||
# timezones list
|
||||
def todo_factory(x):
|
||||
return Todo._from_container(x, tz=calendar._timezones)
|
||||
|
||||
calendar.todos = set(map(todo_factory, lines))
|
||||
@@ -0,0 +1,45 @@
|
||||
from typing import Callable, Dict, List, NamedTuple, Optional, Tuple
|
||||
|
||||
from ics.grammar.parse import ContentLine
|
||||
|
||||
|
||||
class ParserOption(NamedTuple):
|
||||
required: bool = False
|
||||
multiple: bool = False
|
||||
default: Optional[List[ContentLine]] = None
|
||||
|
||||
|
||||
class Parser:
|
||||
@classmethod
|
||||
def get_parsers(cls) -> Dict[str, Tuple[Callable, ParserOption]]:
|
||||
methods = [
|
||||
(method_name, getattr(cls, method_name))
|
||||
for method_name in dir(cls)
|
||||
if callable(getattr(cls, method_name))
|
||||
]
|
||||
parsers = [
|
||||
(method_name, method_callable)
|
||||
for (method_name, method_callable) in methods
|
||||
if method_name.startswith("parse_")
|
||||
]
|
||||
return {
|
||||
method_name.split("_", 1)[1]
|
||||
.upper()
|
||||
.replace("_", "-"): (
|
||||
method_callable,
|
||||
getattr(method_callable, "options", ParserOption()),
|
||||
)
|
||||
for (method_name, method_callable) in parsers
|
||||
}
|
||||
|
||||
|
||||
def option(
|
||||
required: bool = False,
|
||||
multiple: bool = False,
|
||||
default: Optional[List[ContentLine]] = None,
|
||||
) -> Callable:
|
||||
def decorator(fn):
|
||||
fn.options = ParserOption(required, multiple, default)
|
||||
return fn
|
||||
|
||||
return decorator
|
||||
@@ -0,0 +1,83 @@
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from ics.alarm.utils import get_type_from_container
|
||||
from ics.grammar.parse import ContentLine
|
||||
from ics.parsers.parser import Parser, option
|
||||
from ics.utils import iso_to_arrow, parse_duration, unescape_string
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .todo_parser import Todo
|
||||
|
||||
|
||||
class TodoParser(Parser):
|
||||
@option(required=True)
|
||||
def parse_dtstamp(todo: "Todo", line: ContentLine):
|
||||
if line:
|
||||
# get the dict of vtimezones passed to the classmethod
|
||||
tz_dict = todo._classmethod_kwargs["tz"]
|
||||
todo.dtstamp = iso_to_arrow(line, tz_dict)
|
||||
|
||||
@option(required=True)
|
||||
def parse_uid(todo: "Todo", line: ContentLine):
|
||||
if line:
|
||||
todo.uid = line.value
|
||||
|
||||
def parse_completed(todo: "Todo", line: ContentLine):
|
||||
if line:
|
||||
# get the dict of vtimezones passed to the classmethod
|
||||
tz_dict = todo._classmethod_kwargs["tz"]
|
||||
todo.completed = iso_to_arrow(line, tz_dict)
|
||||
|
||||
def parse_created(todo: "Todo", line: ContentLine):
|
||||
if line:
|
||||
# get the dict of vtimezones passed to the classmethod
|
||||
tz_dict = todo._classmethod_kwargs["tz"]
|
||||
todo.created = iso_to_arrow(line, tz_dict)
|
||||
|
||||
def parse_description(todo: "Todo", line: ContentLine):
|
||||
todo.description = unescape_string(line.value) if line else None
|
||||
|
||||
def parse_dtstart(todo: "Todo", line: ContentLine):
|
||||
if line:
|
||||
# get the dict of vtimezones passed to the classmethod
|
||||
tz_dict = todo._classmethod_kwargs["tz"]
|
||||
todo.begin = iso_to_arrow(line, tz_dict)
|
||||
|
||||
def parse_location(todo: "Todo", line: ContentLine):
|
||||
todo.location = unescape_string(line.value) if line else None
|
||||
|
||||
def parse_percent_complete(todo: "Todo", line: ContentLine):
|
||||
todo.percent = int(line.value) if line else None
|
||||
|
||||
def parse_priority(todo: "Todo", line: ContentLine):
|
||||
todo.priority = int(line.value) if line else None
|
||||
|
||||
def parse_summary(todo: "Todo", line: ContentLine):
|
||||
todo.name = unescape_string(line.value) if line else None
|
||||
|
||||
def parse_url(todo: "Todo", line: ContentLine):
|
||||
todo.url = unescape_string(line.value) if line else None
|
||||
|
||||
def parse_due(todo: "Todo", line: ContentLine):
|
||||
if line:
|
||||
# TODO: DRY [1]
|
||||
if todo._duration:
|
||||
raise ValueError("A todo can't have both DUE and DURATION")
|
||||
# get the dict of vtimezones passed to the classmethod
|
||||
tz_dict = todo._classmethod_kwargs["tz"]
|
||||
todo._due_time = iso_to_arrow(line, tz_dict)
|
||||
|
||||
def parse_duration(todo: "Todo", line: ContentLine):
|
||||
if line:
|
||||
# TODO: DRY [1]
|
||||
if todo._due_time: # pragma: no cover
|
||||
raise ValueError("An todo can't have both DUE and DURATION")
|
||||
todo._duration = parse_duration(line.value)
|
||||
|
||||
@option(multiple=True)
|
||||
def parse_valarm(todo: "Todo", lines: List[ContentLine]):
|
||||
todo.alarms = [get_type_from_container(x)._from_container(x) for x in lines]
|
||||
|
||||
def parse_status(todo: "Todo", line: ContentLine):
|
||||
if line:
|
||||
todo.status = line.value
|
||||
Reference in New Issue
Block a user