******** Tutorial ******** Login to your account ===================== All actions in the platform need to be authenticated. In :py:mod:`qmenta.core`, the :py:func:`platform.post` function has an instance of :py:class:`qmenta.core.platform.Auth` for authentication. In order to create an :py:class:`Auth` instance, you must use the :py:func:`login` function: .. code-block:: python from qmenta.core.auth import Auth auth = Auth.login('tutorial@qmenta.com', 'MyPassword1%') The returned :py:obj:`auth` object is used in all the :py:func:`post` calls in the remainder of this tutorial. Instead of hard-coding the password, it is recommended to use :py:func:`getpass.getpass` or to read it from a configuration file. Projects ======== All requests to the platform can be done using a :py:func:`post` call. We use :py:func:`parse_response` to check the validity of the response and to extract the JSON information from the :py:class:`Response` object: .. code-block:: python from qmenta.core.platform import post, parse_response projects = parse_response(post( auth, 'projectset_manager/get_projectset_list' )) The returned value will be a list of dictionaries, where each dictionary represents a project and its information. For example: .. code-block:: python print(projects) >>> [ { '_id': 1, 'user_id': 'tutorial@qmenta.com', 'tags': ['test', 'example'], 'name': 'Example project', 'description': 'Example project for the qmenta.core tutorial', 'time_created': '2020-01-01 08:00:14' }, { '_id': 2, 'user_id': 'tutorial@qmenta.com' 'tags': ['example'], 'name': 'Another example project', 'description': 'Of course you can have more than one project', 'time_created': '2020-01-02 9:00:00' } ] To find a project with a specific name you can use, for example: .. code-block:: python [p for p in projects if p['name'].startswith('Example') >>> [ { '_id': 1, 'user_id': 'tutorial@qmenta.com', 'tags': ['test', 'example'], 'name': 'Example project', 'description': 'Example project for the qmenta.core tutorial', 'time_created': '2020-01-01 08:00:14' } ] .. Create a new project .. --------------------- .. To create a new project, you use the .. `projectset_manager/upsert_project` endpoint: .. .. code-block:: python .. parse_response(post( .. auth, 'projectset_manager/upsert_project', .. data={ .. 'name': 'My brand new project', .. 'description': 'It is still empty', .. } .. )) .. FIXME: abbrev is missing. We should auto-generate this in the platform. .. By using :py:func:`parse_response`, an exception will automatically .. be raised when the platform response indicates an error. Subjects ======== Get existing subjects --------------------- To get the list of existing subjects in a project, you first activate the project, and then request the list of subjects: .. code-block:: python post(auth, 'projectset_manager/activate_project', data={'project_id': 6}) subjects = parse_response(post(auth, 'patient_manager/get_patient_list')) You can then view the subject data as a dict: .. code-block:: python print(subjects[0]) >>> { '_id': 5395.0, 'age_at_scan': None, 'config': { 'time_created': {'$date': 1545840544017}, 'time_modified': {'$date': 1545840557867} }, 'container_id': 26817, 'data_location': 'us', 'date_at_scan': None, 'owner': 'Tutorial user', 'patient_secret_name': '123_4567', 'qa_comments': '', 'qa_status': '', 'site': '', 'ssid': '1', 'tags': ['healthy'], 'user_id': 'tutorial@qmenta.com' } Create a new session -------------------- To add a new subject to an existing project, you only need to specify its name: .. code-block:: python resp = post( auth, 'patient_manager/upsert_patient', data={'secret_name': 'john-doe'} ) parse_response(resp) >>> { 'success': 1, 'message': 'Session data inserted successfully !', 'ssid': '1' } This will automatically create the first session, with session ID :py:obj:`ssid = 1`. Executing the same command again will add another session with :py:obj:`ssid = 2` to the subject. To get the Session IDs belonging to the newly added subject with the given :py:obj:`patient_secret_name`, execute: .. code-block:: python subjects = parse_response(post(auth, 'patient_manager/get_patient_list')) [(s['_id'], s['patient_secret_name'], s['ssid']) for s in subjects if s['patient_secret_name'] == 'john-doe'] >>> [ (416522.0, 'john-doe', '1'), (416523.0, 'john-doe', '2') ] .. FIXME: These different IDs are confusing for the developers. .. also, secret_name and patient_secret_name is not consistent Upload data ----------- Sessions can be uploaded using the :py:mod:`qmenta.core.upload` module. To upload a single ZIP file to a project with Project ID `100`, use: .. code-block:: python from qmenta.core.upload import SingleUpload, FileInfo upload = SingleUpload( auth, '/home/tutorial/data/file0.zip', FileInfo(project_id=100) ) upload.start() It is optional to add `name`, `subject_name` and `session_id` as attributes of :py:class:`FileInfo`. If they are not specified, values will be chosen automatically. Instead of specifying a ZIP file to be uploaded, a directory may be given, and it will automatically be zipped before uploading. When uploading a large amount of files, use :py:class:`MultipleThreadedUploads`: .. code-block:: python from qmenta.core.upload import MultiThreadedUploads, FileInfo multi_upload = MultipleThreadedUploads(auth) pid = 100 multi_upload.add_upload( '/home/tutorial/data/file1.zip', FileInfo(project_id=pid, name='file1') ) multi_upload.add_upload( '/home/tim/data/file2.zip', FileInfo(project_id=pid, name='file2') ) When using :py:class:`MultipleThreadedUploads`, the uploads are started automatically, and multiple files will be uploaded in parallel. In order to see the upload progress, connect to the :py:mod:`blinker` signals `upload-status` and `upload-progress` before starting the uploads. For example: .. code-block:: python from blinker import signal def progress_changed(caller, upload_index): single_upload = caller bytes_in_mb = 1024.0 * 1024.0 percentage = (100.0 * single_upload.bytes_uploaded / single_upload.file_size) progress_message = ('[{:.0f}%] Progress for upload {} ({}): {:.2f}' ' / {:.2f} MB').format( percentage, upload_index, single_upload.upload_filename, single_upload.bytes_uploaded / bytes_in_mb, single_upload.file_size / bytes_in_mb ) print(progress_message) def status_changed(caller, upload_index): single_upload = caller print('Status changed to {}'.format(single_upload.status.name)) on_progress = signal('upload-progress') on_progress.connect(progress_changed) on_status = signal('upload-status') on_status.connect(status_changed) .. TODO: Document how to start an analysis .. Analyses .. ======== .. To list all analysis in the currently active project, execute: .. .. code-block:: python .. parse_response(post(auth, 'analysis_manager/get_analysis_list')) .. If there are a lot of analyses in the project, you may want to filter .. by analysis name: .. .. code-block:: python .. parse_response(post( .. auth, .. 'analysis_manager/get_analysis_list', .. data={'p_n': 'my analysis name'} .. )) .. >>> .. [{ '_id': 58143, .. 'config': {'time_project_end': {'$date': 1537355939265}, .. 'time_project_start': {'$date': 1537355930484}}, .. 'description': '', .. 'in_container_id': 23278, .. 'name': 'My analysis name 1.', .. 'owner': 'Tutorial user', .. 'patient_secret_name': '123_XX', .. 'projectset_id': 62, .. 'settings': {'age_months': 1000, .. 'input': {'container_id': 23278, .. 'date': {'$date': 1536796800000} .. } .. 'state': 'running', .. 'tags': ['example', 'test'], .. 'type_name': 'Example tool', .. 'version': '1.0' .. }] .. Start analysis .. -------------- .. In order to start a new analysis, you first need to know which data is .. available for a given subject: .. .. code-block:: python .. parse_response(post( .. auth, .. 'patient_manager/get_patient_profile_data', .. data={ .. } .. ))