Great Migration: spline.plugins => splinext
[zzz-spline-users.git] / splinext / users / model / __init__.py
1 # encoding: utf8
2 import colorsys
3 from math import sin, pi
4 import random
5
6 from sqlalchemy import Column, ForeignKey
7 from sqlalchemy.orm import relation
8 from sqlalchemy.types import Integer, Unicode
9
10 from spline.model.meta import TableBase
11
12 class User(TableBase):
13 __tablename__ = 'users'
14 id = Column(Integer, primary_key=True)
15 name = Column(Unicode(length=20), nullable=False)
16 unique_identifier = Column(Unicode(length=32), nullable=False)
17
18 def __init__(self, *args, **kwargs):
19 # Generate a unique hash if one isn't provided (which it shouldn't be)
20 if 'unique_identifier' not in kwargs:
21 ident = u''.join(random.choice(u'0123456789abcdef')
22 for _ in range(32))
23 kwargs['unique_identifier'] = ident
24
25 super(User, self).__init__(*args, **kwargs)
26
27 @property
28 def unique_colors(self):
29 """Returns a list of (width, '#rrggbb') tuples that semi-uniquely
30 identify this user.
31 """
32 width_blob, colors_blob = self.unique_identifier[0:8], \
33 self.unique_identifier[8:32]
34
35 widths = []
36 for i in range(4):
37 width_hex = width_blob[i*2:i*2+2]
38 widths.append(int(width_hex, 16))
39 total_width = sum(widths)
40
41 ret = []
42 last_hue = None
43 for i in range(4):
44 raw_hue = int(colors_blob[i*6:i*6+2], 16) / 256.0
45 if last_hue:
46 # Make adjacent hues relatively close together, to avoid green
47 # + purple sorts of clashes.
48 # Minimum distance is 0.1; maximum is 0.35. Leaves half the
49 # spectrum available for any given color.
50 # Change 0.0–0.1 to -0.35–-0.1, 0.1–0.35
51 hue_offset = raw_hue * 0.5 - 0.25
52 if raw_hue < 0:
53 raw_hue -= 0.1
54 else:
55 raw_hue += 0.1
56
57 h = last_hue + raw_hue
58 else:
59 h = raw_hue
60 last_hue = h
61
62 l = int(colors_blob[i*6+2:i*6+4], 16) / 256.0
63 s = int(colors_blob[i*6+4:i*6+6], 16) / 256.0
64
65 # Secondary colors are extremely biased against when picking
66 # randomly from the hue spectrum.
67 # To alleviate this, try to bias hue towards secondary colors.
68 # This adjustment is based purely on experimentation; sin() works
69 # well because hue is periodic, * 6 means each period is 1/3 the
70 # hue spectrum, and the final / 24 is eyeballed
71 h += sin(h * pi * 6) / 24
72
73 # Cap lightness to 0.4 to 0.95, so it's not too close to white or
74 # black
75 l = l * 0.6 + 0.3
76
77 # Cap saturation to 0.5 to 1.0, so the color isn't too gray
78 s = s * 0.6 + 0.3
79
80 r, g, b = colorsys.hls_to_rgb(h, l, s)
81 color = "#{0:02x}{1:02x}{2:02x}".format(
82 int(r * 256),
83 int(g * 256),
84 int(b * 256),
85 )
86
87 ret.append((1.0 * widths[i] / total_width, color))
88
89 return ret
90
91
92 class OpenID(TableBase):
93 __tablename__ = 'openid'
94 openid = Column(Unicode(length=255), primary_key=True)
95 user_id = Column(Integer, ForeignKey('users.id'))
96 user = relation(User, lazy=False, backref='openids')