]> git.ipfire.org Git - ipfire.org.git/blame - src/backend/util.py
Use Python's internal asyncio stuff instead of Tornado's
[ipfire.org.git] / src / backend / util.py
CommitLineData
9523790a 1#!/usr/bin/python3
66862195 2
5ef115cd
MT
3import PIL.Image
4import PIL.ImageFilter
2b72638d 5import PIL.ImageOps
5ef115cd
MT
6import io
7import logging
e96e445b 8import random
9523790a 9import re
e96e445b 10import string
75d9b3da 11import unicodedata
e96e445b 12
9523790a
MT
13def parse_search_query(query):
14 q = []
15 for word in query.split():
16 # Is this lexeme negated?
17 negated = word.startswith("!")
18
19 # Remove any special characters
20 word = re.sub(r"\W+", "", word, flags=re.UNICODE)
21 if not word:
22 continue
23
24 # Restore negation
25 if negated:
26 word = "!%s" % word
27
28 q.append(word)
29
30 return " & ".join(q)
31
84604476
MT
32def format_size(s, max_unit=None):
33 units = ("B", "kB", "MB", "GB", "TB")
66862195
MT
34
35 i = 0
36 while s >= 1024 and i < len(units) - 1:
37 s /= 1024
38 i += 1
39
84604476
MT
40 if max_unit and units[i] == max_unit:
41 break
42
66862195
MT
43 return "%.0f%s" % (s, units[i])
44
5ac74b02 45def format_time(s, shorter=True):
66862195
MT
46 #_ = handler.locale.translate
47 _ = lambda x: x
48
49 hrs, s = divmod(s, 3600)
50 min, s = divmod(s, 60)
51
52 if s >= 30:
53 min += 1
54
55 if shorter and not hrs:
56 return _("%(min)d min") % { "min" : min }
57
58 return _("%(hrs)d:%(min)02d hrs") % {"hrs" : hrs, "min" : min}
e96e445b
MT
59
60def random_string(length=8):
61 input_chars = string.ascii_letters + string.digits
62
63 r = (random.choice(input_chars) for i in range(length))
64
65 return "".join(r)
75d9b3da
MT
66
67def normalize(s):
68 # Remove any non-ASCII characters
69 try:
70 s = unicodedata.normalize("NFKD", s)
71 except TypeError:
72 pass
73
74 # Remove excessive whitespace
75 s = re.sub(r"[^\w]+", " ", s)
76
77 return "-".join(s.split())
5ef115cd 78
2de3dacc 79def generate_thumbnail(data, size, square=False, **args):
5ef115cd
MT
80 assert data, "No image data received"
81
82 image = PIL.Image.open(io.BytesIO(data))
83
84 # Save image format
85 format = image.format
86
87 # Remove any alpha-channels
88 if image.format == "JPEG" and not image.mode == "RGB":
89 # Make a white background
90 background = PIL.Image.new("RGBA", image.size, (255,255,255))
91
92 # Convert image to RGBA if not in RGBA, yet
93 if not image.mode == "RGBA":
94 image = image.convert("RGBA")
95
96 # Flatten both images together
97 flattened_image = PIL.Image.alpha_composite(background, image)
98
99 # Remove the alpha channel
100 image = flattened_image.convert("RGB")
101
102 # Resize the image to the desired resolution
2de3dacc 103 if square:
7e222133 104 image = PIL.ImageOps.fit(image, (size, size), PIL.Image.LANCZOS)
2de3dacc
MT
105 else:
106 image.thumbnail((size, size), PIL.Image.LANCZOS)
5ef115cd
MT
107
108 if image.format == "JPEG":
109 # Apply a gaussian blur to make compression easier
110 image = image.filter(PIL.ImageFilter.GaussianBlur(radius=0.05))
111
112 # Arguments to optimise the compression
113 args.update({
114 "subsampling" : "4:2:0",
115 "quality" : 70,
116 })
117
118 with io.BytesIO() as f:
119 # If writing out the image does not work with optimization,
120 # we try to write it out without any optimization.
121 try:
122 image.save(f, format, optimize=True, **args)
123 except:
124 image.save(f, format, **args)
125
126 return f.getvalue()