Skip to content

Commit

Permalink
[feat] rewritten, based on *tentacles* ORM (start with *mother* rev. 78)
Browse files Browse the repository at this point in the history
  • Loading branch information
Guillaume Bour committed Jul 19, 2010
1 parent dd35826 commit 9bdf779
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 288 deletions.
5 changes: 4 additions & 1 deletion strawberry/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@

UUID = '554495b2-497a-4835-8786-a6b870815313'

from data import *
from link import *
from strawberry import *
from link import Link


32 changes: 32 additions & 0 deletions strawberry/data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -*- coding: utf8 -*-
__version__ = "$Revision$ $Date$"
__author__ = "Guillaume Bour <guillaume@bour.cc>"
__license__ = """
Copyright (C) 2010, Guillaume Bour <guillaume@bour.cc>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA..
"""
from tentacles import Object
from tentacles.fields import *


class Tag(Object):
__stor_name__ = 'delicious__tag'

id = Integer(pk=True, autoincrement=True)
tag = String(unique=True)
description = String()


341 changes: 162 additions & 179 deletions strawberry/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,190 +2,173 @@
__version__ = "$Revision$ $Date$"
__author__ = "Guillaume Bour <guillaume@bour.cc>"
__license__ = """
Copyright (C) 2010, Guillaume Bour <guillaume@bour.cc>
Copyright (C) 2010, Guillaume Bour <guillaume@bour.cc>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA..
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA..
"""
import sqlite3
from twisted.web import server
from twisted.web import server
from mother.callable import Callable, callback


class Link(Callable):
def __init__(self, db):
self.db = db
self.cursor = db.cursor()

def GET(self, id):
self.cursor.execute("""
SELECT id, link, name, description, stars, valid, creation, last_change, last_check
FROM delicious__link
WHERE id = ?
""", (id,))
res = self.cursor.fetchone()
if not res:
return (404, None)

res = {
'id' : res[0],
'link' : res[1],
'name' : res[2],
'description' : res[3],
'stars' : res[4],
'valid' : res[5],
'creation' : res[6],
'last_change' : res[7],
'last_check' : res[8],
'tags' : [],
}

self.cursor.execute("""
SELECT id, tag FROM delicious__tag_link, delicious__tag
WHERE link_id = ? AND tag_id = id
""", (res['id'],))

for (id, name) in self.cursor.fetchall():
res['tags'].append({'id': id, 'tag': name})

return res


def PUT(self, content):
if 'link' not in content or 'name' not in content:
return (400, "link, name are required keys")

fields = ['link', 'name', 'description', 'stars', 'valid', 'creation',
'last_change', 'last_check']
f = []
v = []

for key in content.iterkeys():
# other are ignored
if key in fields:
f.append(key); v.append("'%s'" % content[key])

r = "INSERT INTO delicious__link (%s) VALUES (%s)" % (', '.join(f), ', '.join(v))
print r
try:
self.cursor.execute(r)
self.cursor.execute('SELECT last_insert_rowid()')
linkid = self.cursor.fetchone()[0]
except sqlite3.IntegrityError:
return (409, "duplicate link")

if content.has_key('tags'):
inttags = []
strtags = filter(lambda x: isinstance(x, str) or isinstance(x, unicode), content['tags'])

if len(strtags) > 0:
r = "SELECT tag, id FROM delicious__tag WHERE tag IN (%s)" % \
', '.join(["'%s'" % t for t in strtags])
self.cursor.execute(r)
for (tag, id) in self.cursor.fetchall():
inttags.append(id)
strtags.remove(tag)

if len(strtags) > 0:
r = "INSERT INTO delicious__tag VALUES(NULL, ?, NULL)"
for tag in strtags:
print repr(tag)
self.cursor.execute(r, (tag,))
inttags.append(self.cursor.lastrowid)

inttags.extend(filter(lambda x: isinstance(x, int), content['tags']))
r = "INSERT INTO delicious__tag_link VALUES(?, ?)"
for tagid in inttags:
self.cursor.execute(r, (tagid, linkid))

self.db.commit()
return linkid

def DELETE(self, id):
r = "DELETE FROM delicious__link WHERE id = ?"
try:
self.cursor.execute(r, (id,))
if self.cursor.rowcount == 0:
self.db.rollback()
return (404, None)
except sqlite3.IntegrityError:
self.db.rollback()
return (401, "not found")

self.db.commit()
return True

@callback(method=['GET', 'PUT', 'DELETE'])
def icon(self, request, id, content=None):
print "icon:", request.method, id
return self._binary('icon', request, id, content)

@callback(method=['GET', 'PUT', 'DELETE'])
def screenshot(self, request, id, content=None):
print "screenshot:", request.method, id
return self._binary('screenshot', request, id, content)

def _binary(self, field, request, id, content):
if request.method == 'PUT':
data = content.read()
r = "UPDATE delicious__link SET %s = ? WHERE id = ?" % field
self.cursor.execute(r, (sqlite3.Binary(data), id))
self.db.commit()

if self.cursor.rowcount == 1:
return None
return (404, None)

elif request.method == 'DELETE':
r = "UPDATE delicious__link SET %s = NULL WHERE id = ?" % field
self.cursor.execute(r, (id,))
self.db.commit()

if self.cursor.rowcount == 1:
return None
return (404, None)


# GET
r = "SELECT %s FROM delicious__link WHERE id = ?" % field
self.cursor.execute(r, (id,))

# sqlite BLOB is mapped to buffer python type
data = self.cursor.fetchone()[0]
if data is None:
return (404, None)

request.setHeader('Content-Type', "image/vnd.microsoft.icon")
request.write(str(data))
request.finish()

return server.NOT_DONE_YET

@callback
def all(self):
r = "SELECT id FROM delicious__link"
return map(lambda x: x[0], self.cursor.execute(r))

return self.cursor.fetchall()

@callback
def bytag(self, tagname):
r = """SELECT l.id
FROM delicious__link as l, delicious__tag as t, delicious__tag_link as tl
WHERE l.id = tl.link_id AND tl.tag_id = t.id
AND t.tag = ?
"""
return map(lambda x: x[0], self.cursor.execute(r, (tagname,)))
from data import *

from tentacles import Object
from tentacles.fields import *
from tentacles.queryset import filter


class Link(Object, Callable):
__stor_name__ = 'delicious__link'

id = Integer(pk=True, autoincrement=True)
link = String(unique=True, allow_none=False)
name = String()
description = String()
stars = Integer(default=0)
valid = Boolean(default=False)
creation = Datetime(default='now')
last_change = Datetime()
last_check = Datetime()
icon = Binary()
screenshot = Binary()

tags = ReferenceSet(Tag)


def GET(self, id):
link = list(filter(lambda x: x.id == id, Link))
if len(link) == 0:
return (404, None)

link = link[0]
res = {}
for name, fld in link.__fields__.iteritems():
if isinstance(fld, Reference):
continue

res[name] = getattr(link, name)
#TODO: should be done in a generic way
# the best way should probably be to have a tentacles json backend
# For that, we need to be able to switch backend for an object

# JSON encoder should also understand datetime object
if isinstance(fld, Datetime) and res[name] is not None:
res[name] = str(res[name])

res['tags'] = []
for tag in link.tags:
res['tags'].append({'id': tag.id, 'tag': tag.tag})

return res

def PUT(self, content):
if 'link' not in content or 'name' not in content:
return (400, "link, name are mandatory keys")

if 'id' in content and len(filter(lambda x: x.id == content['id'], Link)) > 0:
return (400, "id already exists")
if len(filter(lambda x: x.link == content['link'], Link)) > 0:
return (400, "name must be unique")

link = Link()
for key, value in content.iteritems():
if key == 'tags':
continue

if not key in link.__fields__:
return(409, "unknown field '%s'" % key)

setattr(link, key, value)

"""We query all tags (by tagname), and create new ones"""
if 'tags' in content:
link.tags = list(filter(lambda t: t.tag in content['tags'], Tag))
link.tags.extend([Tag(tag=t) for t in content['tags'] if t not in [tag.tag for tag in link.tags]])

link.save()
return link.id

def DELETE(self, id):
"""
NOTE: associated tags, even if specially created for this link, are not deleted
"""
links = list(filter(lambda x: x.id == id, Link))
if len(links) == 0:
return (404, "not found")
elif len(links) > 1:
return (500, "return several links for the same id")

print 'ZOZ', id, links
links[0].delete()
return (200, True)

@callback(name='icon', method=['GET', 'PUT', 'DELETE'])
def _icon(self, request, id, content=None):
return self._binary('icon', request, id, content)

@callback(name='screenshot', method=['GET', 'PUT', 'DELETE'])
def _screenshot(self, request, id, content=None):
return self._binary('screenshot', request, id, content)

def _binary(self, field, request, id, content):
link = list(filter(lambda x: x.id == id, Link))
if len(link) == 0:
return (404, None)
link = link[0]

if request.method == 'PUT':
setattr(link, field, content.read())
print 'LNK=', link, link.id
link.save()
print(list(filter(lambda x: x.id == id, Link)))

return None

elif request.method == 'DELETE':
setattr(link, field, None)
link.save()
return None

# GET
data = getattr(link, field)
if data is None:
return (204, None)

import magic
m = magic.open(magic.MAGIC_MIME)
m.load()
mime = m.buffer(data)
m.close()

if mime is not None and len(mime) > 0:
request.setHeader('Content-Type', mime.split(';')[0])
request.write(str(getattr(link, field)))
request.finish()

return server.NOT_DONE_YET

@callback
def all(self):
res = map(lambda x: x.id, Link)
print res
return res

@callback
def bytag(self, tagname):
res = list(filter(lambda x: tagname in [name for tag in x.tags], Link))
print res
return res


Loading

0 comments on commit 9bdf779

Please sign in to comment.