girder-sample-tracker 2.0.1__tar.gz → 2.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. {girder_sample_tracker-2.0.1/girder_sample_tracker.egg-info → girder_sample_tracker-2.0.2}/PKG-INFO +3 -2
  2. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/girder_sample_tracker/models/sample.py +9 -0
  3. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/girder_sample_tracker/rest/sample.py +50 -17
  4. girder_sample_tracker-2.0.2/girder_sample_tracker/web_client/dist/girder-plugin-sample-tracker.umd.cjs +77 -0
  5. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2/girder_sample_tracker.egg-info}/PKG-INFO +3 -2
  6. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/setup.py +1 -1
  7. girder_sample_tracker-2.0.1/girder_sample_tracker/web_client/dist/girder-plugin-sample-tracker.umd.cjs +0 -72
  8. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/.github/workflows/release.yml +0 -0
  9. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/.gitignore +0 -0
  10. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/LICENSE +0 -0
  11. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/README.md +0 -0
  12. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/girder_sample_tracker/__init__.py +0 -0
  13. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/girder_sample_tracker/models/__init__.py +0 -0
  14. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/girder_sample_tracker/rest/__init__.py +0 -0
  15. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/girder_sample_tracker/web_client/dist/style.css +0 -0
  16. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/girder_sample_tracker.egg-info/SOURCES.txt +0 -0
  17. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/girder_sample_tracker.egg-info/dependency_links.txt +0 -0
  18. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/girder_sample_tracker.egg-info/entry_points.txt +0 -0
  19. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/girder_sample_tracker.egg-info/not-zip-safe +0 -0
  20. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/girder_sample_tracker.egg-info/requires.txt +0 -0
  21. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/girder_sample_tracker.egg-info/top_level.txt +0 -0
  22. {girder_sample_tracker-2.0.1 → girder_sample_tracker-2.0.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: girder-sample-tracker
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: Girder Plugin adding a simple tracker for material samples
5
5
  License: BSD
6
6
  Classifier: Development Status :: 4 - Beta
@@ -19,6 +19,7 @@ Dynamic: classifier
19
19
  Dynamic: description
20
20
  Dynamic: description-content-type
21
21
  Dynamic: license
22
+ Dynamic: license-file
22
23
  Dynamic: requires-dist
23
24
  Dynamic: requires-python
24
25
  Dynamic: summary
@@ -64,6 +64,15 @@ class Sample(AccessControlledModel):
64
64
 
65
65
  return sample
66
66
 
67
+ def remove_event(self, sample, event, user=None):
68
+ self.collection.update_one(
69
+ {
70
+ "_id": sample["_id"],
71
+ },
72
+ {"$pull": {"events": {**event}}},
73
+ )
74
+ return self.load(sample["_id"], user=user)
75
+
67
76
  def qr_code(self, sample, url):
68
77
  buf = io.BytesIO()
69
78
  qr = qrcode.QRCode(
@@ -3,6 +3,7 @@ import math
3
3
  from urllib.parse import urlparse
4
4
 
5
5
  import cherrypy
6
+ import dateutil.parser
6
7
  from girder.api import access
7
8
  from girder.api.describe import Description, autoDescribeRoute
8
9
  from girder.api.rest import (
@@ -13,23 +14,13 @@ from girder.api.rest import (
13
14
  )
14
15
  from girder.constants import AccessType, SortDir, TokenScope
15
16
  from girder.exceptions import RestException, ValidationException
17
+ from girder.models.user import User
16
18
  from girder.utility import ziputil
17
19
  from girder.utility.progress import ProgressContext
18
20
 
19
21
  from ..models.sample import Sample as SampleModel
20
22
 
21
23
 
22
- def bytesio_iterator(bio, chunk_size=4096):
23
- """
24
- Iterates over the content of a BytesIO object in chunks.
25
- """
26
- while True:
27
- chunk = bio.read(chunk_size)
28
- if not chunk:
29
- break
30
- yield chunk
31
-
32
-
33
24
  class Sample(Resource):
34
25
  def __init__(self):
35
26
  super(Sample, self).__init__()
@@ -46,17 +37,25 @@ class Sample(Resource):
46
37
  self.route("GET", (":id", "access"), self.get_access)
47
38
  self.route("PUT", (":id", "access"), self.update_access)
48
39
  self.route("POST", (":id", "event"), self.create_event)
40
+ self.route("DELETE", (":id", "event"), self.delete_event)
49
41
 
50
42
  @access.public
51
43
  @autoDescribeRoute(
52
- Description("List samples").pagingParams(
44
+ Description("List samples")
45
+ .param("query", "A regular expression to filter sample names", required=False)
46
+ .pagingParams(
53
47
  defaultSort="name", defaultSortDir=SortDir.DESCENDING
54
48
  )
55
49
  )
56
50
  @filtermodel(model="sample", plugin="sample_tracker")
57
- def list_samples(self, limit, offset, sort):
51
+ def list_samples(self, query, limit, offset, sort):
52
+ if query:
53
+ query = {"name": {"$regex": query, "$options": "i"}}
54
+ else:
55
+ query = {}
56
+
58
57
  return SampleModel().findWithPermissions(
59
- query={},
58
+ query=query,
60
59
  offset=offset,
61
60
  limit=limit,
62
61
  sort=sort,
@@ -130,15 +129,25 @@ class Sample(Resource):
130
129
  raise ValidationException(
131
130
  "Batch size must be at least 1, but no more than 64."
132
131
  )
132
+
133
133
  if not eventTypes:
134
134
  eventTypes = []
135
135
  user = self.getCurrentUser()
136
136
  samples = []
137
137
  if batchSize > 1:
138
- format_str = "{name}{i:0" + str(math.ceil(math.log10(batchSize))) + "d}"
138
+ if "{number" not in name:
139
+ name = name + "{number:0" + str(math.ceil(math.log10(batchSize))) + "d}"
140
+
141
+ try:
142
+ name.format(number=1)
143
+ except KeyError:
144
+ raise ValidationException(
145
+ "Name must contain a '{number}' placeholder for batch creation."
146
+ )
147
+
139
148
  for i in range(batchSize):
140
149
  sample = SampleModel().create(
141
- format_str.format(name=name, i=i + 1),
150
+ name.format(number=i + 1),
142
151
  user,
143
152
  description=description,
144
153
  eventTypes=eventTypes,
@@ -300,6 +309,26 @@ class Sample(Resource):
300
309
  }
301
310
  return SampleModel().add_event(sample, event)
302
311
 
312
+ @access.user
313
+ @autoDescribeRoute(
314
+ Description("Delete an event for a sample")
315
+ .modelParam(
316
+ "id", "The ID of the sample", model=SampleModel, level=AccessType.ADMIN
317
+ )
318
+ .jsonParam("event", "Event to remove", required=True, requireObject=True)
319
+ )
320
+ @filtermodel(model="sample", plugin="sample_tracker")
321
+ def delete_event(self, sample, event):
322
+ creator = User().load(event["creator"], force=True)
323
+ processed_event = {
324
+ "created": dateutil.parser.parse(event.get("created")),
325
+ "creator": creator["_id"],
326
+ "eventType": event.get("eventType"),
327
+ }
328
+ return SampleModel().remove_event(
329
+ sample, processed_event, user=self.getCurrentUser()
330
+ )
331
+
303
332
  @access.public(scope=TokenScope.DATA_READ, cookie=True)
304
333
  @autoDescribeRoute(
305
334
  Description("Download a sample").modelParam(
@@ -312,7 +341,11 @@ class Sample(Resource):
312
341
  qr_img = SampleModel().qr_code(sample, girder_base)
313
342
  setResponseHeader("Content-Type", "image/png")
314
343
  setContentDisposition(f"{sample['name']}.png")
315
- return bytesio_iterator(qr_img)
344
+
345
+ def stream():
346
+ yield from qr_img
347
+
348
+ return stream
316
349
 
317
350
  @access.public(scope=TokenScope.DATA_READ, cookie=True)
318
351
  @autoDescribeRoute(