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

31 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-24 10:52 +0000

1import re 

2from typing import TYPE_CHECKING 

3 

4from sqlalchemy import text, ForeignKey 

5from sqlalchemy.dialects import mysql 

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

7 

8from rfpy.model.meta import Base 

9 

10if TYPE_CHECKING: 

11 from rfpy.model.humans import Organisation 

12 from rfpy.model.project import Project 

13 

14 

15class Category(Base): 

16 __tablename__ = "categories" 

17 __table_args__ = ({"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4"},) 

18 

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

20 

21 name: Mapped[str] = mapped_column( 

22 mysql.VARCHAR(length=50), nullable=False, server_default=text("''") 

23 ) 

24 description: Mapped[str] = mapped_column( 

25 mysql.VARCHAR(length=255), nullable=False, server_default=text("''") 

26 ) 

27 org_id: Mapped[str] = mapped_column( 

28 mysql.VARCHAR(length=150), 

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

30 nullable=False, 

31 server_default=text("''"), 

32 ) 

33 

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

35 "Organisation", back_populates="categories" 

36 ) 

37 

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

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

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

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

42 ) 

43 

44 def __repr__(self): 

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

46 

47 

48def clean_search_term(term): 

49 if len(term) > 100: 

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

51 

52 cleaned = [] 

53 tokens = term.split() 

54 for token in tokens: 

55 try: 

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

57 if len(word) < 3: 

58 continue 

59 except Exception: 

60 continue 

61 

62 token = token.lstrip("*") 

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

64 

65 cleaned.append(token) 

66 

67 return " ".join(cleaned)