Coverage for an_website/random_text/random_text.py: 100.000%

51 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-11-16 19:56 +0000

1# This program is free software: you can redistribute it and/or modify 

2# it under the terms of the GNU Affero General Public License as 

3# published by the Free Software Foundation, either version 3 of the 

4# License, or (at your option) any later version. 

5# 

6# This program is distributed in the hope that it will be useful, 

7# but WITHOUT ANY WARRANTY; without even the implied warranty of 

8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

9# GNU Affero General Public License for more details. 

10# 

11# You should have received a copy of the GNU Affero General Public License 

12# along with this program. If not, see <https://www.gnu.org/licenses/>. 

13 

14"""The random text API of the website.""" 

15 

16from __future__ import annotations 

17 

18from dataclasses import dataclass 

19from random import Random 

20from typing import ClassVar 

21 

22import regex 

23 

24from ..utils.data_parsing import parse_args 

25from ..utils.request_handler import APIRequestHandler 

26from ..utils.utils import ModuleInfo 

27from . import DIR 

28 

29 

30def get_module_info() -> ModuleInfo: 

31 """Create and return the ModuleInfo for this module.""" 

32 return ModuleInfo( 

33 handlers=((r"/api/zufaelliger-text", RandomText),), 

34 name="Zufälliger Text", 

35 description="Generiere zu jedem Request einen zufälligen Text.", 

36 path="/api/zufaelliger-text", 

37 hidden=True, 

38 ) 

39 

40 

41@dataclass(slots=True) 

42class Arguments: 

43 """The arguments for the random text API.""" 

44 

45 seed: str = "" 

46 words: int = 500 

47 

48 def validate(self) -> None: 

49 """Validate this.""" 

50 self.words = max(0, min(self.words, 2000)) 

51 

52 

53WORDS = tuple( 

54 (DIR / "words.txt") 

55 .read_text(encoding="UTF-8") 

56 .replace("sozial", "asozial") 

57 .replace("Sozial", "Asozial") 

58 .splitlines() 

59) 

60 

61HTML: str = ( 

62 "<!DOCTYPE html>" 

63 '<html lang="de">' 

64 "<head>" 

65 "<title>{title}</title>" 

66 "</head>" 

67 "<body>" 

68 "<h1>{title}</h1>" 

69 "{text}" 

70 "</body>" 

71 "</html>" 

72) 

73HTML_BLOCKS: tuple[str, ...] = ( 

74 "<blockquote>{p}</blockquote>", 

75 "<code>{p}</code>", 

76 "<pre>{p}</pre>", 

77) 

78 

79 

80def generate_random_word(random: Random, title: bool = False) -> str: 

81 """Generate a random word.""" 

82 word = random.choice(WORDS) 

83 return word.title() if title else word 

84 

85 

86def generate_random_text(random: Random, length: int, end: str = "") -> str: 

87 """Generate a random text.""" 

88 text: list[str] = [] 

89 

90 title_next = True 

91 

92 for _ in range(length - 1): 

93 text.append(generate_random_word(random, title=title_next)) 

94 add_punctuation = not random.randrange(9) and not title_next 

95 title_next = False 

96 if add_punctuation: 

97 text.append(random.choice(".?!.?!,;")) 

98 if text[-1] not in ",;": 

99 title_next = True 

100 if not random.randrange(4): 

101 text.append("\n\n") 

102 continue 

103 text.append(" ") 

104 

105 text.extend((generate_random_word(random, title=title_next), end)) 

106 

107 return "".join(text) 

108 

109 

110class RandomText(APIRequestHandler): 

111 """The request handler for the random text API.""" 

112 

113 POSSIBLE_CONTENT_TYPES: ClassVar[tuple[str, ...]] = ( 

114 "text/plain", 

115 "text/html", 

116 ) 

117 

118 @parse_args(type_=Arguments, validation_method="validate") 

119 async def get(self, args: Arguments, *, head: bool = False) -> None: 

120 """Handle GET requests to the random text API.""" 

121 # pylint: disable=unused-argument 

122 random = Random(args.seed or None) 

123 text = generate_random_text(random, args.words, end=".") 

124 if self.content_type == "text/plain": 

125 return await self.finish(text) 

126 

127 title = regex.sub( 

128 r"[.?!,;]*\s+", 

129 " ", 

130 generate_random_text(random, random.randint(2, 4)), 

131 ) 

132 text = "".join( 

133 ( 

134 f"<p>{paragraph}</p>" 

135 if random.randrange(3) 

136 else random.choice(HTML_BLOCKS).format(p=paragraph) 

137 ) 

138 for paragraph in text.split("\n\n") 

139 ) 

140 return await self.finish(HTML.format(title=title, text=text))