4 from math
import sin
, pi
7 from sqlalchemy
import Column
, ForeignKey
, or_
8 from sqlalchemy
.ext
.associationproxy
import association_proxy
9 from sqlalchemy
.orm
import relation
10 from sqlalchemy
.orm
.session
import Session
11 from sqlalchemy
.types
import Integer
, PickleType
, Unicode
13 from spline
.model
.meta
import TableBase
15 class AnonymousUser(object):
16 """Fake user object, for when the user isn't actually logged in.
18 Tests as false and tries to respond to method calls the expected way.
22 def __nonzero__(self
):
27 def can(self
, action
):
28 """Anonymous users aren't ever allowed to do anything."""
32 class User(TableBase
):
33 __tablename__
= 'users'
34 id = Column(Integer
, primary_key
=True)
35 name
= Column(Unicode(length
=20), nullable
=False)
36 unique_identifier
= Column(Unicode(length
=32), nullable
=False)
37 stash
= Column(PickleType(pickler
=json
), nullable
=False, default
=dict())
39 def __init__(self
, *args
, **kwargs
):
40 # Generate a unique hash if one isn't provided (which it shouldn't be)
41 if 'unique_identifier' not in kwargs
:
42 ident
= u
''.join(random
.choice(u
'0123456789abcdef')
44 kwargs
['unique_identifier'] = ident
46 super(User
, self
).__init__(*args
, **kwargs
)
49 _default_permissions
= None
50 def can(self
, action
):
51 """Returns True iff this user has permission to do `action`.
53 If `_root_user_id` is this user's id, all permissions are allowed. The
54 property is usually set by the spline-users after_setup hook.
57 if self
.id == self
._root_user_id
:
60 if action
in self
.permissions
:
63 # Permissions assigned to NULL apply to all roles
64 if self
._default_permissions
is None:
65 session
= Session
.object_session(self
)
66 self
._default_permissions
= [
68 for row
in session
.query(RolePermission
)
69 .filter_by(role_id
=None)
72 return (action
in self
._default_permissions
)
75 def unique_colors(self
):
76 """Returns a list of (width, '#rrggbb') tuples that semi-uniquely
79 width_blob
, colors_blob
= self
.unique_identifier
[0:8], \
80 self
.unique_identifier
[8:32]
84 width_hex
= width_blob
[i
*2:i
*2+2]
85 widths
.append(int(width_hex
, 16))
86 total_width
= sum(widths
)
91 raw_hue
= int(colors_blob
[i
*6:i
*6+2], 16) / 256.0
93 # Make adjacent hues relatively close together, to avoid green
94 # + purple sorts of clashes.
95 # Minimum distance is 0.1; maximum is 0.35. Leaves half the
96 # spectrum available for any given color.
97 # Change 0.0–0.1 to -0.35–-0.1, 0.1–0.35
98 hue_offset
= raw_hue
* 0.5 - 0.25
104 h
= last_hue
+ raw_hue
109 l
= int(colors_blob
[i
*6+2:i
*6+4], 16) / 256.0
110 s
= int(colors_blob
[i
*6+4:i
*6+6], 16) / 256.0
112 # Secondary colors are extremely biased against when picking
113 # randomly from the hue spectrum.
114 # To alleviate this, try to bias hue towards secondary colors.
115 # This adjustment is based purely on experimentation; sin() works
116 # well because hue is periodic, * 6 means each period is 1/3 the
117 # hue spectrum, and the final / 24 is eyeballed
118 h
+= sin(h
* pi
* 6) / 24
120 # Cap lightness to 0.4 to 0.95, so it's not too close to white or
124 # Cap saturation to 0.5 to 1.0, so the color isn't too gray
127 r
, g
, b
= colorsys
.hls_to_rgb(h
, l
, s
)
128 color
= "#{0:02x}{1:02x}{2:02x}".format(
134 ret
.append((1.0 * widths
[i
] / total_width
, color
))
138 class OpenID(TableBase
):
139 __tablename__
= 'openid'
140 openid
= Column(Unicode(length
=255), primary_key
=True)
141 user_id
= Column(Integer
, ForeignKey('users.id'), nullable
=False)
145 class Role(TableBase
):
146 __tablename__
= 'roles'
147 id = Column(Integer
, primary_key
=True, nullable
=False)
148 name
= Column(Unicode(64), nullable
=False)
149 icon
= Column(Unicode(64), nullable
=False)
151 class UserRole(TableBase
):
152 __tablename__
= 'user_roles'
153 user_id
= Column(Integer
, ForeignKey('users.id'), primary_key
=True, nullable
=False, autoincrement
=False)
154 role_id
= Column(Integer
, ForeignKey('roles.id'), primary_key
=True, nullable
=False, autoincrement
=False)
156 class RolePermission(TableBase
):
157 __tablename__
= 'role_permissions'
158 id = Column(Integer
, nullable
=False, primary_key
=True)
159 role_id
= Column(Integer
, ForeignKey('roles.id'), nullable
=True)
160 permission
= Column(Unicode(64), nullable
=False)
164 OpenID
.user
= relation(User
, lazy
=False, backref
='openids')
166 Role
.role_permissions
= relation(RolePermission
, backref
='role')
168 User
.roles
= relation(Role
, secondary
=UserRole
.__table__
, backref
='users')
169 User
.role_permissions
= relation(RolePermission
,
170 primaryjoin
=User
.id==UserRole
.user_id
,
171 secondary
=UserRole
.__table__
,
172 secondaryjoin
=UserRole
.role_id
==RolePermission
.role_id
,
173 foreign_keys
=[UserRole
.user_id
, RolePermission
.role_id
],
175 User
.permissions
= association_proxy('role_permissions', 'permission')
177 UserRole
.user
= relation(User
)
178 UserRole
.role
= relation(Role
)