Skip to content

server_handler

ServerCommunication

A class used to represent the server communication

Attributes:

Name Type Description
user_auth str

The user authentication.

api_key str

The API key.

api_params dict

The API parameters.

headers dict

The headers for the requests.

api_version str

The version of the API.

debug bool

Debug mode status.

HOST str

The host URL.

Source code in syncsketch_common/server_handler.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
class ServerCommunication:
    """
    A class used to represent the server communication

    Attributes:
        user_auth (str): The user authentication.
        api_key (str): The API key.
        api_params (dict): The API parameters.
        headers (dict): The headers for the requests.
        api_version (str): The version of the API.
        debug (bool): Debug mode status.
        HOST (str): The host URL.
    """
    log = logging.getLogger(__name__)

    def __init__(
        self,
        user_auth,
        api_key,
        host="https://www.syncsketch.com",
        use_expiring_token=False,
        debug=False,
        api_version="v1",
        use_header_auth=False,
        log=None
    ):
        """
        Constructs all the necessary attributes for the
        ServerCommunication object.

        Args:
            user_auth (str): The user authentication.
            api_key (str): The API key.
            host (str, optional): The host URL.
                Defaults to "https://www.syncsketch.com".
            use_expiring_token (bool, optional): Use of expiring token.
                Defaults to False.
            debug (bool, optional): Debug mode status.
                Defaults to False.
            api_version (str, optional): The version of the API.
                Defaults to "v1".
            use_header_auth (bool, optional): Use of header authentication.
                Defaults to False.
        """
        if log:
            self.log = log

        # set initial values
        self.user_auth = user_auth
        self.api_key = api_key
        self.api_params = {}
        self.headers = {}
        auth_type = "apikey"

        if use_expiring_token:
            auth_type = "token"

        if use_header_auth:
            # This will be the preferred way to connect
            # once we fix headers on live
            self.headers = {
                "Authorization": "{} {}:{}".format(
                    auth_type, self.user_auth, self.api_key
                )
            }
        elif use_expiring_token:
            self.api_params = {
                "token": self.api_key,
                "email": self.user_auth
            }
        else:
            self.api_params = {
                "api_key": self.api_key,
                "username": self.user_auth
            }

        self.api_version = api_version
        self.debug = debug
        self.HOST = host

    def get_api_base_url(self, api_version=None):
        """
        Get the base URL of the API.

        Args:
            api_version (str, optional): The version of the API.
                Defaults to None.

        Returns:
            str: The base URL of the API.
        """
        return self.join_url_path(
            self.HOST, "/api/{}/".format(api_version or self.api_version)
        )

    @staticmethod
    def join_url_path(base, *path_segments):
        """
        Join and normalize URL path segments.

        Args:
            base (str): The base URL.
            *path_segments (str): The path segments.

        Returns:
            str: The joined and normalized URL path.
        """
        url_segments = []
        url_segments.append(base.rstrip("/"))
        url_segments.extend(
            segment.strip("/") for segment in path_segments
        )
        url_segments.append("")
        url_string = "/".join(url_segments)
        return url_string

    def _get_unversioned_api_url(self, path):
        """
        Get the unversioned API URL.

        Args:
            path (str): The API path.

        Returns:
            str: The unversioned API URL.
        """
        return self.join_url_path(self.HOST, path)

    def _get_json_response(
        self,
        url,
        method=None,
        get_data=None,
        post_data=None,
        patch_data=None,
        put_data=None,
        api_params=None,
        content_type="application/json",
        raw_response=False,
        **kwargs
    ):
        """
        Function to get a JSON response from the server.

        Args:
            url (str): The URL.
            method (str, optional): The HTTP method. Defaults to None.
            get_data (dict, optional): GET data. Defaults to None.
            post_data (dict, optional): POST data. Defaults to None.
            patch_data (dict, optional): PATCH data. Defaults to None.
            put_data (dict, optional): PUT data. Defaults to None.
            content_type (str, optional): The content type.
                Defaults to "application/json".
            raw_response (bool, optional): Raw response flag.
                Defaults to False.

        Returns:
            dict or requests.Response: The JSON response or raw response.
        """
        url = self._get_unversioned_api_url(url)
        params = api_params or self.api_params.copy()
        headers = self.headers.copy()
        headers["Content-Type"] = content_type

        if get_data:
            params.update(get_data)

        if self.debug:
            self.log.debug("URL: {}, params: {}".format(url, params))

        if post_data or method == "post":
            src_files = kwargs.get("files")
            if src_files:
                new_src_files = {}
                for key, path in src_files.items():
                    with open(path, "rb") as stream:
                        new_src_files[key] = stream.read()
                src_files = new_src_files

            self.log.debug("URL: {}, params: {}".format(url, params))
            self.log.debug("post_data: {}".format(post_data))

            result = requests.post(
                url,
                json=post_data,
                files=src_files,
                params=params,
                headers=headers
            )

        elif patch_data or method == "patch":
            result = requests.patch(
                url,
                params=params,
                json=patch_data,
                headers=headers,
                **kwargs
            )
        elif put_data or method == "put":
            result = requests.put(
                url,
                params=params,
                json=put_data,
                headers=headers,
                **kwargs
            )
        elif method == "delete":
            result = requests.patch(
                url,
                params=params,
                data={"active": False},
                headers=headers,
                **kwargs
            )
        else:
            result = requests.get(
                url,
                params=params,
                headers=headers,
                **kwargs
            )

        if raw_response:
            return result

        result.raise_for_status()

        """ TODO: This is a temporary fix for the API returning
        This needs to be handled in specific api call functions instead.
        Each of those functions should have own error handling.
        """
        try:
            return result.json()
        except Exception as e:
            if self.debug:
                self.log.error(e)

            self.log.error("Error: {}".format(result.text))

            return {"objects": []}

    def is_connected(self):
        """
        Convenience function to check if the API is connected to SyncSketch.
        Checks against Status Code 200 and returns False if not.

        Returns:
            bool: The connection status.
        """
        url = "/api/{}/person/connected/".format(self.api_version)
        params = self.api_params.copy()

        if self.debug:
            self.log.debug("URL: {}, params: {}".format(url, params))

        result = self._get_json_response(url, raw_response=True)
        return result.status_code == 200

    def get_projects(
        self,
        include_deleted=False,
        include_archived=False,
        include_tags=False,
        include_connections=False,
        limit=100,
        offset=0
    ):
        """
        Get a list of currently active projects

        Args:
            include_deleted (bool, optional): Include deleted projects.
                Defaults to False.
            include_archived (bool, optional): Include archived projects.
                Defaults to False.
            include_tags (bool, optional): Include tag list on
                the project object.
                Defaults to False.
            include_connections (bool, optional): Include connections.
                Defaults to False.
            limit (int, optional): The number of projects to return.
                Defaults to 100.
            offset (int, optional): The offset. Defaults to 0.

        Returns:
            dict: Dictionary with meta information and an array
                  of found projects.
        """
        get_params = {
            "active": 1,
            "is_archived": 0,
            "account__active": 1,
            "limit": limit,
            "offset": offset,
        }

        if include_connections:
            get_params["withFullConnections"] = True

        if include_deleted:
            del get_params["active"]

        if include_archived:
            del get_params["active"]
            del get_params["is_archived"]

        if include_tags:
            get_params["include_tags"] = 1

        return self._get_json_response(
            "/api/{}/project/".format(self.api_version),
            get_data=get_params
        )

    def get_projects_by_name(self, name):
        """
        Get a project by name regardless of status

        Args:
            name (str): The name of the project.

        Returns:
            dict: Dictionary with meta information and an array
                  of found projects.
        """
        get_params = {"name__istartswith": name}
        return self._get_json_response(
            "/api/{}/project/".format(self.api_version),
            get_data=get_params
        )

    def get_project_by_id(self, project_id):
        """
        Get single project by id

        Args:
            project_id (int): The id of the project.

        Returns:
            dict: The project.
        """
        return self._get_json_response(
            "/api/{}/project/{}/".format(self.api_version, project_id)
        )

    def get_project_storage(self, project_id):
        """
        Get project storage usage in bytes

        Args:
            project_id (int): The id of the project.

        Returns:
            dict: The project storage.
        """
        return self._get_json_response(
            "/api/v2/project/{}/storage/".format(project_id)
        )

    def update_project(self, project_id, data):
        """
        Update a project

        Args:
            project_id (int): The id of the project.
            data (dict): Dictionary with data for the project.

        Returns:
            dict: The updated project.
        """
        if not isinstance(data, dict):
            self.log.debug("Please make sure you pass a dict as data")
            return False

        return self._get_json_response(
            "/api/{}/project/{}/".format(self.api_version, project_id),
            patch_data=data
        )

    def delete_project(self, project_id):
        """
        Delete a project by id. This method sets the "active" attribute
        of the project to False.

        Args:
            project_id (int): The ID of the project.

        Returns:
            dict: The response from the API call.
        """
        return self._get_json_response(
            "/api/{}/project/{}/".format(self.api_version, project_id),
            patch_data=dict(active=False)
        )

    def duplicate_project(self, project_id, name=None, copy_reviews=False,
                          copy_users=False, copy_settings=False):
        """
        Create a new project from an existing project.

        Args:
            project_id (int): The ID of the project to duplicate.
            name (str, optional): The name of the new project.
            copy_reviews (bool, optional): Whether to copy reviews.
                Defaults to False.
            copy_users (bool, optional): Whether to copy users.
                Defaults to False.
            copy_settings (bool, optional): Whether to copy settings.
                Defaults to False.

        Returns:
            dict: The new project data.
        """
        # Construct the configuration for the new project
        config = dict(
            reviews=copy_reviews,
            users=copy_users,
            settings=copy_settings,
        )
        if name:
            config["name"] = name

        return self._get_json_response(
            "/api/v2/project/{}/duplicate/".format(project_id),
            post_data=config
        )

    def archive_project(self, project_id):
        """
        Archive a project by id. This method sets the "is_archived"
        attribute of the project to True.

        Args:
            project_id (int): The ID of the project.

        Returns:
            dict: The response from the API call.
        """
        return self._get_json_response(
            "/api/{}/project/{}/".format(self.api_version, project_id),
            patch_data=dict(is_archived=True)
        )

    def restore_project(self, project_id):
        """
        Restore (unarchive) a project by id. This method sets the
        "is_archived" attribute of the project to False.

        Args:
            project_id (int): The ID of the project.

        Returns:
            dict: The response from the API call.
        """
        return self._get_json_response(
            "/api/{}/project/{}/".format(self.api_version, project_id),
            patch_data=dict(is_archived=False)
        )

    def create_review(self, project_id, name, description="", data=None):
        """
        Create a review for a specific project.

        Args:
            project_id (int): The ID of the project.
            name (str): The name of the review.
            description (str, optional): The description of the review.
                Defaults to "".
            data (dict, optional): Additional data for the review.
                Defaults to None.

        Returns:
            dict: The response from the API call.
        """
        if data is None:
            data = {}

        post_data = {
            "project": "/api/{}/project/{}/".format(
                self.api_version, project_id
            ),
            "name": name,
            "description": description,
        }

        post_data.update(data)

        return self._get_json_response(
            "/api/{}/review/".format(self.api_version),
            post_data=post_data
        )

    def get_reviews_by_project_id(self, project_id, limit=100, offset=0):
        """
        Get list of reviews by project id.

        Args:
            project_id (int): The ID of the project.
            limit (int, optional): The maximum number of reviews to retrieve.
                Defaults to 100.
            offset (int, optional): The number of reviews to skip
                before starting to collect.
            Defaults to 0.

        Returns:
            dict: Meta information and an array of found projects.
        """
        get_params = {
            "project__id": project_id,
            "project__active": 1,
            "project__is_archived": 0,
            "limit": limit,
            "offset": offset
        }
        self.log.debug("get_reviews_by_project_id: {}".format(get_params))

        return self._get_json_response(
            "/api/{}/review/".format(self.api_version),
            get_data=get_params
        )

    def get_review_by_name(self, name):
        """
        Get reviews by name using a case insensitive startswith query.

        Args:
            name (str): The name of the review.

        Returns:
            dict: Meta information and an array of found projects.
        """
        get_params = {"name__istartswith": name}
        return self._get_json_response(
            "/api/{}/review/".format(self.api_version),
            get_data=get_params
        )

    def get_review_by_id(self, review_id):
        """
        Get single review by id.

        Args:
            review_id (int): The ID of the review.

        Returns:
            dict: The review data.
        """
        return self._get_json_response(
            "/api/{}/review/{}/".format(self.api_version, review_id))

    def get_review_storage(self, review_id):
        """
        Get review storage usage in bytes.

        Args:
            review_id (int): The ID of the review.

        Returns:
            dict: The response from the API call.
        """
        return self._get_json_response(
            "/api/v2/review/{}/storage/".format(review_id)
        )

    def update_review(self, review_id, data):
        """
        Update a review by id.

        Args:
            review_id (int): The ID of the review.
            data (dict): The new data for the review.

        Returns:
            dict/bool: The response from the API call if the data is a dict,
            False otherwise.
        """
        if not isinstance(data, dict):
            self.log.debug("Please make sure you pass a dict as data")
            return False

        return self._get_json_response(
            "/api/{}/review/{}/".format(self.api_version, review_id),
            patch_data=data
        )

    def get_review_item(self, item_id, data=None):
        """
        Get single item by id.

        Args:
            item_id (int): The ID of the item.
            data (dict, optional): Additional data for the item.
                Defaults to None.

        Returns:
            dict: The item data.
        """
        return self._get_json_response(
            "/api/{}/item/{}/".format(self.api_version, item_id),
            get_data=data
        )

    def update_review_item(self, item_id, data=None):
        """
        Update single item by id.

        Args:
            item_id (int): The ID of the item.
            data (dict, optional): Additional data for the item.
                Defaults to None.

        Returns:
            dict: The item data.
        """
        return self._get_json_response(
            "/api/{}/item/{}/".format(self.api_version, item_id),
            patch_data=data
        )

    def upload_review_item(
        self,
        review_id,
        filepath,
        artist_name="",
        file_name="",
        no_convert_flag=False,
        item_parent_id=False
    ):
        """
        Convenience function to upload a file to a review. It will
        automatically create an Item and attach it to the review.

        Args:
            review_id (int): Required review_id
            filepath (str): Path for the file on disk e.g /tmp/movie.webm
            artist_name (str, optional): The name of the artist you want
                associated with this media file. Defaults to "".
            file_name (str, optional): The name of the file. Please make
                sure to pass the correct file extension. Defaults to "".
            no_convert_flag (bool, optional): The video you are uploading
                is already in a browser compatible format. Defaults to False.
            item_parent_id (int, optional): Set when you want to add a new
                version of an item. item_parent_id is the id of the item you
                want to upload a new version for. Defaults to False.

        Returns:
            dict: The response from the API call.
        """
        get_params = self.api_params.copy()

        if no_convert_flag:
            get_params.update({"noConvertFlag": 1})

        if item_parent_id:
            get_params.update({"itemParentId": item_parent_id})

        upload_url = "{}/items/uploadToReview/{}/?{}".format(
            self.HOST, review_id, urlencode(get_params)
        )

        files = {"reviewFile": open(filepath, "rb")}
        result = requests.post(
            upload_url,
            data=dict(
                artist=artist_name, name=file_name
            ),
            files=files,
            headers=self.headers
        )

        try:
            return json.loads(result.text)
        except Exception:
            self.log.error(result.text)

    def get_media_by_review_id(self, review_id):
        """
        Get all media by review id.

        Args:
            review_id (int): The ID of the review.

        Returns:
            dict: The response from the API call.
        """
        get_params = {"reviews__id": review_id, "active": 1}
        return self._get_json_response(
            "/api/{}/item/".format(self.api_version),
            get_data=get_params
        )

    def move_items(self, new_review_id, item_data):
        """
        Move items to a new review.

        Args:
            new_review_id (int): The ID of the new review.
            item_data (dict): The item data.

        Returns:
            dict: The response from the API call.
        """
        return self._get_json_response(
            "/api/v2/move-review-items/",
            method="post",
            post_data={"new_review_id": new_review_id, "item_data": item_data},
            raw_response=True,
        )

    def add_comment(self, item_id, text, review_id, frame=0):
        """
        Add a comment to an item in a review.

        Args:
            item_id (int): The ID of the item.
            text (str): The comment text.
            review_id (int): The ID of the review.
            frame (int, optional): The frame to which the comment refers.
                Defaults to 0.

        Returns:
            dict: The response from the API call.
        """
        item = self.get_item(item_id, data={"review_id": review_id})

        # Ugly method of getting revision id from item data,
        # should fix this with api v2
        revision_id = item.get("revision_id")
        if not revision_id:
            return "error"

        post_data = dict(
            item="/api/{}/item/{}/".format(
                self.api_version, item_id),
            frame=frame,
            revision="/api/{}/revision/{}/".format(
                self.api_version, revision_id),
            type="comment",
            text=text
        )

        return self._get_json_response(
            "/api/{}/frame/".format(self.api_version),
            method="post",
            post_data=post_data
        )

    def get_annotations(self, item_id, revisionId=False, review_id=False):
        """
        Get annotations of an item.

        Args:
            item_id (int): The ID of the item.
            revisionId (bool, optional): The ID of the revision.
                Defaults to False.
            review_id (bool, optional): The ID of the review.
                Defaults to False.

        Returns:
            dict: The response from the API call.
        """
        get_params = {"item__id": item_id, "active": 1}

        if revisionId:
            get_params["revision__id"] = revisionId

        if review_id:
            get_params["revision__review_id"] = review_id

        return self._get_json_response(
            "/api/{}/frame/".format(self.api_version),
            get_data=get_params
        )

    def get_flattened_annotations(self, item_id, review_id,
                                  with_tracing_paper=False,
                                  return_as_base64=False):
        """
        Get flattened annotations of an item in a review.

        Args:
            item_id (int): The ID of the item.
            review_id (int): The ID of the review.
            with_tracing_paper (bool, optional): Include tracing paper in
                the response. Defaults to False.
            return_as_base64 (bool, optional): Return the response as base64.
                Defaults to False.

        Returns:
            dict: The response from the API call.
        """

        get_data_ = {
            "include_data": 1,
            "tracingpaper": 1 if with_tracing_paper else 0,
            "base64": 1 if return_as_base64 else 0,
            "async": 0
        }

        url = "/api/v2/downloads/flattenedSketches/{}/{}/".format(
            review_id, item_id)

        return self._get_json_response(
            url,
            method="post",
            get_data=get_data_
        )

    def get_grease_pencil_overlays(self, review_id, item_id, homedir=None,
                                   api_version=None):
        """
        Fetches grease pencil overlays of a specific item from a specific
        review. The result will be a .zip file.

        Args:
            review_id (int): ID of the review.
            item_id (int): ID of the item.
            homedir (str, optional): Home directory where the .zip file
                is to be stored. If not provided, it will default
                to "/tmp/" directory.
            api_version (str, optional): The API version to use.
                Defaults to None

        Returns:
            str: Local path to the .zip file with grease pencil
                 overlays or False if the request failed.
        """
        api_version = api_version or "v2"

        url = "/api/{}/downloads/greasePencil/{}/{}".format(
            api_version, review_id, item_id)
        result = self._get_json_response(
            url,
            method="post"
        )
        celery_task_id = result.json()

        # Check the celery task
        check_celery_url = "/api/{}/downloads/greasePencil/{}/".format(
            api_version, celery_task_id)
        result = self._get_json_response(
            check_celery_url
        )

        request_processing = True
        while request_processing:
            result_json = result.json()

            if result_json.get("status") == "done":
                data = result_json.get("data")

                # Store the file locally
                local_filename = "/tmp/{}.zip".format(data["fileName"])
                if homedir:
                    local_filename = os.path.join(
                        homedir, "{}.zip".format(data["fileName"]))
                result_s3 = requests.get(data["s3Path"], stream=True)
                with open(local_filename, "wb") as f:
                    for chunk in result_s3.iter_content(chunk_size=1024):
                        if chunk:
                            f.write(chunk)

                request_processing = False
                return local_filename

            if result_json.get("status") == "failed":
                request_processing = False
                return False

            # Wait a bit before checking again
            time.sleep(1)

            # Check the URL again
            result = self._get_json_response(
                check_celery_url
            )

    def get_users_by_project_id(self, project_id):
        """
        Get all users by project id.

        Args:
            project_id (int): The ID of the project.

        Returns:
            dict: The response from the API call.
        """
        return self._get_json_response(
            "/api/v2/all-project-users/{}".format(project_id))

    def get_connections_by_user_id(self, user_id, account_id,
                                   include_inactive=None,
                                   include_archived=None):
        """
        Get all project and account connections for a user. Good for
        checking access for a user that might have left...

        Args:
            user_id (int): The ID of the user.
            account_id (int): The ID of the account.
            include_inactive (bool, optional): Include inactive connections.
                Defaults to None.
            include_archived (bool, optional): Include archived connections.
                Defaults to None.

        Returns:
            dict: The response from the API call.
        """
        data = {}
        if include_inactive is not None:
            data["include_inactive"] = "true" if include_inactive else "false"
        if include_archived is not None:
            data["include_archived"] = "true" if include_archived else "false"
        return self._get_json_response(
            "/api/v2/user/{}/connections/account/{}/".format(
                user_id, account_id),
            get_data=data,
        )

    def get_user_by_id(self, userId):
        """
        Get user by user id.

        Args:
            userId (int): The ID of the user.

        Returns:
            dict: The response from the API call.
        """
        return self._get_json_response(
            "/api/{}/simpleperson/{}".format(self.api_version, userId))

    def get_current_user(self):
        """
        Get the current user.

        Returns:
            dict: The response from the API call.
        """
        return self._get_json_response(
            "/api/{}/simpleperson/currentUser/".format(self.api_version)
        )

__init__(user_auth, api_key, host='https://www.syncsketch.com', use_expiring_token=False, debug=False, api_version='v1', use_header_auth=False, log=None)

Constructs all the necessary attributes for the ServerCommunication object.

Parameters:

Name Type Description Default
user_auth str

The user authentication.

required
api_key str

The API key.

required
host str

The host URL. Defaults to "https://www.syncsketch.com".

'https://www.syncsketch.com'
use_expiring_token bool

Use of expiring token. Defaults to False.

False
debug bool

Debug mode status. Defaults to False.

False
api_version str

The version of the API. Defaults to "v1".

'v1'
use_header_auth bool

Use of header authentication. Defaults to False.

False
Source code in syncsketch_common/server_handler.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def __init__(
    self,
    user_auth,
    api_key,
    host="https://www.syncsketch.com",
    use_expiring_token=False,
    debug=False,
    api_version="v1",
    use_header_auth=False,
    log=None
):
    """
    Constructs all the necessary attributes for the
    ServerCommunication object.

    Args:
        user_auth (str): The user authentication.
        api_key (str): The API key.
        host (str, optional): The host URL.
            Defaults to "https://www.syncsketch.com".
        use_expiring_token (bool, optional): Use of expiring token.
            Defaults to False.
        debug (bool, optional): Debug mode status.
            Defaults to False.
        api_version (str, optional): The version of the API.
            Defaults to "v1".
        use_header_auth (bool, optional): Use of header authentication.
            Defaults to False.
    """
    if log:
        self.log = log

    # set initial values
    self.user_auth = user_auth
    self.api_key = api_key
    self.api_params = {}
    self.headers = {}
    auth_type = "apikey"

    if use_expiring_token:
        auth_type = "token"

    if use_header_auth:
        # This will be the preferred way to connect
        # once we fix headers on live
        self.headers = {
            "Authorization": "{} {}:{}".format(
                auth_type, self.user_auth, self.api_key
            )
        }
    elif use_expiring_token:
        self.api_params = {
            "token": self.api_key,
            "email": self.user_auth
        }
    else:
        self.api_params = {
            "api_key": self.api_key,
            "username": self.user_auth
        }

    self.api_version = api_version
    self.debug = debug
    self.HOST = host

add_comment(item_id, text, review_id, frame=0)

Add a comment to an item in a review.

Parameters:

Name Type Description Default
item_id int

The ID of the item.

required
text str

The comment text.

required
review_id int

The ID of the review.

required
frame int

The frame to which the comment refers. Defaults to 0.

0

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
def add_comment(self, item_id, text, review_id, frame=0):
    """
    Add a comment to an item in a review.

    Args:
        item_id (int): The ID of the item.
        text (str): The comment text.
        review_id (int): The ID of the review.
        frame (int, optional): The frame to which the comment refers.
            Defaults to 0.

    Returns:
        dict: The response from the API call.
    """
    item = self.get_item(item_id, data={"review_id": review_id})

    # Ugly method of getting revision id from item data,
    # should fix this with api v2
    revision_id = item.get("revision_id")
    if not revision_id:
        return "error"

    post_data = dict(
        item="/api/{}/item/{}/".format(
            self.api_version, item_id),
        frame=frame,
        revision="/api/{}/revision/{}/".format(
            self.api_version, revision_id),
        type="comment",
        text=text
    )

    return self._get_json_response(
        "/api/{}/frame/".format(self.api_version),
        method="post",
        post_data=post_data
    )

archive_project(project_id)

Archive a project by id. This method sets the "is_archived" attribute of the project to True.

Parameters:

Name Type Description Default
project_id int

The ID of the project.

required

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
def archive_project(self, project_id):
    """
    Archive a project by id. This method sets the "is_archived"
    attribute of the project to True.

    Args:
        project_id (int): The ID of the project.

    Returns:
        dict: The response from the API call.
    """
    return self._get_json_response(
        "/api/{}/project/{}/".format(self.api_version, project_id),
        patch_data=dict(is_archived=True)
    )

create_review(project_id, name, description='', data=None)

Create a review for a specific project.

Parameters:

Name Type Description Default
project_id int

The ID of the project.

required
name str

The name of the review.

required
description str

The description of the review. Defaults to "".

''
data dict

Additional data for the review. Defaults to None.

None

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
def create_review(self, project_id, name, description="", data=None):
    """
    Create a review for a specific project.

    Args:
        project_id (int): The ID of the project.
        name (str): The name of the review.
        description (str, optional): The description of the review.
            Defaults to "".
        data (dict, optional): Additional data for the review.
            Defaults to None.

    Returns:
        dict: The response from the API call.
    """
    if data is None:
        data = {}

    post_data = {
        "project": "/api/{}/project/{}/".format(
            self.api_version, project_id
        ),
        "name": name,
        "description": description,
    }

    post_data.update(data)

    return self._get_json_response(
        "/api/{}/review/".format(self.api_version),
        post_data=post_data
    )

delete_project(project_id)

Delete a project by id. This method sets the "active" attribute of the project to False.

Parameters:

Name Type Description Default
project_id int

The ID of the project.

required

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
def delete_project(self, project_id):
    """
    Delete a project by id. This method sets the "active" attribute
    of the project to False.

    Args:
        project_id (int): The ID of the project.

    Returns:
        dict: The response from the API call.
    """
    return self._get_json_response(
        "/api/{}/project/{}/".format(self.api_version, project_id),
        patch_data=dict(active=False)
    )

duplicate_project(project_id, name=None, copy_reviews=False, copy_users=False, copy_settings=False)

Create a new project from an existing project.

Parameters:

Name Type Description Default
project_id int

The ID of the project to duplicate.

required
name str

The name of the new project.

None
copy_reviews bool

Whether to copy reviews. Defaults to False.

False
copy_users bool

Whether to copy users. Defaults to False.

False
copy_settings bool

Whether to copy settings. Defaults to False.

False

Returns:

Name Type Description
dict

The new project data.

Source code in syncsketch_common/server_handler.py
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
def duplicate_project(self, project_id, name=None, copy_reviews=False,
                      copy_users=False, copy_settings=False):
    """
    Create a new project from an existing project.

    Args:
        project_id (int): The ID of the project to duplicate.
        name (str, optional): The name of the new project.
        copy_reviews (bool, optional): Whether to copy reviews.
            Defaults to False.
        copy_users (bool, optional): Whether to copy users.
            Defaults to False.
        copy_settings (bool, optional): Whether to copy settings.
            Defaults to False.

    Returns:
        dict: The new project data.
    """
    # Construct the configuration for the new project
    config = dict(
        reviews=copy_reviews,
        users=copy_users,
        settings=copy_settings,
    )
    if name:
        config["name"] = name

    return self._get_json_response(
        "/api/v2/project/{}/duplicate/".format(project_id),
        post_data=config
    )

get_annotations(item_id, revisionId=False, review_id=False)

Get annotations of an item.

Parameters:

Name Type Description Default
item_id int

The ID of the item.

required
revisionId bool

The ID of the revision. Defaults to False.

False
review_id bool

The ID of the review. Defaults to False.

False

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
def get_annotations(self, item_id, revisionId=False, review_id=False):
    """
    Get annotations of an item.

    Args:
        item_id (int): The ID of the item.
        revisionId (bool, optional): The ID of the revision.
            Defaults to False.
        review_id (bool, optional): The ID of the review.
            Defaults to False.

    Returns:
        dict: The response from the API call.
    """
    get_params = {"item__id": item_id, "active": 1}

    if revisionId:
        get_params["revision__id"] = revisionId

    if review_id:
        get_params["revision__review_id"] = review_id

    return self._get_json_response(
        "/api/{}/frame/".format(self.api_version),
        get_data=get_params
    )

get_api_base_url(api_version=None)

Get the base URL of the API.

Parameters:

Name Type Description Default
api_version str

The version of the API. Defaults to None.

None

Returns:

Name Type Description
str

The base URL of the API.

Source code in syncsketch_common/server_handler.py
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def get_api_base_url(self, api_version=None):
    """
    Get the base URL of the API.

    Args:
        api_version (str, optional): The version of the API.
            Defaults to None.

    Returns:
        str: The base URL of the API.
    """
    return self.join_url_path(
        self.HOST, "/api/{}/".format(api_version or self.api_version)
    )

get_connections_by_user_id(user_id, account_id, include_inactive=None, include_archived=None)

Get all project and account connections for a user. Good for checking access for a user that might have left...

Parameters:

Name Type Description Default
user_id int

The ID of the user.

required
account_id int

The ID of the account.

required
include_inactive bool

Include inactive connections. Defaults to None.

None
include_archived bool

Include archived connections. Defaults to None.

None

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
def get_connections_by_user_id(self, user_id, account_id,
                               include_inactive=None,
                               include_archived=None):
    """
    Get all project and account connections for a user. Good for
    checking access for a user that might have left...

    Args:
        user_id (int): The ID of the user.
        account_id (int): The ID of the account.
        include_inactive (bool, optional): Include inactive connections.
            Defaults to None.
        include_archived (bool, optional): Include archived connections.
            Defaults to None.

    Returns:
        dict: The response from the API call.
    """
    data = {}
    if include_inactive is not None:
        data["include_inactive"] = "true" if include_inactive else "false"
    if include_archived is not None:
        data["include_archived"] = "true" if include_archived else "false"
    return self._get_json_response(
        "/api/v2/user/{}/connections/account/{}/".format(
            user_id, account_id),
        get_data=data,
    )

get_current_user()

Get the current user.

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
944
945
946
947
948
949
950
951
952
953
def get_current_user(self):
    """
    Get the current user.

    Returns:
        dict: The response from the API call.
    """
    return self._get_json_response(
        "/api/{}/simpleperson/currentUser/".format(self.api_version)
    )

get_flattened_annotations(item_id, review_id, with_tracing_paper=False, return_as_base64=False)

Get flattened annotations of an item in a review.

Parameters:

Name Type Description Default
item_id int

The ID of the item.

required
review_id int

The ID of the review.

required
with_tracing_paper bool

Include tracing paper in the response. Defaults to False.

False
return_as_base64 bool

Return the response as base64. Defaults to False.

False

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
def get_flattened_annotations(self, item_id, review_id,
                              with_tracing_paper=False,
                              return_as_base64=False):
    """
    Get flattened annotations of an item in a review.

    Args:
        item_id (int): The ID of the item.
        review_id (int): The ID of the review.
        with_tracing_paper (bool, optional): Include tracing paper in
            the response. Defaults to False.
        return_as_base64 (bool, optional): Return the response as base64.
            Defaults to False.

    Returns:
        dict: The response from the API call.
    """

    get_data_ = {
        "include_data": 1,
        "tracingpaper": 1 if with_tracing_paper else 0,
        "base64": 1 if return_as_base64 else 0,
        "async": 0
    }

    url = "/api/v2/downloads/flattenedSketches/{}/{}/".format(
        review_id, item_id)

    return self._get_json_response(
        url,
        method="post",
        get_data=get_data_
    )

get_grease_pencil_overlays(review_id, item_id, homedir=None, api_version=None)

Fetches grease pencil overlays of a specific item from a specific review. The result will be a .zip file.

Parameters:

Name Type Description Default
review_id int

ID of the review.

required
item_id int

ID of the item.

required
homedir str

Home directory where the .zip file is to be stored. If not provided, it will default to "/tmp/" directory.

None
api_version str

The API version to use. Defaults to None

None

Returns:

Name Type Description
str

Local path to the .zip file with grease pencil overlays or False if the request failed.

Source code in syncsketch_common/server_handler.py
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
def get_grease_pencil_overlays(self, review_id, item_id, homedir=None,
                               api_version=None):
    """
    Fetches grease pencil overlays of a specific item from a specific
    review. The result will be a .zip file.

    Args:
        review_id (int): ID of the review.
        item_id (int): ID of the item.
        homedir (str, optional): Home directory where the .zip file
            is to be stored. If not provided, it will default
            to "/tmp/" directory.
        api_version (str, optional): The API version to use.
            Defaults to None

    Returns:
        str: Local path to the .zip file with grease pencil
             overlays or False if the request failed.
    """
    api_version = api_version or "v2"

    url = "/api/{}/downloads/greasePencil/{}/{}".format(
        api_version, review_id, item_id)
    result = self._get_json_response(
        url,
        method="post"
    )
    celery_task_id = result.json()

    # Check the celery task
    check_celery_url = "/api/{}/downloads/greasePencil/{}/".format(
        api_version, celery_task_id)
    result = self._get_json_response(
        check_celery_url
    )

    request_processing = True
    while request_processing:
        result_json = result.json()

        if result_json.get("status") == "done":
            data = result_json.get("data")

            # Store the file locally
            local_filename = "/tmp/{}.zip".format(data["fileName"])
            if homedir:
                local_filename = os.path.join(
                    homedir, "{}.zip".format(data["fileName"]))
            result_s3 = requests.get(data["s3Path"], stream=True)
            with open(local_filename, "wb") as f:
                for chunk in result_s3.iter_content(chunk_size=1024):
                    if chunk:
                        f.write(chunk)

            request_processing = False
            return local_filename

        if result_json.get("status") == "failed":
            request_processing = False
            return False

        # Wait a bit before checking again
        time.sleep(1)

        # Check the URL again
        result = self._get_json_response(
            check_celery_url
        )

get_media_by_review_id(review_id)

Get all media by review id.

Parameters:

Name Type Description Default
review_id int

The ID of the review.

required

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
def get_media_by_review_id(self, review_id):
    """
    Get all media by review id.

    Args:
        review_id (int): The ID of the review.

    Returns:
        dict: The response from the API call.
    """
    get_params = {"reviews__id": review_id, "active": 1}
    return self._get_json_response(
        "/api/{}/item/".format(self.api_version),
        get_data=get_params
    )

get_project_by_id(project_id)

Get single project by id

Parameters:

Name Type Description Default
project_id int

The id of the project.

required

Returns:

Name Type Description
dict

The project.

Source code in syncsketch_common/server_handler.py
343
344
345
346
347
348
349
350
351
352
353
354
355
def get_project_by_id(self, project_id):
    """
    Get single project by id

    Args:
        project_id (int): The id of the project.

    Returns:
        dict: The project.
    """
    return self._get_json_response(
        "/api/{}/project/{}/".format(self.api_version, project_id)
    )

get_project_storage(project_id)

Get project storage usage in bytes

Parameters:

Name Type Description Default
project_id int

The id of the project.

required

Returns:

Name Type Description
dict

The project storage.

Source code in syncsketch_common/server_handler.py
357
358
359
360
361
362
363
364
365
366
367
368
369
def get_project_storage(self, project_id):
    """
    Get project storage usage in bytes

    Args:
        project_id (int): The id of the project.

    Returns:
        dict: The project storage.
    """
    return self._get_json_response(
        "/api/v2/project/{}/storage/".format(project_id)
    )

get_projects(include_deleted=False, include_archived=False, include_tags=False, include_connections=False, limit=100, offset=0)

Get a list of currently active projects

Parameters:

Name Type Description Default
include_deleted bool

Include deleted projects. Defaults to False.

False
include_archived bool

Include archived projects. Defaults to False.

False
include_tags bool

Include tag list on the project object. Defaults to False.

False
include_connections bool

Include connections. Defaults to False.

False
limit int

The number of projects to return. Defaults to 100.

100
offset int

The offset. Defaults to 0.

0

Returns:

Name Type Description
dict

Dictionary with meta information and an array of found projects.

Source code in syncsketch_common/server_handler.py
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
def get_projects(
    self,
    include_deleted=False,
    include_archived=False,
    include_tags=False,
    include_connections=False,
    limit=100,
    offset=0
):
    """
    Get a list of currently active projects

    Args:
        include_deleted (bool, optional): Include deleted projects.
            Defaults to False.
        include_archived (bool, optional): Include archived projects.
            Defaults to False.
        include_tags (bool, optional): Include tag list on
            the project object.
            Defaults to False.
        include_connections (bool, optional): Include connections.
            Defaults to False.
        limit (int, optional): The number of projects to return.
            Defaults to 100.
        offset (int, optional): The offset. Defaults to 0.

    Returns:
        dict: Dictionary with meta information and an array
              of found projects.
    """
    get_params = {
        "active": 1,
        "is_archived": 0,
        "account__active": 1,
        "limit": limit,
        "offset": offset,
    }

    if include_connections:
        get_params["withFullConnections"] = True

    if include_deleted:
        del get_params["active"]

    if include_archived:
        del get_params["active"]
        del get_params["is_archived"]

    if include_tags:
        get_params["include_tags"] = 1

    return self._get_json_response(
        "/api/{}/project/".format(self.api_version),
        get_data=get_params
    )

get_projects_by_name(name)

Get a project by name regardless of status

Parameters:

Name Type Description Default
name str

The name of the project.

required

Returns:

Name Type Description
dict

Dictionary with meta information and an array of found projects.

Source code in syncsketch_common/server_handler.py
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
def get_projects_by_name(self, name):
    """
    Get a project by name regardless of status

    Args:
        name (str): The name of the project.

    Returns:
        dict: Dictionary with meta information and an array
              of found projects.
    """
    get_params = {"name__istartswith": name}
    return self._get_json_response(
        "/api/{}/project/".format(self.api_version),
        get_data=get_params
    )

get_review_by_id(review_id)

Get single review by id.

Parameters:

Name Type Description Default
review_id int

The ID of the review.

required

Returns:

Name Type Description
dict

The review data.

Source code in syncsketch_common/server_handler.py
549
550
551
552
553
554
555
556
557
558
559
560
def get_review_by_id(self, review_id):
    """
    Get single review by id.

    Args:
        review_id (int): The ID of the review.

    Returns:
        dict: The review data.
    """
    return self._get_json_response(
        "/api/{}/review/{}/".format(self.api_version, review_id))

get_review_by_name(name)

Get reviews by name using a case insensitive startswith query.

Parameters:

Name Type Description Default
name str

The name of the review.

required

Returns:

Name Type Description
dict

Meta information and an array of found projects.

Source code in syncsketch_common/server_handler.py
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
def get_review_by_name(self, name):
    """
    Get reviews by name using a case insensitive startswith query.

    Args:
        name (str): The name of the review.

    Returns:
        dict: Meta information and an array of found projects.
    """
    get_params = {"name__istartswith": name}
    return self._get_json_response(
        "/api/{}/review/".format(self.api_version),
        get_data=get_params
    )

get_review_item(item_id, data=None)

Get single item by id.

Parameters:

Name Type Description Default
item_id int

The ID of the item.

required
data dict

Additional data for the item. Defaults to None.

None

Returns:

Name Type Description
dict

The item data.

Source code in syncsketch_common/server_handler.py
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
def get_review_item(self, item_id, data=None):
    """
    Get single item by id.

    Args:
        item_id (int): The ID of the item.
        data (dict, optional): Additional data for the item.
            Defaults to None.

    Returns:
        dict: The item data.
    """
    return self._get_json_response(
        "/api/{}/item/{}/".format(self.api_version, item_id),
        get_data=data
    )

get_review_storage(review_id)

Get review storage usage in bytes.

Parameters:

Name Type Description Default
review_id int

The ID of the review.

required

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
562
563
564
565
566
567
568
569
570
571
572
573
574
def get_review_storage(self, review_id):
    """
    Get review storage usage in bytes.

    Args:
        review_id (int): The ID of the review.

    Returns:
        dict: The response from the API call.
    """
    return self._get_json_response(
        "/api/v2/review/{}/storage/".format(review_id)
    )

get_reviews_by_project_id(project_id, limit=100, offset=0)

Get list of reviews by project id.

Parameters:

Name Type Description Default
project_id int

The ID of the project.

required
limit int

The maximum number of reviews to retrieve. Defaults to 100.

100
offset int

The number of reviews to skip before starting to collect.

0

Returns:

Name Type Description
dict

Meta information and an array of found projects.

Source code in syncsketch_common/server_handler.py
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
def get_reviews_by_project_id(self, project_id, limit=100, offset=0):
    """
    Get list of reviews by project id.

    Args:
        project_id (int): The ID of the project.
        limit (int, optional): The maximum number of reviews to retrieve.
            Defaults to 100.
        offset (int, optional): The number of reviews to skip
            before starting to collect.
        Defaults to 0.

    Returns:
        dict: Meta information and an array of found projects.
    """
    get_params = {
        "project__id": project_id,
        "project__active": 1,
        "project__is_archived": 0,
        "limit": limit,
        "offset": offset
    }
    self.log.debug("get_reviews_by_project_id: {}".format(get_params))

    return self._get_json_response(
        "/api/{}/review/".format(self.api_version),
        get_data=get_params
    )

get_user_by_id(userId)

Get user by user id.

Parameters:

Name Type Description Default
userId int

The ID of the user.

required

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
931
932
933
934
935
936
937
938
939
940
941
942
def get_user_by_id(self, userId):
    """
    Get user by user id.

    Args:
        userId (int): The ID of the user.

    Returns:
        dict: The response from the API call.
    """
    return self._get_json_response(
        "/api/{}/simpleperson/{}".format(self.api_version, userId))

get_users_by_project_id(project_id)

Get all users by project id.

Parameters:

Name Type Description Default
project_id int

The ID of the project.

required

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
889
890
891
892
893
894
895
896
897
898
899
900
def get_users_by_project_id(self, project_id):
    """
    Get all users by project id.

    Args:
        project_id (int): The ID of the project.

    Returns:
        dict: The response from the API call.
    """
    return self._get_json_response(
        "/api/v2/all-project-users/{}".format(project_id))

is_connected()

Convenience function to check if the API is connected to SyncSketch. Checks against Status Code 200 and returns False if not.

Returns:

Name Type Description
bool

The connection status.

Source code in syncsketch_common/server_handler.py
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
def is_connected(self):
    """
    Convenience function to check if the API is connected to SyncSketch.
    Checks against Status Code 200 and returns False if not.

    Returns:
        bool: The connection status.
    """
    url = "/api/{}/person/connected/".format(self.api_version)
    params = self.api_params.copy()

    if self.debug:
        self.log.debug("URL: {}, params: {}".format(url, params))

    result = self._get_json_response(url, raw_response=True)
    return result.status_code == 200

join_url_path(base, *path_segments) staticmethod

Join and normalize URL path segments.

Parameters:

Name Type Description Default
base str

The base URL.

required
*path_segments str

The path segments.

()

Returns:

Name Type Description
str

The joined and normalized URL path.

Source code in syncsketch_common/server_handler.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
@staticmethod
def join_url_path(base, *path_segments):
    """
    Join and normalize URL path segments.

    Args:
        base (str): The base URL.
        *path_segments (str): The path segments.

    Returns:
        str: The joined and normalized URL path.
    """
    url_segments = []
    url_segments.append(base.rstrip("/"))
    url_segments.extend(
        segment.strip("/") for segment in path_segments
    )
    url_segments.append("")
    url_string = "/".join(url_segments)
    return url_string

move_items(new_review_id, item_data)

Move items to a new review.

Parameters:

Name Type Description Default
new_review_id int

The ID of the new review.

required
item_data dict

The item data.

required

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
def move_items(self, new_review_id, item_data):
    """
    Move items to a new review.

    Args:
        new_review_id (int): The ID of the new review.
        item_data (dict): The item data.

    Returns:
        dict: The response from the API call.
    """
    return self._get_json_response(
        "/api/v2/move-review-items/",
        method="post",
        post_data={"new_review_id": new_review_id, "item_data": item_data},
        raw_response=True,
    )

restore_project(project_id)

Restore (unarchive) a project by id. This method sets the "is_archived" attribute of the project to False.

Parameters:

Name Type Description Default
project_id int

The ID of the project.

required

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
def restore_project(self, project_id):
    """
    Restore (unarchive) a project by id. This method sets the
    "is_archived" attribute of the project to False.

    Args:
        project_id (int): The ID of the project.

    Returns:
        dict: The response from the API call.
    """
    return self._get_json_response(
        "/api/{}/project/{}/".format(self.api_version, project_id),
        patch_data=dict(is_archived=False)
    )

update_project(project_id, data)

Update a project

Parameters:

Name Type Description Default
project_id int

The id of the project.

required
data dict

Dictionary with data for the project.

required

Returns:

Name Type Description
dict

The updated project.

Source code in syncsketch_common/server_handler.py
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
def update_project(self, project_id, data):
    """
    Update a project

    Args:
        project_id (int): The id of the project.
        data (dict): Dictionary with data for the project.

    Returns:
        dict: The updated project.
    """
    if not isinstance(data, dict):
        self.log.debug("Please make sure you pass a dict as data")
        return False

    return self._get_json_response(
        "/api/{}/project/{}/".format(self.api_version, project_id),
        patch_data=data
    )

update_review(review_id, data)

Update a review by id.

Parameters:

Name Type Description Default
review_id int

The ID of the review.

required
data dict

The new data for the review.

required

Returns:

Type Description

dict/bool: The response from the API call if the data is a dict,

False otherwise.

Source code in syncsketch_common/server_handler.py
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
def update_review(self, review_id, data):
    """
    Update a review by id.

    Args:
        review_id (int): The ID of the review.
        data (dict): The new data for the review.

    Returns:
        dict/bool: The response from the API call if the data is a dict,
        False otherwise.
    """
    if not isinstance(data, dict):
        self.log.debug("Please make sure you pass a dict as data")
        return False

    return self._get_json_response(
        "/api/{}/review/{}/".format(self.api_version, review_id),
        patch_data=data
    )

update_review_item(item_id, data=None)

Update single item by id.

Parameters:

Name Type Description Default
item_id int

The ID of the item.

required
data dict

Additional data for the item. Defaults to None.

None

Returns:

Name Type Description
dict

The item data.

Source code in syncsketch_common/server_handler.py
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
def update_review_item(self, item_id, data=None):
    """
    Update single item by id.

    Args:
        item_id (int): The ID of the item.
        data (dict, optional): Additional data for the item.
            Defaults to None.

    Returns:
        dict: The item data.
    """
    return self._get_json_response(
        "/api/{}/item/{}/".format(self.api_version, item_id),
        patch_data=data
    )

upload_review_item(review_id, filepath, artist_name='', file_name='', no_convert_flag=False, item_parent_id=False)

Convenience function to upload a file to a review. It will automatically create an Item and attach it to the review.

Parameters:

Name Type Description Default
review_id int

Required review_id

required
filepath str

Path for the file on disk e.g /tmp/movie.webm

required
artist_name str

The name of the artist you want associated with this media file. Defaults to "".

''
file_name str

The name of the file. Please make sure to pass the correct file extension. Defaults to "".

''
no_convert_flag bool

The video you are uploading is already in a browser compatible format. Defaults to False.

False
item_parent_id int

Set when you want to add a new version of an item. item_parent_id is the id of the item you want to upload a new version for. Defaults to False.

False

Returns:

Name Type Description
dict

The response from the API call.

Source code in syncsketch_common/server_handler.py
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
def upload_review_item(
    self,
    review_id,
    filepath,
    artist_name="",
    file_name="",
    no_convert_flag=False,
    item_parent_id=False
):
    """
    Convenience function to upload a file to a review. It will
    automatically create an Item and attach it to the review.

    Args:
        review_id (int): Required review_id
        filepath (str): Path for the file on disk e.g /tmp/movie.webm
        artist_name (str, optional): The name of the artist you want
            associated with this media file. Defaults to "".
        file_name (str, optional): The name of the file. Please make
            sure to pass the correct file extension. Defaults to "".
        no_convert_flag (bool, optional): The video you are uploading
            is already in a browser compatible format. Defaults to False.
        item_parent_id (int, optional): Set when you want to add a new
            version of an item. item_parent_id is the id of the item you
            want to upload a new version for. Defaults to False.

    Returns:
        dict: The response from the API call.
    """
    get_params = self.api_params.copy()

    if no_convert_flag:
        get_params.update({"noConvertFlag": 1})

    if item_parent_id:
        get_params.update({"itemParentId": item_parent_id})

    upload_url = "{}/items/uploadToReview/{}/?{}".format(
        self.HOST, review_id, urlencode(get_params)
    )

    files = {"reviewFile": open(filepath, "rb")}
    result = requests.post(
        upload_url,
        data=dict(
            artist=artist_name, name=file_name
        ),
        files=files,
        headers=self.headers
    )

    try:
        return json.loads(result.text)
    except Exception:
        self.log.error(result.text)