projectal.entities.timelog

  1import projectal
  2from projectal.entity import Entity
  3from projectal.linkers import *
  4from projectal import api
  5
  6
  7class Timelog(
  8    Entity,
  9    FileLinker,
 10    NoteLinker,
 11    TagLinker,
 12):
 13    """
 14    Implementation of the [Timelog](https://projectal.com/docs/latest/#tag/Timelog) API.
 15    """
 16
 17    _path = "timelog"
 18    _name = "timelog"
 19
 20    _links = [
 21        FileLinker,
 22        NoteLinker,
 23        TagLinker,
 24    ]
 25
 26    @classmethod
 27    def create(
 28        cls,
 29        holder,
 30        entities,
 31        batch_linking=True,
 32        disable_system_features=True,
 33        enable_system_features_on_exit=True,
 34    ):
 35        """Create a Timelog
 36
 37        `holder`: An instance or the `uuId` of the Staff
 38
 39        `entities`: `dict` containing the fields of the entity to be created,
 40        or a list of such `dict`s to create in bulk.
 41        """
 42        holder_id = holder["uuId"] if isinstance(holder, dict) else holder
 43        params = "?holder=" + holder_id
 44        out = super().create(
 45            entities,
 46            params,
 47            batch_linking,
 48            disable_system_features,
 49            enable_system_features_on_exit,
 50        )
 51
 52        # Timelogs should always refer to their Staff. We don't get this information from the
 53        # creation api method, but we can insert them ourselves because we know what they are.
 54        def add_fields(obj):
 55            obj.set_readonly("staffRef", holder_id)
 56
 57        if isinstance(out, dict):
 58            add_fields(out)
 59        if isinstance(out, list):
 60            for obj in out:
 61                add_fields(obj)
 62        return out
 63
 64    @classmethod
 65    def list(cls, expand=False, links=None):
 66        """Return a list of all entity UUIDs of this type.
 67
 68        You may pass in `expand=True` to get full Entity objects
 69        instead, but be aware this may be very slow if you have
 70        thousands of objects.
 71
 72        If you are expanding the objects, you may further expand
 73        the results with `links`.
 74        """
 75
 76        payload = {
 77            "name": "List all entities of type {}".format(cls._name.upper()),
 78            "type": "msql",
 79            "start": 0,
 80            "limit": -1,
 81            "select": [["STAFF.{}.uuId".format(cls._name.upper())]],
 82        }
 83        ids = api.query(payload)
 84        ids = [id[0] for id in ids]
 85        if ids:
 86            return cls.get(ids, links=links) if expand else ids
 87        return []
 88
 89    @classmethod
 90    def match(cls, field, term, links=None):
 91        """Find entities where `field`=`term` (exact match), optionally
 92        expanding the results with `links`.
 93
 94        Relies on `Entity.query()` with a pre-built set of rules.
 95        ```
 96        projects = projectal.Project.match('identifier', 'zmb-005')
 97        ```
 98        """
 99        filter = [["STAFF.{}.{}".format(cls._name.upper(), field), "eq", term]]
100        return cls.query(filter, links)
101
102    @classmethod
103    def match_startswith(cls, field, term, links=None):
104        """Find entities where `field` starts with the text `term`,
105        optionally expanding the results with `links`.
106
107        Relies on `Entity.query()` with a pre-built set of rules.
108        ```
109        projects = projectal.Project.match_startswith('name', 'Zomb')
110        ```
111        """
112        filter = [["STAFF.{}.{}".format(cls._name.upper(), field), "prefix", term]]
113        return cls.query(filter, links)
114
115    @classmethod
116    def match_endswith(cls, field, term, links=None):
117        """Find entities where `field` ends with the text `term`,
118        optionally expanding the results with `links`.
119
120        Relies on `Entity.query()` with a pre-built set of rules.
121        ```
122        projects = projectal.Project.match_endswith('identifier', '-2023')
123        ```
124        """
125        term = "(?i).*{}$".format(term)
126        filter = [["STAFF.{}.{}".format(cls._name.upper(), field), "regex", term]]
127        return cls.query(filter, links)
128
129    @classmethod
130    def search(cls, fields=None, term="", case_sensitive=True, links=None):
131        """Find entities that contain the text `term` within `fields`.
132        `fields` is a list of field names to target in the search.
133
134        `case_sensitive`: Optionally turn off case sensitivity in the search.
135
136        Relies on `Entity.query()` with a pre-built set of rules.
137        ```
138        projects = projectal.Project.search(['name', 'description'], 'zombie')
139        ```
140        """
141        filter = []
142        term = "(?{}).*{}.*".format("" if case_sensitive else "?", term)
143        for field in fields:
144            filter.append(
145                ["STAFF.{}.{}".format(cls._name.upper(), field), "regex", term]
146            )
147        filter = ["_or_", filter]
148        return cls.query(filter, links)
149
150    @classmethod
151    def query(cls, filter, links=None, timeout=30):
152        """Run a query on this entity with the supplied filter.
153
154        The query is already set up to target this entity type, and the
155        results will be converted into full objects when found, optionally
156        expanded with the `links` provided. You only need to supply a
157        filter to reduce the result set.
158
159        See [the filter documentation](https://projectal.com/docs/v1.1.1#section/Filter-section)
160        for a detailed overview of the kinds of filters you can construct.
161        """
162        ids = []
163        request_completed = False
164        limit = projectal.query_chunk_size
165        start = 0
166        while not request_completed:
167            payload = {
168                "name": "Python library entity query ({})".format(cls._name.upper()),
169                "type": "msql",
170                "start": start,
171                "limit": limit,
172                "select": [["STAFF.{}.uuId".format(cls._name.upper())]],
173                "filter": filter,
174                "timeout": timeout,
175            }
176            result = projectal.query(payload)
177            ids.extend(result)
178            if len(result) < limit:
179                request_completed = True
180            else:
181                start += limit
182
183        ids = [id[0] for id in ids]
184        if ids:
185            return cls.get(ids, links=links)
186        return []
  8class Timelog(
  9    Entity,
 10    FileLinker,
 11    NoteLinker,
 12    TagLinker,
 13):
 14    """
 15    Implementation of the [Timelog](https://projectal.com/docs/latest/#tag/Timelog) API.
 16    """
 17
 18    _path = "timelog"
 19    _name = "timelog"
 20
 21    _links = [
 22        FileLinker,
 23        NoteLinker,
 24        TagLinker,
 25    ]
 26
 27    @classmethod
 28    def create(
 29        cls,
 30        holder,
 31        entities,
 32        batch_linking=True,
 33        disable_system_features=True,
 34        enable_system_features_on_exit=True,
 35    ):
 36        """Create a Timelog
 37
 38        `holder`: An instance or the `uuId` of the Staff
 39
 40        `entities`: `dict` containing the fields of the entity to be created,
 41        or a list of such `dict`s to create in bulk.
 42        """
 43        holder_id = holder["uuId"] if isinstance(holder, dict) else holder
 44        params = "?holder=" + holder_id
 45        out = super().create(
 46            entities,
 47            params,
 48            batch_linking,
 49            disable_system_features,
 50            enable_system_features_on_exit,
 51        )
 52
 53        # Timelogs should always refer to their Staff. We don't get this information from the
 54        # creation api method, but we can insert them ourselves because we know what they are.
 55        def add_fields(obj):
 56            obj.set_readonly("staffRef", holder_id)
 57
 58        if isinstance(out, dict):
 59            add_fields(out)
 60        if isinstance(out, list):
 61            for obj in out:
 62                add_fields(obj)
 63        return out
 64
 65    @classmethod
 66    def list(cls, expand=False, links=None):
 67        """Return a list of all entity UUIDs of this type.
 68
 69        You may pass in `expand=True` to get full Entity objects
 70        instead, but be aware this may be very slow if you have
 71        thousands of objects.
 72
 73        If you are expanding the objects, you may further expand
 74        the results with `links`.
 75        """
 76
 77        payload = {
 78            "name": "List all entities of type {}".format(cls._name.upper()),
 79            "type": "msql",
 80            "start": 0,
 81            "limit": -1,
 82            "select": [["STAFF.{}.uuId".format(cls._name.upper())]],
 83        }
 84        ids = api.query(payload)
 85        ids = [id[0] for id in ids]
 86        if ids:
 87            return cls.get(ids, links=links) if expand else ids
 88        return []
 89
 90    @classmethod
 91    def match(cls, field, term, links=None):
 92        """Find entities where `field`=`term` (exact match), optionally
 93        expanding the results with `links`.
 94
 95        Relies on `Entity.query()` with a pre-built set of rules.
 96        ```
 97        projects = projectal.Project.match('identifier', 'zmb-005')
 98        ```
 99        """
100        filter = [["STAFF.{}.{}".format(cls._name.upper(), field), "eq", term]]
101        return cls.query(filter, links)
102
103    @classmethod
104    def match_startswith(cls, field, term, links=None):
105        """Find entities where `field` starts with the text `term`,
106        optionally expanding the results with `links`.
107
108        Relies on `Entity.query()` with a pre-built set of rules.
109        ```
110        projects = projectal.Project.match_startswith('name', 'Zomb')
111        ```
112        """
113        filter = [["STAFF.{}.{}".format(cls._name.upper(), field), "prefix", term]]
114        return cls.query(filter, links)
115
116    @classmethod
117    def match_endswith(cls, field, term, links=None):
118        """Find entities where `field` ends with the text `term`,
119        optionally expanding the results with `links`.
120
121        Relies on `Entity.query()` with a pre-built set of rules.
122        ```
123        projects = projectal.Project.match_endswith('identifier', '-2023')
124        ```
125        """
126        term = "(?i).*{}$".format(term)
127        filter = [["STAFF.{}.{}".format(cls._name.upper(), field), "regex", term]]
128        return cls.query(filter, links)
129
130    @classmethod
131    def search(cls, fields=None, term="", case_sensitive=True, links=None):
132        """Find entities that contain the text `term` within `fields`.
133        `fields` is a list of field names to target in the search.
134
135        `case_sensitive`: Optionally turn off case sensitivity in the search.
136
137        Relies on `Entity.query()` with a pre-built set of rules.
138        ```
139        projects = projectal.Project.search(['name', 'description'], 'zombie')
140        ```
141        """
142        filter = []
143        term = "(?{}).*{}.*".format("" if case_sensitive else "?", term)
144        for field in fields:
145            filter.append(
146                ["STAFF.{}.{}".format(cls._name.upper(), field), "regex", term]
147            )
148        filter = ["_or_", filter]
149        return cls.query(filter, links)
150
151    @classmethod
152    def query(cls, filter, links=None, timeout=30):
153        """Run a query on this entity with the supplied filter.
154
155        The query is already set up to target this entity type, and the
156        results will be converted into full objects when found, optionally
157        expanded with the `links` provided. You only need to supply a
158        filter to reduce the result set.
159
160        See [the filter documentation](https://projectal.com/docs/v1.1.1#section/Filter-section)
161        for a detailed overview of the kinds of filters you can construct.
162        """
163        ids = []
164        request_completed = False
165        limit = projectal.query_chunk_size
166        start = 0
167        while not request_completed:
168            payload = {
169                "name": "Python library entity query ({})".format(cls._name.upper()),
170                "type": "msql",
171                "start": start,
172                "limit": limit,
173                "select": [["STAFF.{}.uuId".format(cls._name.upper())]],
174                "filter": filter,
175                "timeout": timeout,
176            }
177            result = projectal.query(payload)
178            ids.extend(result)
179            if len(result) < limit:
180                request_completed = True
181            else:
182                start += limit
183
184        ids = [id[0] for id in ids]
185        if ids:
186            return cls.get(ids, links=links)
187        return []

Implementation of the Timelog API.

@classmethod
def create( cls, holder, entities, batch_linking=True, disable_system_features=True, enable_system_features_on_exit=True):
27    @classmethod
28    def create(
29        cls,
30        holder,
31        entities,
32        batch_linking=True,
33        disable_system_features=True,
34        enable_system_features_on_exit=True,
35    ):
36        """Create a Timelog
37
38        `holder`: An instance or the `uuId` of the Staff
39
40        `entities`: `dict` containing the fields of the entity to be created,
41        or a list of such `dict`s to create in bulk.
42        """
43        holder_id = holder["uuId"] if isinstance(holder, dict) else holder
44        params = "?holder=" + holder_id
45        out = super().create(
46            entities,
47            params,
48            batch_linking,
49            disable_system_features,
50            enable_system_features_on_exit,
51        )
52
53        # Timelogs should always refer to their Staff. We don't get this information from the
54        # creation api method, but we can insert them ourselves because we know what they are.
55        def add_fields(obj):
56            obj.set_readonly("staffRef", holder_id)
57
58        if isinstance(out, dict):
59            add_fields(out)
60        if isinstance(out, list):
61            for obj in out:
62                add_fields(obj)
63        return out

Create a Timelog

holder: An instance or the uuId of the Staff

entities: dict containing the fields of the entity to be created, or a list of such dicts to create in bulk.

@classmethod
def list(cls, expand=False, links=None):
65    @classmethod
66    def list(cls, expand=False, links=None):
67        """Return a list of all entity UUIDs of this type.
68
69        You may pass in `expand=True` to get full Entity objects
70        instead, but be aware this may be very slow if you have
71        thousands of objects.
72
73        If you are expanding the objects, you may further expand
74        the results with `links`.
75        """
76
77        payload = {
78            "name": "List all entities of type {}".format(cls._name.upper()),
79            "type": "msql",
80            "start": 0,
81            "limit": -1,
82            "select": [["STAFF.{}.uuId".format(cls._name.upper())]],
83        }
84        ids = api.query(payload)
85        ids = [id[0] for id in ids]
86        if ids:
87            return cls.get(ids, links=links) if expand else ids
88        return []

Return a list of all entity UUIDs of this type.

You may pass in expand=True to get full Entity objects instead, but be aware this may be very slow if you have thousands of objects.

If you are expanding the objects, you may further expand the results with links.

@classmethod
def match(cls, field, term, links=None):
 90    @classmethod
 91    def match(cls, field, term, links=None):
 92        """Find entities where `field`=`term` (exact match), optionally
 93        expanding the results with `links`.
 94
 95        Relies on `Entity.query()` with a pre-built set of rules.
 96        ```
 97        projects = projectal.Project.match('identifier', 'zmb-005')
 98        ```
 99        """
100        filter = [["STAFF.{}.{}".format(cls._name.upper(), field), "eq", term]]
101        return cls.query(filter, links)

Find entities where field=term (exact match), optionally expanding the results with links.

Relies on Entity.query() with a pre-built set of rules.

projects = projectal.Project.match('identifier', 'zmb-005')
@classmethod
def match_startswith(cls, field, term, links=None):
103    @classmethod
104    def match_startswith(cls, field, term, links=None):
105        """Find entities where `field` starts with the text `term`,
106        optionally expanding the results with `links`.
107
108        Relies on `Entity.query()` with a pre-built set of rules.
109        ```
110        projects = projectal.Project.match_startswith('name', 'Zomb')
111        ```
112        """
113        filter = [["STAFF.{}.{}".format(cls._name.upper(), field), "prefix", term]]
114        return cls.query(filter, links)

Find entities where field starts with the text term, optionally expanding the results with links.

Relies on Entity.query() with a pre-built set of rules.

projects = projectal.Project.match_startswith('name', 'Zomb')
@classmethod
def match_endswith(cls, field, term, links=None):
116    @classmethod
117    def match_endswith(cls, field, term, links=None):
118        """Find entities where `field` ends with the text `term`,
119        optionally expanding the results with `links`.
120
121        Relies on `Entity.query()` with a pre-built set of rules.
122        ```
123        projects = projectal.Project.match_endswith('identifier', '-2023')
124        ```
125        """
126        term = "(?i).*{}$".format(term)
127        filter = [["STAFF.{}.{}".format(cls._name.upper(), field), "regex", term]]
128        return cls.query(filter, links)

Find entities where field ends with the text term, optionally expanding the results with links.

Relies on Entity.query() with a pre-built set of rules.

projects = projectal.Project.match_endswith('identifier', '-2023')
@classmethod
def search(cls, fields=None, term='', case_sensitive=True, links=None):
130    @classmethod
131    def search(cls, fields=None, term="", case_sensitive=True, links=None):
132        """Find entities that contain the text `term` within `fields`.
133        `fields` is a list of field names to target in the search.
134
135        `case_sensitive`: Optionally turn off case sensitivity in the search.
136
137        Relies on `Entity.query()` with a pre-built set of rules.
138        ```
139        projects = projectal.Project.search(['name', 'description'], 'zombie')
140        ```
141        """
142        filter = []
143        term = "(?{}).*{}.*".format("" if case_sensitive else "?", term)
144        for field in fields:
145            filter.append(
146                ["STAFF.{}.{}".format(cls._name.upper(), field), "regex", term]
147            )
148        filter = ["_or_", filter]
149        return cls.query(filter, links)

Find entities that contain the text term within fields. fields is a list of field names to target in the search.

case_sensitive: Optionally turn off case sensitivity in the search.

Relies on Entity.query() with a pre-built set of rules.

projects = projectal.Project.search(['name', 'description'], 'zombie')
@classmethod
def query(cls, filter, links=None, timeout=30):
151    @classmethod
152    def query(cls, filter, links=None, timeout=30):
153        """Run a query on this entity with the supplied filter.
154
155        The query is already set up to target this entity type, and the
156        results will be converted into full objects when found, optionally
157        expanded with the `links` provided. You only need to supply a
158        filter to reduce the result set.
159
160        See [the filter documentation](https://projectal.com/docs/v1.1.1#section/Filter-section)
161        for a detailed overview of the kinds of filters you can construct.
162        """
163        ids = []
164        request_completed = False
165        limit = projectal.query_chunk_size
166        start = 0
167        while not request_completed:
168            payload = {
169                "name": "Python library entity query ({})".format(cls._name.upper()),
170                "type": "msql",
171                "start": start,
172                "limit": limit,
173                "select": [["STAFF.{}.uuId".format(cls._name.upper())]],
174                "filter": filter,
175                "timeout": timeout,
176            }
177            result = projectal.query(payload)
178            ids.extend(result)
179            if len(result) < limit:
180                request_completed = True
181            else:
182                start += limit
183
184        ids = [id[0] for id in ids]
185        if ids:
186            return cls.get(ids, links=links)
187        return []

Run a query on this entity with the supplied filter.

The query is already set up to target this entity type, and the results will be converted into full objects when found, optionally expanded with the links provided. You only need to supply a filter to reduce the result set.

See the filter documentation for a detailed overview of the kinds of filters you can construct.