Coverage for postrfp/model/misc.py: 100%

30 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-22 21:34 +0000

1import re 

2from typing import TYPE_CHECKING 

3 

4from sqlalchemy import text, ForeignKey 

5from sqlalchemy.orm import Mapped, mapped_column, relationship, DynamicMapped 

6from sqlalchemy.types import VARCHAR 

7 

8from postrfp.model.meta import Base 

9 

10if TYPE_CHECKING: 

11 from postrfp.model.humans import Organisation 

12 from postrfp.model.project import Project 

13 

14 

15class Category(Base): 

16 __tablename__ = "categories" 

17 

18 public_attrs = ["id", "name", "description"] 

19 

20 name: Mapped[str] = mapped_column( 

21 VARCHAR(length=50), nullable=False, server_default=text("''") 

22 ) 

23 description: Mapped[str] = mapped_column( 

24 VARCHAR(length=255), nullable=False, server_default=text("''") 

25 ) 

26 org_id: Mapped[str] = mapped_column( 

27 VARCHAR(length=150), 

28 ForeignKey("organisations.id", ondelete="CASCADE", onupdate="CASCADE"), 

29 nullable=False, 

30 server_default=text("''"), 

31 ) 

32 

33 organisation: Mapped["Organisation"] = relationship( 

34 "Organisation", back_populates="categories" 

35 ) 

36 

37 # TODO - does this expose security hole enabling org A to 

38 # get a category and lookup projects for org B? 

39 projects: DynamicMapped["Project"] = relationship( 

40 "Project", secondary="project_categories", lazy="dynamic", viewonly=True 

41 ) 

42 

43 def __repr__(self): 

44 return "(%s) %s" % (self.org_id, self.name) 

45 

46 

47def clean_search_term(term): 

48 if len(term) > 100: 

49 raise ValueError("search term cannot be longer than 100 characters") 

50 

51 cleaned = [] 

52 tokens = term.split() 

53 for token in tokens: 

54 try: 

55 word = re.match(r"\W*(\w+)", token).groups()[0] 

56 if len(word) < 3: 

57 continue 

58 except Exception: 

59 continue 

60 

61 token = token.lstrip("*") 

62 token = token.rstrip("-+~") 

63 

64 cleaned.append(token) 

65 

66 return " ".join(cleaned)