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

50 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-10 18: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 dataclasses import dataclass 

17from random import Random 

18from typing import ClassVar 

19 

20import regex 

21 

22from ..utils.data_parsing import parse_args 

23from ..utils.request_handler import APIRequestHandler 

24from ..utils.utils import ModuleInfo 

25from . import DIR 

26 

27 

28def get_module_info() -> ModuleInfo: 

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

30 return ModuleInfo( 

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

32 name="Zufälliger Text", 

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

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

35 hidden=True, 

36 ) 

37 

38 

39@dataclass(slots=True) 

40class Arguments: 

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

42 

43 seed: str = "" 

44 words: int = 500 

45 

46 def validate(self) -> None: 

47 """Validate this.""" 

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

49 

50 

51WORDS = tuple( 

52 (DIR / "words.txt") 

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

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

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

56 .splitlines() 

57) 

58 

59HTML: str = ( 

60 "<!DOCTYPE html>" 

61 '<html lang="de">' 

62 "<head>" 

63 "<title>{title}</title>" 

64 "</head>" 

65 "<body>" 

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

67 "{text}" 

68 "</body>" 

69 "</html>" 

70) 

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

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

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

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

75) 

76 

77 

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

79 """Generate a random word.""" 

80 word = random.choice(WORDS) 

81 return word.title() if title else word 

82 

83 

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

85 """Generate a random text.""" 

86 text: list[str] = [] 

87 

88 title_next = True 

89 

90 for _ in range(length - 1): 

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

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

93 title_next = False 

94 if add_punctuation: 

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

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

97 title_next = True 

98 if not random.randrange(4): 

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

100 continue 

101 text.append(" ") 

102 

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

104 

105 return "".join(text) 

106 

107 

108class RandomText(APIRequestHandler): 

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

110 

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

112 "text/plain", 

113 "text/html", 

114 ) 

115 

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

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

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

119 # pylint: disable=unused-argument 

120 random = Random(args.seed or None) 

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

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

123 return await self.finish(text) 

124 

125 title = regex.sub( 

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

127 " ", 

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

129 ) 

130 text = "".join( 

131 ( 

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

133 if random.randrange(3) 

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

135 ) 

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

137 ) 

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