• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SwissDataScienceCenter / renku-python / 9058668052

13 May 2024 07:05AM UTC coverage: 77.713% (-8.4%) from 86.115%
9058668052

Pull #3727

github

web-flow
Merge 128d38387 into 050ed61bf
Pull Request #3727: fix: don't fail session launch when gitlab couldn't be reached

15 of 29 new or added lines in 3 files covered. (51.72%)

2594 existing lines in 125 files now uncovered.

23893 of 30745 relevant lines covered (77.71%)

3.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.72
/renku/domain_model/provenance/agent.py
1
# Copyright Swiss Data Science Center (SDSC). A partnership between
2
# École Polytechnique Fédérale de Lausanne (EPFL) and
3
# Eidgenössische Technische Hochschule Zürich (ETHZ).
4
#
5
# Licensed under the Apache License, Version 2.0 (the "License");
6
# you may not use this file except in compliance with the License.
7
# You may obtain a copy of the License at
8
#
9
#     http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS,
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
# See the License for the specific language governing permissions and
15
# limitations under the License.
16
"""Represent provenance agents."""
7✔
17

18
import re
7✔
19
import uuid
7✔
20
from typing import Optional, cast
7✔
21
from urllib.parse import quote
7✔
22

23
from renku.infrastructure.immutable import Slots
7✔
24
from renku.version import __version__, version_url
7✔
25

26

27
class Agent(Slots):
7✔
28
    """Represent executed software."""
29

30
    __slots__ = ("id", "name")
7✔
31

32
    id: str
7✔
33
    name: str
7✔
34

35
    def __init__(self, *, id: str, name: str, **kwargs):
7✔
36
        super().__init__(id=id, name=name, **kwargs)
7✔
37

38
    def __eq__(self, other):
7✔
39
        if self is other:
×
40
            return True
×
41
        if not isinstance(other, SoftwareAgent):
×
42
            return False
×
43
        return self.id == other.id and self.name == other.name
×
44

45
    def __hash__(self):
7✔
46
        return hash((self.id, self.name))
×
47

48
    @property
7✔
49
    def full_identity(self):
7✔
50
        """Return the identity of this Agent."""
51
        return f"{self.name} <{self.id}>"
1✔
52

53

54
class SoftwareAgent(Agent):
7✔
55
    """Represent executed software."""
56

57

58
# set up the default agent
59
RENKU_AGENT = SoftwareAgent(id=version_url, name=f"renku {__version__}")
7✔
60

61

62
class Person(Agent):
7✔
63
    """Represent a person."""
64

65
    __slots__ = ("affiliation", "alternate_name", "email")
7✔
66

67
    affiliation: Optional[str]
7✔
68
    alternate_name: str
7✔
69
    email: str
7✔
70

71
    def __init__(
7✔
72
        self,
73
        *,
74
        affiliation: Optional[str] = None,
75
        alternate_name: Optional[str] = None,
76
        email: Optional[str] = None,
77
        id: Optional[str] = None,
78
        name: str,
79
    ):
80
        self._validate_email(email)
7✔
81

82
        if id is None or id == "mailto:None" or id.startswith("_:"):
7✔
83
            full_identity = Person.get_full_identity(email, affiliation, name)
7✔
84
            id = Person.generate_id(email, full_identity)
7✔
85

86
        affiliation = affiliation or None
7✔
87
        alternate_name = alternate_name or None
7✔
88

89
        super().__init__(
7✔
90
            affiliation=affiliation, alternate_name=alternate_name, email=email, id=cast(str, id), name=name
91
        )
92

93
    def __eq__(self, other):
7✔
94
        if self is other:
1✔
95
            return True
×
96
        if not isinstance(other, Person):
1✔
97
            return False
×
98
        return self.id == other.id and self.full_identity == other.full_identity
1✔
99

100
    def __hash__(self):
7✔
UNCOV
101
        return hash((self.id, self.full_identity))
×
102

103
    @classmethod
7✔
104
    def from_string(cls, string):
7✔
105
        """Create an instance from a 'Name <email>' string."""
106
        regex_pattern = r"([^<>\[\]]*)" r"(?:<{1}\s*(\S+@\S+\.\S+){0,1}\s*>{1}){0,1}\s*" r"(?:\[{1}(.*)\]{1}){0,1}"
3✔
107
        match = re.search(regex_pattern, string)
3✔
108
        if match is None:
3✔
109
            raise ValueError(f"Couldn't parse person from string: {string}")
×
110
        name, email, affiliation = match.groups()
3✔
111
        if name:
3✔
112
            name = name.strip()
3✔
113
        if affiliation:
3✔
114
            affiliation = affiliation.strip()
3✔
115
        affiliation = affiliation or None
3✔
116

117
        return cls(affiliation=affiliation, email=email, name=name)
3✔
118

119
    @classmethod
7✔
120
    def from_dict(cls, data):
7✔
121
        """Create and instance from a dictionary."""
122
        return cls(**data)
1✔
123

124
    @staticmethod
7✔
125
    def generate_id(email, full_identity):
7✔
126
        """Generate identifier for Person."""
127
        # TODO: Do not use email as id
128
        if email:
7✔
129
            return f"mailto:{email}"
7✔
130

131
        id = full_identity or str(uuid.uuid4().hex)
4✔
132
        id = quote(id, safe="")
4✔
133

134
        return f"/persons/{id}"
4✔
135

136
    @staticmethod
7✔
137
    def _validate_email(email):
7✔
138
        """Check that the email is valid."""
139
        if not email:
7✔
140
            return
4✔
141
        if not isinstance(email, str) or not re.match(r"[^@]+@[^@]+\.[^@]+", email):
7✔
142
            raise ValueError("Email address is invalid.")
1✔
143

144
    @staticmethod
7✔
145
    def get_full_identity(email, affiliation, name):
7✔
146
        """Return name, email, and affiliation."""
147
        email = f" <{email}>" if email else ""
7✔
148
        affiliation = f" [{affiliation}]" if affiliation else ""
7✔
149
        return f"{name}{email}{affiliation}"
7✔
150

151
    @property
7✔
152
    def short_name(self):
7✔
153
        """Gives full name in short form."""
154
        names = self.name.split()
×
155
        if len(names) == 1:
×
156
            return self.name
×
157

158
        last_name = names[-1]
×
159
        initials = [name[0] for name in names]
×
160
        initials.pop()
×
161

162
        return "{}.{}".format(".".join(initials), last_name)
×
163

164
    @property
7✔
165
    def full_identity(self):
7✔
166
        """Return name, email, and affiliation."""
167
        return self.get_full_identity(self.email, self.affiliation, self.name)
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc