Clean the ical API

This commit is contained in:
Guillaume Ayoub 2015-02-07 17:26:20 +01:00
parent 46628b7a19
commit 2c4b335fad
6 changed files with 39 additions and 77 deletions

View File

@ -3,7 +3,7 @@
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 Nicolas Kandel # Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter # Copyright © 2008 Pascal Halter
# Copyright © 2008-2013 Guillaume Ayoub # Copyright © 2008-2015 Guillaume Ayoub
# #
# This library is free software: you can redistribute it and/or modify # This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -377,8 +377,8 @@ class Application(object):
item = collection item = collection
else: else:
# Try to get an item matching the path # Try to get an item matching the path
item = collection.get_item( name = xmlutils.name_from_path(environ["PATH_INFO"], collection)
xmlutils.name_from_path(environ["PATH_INFO"], collection)) item = collection.items.get(name)
if item: if item:
# Evolution bug workaround # Evolution bug workaround
@ -414,7 +414,7 @@ class Application(object):
if item_name: if item_name:
# Get collection item # Get collection item
item = collection.get_item(item_name) item = collection.items[item_name]
if item: if item:
items = [item] items = [item]
if collection.resource_type == "calendar": if collection.resource_type == "calendar":
@ -500,7 +500,7 @@ class Application(object):
from_name = xmlutils.name_from_path( from_name = xmlutils.name_from_path(
environ["PATH_INFO"], from_collection) environ["PATH_INFO"], from_collection)
if from_name: if from_name:
item = from_collection.get_item(from_name) item = from_collection.items.get(from_name)
if item: if item:
# Move the item # Move the item
to_url_parts = urlparse(environ["HTTP_DESTINATION"]) to_url_parts = urlparse(environ["HTTP_DESTINATION"])
@ -571,7 +571,7 @@ class Application(object):
collection.set_mimetype(environ.get("CONTENT_TYPE")) collection.set_mimetype(environ.get("CONTENT_TYPE"))
headers = {} headers = {}
item_name = xmlutils.name_from_path(environ["PATH_INFO"], collection) item_name = xmlutils.name_from_path(environ["PATH_INFO"], collection)
item = collection.get_item(item_name) item = collection.items.get(item_name)
# Evolution bug workaround # Evolution bug workaround
etag = environ.get("HTTP_IF_MATCH", "").replace("\\", "") etag = environ.get("HTTP_IF_MATCH", "").replace("\\", "")
@ -588,7 +588,7 @@ class Application(object):
# If the added item doesn't have the same name as the one given # If the added item doesn't have the same name as the one given
# by the client, then there's no obvious way to generate an # by the client, then there's no obvious way to generate an
# etag, we can safely ignore it. # etag, we can safely ignore it.
new_item = collection.get_item(item_name) new_item = collection.items.get(item_name)
if new_item: if new_item:
headers["ETag"] = new_item.etag headers["ETag"] = new_item.etag
else: else:

View File

@ -3,7 +3,7 @@
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 Nicolas Kandel # Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter # Copyright © 2008 Pascal Halter
# Copyright © 2008-2013 Guillaume Ayoub # Copyright © 2008-2015 Guillaume Ayoub
# #
# This library is free software: you can redistribute it and/or modify # This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -194,8 +194,7 @@ class Collection(object):
else: else:
self.owner = None self.owner = None
self.is_principal = principal self.is_principal = principal
self._items = self._parse( self._items = None
self.text, (Event, Todo, Journal, Card, Timezone))
@classmethod @classmethod
def from_path(cls, path, depth="1", include_container=True): def from_path(cls, path, depth="1", include_container=True):
@ -346,10 +345,6 @@ class Collection(object):
return items return items
def get_item(self, name):
"""Get item named ``name`` from collection."""
return self._items.get(name)
def append(self, name, text): def append(self, name, text):
"""Append items from ``text`` to collection. """Append items from ``text`` to collection.
@ -359,14 +354,14 @@ class Collection(object):
new_items = self._parse( new_items = self._parse(
text, (Timezone, Event, Todo, Journal, Card), name) text, (Timezone, Event, Todo, Journal, Card), name)
for new_item in new_items.values(): for new_item in new_items.values():
if new_item.name not in self._items: if new_item.name not in self.items:
self._items[new_item] = new_item self.items[new_item] = new_item
self.write() self.write()
def remove(self, name): def remove(self, name):
"""Remove object named ``name`` from collection.""" """Remove object named ``name`` from collection."""
if name in self._items: if name in self.items:
del self._items[name] del self.items[name]
self.write() self.write()
def replace(self, name, text): def replace(self, name, text):
@ -376,7 +371,7 @@ class Collection(object):
def write(self): def write(self):
"""Write collection with given parameters.""" """Write collection with given parameters."""
text = serialize(self.tag, self.headers, self.items) text = serialize(self.tag, self.headers, self.items.values())
self.save(text) self.save(text)
def set_mimetype(self, mimetype): def set_mimetype(self, mimetype):
@ -459,44 +454,25 @@ class Collection(object):
Header("PRODID:-//Radicale//NONSGML Radicale Server//EN"), Header("PRODID:-//Radicale//NONSGML Radicale Server//EN"),
Header("VERSION:%s" % self.version)) Header("VERSION:%s" % self.version))
def filter_items(self, *item_types):
tags = [item_type.tag for item_type in item_types]
return [item for item in self.items if item.tag in tags]
@property @property
def items(self): def items(self):
"""Get list of all items in collection.""" """Get list of all items in collection."""
return list(self._items.values()) if self._items is None:
self._items = self._parse(
self.text, (Event, Todo, Journal, Card, Timezone))
return self._items
@property
def timezones(self):
"""Get list of all timezones in collection."""
return [
item for item in self.items.values() if item.tag == Timezone.tag]
@property @property
def components(self): def components(self):
"""Get list of all components in collection.""" """Get list of all components in collection."""
return self.filter_items(Event, Todo, Journal, Card) tags = [item_type.tag for item_type in (Event, Todo, Journal, Card)]
return [item for item in self.items.values() if item.tag in tags]
@property
def events(self):
"""Get list of ``Event`` items in calendar."""
return self.filter_items(Event)
@property
def todos(self):
"""Get list of ``Todo`` items in calendar."""
return self.filter_items(Todo)
@property
def journals(self):
"""Get list of ``Journal`` items in calendar."""
return self.filter_items(Journal)
@property
def timezones(self):
"""Get list of ``Timezone`` items in calendar."""
return self.filter_items(Timezone)
@property
def cards(self):
"""Get list of ``Card`` items in address book."""
return self.filter_items(Card)
@property @property
def owner_url(self): def owner_url(self):

View File

@ -139,12 +139,7 @@ class Collection(ical.Collection):
"""Collection's object mapped to the table line.""" """Collection's object mapped to the table line."""
return self.session.query(DBCollection).get(self.path) return self.session.query(DBCollection).get(self.path)
def write(self, headers=None, items=None): def write(self):
headers = headers or self.headers or (
ical.Header("PRODID:-//Radicale//NONSGML Radicale Server//EN"),
ical.Header("VERSION:%s" % self.version))
items = items if items is not None else self.items
if self._db_collection: if self._db_collection:
for item in self._db_collection.items: for item in self._db_collection.items:
for line in item.lines: for line in item.lines:
@ -158,13 +153,13 @@ class Collection(ical.Collection):
db_collection.parent_path = "/".join(self.path.split("/")[:-1]) db_collection.parent_path = "/".join(self.path.split("/")[:-1])
self.session.add(db_collection) self.session.add(db_collection)
for header in headers: for header in self.headers:
db_header = DBHeader() db_header = DBHeader()
db_header.name, db_header.value = header.text.split(":", 1) db_header.name, db_header.value = header.text.split(":", 1)
db_header.collection_path = self.path db_header.collection_path = self.path
self.session.add(db_header) self.session.add(db_header)
for item in items: for item in self.items.values():
db_item = DBItem() db_item = DBItem()
db_item.name = item.name db_item.name = item.name
db_item.tag = item.tag db_item.tag = item.tag
@ -258,11 +253,6 @@ class Collection(ical.Collection):
prop.collection_path = self.path prop.collection_path = self.path
self.session.add(prop) self.session.add(prop)
@property
def items(self):
return self._query(
(ical.Event, ical.Todo, ical.Journal, ical.Card, ical.Timezone))
@property @property
def components(self): def components(self):
return self._query((ical.Event, ical.Todo, ical.Journal, ical.Card)) return self._query((ical.Event, ical.Todo, ical.Journal, ical.Card))

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2012-2013 Guillaume Ayoub # Copyright © 2012-2015 Guillaume Ayoub
# #
# This library is free software: you can redistribute it and/or modify # This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2013 Guillaume Ayoub # Copyright © 2014 Jean-Marc Martins
# Copyright © 2013 Jean-Marc Martins # Copyright © 2014-2015 Guillaume Ayoub
# #
# This library is free software: you can redistribute it and/or modify # This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -43,14 +43,11 @@ class Collection(filesystem.Collection):
ical.Header("PRODID:-//Radicale//NONSGML Radicale Server//EN"), ical.Header("PRODID:-//Radicale//NONSGML Radicale Server//EN"),
ical.Header("VERSION:%s" % self.version)) ical.Header("VERSION:%s" % self.version))
def write(self, headers=None, items=None): def write(self):
self._create_dirs() self._create_dirs()
headers = headers or self.headers for component in self.components:
items = items if items is not None else self.items text = ical.serialize(
timezones = list(set(i for i in items if isinstance(i, ical.Timezone))) self.tag, self.headers, [component] + self.timezones)
components = [i for i in items if isinstance(i, ical.Component)]
for component in components:
text = ical.serialize(self.tag, headers, [component] + timezones)
name = ( name = (
component.name if sys.version_info[0] >= 3 else component.name if sys.version_info[0] >= 3 else
component.name.encode(filesystem.FILESYSTEM_ENCODING)) component.name.encode(filesystem.FILESYSTEM_ENCODING))

View File

@ -3,7 +3,7 @@
# This file is part of Radicale Server - Calendar Server # This file is part of Radicale Server - Calendar Server
# Copyright © 2008 Nicolas Kandel # Copyright © 2008 Nicolas Kandel
# Copyright © 2008 Pascal Halter # Copyright © 2008 Pascal Halter
# Copyright © 2008-2013 Guillaume Ayoub # Copyright © 2008-2015 Guillaume Ayoub
# #
# This library is free software: you can redistribute it and/or modify # This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -445,7 +445,7 @@ def proppatch(path, xml_request, collection):
def put(path, ical_request, collection): def put(path, ical_request, collection):
"""Read PUT requests.""" """Read PUT requests."""
name = name_from_path(path, collection) name = name_from_path(path, collection)
if name in (item.name for item in collection.items): if name in collection.items:
# PUT is modifying an existing item # PUT is modifying an existing item
collection.replace(name, ical_request) collection.replace(name, ical_request)
elif name: elif name:
@ -495,7 +495,6 @@ def report(path, xml_request, collection):
multistatus = ET.Element(_tag("D", "multistatus")) multistatus = ET.Element(_tag("D", "multistatus"))
collection_tag = collection.tag collection_tag = collection.tag
collection_items = collection.items
collection_headers = collection.headers collection_headers = collection.headers
collection_timezones = collection.timezones collection_timezones = collection.timezones
@ -505,7 +504,7 @@ def report(path, xml_request, collection):
if name: if name:
# Reference is an item # Reference is an item
path = "/".join(hreference.split("/")[:-1]) + "/" path = "/".join(hreference.split("/")[:-1]) + "/"
items = (item for item in collection_items if item.name == name) items = [collection.items[name]]
else: else:
# Reference is a collection # Reference is a collection
path = hreference path = hreference