qmenta-client 1.1.dev1245__py3-none-any.whl → 1.1.dev1289__py3-none-any.whl

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.
qmenta/client/Project.py CHANGED
@@ -11,7 +11,6 @@ from enum import Enum
11
11
  from qmenta.client import Account
12
12
  from qmenta.core import errors
13
13
  from qmenta.core import platform
14
- from .Subject import Subject
15
14
 
16
15
  if sys.version_info[0] == 3:
17
16
  # Note: this branch & variable is only needed for python 2/3 compatibility
@@ -161,45 +160,137 @@ class Project:
161
160
  dict
162
161
  A list of dictionary of {"metadata_name": "metadata_value"}
163
162
  """
164
- return self.get_subjects_metadata(cache=False)
163
+ return self.get_subjects_metadata()
165
164
 
166
- def get_subjects_metadata(self, cache=True, search_criteria=None):
165
+ def get_subjects_metadata(self, search_criteria={}, items=(0, 9999)):
167
166
  """
168
- List all subject data from the selected project.
167
+ List all subjects data from the selected project that meet the defined
168
+ search criteria.
169
+
169
170
  Parameters
170
171
  ----------
171
- cache: bool
172
- Whether to use the cached metadata or not
173
-
174
172
  search_criteria: dict
175
173
  Each element is a string and is built using the formatting
176
- "type;value", or "type;operation|value"
174
+ "type;value", or "type;operation|value"
175
+
176
+ Complete search_criteria Dictionary Explanation:
177
+
178
+ search_criteria = {
179
+ "pars_patient_secret_name": "string;SUBJECTID",
180
+ "pars_ssid": "integer;OPERATOR|SSID",
181
+ "pars_modalities": "string;MODALITY",
182
+ "pars_tags": "tags;TAGS",
183
+ "pars_age_at_scan": "integer;OPERATOR|AGE_AT_SCAN",
184
+ "pars_[dicom]_KEY": "KEYTYPE;KEYVALUE",
185
+ "pars_PROJECTMETADATA": "METADATATYPE;METADATAVALUE",
186
+ }
187
+
188
+ Where:
189
+ "pars_patient_secret_name": Applies the search to the 'Subject ID'.
190
+ SUBJECTID is a comma separated list of strings.
191
+ "pars_ssid": Applies the search to the 'Session ID'.
192
+ SSID is an integer.
193
+ OPERATOR is the operator to apply. One of:
194
+ - Equal: eq
195
+ - Different Than: ne
196
+ - Greater Than: gt
197
+ - Greater/Equal To: gte
198
+ - Lower Than: lt
199
+ - Lower/Equal To: lte
200
+
201
+ "pars_modalities": Applies the search to the file 'Modalities'
202
+ available within each Subject ID.
203
+ MODALITY is a comma separated list of string.
204
+ "pars_tags": Applies the search to the file 'Tags' available within
205
+ each Subject ID and to the subject-level 'Tags'.
206
+ TAGS is a comma separated list of strings.
207
+ "pars_age_at_scan": Applies the search to the 'age_at_scan' metadata
208
+ field.
209
+ AGE_AT_SCAN is an integer.
210
+ "pars_[dicom]_KEY": Applies the search to the metadata fields
211
+ available within each file. KEY must be one of the
212
+ metadata keys of the files. The full list of KEYS is shown above via
213
+ 'file_m["metadata"]["info"].keys()'.
214
+ KEYTYPE is the type of the KEY. One of:
215
+ - integer
216
+ - string
217
+ - list
218
+
219
+ if 'integer' you must also include an OPERATOR
220
+ (i.e., "integer;OPERATOR|KEYVALUE").
221
+ KEYVALUE is the expected value of the KEY.
222
+ "pars_[dicom]_PROJECTMETADATA": Applies to the metadata defined
223
+ within the 'Metadata Manager' of the project.
224
+ PROJECTMETADATA is the ID of the metadata field.
225
+ METADATATYPE is the type of the metadata field. One of:
226
+ - string
227
+ - integer
228
+ - list
229
+ - decimal
230
+ - single_option
231
+ - multiple_option
232
+
233
+ if 'integer' or 'decimal' you must also include an OPERATOR
234
+ (i.e., "integer;OPERATOR|METADATAVALUE").
235
+ KEYVALUE is the expected value of the metadata.
236
+
237
+ 1) Example:
238
+ search_criteria = {
239
+ "pars_patient_secret_name": "string;abide",
240
+ "pars_ssid": "integer;eq|2"
241
+ }
242
+
243
+ 2) Example:
244
+ search_criteria = {
245
+ "pars_modalities": "string;T1",
246
+ "pars_tags": "tags;flair",
247
+ "pars_[dicom]_Manufacturer": "string;ge",
248
+ "pars_[dicom]_FlipAngle": "integer;gt|5",
249
+ }
177
250
 
178
- Example:
179
- search_criteria = {"pars_patient_secret_name": "string;abide",
180
- "pars_ssid": "integer;eq|2"}
251
+ Note the search criteria applies to all the files included within a
252
+ session. Hence, it does not imply that all the criteria conditions
253
+ are applied to the same files. In example 2) above, it means that
254
+ any Subject ID/Session ID that has a file classified with a 'T1'
255
+ modality, a file with a 'flair' tag, a file whose Manufacturer
256
+ contains 'ge', and a file whose FlipAngle is greater than '5º' will
257
+ be selected.
181
258
 
182
259
  Returns
183
260
  -------
184
261
  dict
185
262
  A list of dictionary of {"metadata_name": "metadata_value"}
263
+
186
264
  """
187
265
 
188
- if not cache or not self._subjects_metadata:
189
- content = platform.parse_response(platform.post(
190
- self._account.auth, "patient_manager/get_patient_list",
191
- data=search_criteria,
192
- headers={"X-Range": "items=0-9999"}
193
- ))
194
- self._subjects_metadata = content
195
- else:
196
- content = self._subjects_metadata
266
+ assert len(items) == 2, f"The number of elements in items " \
267
+ f"'{len(items)}' should be equal to two."
268
+ assert all([isinstance(item, int) for item in items]), \
269
+ f"All items elements '{items}' should be integers."
270
+
271
+ assert all([key[:5] == "pars_" for key in search_criteria.keys()]), \
272
+ f"All keys of the search_criteria dictionary " \
273
+ f"'{search_criteria.keys()}' must start with 'pars_'."
274
+
275
+ operator_list = ["eq", "ne", "gt", "gte", "lt", "lte"]
276
+ for key, value in search_criteria.items():
277
+ if value.split(";")[0] in ["integer", "decimal"]:
278
+ assert value.split(";")[1].split("|")[0] in operator_list, \
279
+ f"Search criteria of type '{value.split(';')[0]}' must " \
280
+ f"include an operator ({', '.join(operator_list)})."
281
+
282
+ content = platform.parse_response(platform.post(
283
+ self._account.auth, "patient_manager/get_patient_list",
284
+ data=search_criteria,
285
+ headers={"X-Range": f"items={items[0]}-{items[1]}"}
286
+ ))
197
287
  return content
198
288
 
199
289
  @property
200
290
  def subjects(self):
201
291
  """
202
- Return the list of subject names from the selected project.
292
+ Return the list of subject names (Subject ID) from the selected
293
+ project.
203
294
 
204
295
  :return: a list of subject names
205
296
  :rtype: List(Strings)
@@ -211,12 +302,13 @@ class Project:
211
302
 
212
303
  def check_subject_name(self, subject_name):
213
304
  """
214
- Check if a given subject name exists in the selected project.
305
+ Check if a given subject name (Subject ID) exists in the selected
306
+ project.
215
307
 
216
308
  Parameters
217
309
  ----------
218
310
  subject_name : str
219
- name of the subject to check
311
+ Subject ID of the subject to check
220
312
 
221
313
  Returns
222
314
  -------
@@ -403,48 +495,81 @@ class Project:
403
495
  data=search_condition
404
496
  ))
405
497
 
406
- def get_container(self, subject_name):
407
- search_condition = {
498
+ def get_subject_container_id(self, subject_name, ssid):
499
+ """
500
+ Given a Subject ID and Session ID, return its Container ID.
501
+
502
+ Parameters
503
+ ----------
504
+ subject_name : str
505
+ Subject ID of the subject in the project.
506
+ ssid : str
507
+ Session ID of the subject in the project.
508
+
509
+ Returns
510
+ -------
511
+ int or bool
512
+ The Container ID of the subject in the project, or False if
513
+ the subject is not found.
514
+ """
515
+
516
+ search_criteria = {
408
517
  "s_n": subject_name,
518
+ "ssid": ssid
409
519
  }
410
520
  response = self.list_input_containers(
411
- search_condition=search_condition
521
+ search_criteria=search_criteria
412
522
  )
413
523
 
414
- if len(response) > 1:
415
- raise Exception(f"multiple containers for subject {subject_name} "
416
- f"found")
417
- elif len(response) == 1:
418
- return response[0]
419
- else:
420
- return None
524
+ for subject in response:
525
+ if subject["patient_secret_name"] == subject_name and \
526
+ subject["ssid"] == ssid:
527
+ return subject["container_id"]
528
+ return False
421
529
 
422
- def list_input_containers(self, search_condition=None, limit=1000):
530
+ def list_input_containers(self, search_criteria={}, items=(0, 9999)):
423
531
  """
424
- List the containers available to the user.
532
+ Retrieve the list of input containers available to the user under a
533
+ certain search criteria.
425
534
 
426
535
  Parameters
427
536
  ----------
428
- search_condition : dict
429
- d_n: container_name
430
- s_n: subject_id
431
- from_d: from date
432
- to_d: to date
433
- sets: data sets (modalities)
434
- limit : int
435
- Max number of results
537
+ search_criteria : dict
538
+ Each element is a string and is built using the formatting
539
+ "type;value".
540
+
541
+ List of possible keys:
542
+ d_n: container_name # TODO: WHAT IS THIS???
543
+ s_n: subject_id
544
+ Subject ID of the subject in the platform.
545
+ ssid: session_id
546
+ Session ID of the subejct in the platform.
547
+ from_d: from date
548
+ Starting date in which perform the search. Format: DD.MM.YYYY
549
+ to_d: to date
550
+ End date in which perform the search. Format: DD.MM.YYYY
551
+ sets: data sets (modalities) # TODO: WHAT IS THIS???
552
+
553
+ items: Tuple(int, int)
554
+ Starting and ending element of the search.
436
555
 
437
556
  Returns
438
557
  -------
439
558
  dict
440
- List of containers, each a dictionary
441
- {"name": "container-name", "id": "container_id"}
559
+ List of containers, each a dictionary containing the following
560
+ information:
561
+ {"container_name", "container_id", "patient_secret_name", "ssid"}
442
562
  """
443
563
 
444
- req_headers = {"X-Range": "items=0-" + str(limit - 1)}
564
+ assert len(items) == 2, f"The number of elements in items " \
565
+ f"'{len(items)}' should be equal to two."
566
+ assert all([isinstance(item, int) for item in items]), \
567
+ f"All items elements '{items}' should be integers."
568
+
445
569
  response = platform.parse_response(platform.post(
446
570
  self._account.auth, "file_manager/get_container_list",
447
- data=search_condition, headers=req_headers
571
+ data=search_criteria,
572
+ headers={"X-Range": f"items={items[0]}-{items[1]}"}
448
573
  ))
449
574
  containers = [
450
575
  {
@@ -545,12 +670,15 @@ class Project:
545
670
  Returns
546
671
  -------
547
672
  dict
548
- Dictionary with the metadata.
673
+ Dictionary with the metadata. False otherwise.
549
674
  """
550
675
  all_metadata = self.list_container_files_metadata(container_id)
551
- for file_meta in all_metadata:
552
- if file_meta["name"] == filename:
553
- return file_meta
676
+ if all_metadata:
677
+ for file_meta in all_metadata:
678
+ if file_meta["name"] == filename:
679
+ return file_meta
680
+ else:
681
+ return False
554
682
 
555
683
  def change_file_metadata(self, container_id, filename, modality, tags):
556
684
  """
@@ -673,16 +801,17 @@ class Project:
673
801
  container_id, zip_name))
674
802
  return True
675
803
 
676
- def get_subject_id(self, subject_name, cache=False):
804
+ def get_subject_id(self, subject_name, ssid):
677
805
  """
678
- Given a subject name, return its ID in the project.
806
+ Given a Subject ID and Session ID, return its Patient ID in the
807
+ project.
679
808
 
680
809
  Parameters
681
810
  ----------
682
811
  subject_name : str
683
- Name of the subject in the project.
684
- cache : bool
685
- Whether to use the cached metadata or not
812
+ Subject ID of the subject in the project.
813
+ ssid : str
814
+ Session ID of the subject in the project.
686
815
 
687
816
  Returns
688
817
  -------
@@ -691,37 +820,12 @@ class Project:
691
820
  the subject is not found.
692
821
  """
693
822
 
694
- for user in self.get_subjects_metadata(cache):
695
- if user["patient_secret_name"] == subject_name:
823
+ for user in self.get_subjects_metadata():
824
+ if user["patient_secret_name"] == str(subject_name) and \
825
+ user["ssid"] == str(ssid):
696
826
  return int(user["_id"])
697
827
  return False
698
828
 
699
- def get_subject(self, subject_name, cache=True):
700
- """
701
- Return a subject object, representing a subject from the project.
702
-
703
- Parameters
704
- ----------
705
- subject_name : str
706
- Name of the subject.
707
- cache: bool
708
- Whether to use the cached metadata or not
709
-
710
- Returns
711
- -------
712
- Subject or bool
713
- A Subject instance representing the desired subject, or
714
- False if the subject was not found.
715
-
716
- """
717
- subject_id = self.get_subject_id(subject_name, cache=cache)
718
- if subject_id is False:
719
- return False
720
- subj = Subject(subject_name)
721
- subj.subject_id = subject_id
722
- subj.project = self
723
- return subj
724
-
725
829
  def add_subject(self, subject):
726
830
  """
727
831
  Add a subject to the project.
@@ -757,18 +861,114 @@ class Project:
757
861
  "Subject {0} was successfully created".format(subject.name))
758
862
  return True
759
863
 
760
- def delete_session(self, subject_name, session_id, cache=False):
864
+ def change_subject_metadata(self, patient_id, subject_name, ssid, tags,
865
+ age_at_scan, metadata):
866
+ """
867
+ Change the Subject ID, Session ID, Tags, Age at Scan and Metadata of
868
+ the session with Patient ID
869
+
870
+ Parameters
871
+ ----------
872
+ patient_id : Integer
873
+ Patient ID representing the session to modify.
874
+ subject_name : String
875
+ Represents the new Subject ID.
876
+ ssid : String
877
+ Represents the new Session ID.
878
+ tags : list of strings in lowercase
879
+ Represents the new tags of the session.
880
+ age_at_scan : Integer
881
+ Represents the new Age at Scan of the Session.
882
+ metadata : Dictionary
883
+ Each pair key/value representing the new metadata values.
884
+
885
+ The keys must either all start with "md\\_" or none start
886
+ with "md\\_".
887
+
888
+ The key represents the ID of the metadata field.
889
+
890
+ Returns
891
+ -------
892
+ bool
893
+ True if correctly modified, False otherwise
894
+ """
895
+ logger = logging.getLogger(logger_name)
896
+
897
+ try:
898
+ patient_id = str(int(patient_id))
899
+ except ValueError:
900
+ raise ValueError(f"'patient_id': '{patient_id}' not valid. "
901
+ f"Must be convertible to int.")
902
+
903
+ assert isinstance(tags, list) and \
904
+ all(isinstance(item, str) for item in tags), \
905
+ f"tags: '{tags}' should be a list of strings."
906
+ tags = [tag.lower() for tag in tags]
907
+
908
+ assert subject_name is not None and subject_name != "", \
909
+ "subject_name must be a non empty string."
910
+ assert ssid is not None and ssid != "", \
911
+ "ssid must be a non empty string."
912
+
913
+ try:
914
+ age_at_scan = str(int(age_at_scan)) if age_at_scan else None
915
+ except ValueError:
916
+ raise ValueError(f"age_at_scan: '{age_at_scan}' not valid. "
917
+ f"Must be an integer.")
918
+
919
+ assert isinstance(metadata, dict), \
920
+ f"metadata: '{metadata}' should be a dictionary."
921
+
922
+ assert all("md_" == key[:3] for key in metadata.keys()) or \
923
+ all("md_" != key[:3] for key in metadata.keys()), \
924
+ f"metadata: '{metadata}' must be a dictionary whose keys " \
925
+ f"are either all starting with 'md_' or none."
926
+
927
+ metadata_keys = self.metadata_parameters.keys()
928
+ assert \
929
+ all([key[3:] in metadata_keys
930
+ if "md_" == key[:3] else key in metadata_keys
931
+ for key in metadata.keys()]), \
932
+ f"Some metadata keys provided ({', '.join(metadata.keys())}) " \
933
+ f"are not available in the project. They can be added via the " \
934
+ f"Metadata Manager via the QMENTA Platform graphical user " \
935
+ f"interface (GUI)."
936
+
937
+ post_data = {
938
+ "patient_id": patient_id,
939
+ "secret_name": str(subject_name),
940
+ "ssid": str(ssid),
941
+ "tags": ",".join(tags),
942
+ "age_at_scan": age_at_scan,
943
+ }
944
+ for key, value in metadata.items():
945
+ id = key[3:] if "md_" == key[:3] else key
946
+ post_data[f"last_vals.{id}"] = value
947
+
948
+ try:
949
+ platform.parse_response(platform.post(
950
+ self._account.auth,
951
+ "patient_manager/upsert_patient",
952
+ data=post_data
953
+ ))
954
+ except errors.PlatformError:
955
+ logger.error(f"Patient ID '{patient_id}' could not be modified.")
956
+ return False
957
+
958
+ logger.info(f"Patient ID '{patient_id}' successfully modified.")
959
+ return True
960
+
961
+ def delete_session(self, subject_name, session_id):
761
962
  """
762
- Delete a session from a subject within a project.
963
+ Delete a session from a subject within a project providing the
964
+ Subject ID and Session ID.
763
965
 
764
966
  Parameters
765
967
  ----------
766
968
  subject_name : str
767
- Name of the subject
969
+ Subject ID of the subject
768
970
  session_id : int
769
- The SSID of the session that will be deleted
770
- cache : bool
771
- Whether to use the cached metadata or not
971
+ The Session ID of the session that will be deleted
772
972
 
773
973
  Returns
774
974
  -------
@@ -776,31 +976,31 @@ class Project:
776
976
  True if correctly deleted, False otherwise.
777
977
  """
778
978
  logger = logging.getLogger(logger_name)
779
- all_sessions = self.get_subjects_metadata(cache)
979
+ all_sessions = self.get_subjects_metadata()
780
980
 
781
- sessions_to_del = [
981
+ session_to_del = [
782
982
  s for s in all_sessions if
783
- s["patient_secret_name"] == subject_name and int(
784
- s["ssid"]
785
- ) == session_id
983
+ s["patient_secret_name"] == subject_name and
984
+ s["ssid"] == session_id
786
985
  ]
787
986
 
788
- if not sessions_to_del:
987
+ if not session_to_del:
789
988
  logger.error(
790
989
  f"Session {subject_name}/{session_id} could not be found "
791
990
  f"in this project."
792
991
  )
793
992
  return False
794
- elif len(sessions_to_del) > 1:
993
+ elif len(session_to_del) > 1:
795
994
  raise RuntimeError(
796
- "Multiple sessions with same SID and SSID. Contact support."
995
+ "Multiple sessions with same Subject ID and Session ID."
996
+ " Contact support."
797
997
  )
798
998
  else:
799
999
  logger.info("{}/{} found (id {})".format(
800
- subject_name, session_id, sessions_to_del[0]["_id"]
1000
+ subject_name, session_id, session_to_del[0]["_id"]
801
1001
  ))
802
1002
 
803
- session = sessions_to_del[0]
1003
+ session = session_to_del[0]
804
1004
 
805
1005
  try:
806
1006
  platform.parse_response(platform.post(
@@ -820,14 +1020,46 @@ class Project:
820
1020
  )
821
1021
  return True
822
1022
 
1023
+ def delete_session_by_patientid(self, patient_id):
1024
+ """
1025
+ Delete a session from a subject within a project providing the
1026
+ Patient ID.
1027
+
1028
+ Parameters
1029
+ ----------
1030
+ patient_id : str
1031
+ Patient ID of the Session ID/Subject ID
1032
+
1033
+ Returns
1034
+ -------
1035
+ bool
1036
+ True if correctly deleted, False otherwise.
1037
+ """
1038
+ logger = logging.getLogger(logger_name)
1039
+
1040
+ try:
1041
+ platform.parse_response(platform.post(
1042
+ self._account.auth, "patient_manager/delete_patient",
1043
+ data={
1044
+ "patient_id": str(int(patient_id)), "delete_files": 1
1045
+ }
1046
+ ))
1047
+ except errors.PlatformError:
1048
+ logger.error(f"Patient ID {patient_id} could not be deleted.")
1049
+ return False
1050
+
1051
+ logger.info(f"Patient ID {patient_id} successfully deleted.")
1052
+ return True
1053
+
823
1054
  def delete_subject(self, subject_name):
824
1055
  """
825
- Delete a subject from the project.
1056
+ Delete a subject from the project. It deletes all its available
1057
+ sessions only providing the Subject ID.
826
1058
 
827
1059
  Parameters
828
1060
  ----------
829
1061
  subject_name : str
830
- Name of the subject to be deleted.
1062
+ Subject ID of the subject to be deleted.
831
1063
 
832
1064
  Returns
833
1065
  -------
@@ -837,7 +1069,7 @@ class Project:
837
1069
 
838
1070
  logger = logging.getLogger(logger_name)
839
1071
  # Always fetch the session IDs from the platform before deleting them
840
- all_sessions = self.get_subjects_metadata(False)
1072
+ all_sessions = self.get_subjects_metadata()
841
1073
 
842
1074
  sessions_to_del = [
843
1075
  s for s in all_sessions if s["patient_secret_name"] == subject_name
@@ -852,7 +1084,7 @@ class Project:
852
1084
  return False
853
1085
 
854
1086
  for ssid in [s["ssid"] for s in sessions_to_del]:
855
- if not self.delete_session(subject_name, ssid, cache=True):
1087
+ if not self.delete_session(subject_name, ssid):
856
1088
  return False
857
1089
  return True
858
1090
 
@@ -937,16 +1169,16 @@ class Project:
937
1169
  add_to_container_id=0, chunk_size=2 ** 9,
938
1170
  split_data=False):
939
1171
  """
940
- Upload a file to the platform, associated with the current user.
1172
+ Upload a ZIP file to the platform.
941
1173
 
942
1174
  Parameters
943
1175
  ----------
944
1176
  file_path : str
945
- Path to the file to upload.
1177
+ Path to the ZIP file to upload.
946
1178
  subject_name : str
947
- Subject to which this file will belong
1179
+ Subject ID of the data to upload in the project in QMENTA Platform.
948
1180
  ssid : str
949
- The ID of the timepoint
1181
+ Session ID of the Subject ID (i.e., ID of the timepoint).
950
1182
  date_of_scan : str
951
1183
  Date of scan/creation of the file
952
1184
  description : str
@@ -956,7 +1188,7 @@ class Project:
956
1188
  name : str
957
1189
  Name of the file in the platform
958
1190
  input_data_type : str
959
- mri_brain_data:1.0 or gametection:1.0
1191
+ qmenta_medical_image_data:3.11
960
1192
  add_to_container_id : int
961
1193
  ID of the container to which this file should be added (if id > 0)
962
1194
  chunk_size : int
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: qmenta-client
3
- Version: 1.1.dev1245
3
+ Version: 1.1.dev1289
4
4
  Summary: Python client lib to interact with the QMENTA platform.
5
5
  Author: QMENTA
6
6
  Author-email: dev@qmenta.com
@@ -1,10 +1,10 @@
1
1
  qmenta/__init__.py,sha256=jv2YF__bseklT3OWEzlqJ5qE24c4aWd5F4r0TTjOrWQ,65
2
2
  qmenta/client/Account.py,sha256=MEljEy9cmg2uP2FG1d3TZAgfj66EE2_3PQAZ9rvpCXY,9647
3
3
  qmenta/client/File.py,sha256=ZgvSqejIosUt4uoX7opUnPnp5XGEaJNMRwFC0mQVB8k,5344
4
- qmenta/client/Project.py,sha256=hWow6ZjVNh_mAtUs4joLJ801oMfXuBqkxUc3Ov0zuQ4,50310
4
+ qmenta/client/Project.py,sha256=Y0G5vSVODThAL8kGwmtkcZrE49vB2LVlNf2eRrqr8dg,59767
5
5
  qmenta/client/Subject.py,sha256=lhxxVdQ6d-GNoQC8mrJwa4L1f44nJc4PcJtDspmKN7I,8756
6
6
  qmenta/client/__init__.py,sha256=AjTojBhZeW5nl0i605KS8S1Gl5tPNc1hdzD47BGNfoI,147
7
7
  qmenta/client/utils.py,sha256=5DK2T_HQprrCwLS0Ycm2CjseaYmAUKaJkJvYoW-Rqzc,2479
8
- qmenta_client-1.1.dev1245.dist-info/METADATA,sha256=HbS0xu5wSlXrV-7QB9Dr_gi0jRwwDj1zTQ0roFEr5p8,672
9
- qmenta_client-1.1.dev1245.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
10
- qmenta_client-1.1.dev1245.dist-info/RECORD,,
8
+ qmenta_client-1.1.dev1289.dist-info/METADATA,sha256=khcRoMKuV0hhGvxc-9tjMy9RRIXugkzyE5sKZ_PuF4M,672
9
+ qmenta_client-1.1.dev1289.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
10
+ qmenta_client-1.1.dev1289.dist-info/RECORD,,