Compare commits
329 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f09cd9d77 | |||
| 29a361892b | |||
| 2672b40aff | |||
| 35b6911b27 | |||
| a4f0a2cc2b | |||
| 1970890ecc | |||
| 13df5135b8 | |||
| 4e206b5c60 | |||
| e696091555 | |||
| a512dbb4e4 | |||
| 9a1f28516d | |||
| 92892b83d8 | |||
| 8904e9eeb4 | |||
| 68dc1794ee | |||
| 7e7940f25b | |||
| 29c97cde45 | |||
| 02f4f4fe41 | |||
| b2dd5bfedd | |||
| 09b3edcc23 | |||
| a44036863d | |||
| 5806c032dd | |||
| cff20aec54 | |||
| 144d51b147 | |||
| 09f61a6efd | |||
| e61bf0f78f | |||
| 6ac72ce8ee | |||
| 0b9ef942f5 | |||
| 6988eae46f | |||
| 31fa619f82 | |||
| 409ac68df0 | |||
| 81051a87c1 | |||
| fca386706b | |||
| 80036b0b98 | |||
| 92c9a0882e | |||
| 239c466264 | |||
| 7dd490b24f | |||
| 54c3d6fbaf | |||
| 7b7cd86f50 | |||
| 3fde452d1e | |||
| 113d1f1993 | |||
| d2bacb9ec2 | |||
| d461e64b97 | |||
| dc923c3105 | |||
| 46437e7af4 | |||
| 6ba632b14d | |||
| c67eb0520e | |||
| b7b171289c | |||
| c4ac12d9de | |||
| 6abeafdd9e | |||
| a016ed9201 | |||
| 9e0f71e5c0 | |||
| e02c4a76e8 | |||
| 8e14cf1904 | |||
| 1c868b6b60 | |||
| 809dff5fb0 | |||
| e5074acff9 | |||
| 85d2065c29 | |||
| bb48054fea | |||
| 5a9b4137da | |||
| 6c7dc1eed9 | |||
| d47bf7d842 | |||
| d07bab61e7 | |||
| 3f7290ed12 | |||
| ed92678a31 | |||
| 0428d2149f | |||
| da2d646f78 | |||
| a6543b122f | |||
| 4e26ebfbe2 | |||
| 4f79698987 | |||
| 2c10134703 | |||
| 27346b6d38 | |||
| 816f4373dd | |||
| 434ada8bc1 | |||
| 066bc44457 | |||
| 633267b025 | |||
| bd4ae7c5c5 | |||
| 501f9b8648 | |||
| 7d2a03e19d | |||
| c6c7d1731a | |||
| a839073968 | |||
| 883ec6e6ae | |||
| 87002257be | |||
| 1bc0cf04f3 | |||
| d0e2350ab7 | |||
| 255681a887 | |||
| 614eefc393 | |||
| d5b506f298 | |||
| dcd28b5d79 | |||
| c739594ba8 | |||
| 1d4867fb9b | |||
| ec94a42ac6 | |||
| 6e64770380 | |||
| ef92127ed2 | |||
| fb8a40bf76 | |||
| 81fab3ec6b | |||
| 9a0c36c665 | |||
| 995526c786 | |||
| 5a66d912ae | |||
| 78abbb18c4 | |||
| 26abdc952a | |||
| 9a0207ba09 | |||
| 4369f8c5c8 | |||
| bc432c2f13 | |||
| d1d3876902 | |||
| b1b144969b | |||
| 4e3ee488be | |||
| b2e2455b16 | |||
| ede286f4c0 | |||
| 56547a4573 | |||
| 13886b9caf | |||
| 5342cc4827 | |||
| 6fe338628d | |||
| 48883466fb | |||
| 47917d99b0 | |||
| 7703de0a37 | |||
| 81b96cb6ac | |||
| 2334aefb09 | |||
| 800b0018a0 | |||
| e6cf22e97a | |||
| f5abcec6d0 | |||
| fcd54a0899 | |||
| adfba846ce | |||
| 4933ca4abf | |||
| 8ce595cb4a | |||
| 1114b210f9 | |||
| 00c1e378d5 | |||
| 1291b5799b | |||
| 75fb2da7c2 | |||
| d760fa47ec | |||
| e61968d88c | |||
| 01e3e5aa47 | |||
| 7f2c26eb6b | |||
| 5f35da3be9 | |||
| 98c3b89d75 | |||
| 5796ff6cdb | |||
| df4706aa56 | |||
| 855d499eb8 | |||
| a19ce15168 | |||
| b2d89cad21 | |||
| f6c28689ce | |||
| 5b3076b775 | |||
| 16511f8dc0 | |||
| 8a673a54a9 | |||
| f98b40cce3 | |||
| 3a76358557 | |||
| b6b25f824e | |||
| aa032895b6 | |||
| 6f871f0523 | |||
| 5e7b3712b7 | |||
| 1db0fca68c | |||
| dff3496712 | |||
| dbcb691fbc | |||
| 4572d15b10 | |||
| 779c4e6d70 | |||
| 12ba5b347e | |||
| 8dc3355f51 | |||
| 9e095e8673 | |||
| 089c10facd | |||
| 236d53f78a | |||
| 257c32665e | |||
| 82f7ee6f0a | |||
| 748faf277d | |||
| 90f5e7602c | |||
| 6b4dc9a6b2 | |||
| efa8c46b61 | |||
| 9527425c38 | |||
| 6ab9252398 | |||
| 661c25c8fb | |||
| 28ee9ec201 | |||
| 6155962bd8 | |||
| 607ad2c721 | |||
| 7866e1082e | |||
| 0bd4b3b093 | |||
| 94f47eeb17 | |||
| 11fee54d36 | |||
| b6943bbe6f | |||
| 8b25fa1470 | |||
| 617e202a02 | |||
| 76da3cacfb | |||
| b3e9f425c0 | |||
| 739837bad2 | |||
| 0110b5b11a | |||
| 0d31337350 | |||
| 12eb88819e | |||
| 3d7d138841 | |||
| 60bc4df16a | |||
| d99c05761d | |||
| dc326e6c6b | |||
| eb10e93300 | |||
| 64434cd7f5 | |||
| 35a121abd9 | |||
| fe5e7b19b0 | |||
| adae615e29 | |||
| 203230fd8b | |||
| 64df2f241c | |||
| 110f10623e | |||
| 4dda9b8fe5 | |||
| f494a663de | |||
| ff44f4ecb1 | |||
| 8da5086419 | |||
| f46dd7e0c6 | |||
| dac3a86bcc | |||
| c7a603ad45 | |||
| aaaf323b43 | |||
| 0173e76c76 | |||
| 3c2a437f98 | |||
| 282623ce10 | |||
| 0409f2c0f3 | |||
| 12528205f5 | |||
| 063191e961 | |||
| f9da5422c6 | |||
| 41e66dda0d | |||
| 5eee3fee07 | |||
| e14c42dfd1 | |||
| 062808c568 | |||
| 8b9f2db176 | |||
| 5b458c630e | |||
| 25c4b35eb3 | |||
| 21b1b2c30a | |||
| 1a063bbbf7 | |||
| beb550624e | |||
| 50c6e2c840 | |||
| 3f986fced3 | |||
| ca234536bc | |||
| f155129f7c | |||
| 37d628d27b | |||
| 7b6e0aa6ed | |||
| 2d449a328b | |||
| ad47f649db | |||
| 750657db19 | |||
| 453a2e4211 | |||
| 2ca184538d | |||
| 79a832360b | |||
| b2945ad3ff | |||
| 3e7b620e1e | |||
| 028ca641ef | |||
| 11bc10d072 | |||
| 28e68e090c | |||
| 6eb73555ed | |||
| e6727a9552 | |||
| b36e7e000f | |||
| 2dba909eba | |||
| 21abd5181a | |||
| 969b6a92e5 | |||
| 77dcf53c4b | |||
| 7fbfdb4b92 | |||
| a4f7ce326e | |||
| a3830a95fd | |||
| 78068d0098 | |||
| 36d5abf7d4 | |||
| bce4490882 | |||
| 6d4fc17b4c | |||
| 827df8e574 | |||
| 075d77fea6 | |||
| 6b276243de | |||
| e671a6d086 | |||
| 824c0d8401 | |||
| 48e4ad1e93 | |||
| cf2c9d12a5 | |||
| 578ffdb8c5 | |||
| f58c13c0d1 | |||
| c8210342c2 | |||
| c6d6a3e025 | |||
| ba7911bcf5 | |||
| 2e428f9b3c | |||
| 3c8aff3487 | |||
| 9657bd370c | |||
| fd0d04b70e | |||
| 7310fbfdd4 | |||
| 90cae9a3c5 | |||
| 01091405d1 | |||
| cff738aa31 | |||
| 30a3cd8f05 | |||
| c9746a6d57 | |||
| 251f8fb330 | |||
| 0b74502dd7 | |||
| a06e682698 | |||
| f258de4794 | |||
| 9320b566f3 | |||
| 79ac5366a6 | |||
| 6cc909ddd9 | |||
| 0dca458a0a | |||
| e92562ccf3 | |||
| 6ae95e0f9c | |||
| 32954964f0 | |||
| 2f8ecf7430 | |||
| ad0a01c5ce | |||
| def3d3c27b | |||
| 6cde8d6e8a | |||
| d63d24201e | |||
| dd5b355305 | |||
| db7ef190fd | |||
| c20c805e19 | |||
| 665f6b8c32 | |||
| 9f5c453228 | |||
| 3d7fc85a79 | |||
| 99a80ba85e | |||
| 5d059b6770 | |||
| ee22740a93 | |||
| 0e2105c56e | |||
| 1d7b120f26 | |||
| b00cc3d08e | |||
| de4ea698b8 | |||
| 91bf8c111d | |||
| 72dfd4807e | |||
| e2079ff6a9 | |||
| cb5cbeaad6 | |||
| 18744d218a | |||
| 5f05e9853f | |||
| 7295689e12 | |||
| 09ce21e475 | |||
| 3ff444aa52 | |||
| 799d9428ec | |||
| ea8be095ed | |||
| 135c7215f5 | |||
| eee726e252 | |||
| 79dba3318f | |||
| 60d253343c | |||
| 2ea43d54d6 | |||
| 37480b1c9e | |||
| b1660d5dbf | |||
| 8b48167dca | |||
| f3dbddb6e1 | |||
| 19da6c0f5f | |||
| a8c75b0000 | |||
| 4e8c3cbb08 | |||
| d01d7824bc | |||
| f643ee8e89 | |||
| 9a27ed1544 |
@@ -0,0 +1,66 @@
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
python: circleci/python@1.3.2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: "cimg/python:<<parameters.tag>>"
|
||||
- image: "circleci/node:12"
|
||||
- image: "circleci/redis:6"
|
||||
- image: "circleci/postgres:12"
|
||||
environment:
|
||||
POSTGRES_DB: speckle2_test
|
||||
POSTGRES_PASSWORD: speckle
|
||||
POSTGRES_USER: speckle
|
||||
- image: "speckle/speckle-server"
|
||||
command: ["bash", "-c", "/wait && node bin/www"]
|
||||
environment:
|
||||
POSTGRES_URL: "localhost"
|
||||
POSTGRES_USER: "speckle"
|
||||
POSTGRES_PASSWORD: "speckle"
|
||||
POSTGRES_DB: "speckle2_test"
|
||||
REDIS_URL: "redis://localhost"
|
||||
SESSION_SECRET: "keyboard cat"
|
||||
STRATEGY_LOCAL: "true"
|
||||
CANONICAL_URL: "http://localhost:3000"
|
||||
WAIT_HOSTS: localhost:5432, localhost:6379
|
||||
parameters:
|
||||
tag:
|
||||
default: "3.8"
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- run: python --version
|
||||
- run:
|
||||
command: python -m pip install --upgrade pip
|
||||
name: upgrade pip
|
||||
- python/install-packages:
|
||||
pkg-manager: poetry
|
||||
- run: poetry run pytest
|
||||
|
||||
deploy:
|
||||
docker:
|
||||
- image: "circleci/python:3.8"
|
||||
steps:
|
||||
- checkout
|
||||
- run: poetry build
|
||||
- run: poetry publish -u specklesystems -p $PYPI_PASSWORD
|
||||
|
||||
workflows:
|
||||
main:
|
||||
jobs:
|
||||
- build:
|
||||
matrix:
|
||||
parameters:
|
||||
tag: ["3.6", "3.7", "3.8", "3.9"]
|
||||
|
||||
publish:
|
||||
jobs:
|
||||
- deploy:
|
||||
filters:
|
||||
tags:
|
||||
only: /[0-9]+(\.[0-9]+)*/
|
||||
branches:
|
||||
ignore: /.*/ # For testing only! /ci\/.*/
|
||||
+12
-11
@@ -1,4 +1,5 @@
|
||||
# Speckle Contribution Guidelines
|
||||
|
||||
## Introduction
|
||||
|
||||
Thank you for reading this! Speckle's a rather wide network of parts that depend on each other, either directly, indirectly or even just cosmetically.
|
||||
@@ -9,41 +10,41 @@ This means that what might look like a simple quick change in one repo may have
|
||||
|
||||
## Bugs & Issues 🐞
|
||||
|
||||
### Found a new bug?
|
||||
### Found a new bug?
|
||||
|
||||
- First step is to check whether this is a new bug! We encourage you to search through the issues of the project in question **and** associated repos!
|
||||
|
||||
- If you come up with nothing, **open a new issue with a clear title and description**, as much relevant information as possible: system configuration, code samples & steps to reproduce the problem.
|
||||
- If you come up with nothing, **open a new issue with a clear title and description**, as much relevant information as possible: system configuration, code samples & steps to reproduce the problem.
|
||||
|
||||
- Can't mention this often enough: tells us how to reproduce the problem! We will ignore or flag as such issues without reproduction steps.
|
||||
- Can't mention this often enough: tells us how to reproduce the problem! We will ignore or flag as such issues without reproduction steps.
|
||||
|
||||
- Try to reference & note all potentially affected projects.
|
||||
|
||||
### Sending a PR for Bug Fixes
|
||||
|
||||
You fixed something! Great! We hope you logged it first :) Make sure though that you've covered the lateral thinking needed for a bug report, as described above, also in your implementation! If there any tests, make sure they all pass. If there are none, it means they're missing - so add them!
|
||||
You fixed something! Great! We hope you logged it first :) Make sure though that you've covered the lateral thinking needed for a bug report, as described above, also in your implementation! If there any tests, make sure they all pass. If there are none, it means they're missing - so add them!
|
||||
|
||||
## New Features 🎉
|
||||
|
||||
The golden rule is to Discuss First!
|
||||
|
||||
- Before embarking on adding a new feature, suggest it first as an issue with the `enhancement` label and/or title - this will allow relevant people to pitch in
|
||||
- We'll now discuss your requirements and see how and if they fit within the Speckle ecosystem.
|
||||
- The last step is to actually start writing code & submit a PR so we can follow along!
|
||||
- All new features should, if and where possible, come with tests. We won't merge without!
|
||||
- We'll now discuss your requirements and see how and if they fit within the Speckle ecosystem.
|
||||
- The last step is to actually start writing code & submit a PR so we can follow along!
|
||||
- All new features should, if and where possible, come with tests. We won't merge without!
|
||||
|
||||
> Many clients may potentially have overlapping scopes, some features might already be in dev somewhere else, or might have been postponed to the next major release due to api instability in that area. For example, adding a delete stream button in the accounts panel in rhino: this feature was planned for speckle admin, and the whole functionality of the accounts panel in rhino is to be greatly reduced!
|
||||
|
||||
## Cosmetic Patches ✨
|
||||
|
||||
Changes that are cosmetic in nature and do not add anything substantial to the stability or functionality of Speckle **will generally not be accepted**.
|
||||
Changes that are cosmetic in nature and do not add anything substantial to the stability or functionality of Speckle **will generally not be accepted**.
|
||||
|
||||
Why? However trivial the changes might seem, there might be subtle reasons for the original code to be as it is. Furthermore, there are a lot of potential hidden costs (that even maintainers themselves are not aware of fully!) and they eat up review time unncessarily.
|
||||
|
||||
> **Examples**: modifying the colour of an UI element in one client may have a big hidden cost and need propagation in several other clients that implement a similar ui element. Changing the default port or specifiying `localhost` instead of `0.0.0.0` breaks cross-vm debugging and developing.
|
||||
|
||||
> **Examples**: modifying the colour of an UI element in one client may have a big hidden cost and need propagation in several other clients that implement a similar ui element. Changing the default port or specifiying `localhost` instead of `0.0.0.0` breaks cross-vm debugging and developing.
|
||||
|
||||
## Wrap up
|
||||
Don't worry if you get things wrong. We all do, including project owners: this document should've been here a long time ago. There's plenty of room for discussion either on our community [forum](https://discourse.speckle.works) or [chat](https://speckle-works.slack.com/join/shared_invite/enQtNjY5Mzk2NTYxNTA4LTU4MWI5ZjdhMjFmMTIxZDIzOTAzMzRmMTZhY2QxMmM1ZjVmNzJmZGMzMDVlZmJjYWQxYWU0MWJkYmY3N2JjNGI).
|
||||
|
||||
Don't worry if you get things wrong. We all do, including project owners: this document should've been here a long time ago. There's plenty of room for discussion on our community [forum](https://discourse.speckle.works).
|
||||
|
||||
🙌❤️💙💚💜🙌
|
||||
|
||||
Vendored
+6
@@ -11,6 +11,12 @@
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": false
|
||||
},
|
||||
{
|
||||
"name": "Python: Test debug config",
|
||||
"type": "python",
|
||||
"request": "test",
|
||||
"console": "integratedTerminal",
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,35 +1,153 @@
|
||||
# speckle-py 🥧
|
||||
|
||||
[](https://twitter.com/SpeckleSystems) [](https://discourse.speckle.works)
|
||||
[](https://speckle-works.slack.com/join/shared_invite/enQtNjY5Mzk2NTYxNTA4LTU4MWI5ZjdhMjFmMTIxZDIzOTAzMzRmMTZhY2QxMmM1ZjVmNzJmZGMzMDVlZmJjYWQxYWU0MWJkYmY3N2JjNGI) [](https://speckle.systems)
|
||||
[](https://twitter.com/SpeckleSystems) [](https://discourse.speckle.works) [](https://speckle.systems) [](https://speckle.guide/dev/)
|
||||
|
||||
## Introduction
|
||||
|
||||
> ⚠ This is the start of the Python client for Speckle 2.0. It is currently quite nebulous and may be trashed and rebuilt at any moment! It is compatible with Python 3.6+ ⚠
|
||||
>
|
||||
|
||||
## Documentation
|
||||
|
||||
Comprehensive developer and user documentation can be found in our:
|
||||
|
||||
#### 📚 [Speckle Docs website](https://speckle.guide/dev/)
|
||||
|
||||
## Developing & Debugging
|
||||
|
||||
To get started, create a virtual environment and pip install the requirements.
|
||||
### Installation
|
||||
|
||||
on windows:
|
||||
This project uses python-poetry for dependency management, make sure you follow the official [docs](https://python-poetry.org/docs/#installation) to get poetry.
|
||||
|
||||
To bootstrap the project environment run `$ poetry install`. This will create a new virtual-env for the project and install both the package and dev dependencies.
|
||||
|
||||
If this is your first time using poetry and you're used to creating your venvs within the project directory, run `poetry config virtualenvs.in-project true` to configure poetry to do the same.
|
||||
|
||||
To execute any python script run `$ poetry run python my_script.py`
|
||||
|
||||
> Alternatively you may roll your own virtual-env with either venv, virtualenv, pyenv-virtualenv etc. Poetry will play along an recognize if it is invoked from inside a virtual environment.
|
||||
|
||||
### Local Data Paths
|
||||
|
||||
It may be helpful to know where the local accounts and object cache dbs are stored. Depending on on your OS, you can find the dbs at:
|
||||
- Windows: `APPDATA` or `<USER>\AppData\Roaming\Speckle`
|
||||
- Linux: `$XDG_DATA_HOME` or by default `~/.local/share/Speckle`
|
||||
- Mac: `~/.config/Speckle`
|
||||
|
||||
## Overview of functionality
|
||||
|
||||
The `SpeckleClient` is the entry point for interacting with the GraphQL API. You'll need to have a running server to use this.
|
||||
|
||||
```py
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.api.credentials import get_default_account, get_local_accounts
|
||||
|
||||
all_accounts = get_local_accounts() # get back a list
|
||||
account = get_default_account()
|
||||
|
||||
client = SpeckleClient(host="speckle.xyz")
|
||||
# client = SpeckleClient(host="yourserver.com") or whatever your host is
|
||||
|
||||
client.authenticate(account.token)
|
||||
```
|
||||
python -m venv venv
|
||||
venv\Scripts\activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
Interacting with streams is meant to be intuitive and evocative of PySpeckle 1.0
|
||||
|
||||
```py
|
||||
# get your streams
|
||||
stream_list = client.stream.list()
|
||||
|
||||
# search your streams
|
||||
results = client.user.search("mech")
|
||||
|
||||
# create a stream
|
||||
new_stream_id = client.stream.create(name="a shiny new stream")
|
||||
|
||||
# get a stream
|
||||
new_stream = client.stream.get(id=new_stream_id)
|
||||
```
|
||||
|
||||
New in 2.0: commits! Here are some basic commit interactions.
|
||||
|
||||
```py
|
||||
# get list of commits
|
||||
commits = client.commit.list("stream id")
|
||||
|
||||
# get a specific commit
|
||||
commit = client.commit.get("stream id", "commit id")
|
||||
|
||||
# create a commit
|
||||
commit_id = client.commit.create("stream id", "object id", "this is a commit message to describe the commit")
|
||||
|
||||
# delete a commit
|
||||
deleted = client.commit.delete("stream id", "commit id")
|
||||
```
|
||||
|
||||
The `BaseObjectSerializer` is used for decomposing and serializing `Base` objects so they can be sent / received to the server. You can use it directly to get the id (hash) and a serializable object representation of the decomposed `Base`. You can learn more about the Speckle `Base` object [here](https://discourse.speckle.works/t/core-2-0-the-base-object/782) and the decomposition API [here](https://discourse.speckle.works/t/core-2-0-decomposition-api/911).
|
||||
|
||||
```py
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.serialization.base_object_serializer import BaseObjectSerializer
|
||||
|
||||
detached_base = Base()
|
||||
detached_base.name = "this will get detached"
|
||||
|
||||
base_obj = Base()
|
||||
base_obj.name = "my base"
|
||||
base_obj["@nested"] = detached_base
|
||||
|
||||
serializer = BaseObjectSerializer()
|
||||
hash, obj_dict = serializer.traverse_base(base_obj)
|
||||
```
|
||||
|
||||
If you use the `operations`, you will not need to interact with the serializer directly as this will be taken care of for you. You will just need to provide a transport to indicate where the objects should be sent / received from. At the moment, just the `MemoryTransport` and the `ServerTransport` are fully functional at the moment. If you'd like to learn more about Transports in Speckle 2.0, have a look [here](https://discourse.speckle.works/t/core-2-0-transports/919).
|
||||
|
||||
```py
|
||||
from specklepy.transports.memory import MemoryTransport
|
||||
from specklepy.api import operations
|
||||
|
||||
transport = MemoryTransport()
|
||||
|
||||
# this serialises the object and sends it to the transport
|
||||
hash = operations.send(base=base_obj, transports=[transport])
|
||||
|
||||
# if the object had detached objects, you can see these as well
|
||||
saved_objects = transport.objects # a dict with the obj hash as the key
|
||||
|
||||
# this receives and object from the given transport, deserialises it, and recomposes it into a base object
|
||||
received_base = operations.receive(obj_id=hash, remote_transport=transport)
|
||||
```
|
||||
|
||||
You can also use the GraphQL API to send and receive objects.
|
||||
|
||||
```py
|
||||
# create a test base object
|
||||
test_base = Base()
|
||||
test_base.testing = "a test base obj"
|
||||
|
||||
# run it through the serialiser
|
||||
s = BaseObjectSerializer()
|
||||
hash, obj = s.traverse_base(test_base)
|
||||
|
||||
# send it to the server
|
||||
objCreate = client.object.create(stream_id="stream id", objects=[obj])
|
||||
|
||||
received_base = client.object.get("stream id", hash)
|
||||
```
|
||||
|
||||
This doc is not complete - there's more to see so have a dive into the code and play around! Please feel free to provide feedback, submit issues, or discuss new features ✨
|
||||
|
||||
## Contributing
|
||||
|
||||
Please make sure you read the [contribution guidelines](.github/CONTRIBUTING.md) for an overview of the best practices we try to follow.
|
||||
|
||||
## Community
|
||||
|
||||
The Speckle Community hangs out in two main places, usually:
|
||||
The Speckle Community hangs out on [the forum](https://discourse.speckle.works), do join and introduce yourself & feel free to ask us questions!
|
||||
|
||||
- on [the forum](https://discourse.speckle.works)
|
||||
- on [the chat](https://speckle-works.slack.com/join/shared_invite/enQtNjY5Mzk2NTYxNTA4LTU4MWI5ZjdhMjFmMTIxZDIzOTAzMzRmMTZhY2QxMmM1ZjVmNzJmZGMzMDVlZmJjYWQxYWU0MWJkYmY3N2JjNGI)
|
||||
## Security
|
||||
|
||||
Do join and introduce yourself!
|
||||
For any security vulnerabilities or concerns, please contact us directly at security[at]speckle.systems.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
"""This is an example showcasing the usage of speckle `Base` class."""
|
||||
|
||||
# the speckle.objects module exposes all speckle provided classes
|
||||
from specklepy.objects import Base
|
||||
from specklepy.api import operations
|
||||
from devtools import debug
|
||||
|
||||
|
||||
class ExampleSub(Base):
|
||||
"""
|
||||
Inheriting from `Base` is done with in the standard way by default.
|
||||
|
||||
The syntax is similar to the stdlib dataclass syntax.
|
||||
No __init__ method definition is required, that is done automatically by the base
|
||||
type. Also the attributes defined this way are instance attributes despite they
|
||||
might look like class attributes.
|
||||
|
||||
The speckle Base uses the pydantic BaseModel in the background, but ideally that
|
||||
is not the consumers concern.
|
||||
|
||||
**Important note:** currently the way how serialization works, requires
|
||||
each attribute to have a valid default value, just like `foo` has. This includes
|
||||
default values for all primitives and complex datastructures.
|
||||
Failing to provide a default, breaks the receiving end of the transport.
|
||||
"""
|
||||
|
||||
foo: str = "bar"
|
||||
|
||||
|
||||
class SpeckleSub(ExampleSub, speckle_type="custom_speckle_sub"):
|
||||
"""
|
||||
Example custom type name registration.
|
||||
|
||||
This is an optional feature.
|
||||
The default value of the speckle_type is generated from the name of the class, but
|
||||
optionally it may be overridden. This is useful, since the speckle_type has to be
|
||||
unique for each subclass of speckle Base.
|
||||
"""
|
||||
|
||||
magic: str = "trick"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# example usage
|
||||
custom_sub = SpeckleSub(
|
||||
foo=123,
|
||||
magic="trick",
|
||||
bar="baric",
|
||||
extra=123,
|
||||
)
|
||||
# support for dynamic attributes
|
||||
custom_sub.extra_extra = "what is this?"
|
||||
debug(custom_sub.json())
|
||||
|
||||
serialized = operations.serialize(custom_sub)
|
||||
deserialized = operations.deserialize(serialized)
|
||||
# the only difference should be between the two data is that the deserialized
|
||||
# instance id attribute is not None.
|
||||
debug(deserialized.json())
|
||||
Generated
+795
@@ -0,0 +1,795 @@
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
version = "3.7.4.post0"
|
||||
description = "Async http client/server framework (asyncio)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
async-timeout = ">=3.0,<4.0"
|
||||
attrs = ">=17.3.0"
|
||||
chardet = ">=2.0,<5.0"
|
||||
idna-ssl = {version = ">=1.0", markers = "python_version < \"3.7\""}
|
||||
multidict = ">=4.5,<7.0"
|
||||
typing-extensions = ">=3.6.5"
|
||||
yarl = ">=1.0,<2.0"
|
||||
|
||||
[package.extras]
|
||||
speedups = ["aiodns", "brotlipy", "cchardet"]
|
||||
|
||||
[[package]]
|
||||
name = "appdirs"
|
||||
version = "1.4.4"
|
||||
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "async-timeout"
|
||||
version = "3.0.1"
|
||||
description = "Timeout context manager for asyncio programs"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5.3"
|
||||
|
||||
[[package]]
|
||||
name = "atomicwrites"
|
||||
version = "1.4.0"
|
||||
description = "Atomic file writes."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "20.3.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"]
|
||||
docs = ["furo", "sphinx", "zope.interface"]
|
||||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
|
||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "20.8b1"
|
||||
description = "The uncompromising code formatter."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
appdirs = "*"
|
||||
click = ">=7.1.2"
|
||||
dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""}
|
||||
mypy-extensions = ">=0.4.3"
|
||||
pathspec = ">=0.6,<1"
|
||||
regex = ">=2020.1.8"
|
||||
toml = ">=0.10.1"
|
||||
typed-ast = ">=1.4.0"
|
||||
typing-extensions = ">=3.7.4"
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.3.2)", "aiohttp-cors"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2021.5.30"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "chardet"
|
||||
version = "4.0.0"
|
||||
description = "Universal encoding detector for Python 2 and 3"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "7.1.2"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.4"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "dataclasses"
|
||||
version = "0.8"
|
||||
description = "A backport of the dataclasses module for Python 3.6"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6, <3.7"
|
||||
|
||||
[[package]]
|
||||
name = "gql"
|
||||
version = "3.0.0a6"
|
||||
description = "GraphQL client for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
aiohttp = {version = ">=3.7.1,<3.8.0", optional = true, markers = "extra == \"all\""}
|
||||
graphql-core = ">=3.1.5,<3.2"
|
||||
requests = {version = ">=2.23,<3", optional = true, markers = "extra == \"all\""}
|
||||
websockets = {version = ">=9,<10", optional = true, markers = "extra == \"all\""}
|
||||
yarl = ">=1.6,<2.0"
|
||||
|
||||
[package.extras]
|
||||
aiohttp = ["aiohttp (>=3.7.1,<3.8.0)"]
|
||||
all = ["aiohttp (>=3.7.1,<3.8.0)", "requests (>=2.23,<3)", "websockets (>=9,<10)"]
|
||||
dev = ["aiohttp (>=3.7.1,<3.8.0)", "requests (>=2.23,<3)", "websockets (>=9,<10)", "black (==19.10b0)", "check-manifest (>=0.42,<1)", "flake8 (==3.8.1)", "isort (==4.3.21)", "mypy (==0.770)", "sphinx (>=3.0.0,<4)", "sphinx_rtd_theme (>=0.4,<1)", "sphinx-argparse (==0.2.5)", "parse (==1.15.0)", "pytest (==5.4.2)", "pytest-asyncio (==0.11.0)", "pytest-cov (==2.8.1)", "mock (==4.0.2)", "vcrpy (==4.0.2)", "aiofiles"]
|
||||
requests = ["requests (>=2.23,<3)"]
|
||||
test = ["aiohttp (>=3.7.1,<3.8.0)", "requests (>=2.23,<3)", "websockets (>=9,<10)", "parse (==1.15.0)", "pytest (==5.4.2)", "pytest-asyncio (==0.11.0)", "pytest-cov (==2.8.1)", "mock (==4.0.2)", "vcrpy (==4.0.2)", "aiofiles"]
|
||||
test_no_transport = ["parse (==1.15.0)", "pytest (==5.4.2)", "pytest-asyncio (==0.11.0)", "pytest-cov (==2.8.1)", "mock (==4.0.2)", "vcrpy (==4.0.2)", "aiofiles"]
|
||||
websockets = ["websockets (>=9,<10)"]
|
||||
|
||||
[[package]]
|
||||
name = "graphql-core"
|
||||
version = "3.1.5"
|
||||
description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6,<4"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "2.10"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "idna-ssl"
|
||||
version = "1.1.0"
|
||||
description = "Patch ssl.match_hostname for Unicode(idna) domains support"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
idna = ">=2.0"
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "3.4.0"
|
||||
description = "Read metadata from Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
|
||||
zipp = ">=0.5"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "1.1.1"
|
||||
description = "iniconfig: brain-dead simple config-ini parsing"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "isort"
|
||||
version = "5.7.0"
|
||||
description = "A Python utility / library to sort Python imports."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6,<4.0"
|
||||
|
||||
[package.extras]
|
||||
pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
|
||||
requirements_deprecated_finder = ["pipreqs", "pip-api"]
|
||||
colors = ["colorama (>=0.4.3,<0.5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "multidict"
|
||||
version = "5.1.0"
|
||||
description = "multidict implementation"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "0.4.3"
|
||||
description = "Experimental type system extensions for programs checked with the mypy typechecker."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "20.9"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2"
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.8.1"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "0.13.1"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[package.dependencies]
|
||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox"]
|
||||
|
||||
[[package]]
|
||||
name = "py"
|
||||
version = "1.10.0"
|
||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "1.7.3"
|
||||
description = "Data validation and settings management using python 3.6 type hinting"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""}
|
||||
|
||||
[package.extras]
|
||||
dotenv = ["python-dotenv (>=0.10.4)"]
|
||||
email = ["email-validator (>=1.0.3)"]
|
||||
typing_extensions = ["typing-extensions (>=3.7.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "2.4.7"
|
||||
description = "Python parsing module"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "6.2.2"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
||||
attrs = ">=19.2.0"
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<1.0.0a1"
|
||||
py = ">=1.8.2"
|
||||
toml = "*"
|
||||
|
||||
[package.extras]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-ordering"
|
||||
version = "0.6"
|
||||
description = "pytest plugin to run your tests in a specific order"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
pytest = "*"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "2020.11.13"
|
||||
description = "Alternative regular expression module, to replace re."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.25.1"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2017.4.17"
|
||||
chardet = ">=3.0.2,<5"
|
||||
idna = ">=2.5,<3"
|
||||
urllib3 = ">=1.21.1,<1.27"
|
||||
|
||||
[package.extras]
|
||||
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "typed-ast"
|
||||
version = "1.4.2"
|
||||
description = "a fork of Python 2 and 3 ast modules with type comment support"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "3.7.4.3"
|
||||
description = "Backported and Experimental Type Hints for Python 3.5+"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.5"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotlipy (>=0.6.0)"]
|
||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "websockets"
|
||||
version = "9.1"
|
||||
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
|
||||
[[package]]
|
||||
name = "yarl"
|
||||
version = "1.6.3"
|
||||
description = "Yet another URL library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
idna = ">=2.0"
|
||||
multidict = ">=4.0"
|
||||
typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""}
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.4.0"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.6.5"
|
||||
content-hash = "84e846c1bb02924ceada07406e95032e0632d229a36657ba2b85129e68f1526d"
|
||||
|
||||
[metadata.files]
|
||||
aiohttp = [
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:3cf75f7cdc2397ed4442594b935a11ed5569961333d49b7539ea741be2cc79d5"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4b302b45040890cea949ad092479e01ba25911a15e648429c7c5aae9650c67a8"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:393f389841e8f2dfc86f774ad22f00923fdee66d238af89b70ea314c4aefd290"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:c6e9dcb4cb338d91a73f178d866d051efe7c62a7166653a91e7d9fb18274058f"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:5df68496d19f849921f05f14f31bd6ef53ad4b00245da3195048c69934521809"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-win32.whl", hash = "sha256:3d78619672183be860b96ed96f533046ec97ca067fd46ac1f6a09cd9b7484287"},
|
||||
{file = "aiohttp-3.7.4.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:f705e12750171c0ab4ef2a3c76b9a4024a62c4103e3a55dd6f99265b9bc6fcfc"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:230a8f7e24298dea47659251abc0fd8b3c4e38a664c59d4b89cca7f6c09c9e87"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2e19413bf84934d651344783c9f5e22dee452e251cfd220ebadbed2d9931dbf0"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e4b2b334e68b18ac9817d828ba44d8fcb391f6acb398bcc5062b14b2cbeac970"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:d012ad7911653a906425d8473a1465caa9f8dea7fcf07b6d870397b774ea7c0f"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:40eced07f07a9e60e825554a31f923e8d3997cfc7fb31dbc1328c70826e04cde"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:209b4a8ee987eccc91e2bd3ac36adee0e53a5970b8ac52c273f7f8fd4872c94c"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:14762875b22d0055f05d12abc7f7d61d5fd4fe4642ce1a249abdf8c700bf1fd8"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-win32.whl", hash = "sha256:7615dab56bb07bff74bc865307aeb89a8bfd9941d2ef9d817b9436da3a0ea54f"},
|
||||
{file = "aiohttp-3.7.4.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:d9e13b33afd39ddeb377eff2c1c4f00544e191e1d1dee5b6c51ddee8ea6f0cf5"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:547da6cacac20666422d4882cfcd51298d45f7ccb60a04ec27424d2f36ba3eaf"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:af9aa9ef5ba1fd5b8c948bb11f44891968ab30356d65fd0cc6707d989cd521df"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:64322071e046020e8797117b3658b9c2f80e3267daec409b350b6a7a05041213"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bb437315738aa441251214dad17428cafda9cdc9729499f1d6001748e1d432f4"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:e54962802d4b8b18b6207d4a927032826af39395a3bd9196a5af43fc4e60b009"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:a00bb73540af068ca7390e636c01cbc4f644961896fa9363154ff43fd37af2f5"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:79ebfc238612123a713a457d92afb4096e2148be17df6c50fb9bf7a81c2f8013"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-win32.whl", hash = "sha256:515dfef7f869a0feb2afee66b957cc7bbe9ad0cdee45aec7fdc623f4ecd4fb16"},
|
||||
{file = "aiohttp-3.7.4.post0-cp38-cp38-win_amd64.whl", hash = "sha256:114b281e4d68302a324dd33abb04778e8557d88947875cbf4e842c2c01a030c5"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:7b18b97cf8ee5452fa5f4e3af95d01d84d86d32c5e2bfa260cf041749d66360b"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:15492a6368d985b76a2a5fdd2166cddfea5d24e69eefed4630cbaae5c81d89bd"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bdb230b4943891321e06fc7def63c7aace16095be7d9cf3b1e01be2f10fba439"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:cffe3ab27871bc3ea47df5d8f7013945712c46a3cc5a95b6bee15887f1675c22"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:a5ca29ee66f8343ed336816c553e82d6cade48a3ad702b9ffa6125d187e2dedb"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:17c073de315745a1510393a96e680d20af8e67e324f70b42accbd4cb3315c9fb"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-win32.whl", hash = "sha256:932bb1ea39a54e9ea27fc9232163059a0b8855256f4052e776357ad9add6f1c9"},
|
||||
{file = "aiohttp-3.7.4.post0-cp39-cp39-win_amd64.whl", hash = "sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe"},
|
||||
{file = "aiohttp-3.7.4.post0.tar.gz", hash = "sha256:493d3299ebe5f5a7c66b9819eacdcfbbaaf1a8e84911ddffcdc48888497afecf"},
|
||||
]
|
||||
appdirs = [
|
||||
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
|
||||
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
|
||||
]
|
||||
async-timeout = [
|
||||
{file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"},
|
||||
{file = "async_timeout-3.0.1-py3-none-any.whl", hash = "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"},
|
||||
]
|
||||
atomicwrites = [
|
||||
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
|
||||
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
|
||||
]
|
||||
attrs = [
|
||||
{file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"},
|
||||
{file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"},
|
||||
]
|
||||
black = [
|
||||
{file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"},
|
||||
{file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"},
|
||||
]
|
||||
chardet = [
|
||||
{file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
|
||||
{file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
|
||||
]
|
||||
click = [
|
||||
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
|
||||
{file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
||||
]
|
||||
dataclasses = [
|
||||
{file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"},
|
||||
{file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"},
|
||||
]
|
||||
gql = [
|
||||
{file = "gql-3.0.0a6.tar.gz", hash = "sha256:bdcbf60bc37b11d6d2f2ed271f69292c4e96d56df7000ba1dad52e487330bdce"},
|
||||
]
|
||||
graphql-core = [
|
||||
{file = "graphql-core-3.1.5.tar.gz", hash = "sha256:a755635d1d364a17e8d270347000722351aaa03f1ab7d280878aae82fc68b1f3"},
|
||||
{file = "graphql_core-3.1.5-py3-none-any.whl", hash = "sha256:91d96ef0e86665777bb7115d3bbb6b0326f43dc7dbcdd60da5486a27a50cfb11"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
|
||||
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
|
||||
]
|
||||
idna-ssl = [
|
||||
{file = "idna-ssl-1.1.0.tar.gz", hash = "sha256:a933e3bb13da54383f9e8f35dc4f9cb9eb9b3b78c6b36f311254d6d0d92c6c7c"},
|
||||
]
|
||||
importlib-metadata = [
|
||||
{file = "importlib_metadata-3.4.0-py3-none-any.whl", hash = "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771"},
|
||||
{file = "importlib_metadata-3.4.0.tar.gz", hash = "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"},
|
||||
]
|
||||
iniconfig = [
|
||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||
]
|
||||
isort = [
|
||||
{file = "isort-5.7.0-py3-none-any.whl", hash = "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"},
|
||||
{file = "isort-5.7.0.tar.gz", hash = "sha256:c729845434366216d320e936b8ad6f9d681aab72dc7cbc2d51bedc3582f3ad1e"},
|
||||
]
|
||||
multidict = [
|
||||
{file = "multidict-5.1.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f"},
|
||||
{file = "multidict-5.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf"},
|
||||
{file = "multidict-5.1.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281"},
|
||||
{file = "multidict-5.1.0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d"},
|
||||
{file = "multidict-5.1.0-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d"},
|
||||
{file = "multidict-5.1.0-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da"},
|
||||
{file = "multidict-5.1.0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224"},
|
||||
{file = "multidict-5.1.0-cp36-cp36m-win32.whl", hash = "sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26"},
|
||||
{file = "multidict-5.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6"},
|
||||
{file = "multidict-5.1.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76"},
|
||||
{file = "multidict-5.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a"},
|
||||
{file = "multidict-5.1.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f"},
|
||||
{file = "multidict-5.1.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348"},
|
||||
{file = "multidict-5.1.0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93"},
|
||||
{file = "multidict-5.1.0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9"},
|
||||
{file = "multidict-5.1.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37"},
|
||||
{file = "multidict-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5"},
|
||||
{file = "multidict-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632"},
|
||||
{file = "multidict-5.1.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952"},
|
||||
{file = "multidict-5.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79"},
|
||||
{file = "multidict-5.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456"},
|
||||
{file = "multidict-5.1.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7"},
|
||||
{file = "multidict-5.1.0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635"},
|
||||
{file = "multidict-5.1.0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a"},
|
||||
{file = "multidict-5.1.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea"},
|
||||
{file = "multidict-5.1.0-cp38-cp38-win32.whl", hash = "sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656"},
|
||||
{file = "multidict-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3"},
|
||||
{file = "multidict-5.1.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93"},
|
||||
{file = "multidict-5.1.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647"},
|
||||
{file = "multidict-5.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d"},
|
||||
{file = "multidict-5.1.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8"},
|
||||
{file = "multidict-5.1.0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1"},
|
||||
{file = "multidict-5.1.0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841"},
|
||||
{file = "multidict-5.1.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda"},
|
||||
{file = "multidict-5.1.0-cp39-cp39-win32.whl", hash = "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"},
|
||||
{file = "multidict-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359"},
|
||||
{file = "multidict-5.1.0.tar.gz", hash = "sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5"},
|
||||
]
|
||||
mypy-extensions = [
|
||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||
]
|
||||
packaging = [
|
||||
{file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
|
||||
{file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"},
|
||||
]
|
||||
pathspec = [
|
||||
{file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"},
|
||||
{file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"},
|
||||
]
|
||||
pluggy = [
|
||||
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
|
||||
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
|
||||
]
|
||||
py = [
|
||||
{file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
|
||||
{file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
|
||||
]
|
||||
pydantic = [
|
||||
{file = "pydantic-1.7.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c59ea046aea25be14dc22d69c97bee629e6d48d2b2ecb724d7fe8806bf5f61cd"},
|
||||
{file = "pydantic-1.7.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a4143c8d0c456a093387b96e0f5ee941a950992904d88bc816b4f0e72c9a0009"},
|
||||
{file = "pydantic-1.7.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:d8df4b9090b595511906fa48deda47af04e7d092318bfb291f4d45dfb6bb2127"},
|
||||
{file = "pydantic-1.7.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:514b473d264671a5c672dfb28bdfe1bf1afd390f6b206aa2ec9fed7fc592c48e"},
|
||||
{file = "pydantic-1.7.3-cp36-cp36m-win_amd64.whl", hash = "sha256:dba5c1f0a3aeea5083e75db9660935da90216f8a81b6d68e67f54e135ed5eb23"},
|
||||
{file = "pydantic-1.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:59e45f3b694b05a69032a0d603c32d453a23f0de80844fb14d55ab0c6c78ff2f"},
|
||||
{file = "pydantic-1.7.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5b24e8a572e4b4c18f614004dda8c9f2c07328cb5b6e314d6e1bbd536cb1a6c1"},
|
||||
{file = "pydantic-1.7.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:b2b054d095b6431cdda2f852a6d2f0fdec77686b305c57961b4c5dd6d863bf3c"},
|
||||
{file = "pydantic-1.7.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:025bf13ce27990acc059d0c5be46f416fc9b293f45363b3d19855165fee1874f"},
|
||||
{file = "pydantic-1.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6e3874aa7e8babd37b40c4504e3a94cc2023696ced5a0500949f3347664ff8e2"},
|
||||
{file = "pydantic-1.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e682f6442ebe4e50cb5e1cfde7dda6766fb586631c3e5569f6aa1951fd1a76ef"},
|
||||
{file = "pydantic-1.7.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:185e18134bec5ef43351149fe34fda4758e53d05bb8ea4d5928f0720997b79ef"},
|
||||
{file = "pydantic-1.7.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:f5b06f5099e163295b8ff5b1b71132ecf5866cc6e7f586d78d7d3fd6e8084608"},
|
||||
{file = "pydantic-1.7.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:24ca47365be2a5a3cc3f4a26dcc755bcdc9f0036f55dcedbd55663662ba145ec"},
|
||||
{file = "pydantic-1.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:d1fe3f0df8ac0f3a9792666c69a7cd70530f329036426d06b4f899c025aca74e"},
|
||||
{file = "pydantic-1.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f6864844b039805add62ebe8a8c676286340ba0c6d043ae5dea24114b82a319e"},
|
||||
{file = "pydantic-1.7.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ecb54491f98544c12c66ff3d15e701612fc388161fd455242447083350904730"},
|
||||
{file = "pydantic-1.7.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:ffd180ebd5dd2a9ac0da4e8b995c9c99e7c74c31f985ba090ee01d681b1c4b95"},
|
||||
{file = "pydantic-1.7.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8d72e814c7821125b16f1553124d12faba88e85405b0864328899aceaad7282b"},
|
||||
{file = "pydantic-1.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:475f2fa134cf272d6631072554f845d0630907fce053926ff634cc6bc45bf1af"},
|
||||
{file = "pydantic-1.7.3-py3-none-any.whl", hash = "sha256:38be427ea01a78206bcaf9a56f835784afcba9e5b88fbdce33bbbfbcd7841229"},
|
||||
{file = "pydantic-1.7.3.tar.gz", hash = "sha256:213125b7e9e64713d16d988d10997dabc6a1f73f3991e1ff8e35ebb1409c7dc9"},
|
||||
]
|
||||
pyparsing = [
|
||||
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
|
||||
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
|
||||
]
|
||||
pytest = [
|
||||
{file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"},
|
||||
{file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"},
|
||||
]
|
||||
pytest-ordering = [
|
||||
{file = "pytest-ordering-0.6.tar.gz", hash = "sha256:561ad653626bb171da78e682f6d39ac33bb13b3e272d406cd555adb6b006bda6"},
|
||||
{file = "pytest_ordering-0.6-py2-none-any.whl", hash = "sha256:27fba3fc265f5d0f8597e7557885662c1bdc1969497cd58aff6ed21c3b617de2"},
|
||||
{file = "pytest_ordering-0.6-py3-none-any.whl", hash = "sha256:3f314a178dbeb6777509548727dc69edf22d6d9a2867bf2d310ab85c403380b6"},
|
||||
]
|
||||
regex = [
|
||||
{file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"},
|
||||
{file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"},
|
||||
{file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"},
|
||||
{file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"},
|
||||
{file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"},
|
||||
{file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"},
|
||||
{file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"},
|
||||
{file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"},
|
||||
{file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"},
|
||||
{file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"},
|
||||
{file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"},
|
||||
{file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"},
|
||||
{file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"},
|
||||
{file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"},
|
||||
{file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"},
|
||||
{file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"},
|
||||
{file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"},
|
||||
{file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"},
|
||||
{file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"},
|
||||
{file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"},
|
||||
{file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"},
|
||||
{file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"},
|
||||
{file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"},
|
||||
{file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"},
|
||||
{file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"},
|
||||
{file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"},
|
||||
{file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"},
|
||||
{file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"},
|
||||
{file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"},
|
||||
{file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"},
|
||||
{file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"},
|
||||
{file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"},
|
||||
{file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"},
|
||||
{file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"},
|
||||
{file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"},
|
||||
{file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"},
|
||||
{file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"},
|
||||
{file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"},
|
||||
{file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"},
|
||||
{file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"},
|
||||
{file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
|
||||
{file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
|
||||
]
|
||||
toml = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
typed-ast = [
|
||||
{file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"},
|
||||
{file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487"},
|
||||
{file = "typed_ast-1.4.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412"},
|
||||
{file = "typed_ast-1.4.2-cp35-cp35m-win32.whl", hash = "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400"},
|
||||
{file = "typed_ast-1.4.2-cp35-cp35m-win_amd64.whl", hash = "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606"},
|
||||
{file = "typed_ast-1.4.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64"},
|
||||
{file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07"},
|
||||
{file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc"},
|
||||
{file = "typed_ast-1.4.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a"},
|
||||
{file = "typed_ast-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151"},
|
||||
{file = "typed_ast-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3"},
|
||||
{file = "typed_ast-1.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41"},
|
||||
{file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f"},
|
||||
{file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581"},
|
||||
{file = "typed_ast-1.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37"},
|
||||
{file = "typed_ast-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd"},
|
||||
{file = "typed_ast-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496"},
|
||||
{file = "typed_ast-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc"},
|
||||
{file = "typed_ast-1.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"},
|
||||
{file = "typed_ast-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea"},
|
||||
{file = "typed_ast-1.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787"},
|
||||
{file = "typed_ast-1.4.2-cp38-cp38-win32.whl", hash = "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2"},
|
||||
{file = "typed_ast-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937"},
|
||||
{file = "typed_ast-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1"},
|
||||
{file = "typed_ast-1.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6"},
|
||||
{file = "typed_ast-1.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166"},
|
||||
{file = "typed_ast-1.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d"},
|
||||
{file = "typed_ast-1.4.2-cp39-cp39-win32.whl", hash = "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b"},
|
||||
{file = "typed_ast-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440"},
|
||||
{file = "typed_ast-1.4.2.tar.gz", hash = "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"},
|
||||
{file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
|
||||
{file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"},
|
||||
]
|
||||
urllib3 = [
|
||||
{file = "urllib3-1.26.5-py2.py3-none-any.whl", hash = "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c"},
|
||||
{file = "urllib3-1.26.5.tar.gz", hash = "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"},
|
||||
]
|
||||
websockets = [
|
||||
{file = "websockets-9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d144b350045c53c8ff09aa1cfa955012dd32f00c7e0862c199edcabb1a8b32da"},
|
||||
{file = "websockets-9.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b4ad84b156cf50529b8ac5cc1638c2cf8680490e3fccb6121316c8c02620a2e4"},
|
||||
{file = "websockets-9.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2cf04601633a4ec176b9cc3d3e73789c037641001dbfaf7c411f89cd3e04fcaf"},
|
||||
{file = "websockets-9.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:5c8f0d82ea2468282e08b0cf5307f3ad022290ed50c45d5cb7767957ca782880"},
|
||||
{file = "websockets-9.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:caa68c95bc1776d3521f81eeb4d5b9438be92514ec2a79fececda814099c8314"},
|
||||
{file = "websockets-9.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d2c2d9b24d3c65b5a02cac12cbb4e4194e590314519ed49db2f67ef561c3cf58"},
|
||||
{file = "websockets-9.1-cp36-cp36m-win32.whl", hash = "sha256:f31722f1c033c198aa4a39a01905951c00bd1c74f922e8afc1b1c62adbcdd56a"},
|
||||
{file = "websockets-9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:3ddff38894c7857c476feb3538dd847514379d6dc844961dc99f04b0384b1b1b"},
|
||||
{file = "websockets-9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:51d04df04ed9d08077d10ccbe21e6805791b78eac49d16d30a1f1fe2e44ba0af"},
|
||||
{file = "websockets-9.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f68c352a68e5fdf1e97288d5cec9296664c590c25932a8476224124aaf90dbcd"},
|
||||
{file = "websockets-9.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:b43b13e5622c5a53ab12f3272e6f42f1ce37cd5b6684b2676cb365403295cd40"},
|
||||
{file = "websockets-9.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:9147868bb0cc01e6846606cd65cbf9c58598f187b96d14dd1ca17338b08793bb"},
|
||||
{file = "websockets-9.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:836d14eb53b500fd92bd5db2fc5894f7c72b634f9c2a28f546f75967503d8e25"},
|
||||
{file = "websockets-9.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:48c222feb3ced18f3dc61168ca18952a22fb88e5eb8902d2bf1b50faefdc34a2"},
|
||||
{file = "websockets-9.1-cp37-cp37m-win32.whl", hash = "sha256:900589e19200be76dd7cbaa95e9771605b5ce3f62512d039fb3bc5da9014912a"},
|
||||
{file = "websockets-9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ab5ee15d3462198c794c49ccd31773d8a2b8c17d622aa184f669d2b98c2f0857"},
|
||||
{file = "websockets-9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:85e701a6c316b7067f1e8675c638036a796fe5116783a4c932e7eb8e305a3ffe"},
|
||||
{file = "websockets-9.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b2e71c4670ebe1067fa8632f0d081e47254ee2d3d409de54168b43b0ba9147e0"},
|
||||
{file = "websockets-9.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:230a3506df6b5f446fed2398e58dcaafdff12d67fe1397dff196411a9e820d02"},
|
||||
{file = "websockets-9.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:7df3596838b2a0c07c6f6d67752c53859a54993d4f062689fdf547cb56d0f84f"},
|
||||
{file = "websockets-9.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:826ccf85d4514609219725ba4a7abd569228c2c9f1968e8be05be366f68291ec"},
|
||||
{file = "websockets-9.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:0dd4eb8e0bbf365d6f652711ce21b8fd2b596f873d32aabb0fbb53ec604418cc"},
|
||||
{file = "websockets-9.1-cp38-cp38-win32.whl", hash = "sha256:1d0971cc7251aeff955aa742ec541ee8aaea4bb2ebf0245748fbec62f744a37e"},
|
||||
{file = "websockets-9.1-cp38-cp38-win_amd64.whl", hash = "sha256:7189e51955f9268b2bdd6cc537e0faa06f8fffda7fb386e5922c6391de51b077"},
|
||||
{file = "websockets-9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e9e5fd6dbdf95d99bc03732ded1fc8ef22ebbc05999ac7e0c7bf57fe6e4e5ae2"},
|
||||
{file = "websockets-9.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9e7fdc775fe7403dbd8bc883ba59576a6232eac96dacb56512daacf7af5d618d"},
|
||||
{file = "websockets-9.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:597c28f3aa7a09e8c070a86b03107094ee5cdafcc0d55f2f2eac92faac8dc67d"},
|
||||
{file = "websockets-9.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:ad893d889bc700a5835e0a95a3e4f2c39e91577ab232a3dc03c262a0f8fc4b5c"},
|
||||
{file = "websockets-9.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:1d6b4fddb12ab9adf87b843cd4316c4bd602db8d5efd2fb83147f0458fe85135"},
|
||||
{file = "websockets-9.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:ebf459a1c069f9866d8569439c06193c586e72c9330db1390af7c6a0a32c4afd"},
|
||||
{file = "websockets-9.1-cp39-cp39-win32.whl", hash = "sha256:be5fd35e99970518547edc906efab29afd392319f020c3c58b0e1a158e16ed20"},
|
||||
{file = "websockets-9.1-cp39-cp39-win_amd64.whl", hash = "sha256:85db8090ba94e22d964498a47fdd933b8875a1add6ebc514c7ac8703eb97bbf0"},
|
||||
{file = "websockets-9.1.tar.gz", hash = "sha256:276d2339ebf0df4f45df453923ebd2270b87900eda5dfd4a6b0cfa15f82111c3"},
|
||||
]
|
||||
yarl = [
|
||||
{file = "yarl-1.6.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434"},
|
||||
{file = "yarl-1.6.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478"},
|
||||
{file = "yarl-1.6.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6"},
|
||||
{file = "yarl-1.6.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e"},
|
||||
{file = "yarl-1.6.3-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406"},
|
||||
{file = "yarl-1.6.3-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76"},
|
||||
{file = "yarl-1.6.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366"},
|
||||
{file = "yarl-1.6.3-cp36-cp36m-win32.whl", hash = "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721"},
|
||||
{file = "yarl-1.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643"},
|
||||
{file = "yarl-1.6.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e"},
|
||||
{file = "yarl-1.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3"},
|
||||
{file = "yarl-1.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8"},
|
||||
{file = "yarl-1.6.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a"},
|
||||
{file = "yarl-1.6.3-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c"},
|
||||
{file = "yarl-1.6.3-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f"},
|
||||
{file = "yarl-1.6.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970"},
|
||||
{file = "yarl-1.6.3-cp37-cp37m-win32.whl", hash = "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e"},
|
||||
{file = "yarl-1.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50"},
|
||||
{file = "yarl-1.6.3-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2"},
|
||||
{file = "yarl-1.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec"},
|
||||
{file = "yarl-1.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71"},
|
||||
{file = "yarl-1.6.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc"},
|
||||
{file = "yarl-1.6.3-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959"},
|
||||
{file = "yarl-1.6.3-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2"},
|
||||
{file = "yarl-1.6.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2"},
|
||||
{file = "yarl-1.6.3-cp38-cp38-win32.whl", hash = "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896"},
|
||||
{file = "yarl-1.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a"},
|
||||
{file = "yarl-1.6.3-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e"},
|
||||
{file = "yarl-1.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724"},
|
||||
{file = "yarl-1.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c"},
|
||||
{file = "yarl-1.6.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25"},
|
||||
{file = "yarl-1.6.3-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96"},
|
||||
{file = "yarl-1.6.3-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0"},
|
||||
{file = "yarl-1.6.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4"},
|
||||
{file = "yarl-1.6.3-cp39-cp39-win32.whl", hash = "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424"},
|
||||
{file = "yarl-1.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6"},
|
||||
{file = "yarl-1.6.3.tar.gz", hash = "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10"},
|
||||
]
|
||||
zipp = [
|
||||
{file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"},
|
||||
{file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"},
|
||||
]
|
||||
@@ -0,0 +1,48 @@
|
||||
[tool.poetry]
|
||||
name = "specklepy"
|
||||
version = "2.2.6"
|
||||
description = "The Python SDK for Speckle 2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Speckle Systems <devops@speckle.systems>"]
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/specklesystems/speckle-py"
|
||||
documentation = "https://speckle.guide/dev/py-examples.html"
|
||||
homepage = "https://speckle.systems/"
|
||||
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.6.5"
|
||||
pydantic = "^1.7.3"
|
||||
appdirs = "^1.4.4"
|
||||
gql = {version = ">=3.0.0a6", extras = ["all"], allow-prereleases = true}
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = "^20.8b1"
|
||||
isort = "^5.7.0"
|
||||
pytest = "^6.2.2"
|
||||
pytest-ordering = "^0.6"
|
||||
|
||||
|
||||
[tool.black]
|
||||
exclude = '''
|
||||
/(
|
||||
\.eggs
|
||||
| \.git
|
||||
| \.hg
|
||||
| \.mypy_cache
|
||||
| \.tox
|
||||
| \.venv
|
||||
| _build
|
||||
| buck-out
|
||||
| build
|
||||
| dist
|
||||
)/
|
||||
'''
|
||||
include = '\.pyi?$'
|
||||
line-length = 88
|
||||
target-version = ["py36", "py37", "py38"]
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
@@ -1,31 +0,0 @@
|
||||
aiohttp==3.7.1
|
||||
appdirs==1.4.4
|
||||
astroid==2.4.2
|
||||
async-timeout==3.0.1
|
||||
attrs==20.3.0
|
||||
black==20.8b1
|
||||
certifi==2020.11.8
|
||||
chardet==3.0.4
|
||||
click==7.1.2
|
||||
colorama==0.4.4
|
||||
gql==3.0.0a4
|
||||
graphql-core==3.1.2
|
||||
idna==2.10
|
||||
isort==5.6.4
|
||||
lazy-object-proxy==1.4.3
|
||||
mccabe==0.6.1
|
||||
multidict==5.0.0
|
||||
mypy-extensions==0.4.3
|
||||
pathspec==0.8.1
|
||||
pydantic==1.7.2
|
||||
pylint==2.6.0
|
||||
regex==2020.11.11
|
||||
requests==2.24.0
|
||||
six==1.15.0
|
||||
toml==0.10.2
|
||||
typed-ast==1.4.1
|
||||
typing-extensions==3.7.4.3
|
||||
urllib3==1.25.11
|
||||
websockets==8.1
|
||||
wrapt==1.12.1
|
||||
yarl==1.5.1
|
||||
@@ -1,48 +0,0 @@
|
||||
from typing import List, Optional
|
||||
from gql import gql
|
||||
from pydantic.main import BaseModel
|
||||
from speckle.api.resource import ResourceBase
|
||||
from speckle.api.models import Branch
|
||||
|
||||
NAME = "branch"
|
||||
METHODS = ["create"]
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for branches"""
|
||||
|
||||
def __init__(self, me, basepath, client) -> None:
|
||||
super().__init__(
|
||||
me=me, basepath=basepath, client=client, name=NAME, methods=METHODS
|
||||
)
|
||||
self.schema = Branch
|
||||
|
||||
def create(
|
||||
self, streamId: str, name: str, description: str = "No description provided"
|
||||
) -> str:
|
||||
"""Create a new branch on this stream
|
||||
|
||||
Arguments:
|
||||
name {str} -- the name of the new branch
|
||||
description {str} -- a short description of the branch
|
||||
|
||||
Returns:
|
||||
id {str} -- the newly created branch's id
|
||||
"""
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
mutation BranchCreate($branch: BranchCreateInput!){
|
||||
branchCreate(branch: $branch)
|
||||
}
|
||||
"""
|
||||
)
|
||||
params = {
|
||||
"branch": {
|
||||
"streamId": streamId,
|
||||
"name": name,
|
||||
"description": description,
|
||||
}
|
||||
}
|
||||
|
||||
return self.make_request(query=query, params=params, parse_response=False)
|
||||
@@ -1,19 +0,0 @@
|
||||
from typing import Optional, List
|
||||
from gql import gql
|
||||
from pydantic.main import BaseModel
|
||||
from speckle.api.resource import ResourceBase
|
||||
from speckle.api.models import Commit
|
||||
|
||||
|
||||
NAME = "commit"
|
||||
METHODS = []
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for commits"""
|
||||
|
||||
def __init__(self, me, basepath, client) -> None:
|
||||
super().__init__(
|
||||
me=me, basepath=basepath, client=client, name=NAME, methods=METHODS
|
||||
)
|
||||
self.schema = Commit
|
||||
@@ -1,79 +0,0 @@
|
||||
from typing import Dict
|
||||
from gql import gql
|
||||
from gql.client import Client
|
||||
from speckle.api.resource import ResourceBase
|
||||
|
||||
|
||||
NAME = "server"
|
||||
METHODS = ["get", "apps"]
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for the server"""
|
||||
|
||||
def __init__(self, me, basepath, client) -> None:
|
||||
super().__init__(
|
||||
me=me, basepath=basepath, client=client, name=NAME, methods=METHODS
|
||||
)
|
||||
|
||||
def get(self) -> Dict:
|
||||
"""Get the server info
|
||||
|
||||
Returns:
|
||||
dict -- the server info in dictionary form
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
query Server {
|
||||
serverInfo {
|
||||
name
|
||||
company
|
||||
description
|
||||
adminContact
|
||||
canonicalUrl
|
||||
roles {
|
||||
name
|
||||
description
|
||||
resourceTarget
|
||||
}
|
||||
scopes {
|
||||
name
|
||||
description
|
||||
}
|
||||
authStrategies{
|
||||
id
|
||||
name
|
||||
icon
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
return self.make_request(query=query)
|
||||
|
||||
def apps(self) -> Dict:
|
||||
"""Get the apps registered on the server
|
||||
|
||||
Returns:
|
||||
dict -- a dictionary of apps registered on the server
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
query Apps {
|
||||
apps {
|
||||
id
|
||||
name
|
||||
description
|
||||
termsAndConditionsLink
|
||||
logo
|
||||
author {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
return self.make_request(query=query)
|
||||
@@ -1,17 +0,0 @@
|
||||
from typing import List
|
||||
|
||||
|
||||
class SpeckleException(Exception):
|
||||
def __init__(self, message: str, exception: Exception = None) -> None:
|
||||
self.message = message
|
||||
self.exception = exception
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"SpeckleException: {self.message}"
|
||||
|
||||
|
||||
class GraphQLException(SpeckleException):
|
||||
def __init__(self, message: str, errors: List, data=None) -> None:
|
||||
super().__init__(message=message)
|
||||
self.errors = errors
|
||||
self.data = data
|
||||
@@ -1,51 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Beam.json
|
||||
# timestamp: 2020-11-24T16:33:00+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Beam(BaseModel):
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,51 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Brace.json
|
||||
# timestamp: 2020-11-24T16:33:02+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Brace(BaseModel):
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,52 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Column.json
|
||||
# timestamp: 2020-11-24T16:33:04+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Column(BaseModel):
|
||||
height: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,57 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Duct.json
|
||||
# timestamp: 2020-11-24T16:33:07+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Duct(BaseModel):
|
||||
width: Optional[float] = None
|
||||
height: Optional[float] = None
|
||||
diameter: Optional[float] = None
|
||||
length: Optional[float] = None
|
||||
level: Optional[Level] = None
|
||||
velocity: Optional[float] = None
|
||||
system: Optional[Optional[str]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,51 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Element.json
|
||||
# timestamp: 2020-11-24T16:33:08+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Element(BaseModel):
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,56 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Floor.json
|
||||
# timestamp: 2020-11-24T16:33:10+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ICurve(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Floor(BaseModel):
|
||||
holes: Optional[List[ICurve]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,67 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: GridLine.json
|
||||
# timestamp: 2020-11-24T16:33:12+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Interval(BaseModel):
|
||||
start: Optional[Optional[float]] = None
|
||||
end: Optional[Optional[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Line(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
domain: Optional[Interval] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class GridLine(BaseModel):
|
||||
baseLine: Optional[Line] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,40 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Level.json
|
||||
# timestamp: 2020-11-24T16:33:15+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,51 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Opening.json
|
||||
# timestamp: 2020-11-24T16:33:18+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Opening(BaseModel):
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,56 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Roof.json
|
||||
# timestamp: 2020-11-24T16:33:33+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ICurve(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Roof(BaseModel):
|
||||
holes: Optional[List[ICurve]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,59 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Room.json
|
||||
# timestamp: 2020-11-24T16:33:34+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ICurve(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Room(BaseModel):
|
||||
number: Optional[Optional[str]] = None
|
||||
area: Optional[float] = None
|
||||
volume: Optional[float] = None
|
||||
holes: Optional[List[ICurve]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,28 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Topography.json
|
||||
# timestamp: 2020-11-24T16:33:35+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Topography(BaseModel):
|
||||
baseGeometry: Optional[Mesh] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,52 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Wall.json
|
||||
# timestamp: 2020-11-24T16:33:37+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Wall(BaseModel):
|
||||
height: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,61 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Arc.json
|
||||
# timestamp: 2020-11-24T16:32:59+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Interval(BaseModel):
|
||||
start: Optional[Optional[float]] = None
|
||||
end: Optional[Optional[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Point(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Vector(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Plane(BaseModel):
|
||||
origin: Optional[Point] = None
|
||||
normal: Optional[Vector] = None
|
||||
xdir: Optional[Vector] = None
|
||||
ydir: Optional[Vector] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Arc(BaseModel):
|
||||
radius: Optional[Optional[float]] = None
|
||||
startAngle: Optional[Optional[float]] = None
|
||||
endAngle: Optional[Optional[float]] = None
|
||||
angleRadians: Optional[Optional[float]] = None
|
||||
plane: Optional[Plane] = None
|
||||
domain: Optional[Interval] = None
|
||||
startPoint: Optional[Point] = None
|
||||
midPoint: Optional[Point] = None
|
||||
endPoint: Optional[Point] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,25 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Block.json
|
||||
# timestamp: 2020-11-24T16:33:01+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Base(BaseModel):
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Block(BaseModel):
|
||||
description: Optional[Optional[str]] = None
|
||||
objects: Optional[List[Base]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,56 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Box.json
|
||||
# timestamp: 2020-11-24T16:33:02+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Interval(BaseModel):
|
||||
start: Optional[Optional[float]] = None
|
||||
end: Optional[Optional[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Point(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Vector(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Plane(BaseModel):
|
||||
origin: Optional[Point] = None
|
||||
normal: Optional[Vector] = None
|
||||
xdir: Optional[Vector] = None
|
||||
ydir: Optional[Vector] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Box(BaseModel):
|
||||
basePlane: Optional[Plane] = None
|
||||
xSize: Optional[Interval] = None
|
||||
ySize: Optional[Interval] = None
|
||||
zSize: Optional[Interval] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,55 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Circle.json
|
||||
# timestamp: 2020-11-24T16:33:04+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Interval(BaseModel):
|
||||
start: Optional[Optional[float]] = None
|
||||
end: Optional[Optional[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Point(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Vector(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Plane(BaseModel):
|
||||
origin: Optional[Point] = None
|
||||
normal: Optional[Vector] = None
|
||||
xdir: Optional[Vector] = None
|
||||
ydir: Optional[Vector] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Circle(BaseModel):
|
||||
radius: Optional[Optional[float]] = None
|
||||
plane: Optional[Plane] = None
|
||||
domain: Optional[Interval] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,44 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Curve.json
|
||||
# timestamp: 2020-11-24T16:33:05+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Interval(BaseModel):
|
||||
start: Optional[Optional[float]] = None
|
||||
end: Optional[Optional[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Polyline(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
closed: Optional[bool] = None
|
||||
domain: Optional[Interval] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Curve(BaseModel):
|
||||
degree: Optional[int] = None
|
||||
periodic: Optional[bool] = None
|
||||
rational: Optional[bool] = None
|
||||
points: Optional[List[float]] = None
|
||||
weights: Optional[List[float]] = None
|
||||
knots: Optional[List[float]] = None
|
||||
domain: Optional[Interval] = None
|
||||
displayValue: Optional[Polyline] = None
|
||||
closed: Optional[bool] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,56 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Ellipse.json
|
||||
# timestamp: 2020-11-24T16:33:08+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Interval(BaseModel):
|
||||
start: Optional[Optional[float]] = None
|
||||
end: Optional[Optional[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Point(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Vector(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Plane(BaseModel):
|
||||
origin: Optional[Point] = None
|
||||
normal: Optional[Vector] = None
|
||||
xdir: Optional[Vector] = None
|
||||
ydir: Optional[Vector] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Ellipse(BaseModel):
|
||||
firstRadius: Optional[Optional[float]] = None
|
||||
secondRadius: Optional[Optional[float]] = None
|
||||
plane: Optional[Plane] = None
|
||||
domain: Optional[Interval] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,39 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Extrusion.json
|
||||
# timestamp: 2020-11-24T16:33:09+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Base(BaseModel):
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Point(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Extrusion(BaseModel):
|
||||
length: Optional[Optional[float]] = None
|
||||
capped: Optional[Optional[bool]] = None
|
||||
profile: Optional[Base] = None
|
||||
pathStart: Optional[Point] = None
|
||||
pathEnd: Optional[Point] = None
|
||||
pathCurve: Optional[Base] = None
|
||||
pathTangent: Optional[Base] = None
|
||||
profiles: Optional[List[Base]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,27 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Line.json
|
||||
# timestamp: 2020-11-24T16:33:16+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Interval(BaseModel):
|
||||
start: Optional[Optional[float]] = None
|
||||
end: Optional[Optional[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Line(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
domain: Optional[Interval] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,20 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Mesh.json
|
||||
# timestamp: 2020-11-24T16:33:16+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,36 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Plane.json
|
||||
# timestamp: 2020-11-24T16:33:19+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Point(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Vector(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Plane(BaseModel):
|
||||
origin: Optional[Point] = None
|
||||
normal: Optional[Vector] = None
|
||||
xdir: Optional[Vector] = None
|
||||
ydir: Optional[Vector] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,17 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Point.json
|
||||
# timestamp: 2020-11-24T16:33:20+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Point(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,32 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Polycurve.json
|
||||
# timestamp: 2020-11-24T16:33:20+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ICurve(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Interval(BaseModel):
|
||||
start: Optional[Optional[float]] = None
|
||||
end: Optional[Optional[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Polycurve(BaseModel):
|
||||
segments: Optional[List[ICurve]] = None
|
||||
domain: Optional[Interval] = None
|
||||
closed: Optional[bool] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,28 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Polyline.json
|
||||
# timestamp: 2020-11-24T16:33:21+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Interval(BaseModel):
|
||||
start: Optional[Optional[float]] = None
|
||||
end: Optional[Optional[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Polyline(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
closed: Optional[bool] = None
|
||||
domain: Optional[Interval] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,17 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Vector.json
|
||||
# timestamp: 2020-11-24T16:33:36+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Vector(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,18 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Interval.json
|
||||
# timestamp: 2020-11-24T16:33:14+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Interval(BaseModel):
|
||||
start: Optional[Optional[float]] = None
|
||||
end: Optional[Optional[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,27 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: Interval2d.json
|
||||
# timestamp: 2020-11-24T16:33:14+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Interval(BaseModel):
|
||||
start: Optional[Optional[float]] = None
|
||||
end: Optional[Optional[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Interval2d(BaseModel):
|
||||
u: Optional[Interval] = None
|
||||
v: Optional[Interval] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,54 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: AdaptiveComponent.json
|
||||
# timestamp: 2020-11-24T16:32:59+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class AdaptiveComponent(BaseModel):
|
||||
family: Optional[Optional[str]] = None
|
||||
flipped: Optional[bool] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,22 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: DetailCurve.json
|
||||
# timestamp: 2020-11-24T16:33:06+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ICurve(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class DetailCurve(BaseModel):
|
||||
baseCurve: Optional[ICurve] = None
|
||||
lineStyle: Optional[Optional[str]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,94 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: DirectShape.json
|
||||
# timestamp: 2020-11-24T16:33:06+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Category(Enum):
|
||||
integer_0 = 0
|
||||
integer_1 = 1
|
||||
integer_2 = 2
|
||||
integer_3 = 3
|
||||
integer_4 = 4
|
||||
integer_5 = 5
|
||||
integer_6 = 6
|
||||
integer_7 = 7
|
||||
integer_8 = 8
|
||||
integer_9 = 9
|
||||
integer_10 = 10
|
||||
integer_11 = 11
|
||||
integer_12 = 12
|
||||
integer_13 = 13
|
||||
integer_14 = 14
|
||||
integer_15 = 15
|
||||
integer_16 = 16
|
||||
integer_17 = 17
|
||||
integer_18 = 18
|
||||
integer_19 = 19
|
||||
integer_20 = 20
|
||||
integer_21 = 21
|
||||
integer_22 = 22
|
||||
integer_23 = 23
|
||||
integer_24 = 24
|
||||
integer_25 = 25
|
||||
integer_26 = 26
|
||||
integer_27 = 27
|
||||
integer_28 = 28
|
||||
integer_29 = 29
|
||||
integer_30 = 30
|
||||
integer_31 = 31
|
||||
integer_32 = 32
|
||||
integer_33 = 33
|
||||
integer_34 = 34
|
||||
integer_35 = 35
|
||||
integer_36 = 36
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class DirectShape(BaseModel):
|
||||
category: Optional[Category] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,66 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: FamilyInstance.json
|
||||
# timestamp: 2020-11-24T16:33:10+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Element(BaseModel):
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class FamilyInstance(BaseModel):
|
||||
family: Optional[Optional[str]] = None
|
||||
flipped: Optional[bool] = None
|
||||
host: Optional[Element] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,22 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: ModelCurve.json
|
||||
# timestamp: 2020-11-24T16:33:17+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ICurve(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class ModelCurve(BaseModel):
|
||||
baseCurve: Optional[ICurve] = None
|
||||
lineStyle: Optional[Optional[str]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,53 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitBeam.json
|
||||
# timestamp: 2020-11-24T16:33:22+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitBeam(BaseModel):
|
||||
family: Optional[Optional[str]] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,53 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitBrace.json
|
||||
# timestamp: 2020-11-24T16:33:22+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitBrace(BaseModel):
|
||||
family: Optional[Optional[str]] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,47 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitCategory.json
|
||||
# timestamp: 2020-11-24T16:33:23+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class RevitCategory(Enum):
|
||||
integer_0 = 0
|
||||
integer_1 = 1
|
||||
integer_2 = 2
|
||||
integer_3 = 3
|
||||
integer_4 = 4
|
||||
integer_5 = 5
|
||||
integer_6 = 6
|
||||
integer_7 = 7
|
||||
integer_8 = 8
|
||||
integer_9 = 9
|
||||
integer_10 = 10
|
||||
integer_11 = 11
|
||||
integer_12 = 12
|
||||
integer_13 = 13
|
||||
integer_14 = 14
|
||||
integer_15 = 15
|
||||
integer_16 = 16
|
||||
integer_17 = 17
|
||||
integer_18 = 18
|
||||
integer_19 = 19
|
||||
integer_20 = 20
|
||||
integer_21 = 21
|
||||
integer_22 = 22
|
||||
integer_23 = 23
|
||||
integer_24 = 24
|
||||
integer_25 = 25
|
||||
integer_26 = 26
|
||||
integer_27 = 27
|
||||
integer_28 = 28
|
||||
integer_29 = 29
|
||||
integer_30 = 30
|
||||
integer_31 = 31
|
||||
integer_32 = 32
|
||||
integer_33 = 33
|
||||
integer_34 = 34
|
||||
integer_35 = 35
|
||||
integer_36 = 36
|
||||
@@ -1,57 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitColumn.json
|
||||
# timestamp: 2020-11-24T16:33:24+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitColumn(BaseModel):
|
||||
family: Optional[Optional[str]] = None
|
||||
topLevel: Optional[Level] = None
|
||||
baseOffset: Optional[float] = None
|
||||
topOffset: Optional[float] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
height: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,59 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitDuct.json
|
||||
# timestamp: 2020-11-24T16:33:25+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitDuct(BaseModel):
|
||||
family: Optional[Optional[str]] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
width: Optional[float] = None
|
||||
height: Optional[float] = None
|
||||
diameter: Optional[float] = None
|
||||
length: Optional[float] = None
|
||||
level: Optional[Level] = None
|
||||
velocity: Optional[float] = None
|
||||
system: Optional[Optional[str]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,78 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitExtrusionRoof.json
|
||||
# timestamp: 2020-11-24T16:33:25+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ICurve(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Interval(BaseModel):
|
||||
start: Optional[Optional[float]] = None
|
||||
end: Optional[Optional[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Line(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
domain: Optional[Interval] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitExtrusionRoof(BaseModel):
|
||||
start: Optional[float] = None
|
||||
end: Optional[float] = None
|
||||
referenceLine: Optional[Line] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
holes: Optional[List[ICurve]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,57 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitFloor.json
|
||||
# timestamp: 2020-11-24T16:33:26+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ICurve(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitFloor(BaseModel):
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
holes: Optional[List[ICurve]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,58 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitFootprintRoof.json
|
||||
# timestamp: 2020-11-24T16:33:27+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ICurve(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitFootprintRoof(BaseModel):
|
||||
cutOffLevel: Optional[Level] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
holes: Optional[List[ICurve]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,55 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitLevel.json
|
||||
# timestamp: 2020-11-24T16:33:28+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitLevel(BaseModel):
|
||||
createView: Optional[bool] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,68 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitRoom.json
|
||||
# timestamp: 2020-11-24T16:33:28+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ICurve(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Point(BaseModel):
|
||||
value: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitRoom(BaseModel):
|
||||
basePoint: Optional[Point] = None
|
||||
number: Optional[Optional[str]] = None
|
||||
area: Optional[float] = None
|
||||
volume: Optional[float] = None
|
||||
holes: Optional[List[ICurve]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,53 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitShaft.json
|
||||
# timestamp: 2020-11-24T16:33:29+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitShaft(BaseModel):
|
||||
topLevel: Optional[Level] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,28 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitTopography.json
|
||||
# timestamp: 2020-11-24T16:33:30+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitTopography(BaseModel):
|
||||
baseGeometry: Optional[Mesh] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,11 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitUtils.json
|
||||
# timestamp: 2020-11-24T16:33:30+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class RevitUtils(BaseModel):
|
||||
pass
|
||||
@@ -1,64 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitVerticalOpening.json
|
||||
# timestamp: 2020-11-24T16:33:31+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Element(BaseModel):
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitVerticalOpening(BaseModel):
|
||||
host: Optional[Element] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,45 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitWall.json
|
||||
# timestamp: 2020-11-24T16:33:32+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitWall(BaseModel):
|
||||
topLevel: Optional[Level] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,58 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RevitWallOpening.json
|
||||
# timestamp: 2020-11-24T16:33:33+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class IGeometry(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
vertices: Optional[List[float]] = None
|
||||
faces: Optional[List[int]] = None
|
||||
colors: Optional[List[int]] = None
|
||||
textureCoordinates: Optional[List[float]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class Level(BaseModel):
|
||||
name: Optional[Optional[str]] = None
|
||||
elevation: Optional[float] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
class RevitWall(BaseModel):
|
||||
topLevel: Optional[Level] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
|
||||
|
||||
class RevitWallOpening(BaseModel):
|
||||
host: Optional[RevitWall] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
baseGeometry: Optional[IGeometry] = None
|
||||
displayMesh: Optional[Mesh] = None
|
||||
type: Optional[Optional[str]] = None
|
||||
level: Optional[Level] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
|
||||
|
||||
Level.update_forward_refs()
|
||||
@@ -1,22 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: RoomBoundaryLine.json
|
||||
# timestamp: 2020-11-24T16:33:35+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ICurve(BaseModel):
|
||||
__root__: Optional[Dict[str, Any]]
|
||||
|
||||
|
||||
class RoomBoundaryLine(BaseModel):
|
||||
baseCurve: Optional[ICurve] = None
|
||||
parameters: Optional[Optional[Dict[str, Any]]] = None
|
||||
id: Optional[Optional[str]] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[Optional[str]] = None
|
||||
speckle_type: Optional[Optional[str]] = None
|
||||
@@ -1,9 +1,19 @@
|
||||
import re
|
||||
from gql.client import SyncClientSession
|
||||
from speckle.logging.exceptions import SpeckleException
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from typing import Dict
|
||||
|
||||
from speckle.api import resources
|
||||
from speckle.api.resources import stream, server, user, subscriptions
|
||||
from specklepy.api import resources
|
||||
from specklepy.api.resources import (
|
||||
branch,
|
||||
commit,
|
||||
stream,
|
||||
object,
|
||||
server,
|
||||
user,
|
||||
subscriptions,
|
||||
)
|
||||
from specklepy.api.models import ServerInfo
|
||||
from gql import Client, gql
|
||||
from gql.transport.requests import RequestsHTTPTransport
|
||||
from gql.transport.aiohttp import AIOHTTPTransport
|
||||
@@ -11,7 +21,34 @@ from gql.transport.websockets import WebsocketsTransport
|
||||
|
||||
|
||||
class SpeckleClient:
|
||||
DEFAULT_HOST = "staging.speckle.dev"
|
||||
"""
|
||||
The `SpeckleClient` is your entry point for interacting with your Speckle Server's GraphQL API.
|
||||
You'll need to have access to a server to use it, or you can use our public server `speckle.xyz`.
|
||||
|
||||
To authenticate the client, you'll need to have downloaded the [Speckle Manager](https://speckle.guide/#speckle-manager)
|
||||
and added your account.
|
||||
|
||||
```py
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.api.credentials import get_default_account
|
||||
|
||||
# initialise the client
|
||||
client = SpeckleClient(host="speckle.xyz") # or whatever your host is
|
||||
# client = SpeckleClient(host="localhost:3000", use_ssl=False) or use local server
|
||||
|
||||
# authenticate the client with a token (account has been added in Speckle Manager)
|
||||
account = get_default_account()
|
||||
client.authenticate(token=account.token)
|
||||
|
||||
# create a new stream. this returns the stream id
|
||||
new_stream_id = client.stream.create(name="a shiny new stream")
|
||||
|
||||
# use that stream id to get the stream from the server
|
||||
new_stream = client.stream.get(id=new_stream_id)
|
||||
```
|
||||
"""
|
||||
|
||||
DEFAULT_HOST = "speckle.xyz"
|
||||
USE_SSL = True
|
||||
|
||||
def __init__(self, host: str = DEFAULT_HOST, use_ssl: bool = USE_SSL) -> None:
|
||||
@@ -22,6 +59,9 @@ class SpeckleClient:
|
||||
ws_protocol = "wss"
|
||||
http_protocol = "https"
|
||||
|
||||
# sanitise host input by removing protocol and trailing slash
|
||||
host = re.sub(r"((^\w+:|^)\/\/)|(\/$)", "", host)
|
||||
|
||||
self.url = f"{http_protocol}://{host}"
|
||||
self.graphql = self.url + "/graphql"
|
||||
self.ws_url = f"{ws_protocol}://{host}/graphql"
|
||||
@@ -34,6 +74,19 @@ class SpeckleClient:
|
||||
|
||||
self._init_resources()
|
||||
|
||||
# Check compatibility with the server
|
||||
try:
|
||||
serverInfo = self.server.get()
|
||||
if not isinstance(serverInfo, ServerInfo):
|
||||
raise Exception("Couldn't get ServerInfo")
|
||||
except Exception as ex:
|
||||
raise SpeckleException(f"{self.url} is not a compatible Speckle Server", ex)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"SpeckleClient( server: {self.url}, authenticated: {self.me is not None} )"
|
||||
)
|
||||
|
||||
def authenticate(self, token: str) -> None:
|
||||
"""Authenticate the client using a personal access token
|
||||
The token is saved in the client object and a synchronous GraphQL entrypoint is created
|
||||
@@ -65,6 +118,15 @@ class SpeckleClient:
|
||||
self.stream = stream.Resource(
|
||||
me=self.me, basepath=self.url, client=self.httpclient
|
||||
)
|
||||
self.commit = commit.Resource(
|
||||
me=self.me, basepath=self.url, client=self.httpclient
|
||||
)
|
||||
self.branch = branch.Resource(
|
||||
me=self.me, basepath=self.url, client=self.httpclient
|
||||
)
|
||||
self.object = object.Resource(
|
||||
me=self.me, basepath=self.url, client=self.httpclient
|
||||
)
|
||||
self.server = server.Resource(
|
||||
me=self.me, basepath=self.url, client=self.httpclient
|
||||
)
|
||||
@@ -82,4 +144,4 @@ class SpeckleClient:
|
||||
except:
|
||||
raise SpeckleException(
|
||||
f"Method {name} is not supported by the SpeckleClient class"
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,171 @@
|
||||
import os
|
||||
from specklepy.transports.server.server import ServerTransport
|
||||
from warnings import warn
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
from urllib.parse import urlparse, unquote
|
||||
from specklepy.api.models import ServerInfo
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.transports.sqlite import SQLiteTransport
|
||||
from specklepy.logging.exceptions import SpeckleException, SpeckleWarning
|
||||
|
||||
|
||||
class UserInfo(BaseModel):
|
||||
name: str
|
||||
email: str
|
||||
company: Optional[str]
|
||||
id: str
|
||||
|
||||
|
||||
class Account(BaseModel):
|
||||
isDefault: bool = None
|
||||
token: str
|
||||
refreshToken: str = None
|
||||
serverInfo: ServerInfo
|
||||
userInfo: UserInfo
|
||||
id: str = None
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Account(email: {self.userInfo.email}, server: {self.serverInfo.url}, isDefault: {self.isDefault})"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
def get_local_accounts(base_path: str = None) -> List[Account]:
|
||||
"""Gets all the accounts present in this environment
|
||||
|
||||
Arguments:
|
||||
base_path {str} -- custom base path if you are not using the system default
|
||||
|
||||
Returns:
|
||||
List[Account] -- list of all local accounts or an empty list if no accounts were found
|
||||
"""
|
||||
account_storage = SQLiteTransport(scope="Accounts", base_path=base_path)
|
||||
json_path = os.path.join(account_storage._base_path, "Accounts")
|
||||
os.makedirs(json_path, exist_ok=True)
|
||||
json_acct_files = [file for file in os.listdir(json_path) if file.endswith(".json")]
|
||||
|
||||
accounts = []
|
||||
res = account_storage.get_all_objects()
|
||||
if res:
|
||||
accounts.extend(Account.parse_raw(r[1]) for r in res)
|
||||
if json_acct_files:
|
||||
try:
|
||||
accounts.extend(
|
||||
Account.parse_file(os.path.join(json_path, json_file))
|
||||
for json_file in json_acct_files
|
||||
)
|
||||
except Exception as ex:
|
||||
raise SpeckleException(
|
||||
"Invalid json accounts could not be read. Please fix or remove them.",
|
||||
ex,
|
||||
)
|
||||
|
||||
return accounts
|
||||
|
||||
|
||||
def get_default_account(base_path: str = None) -> Account:
|
||||
"""Gets this environment's default account if any. If there is no default, the first found will be returned and set as default.
|
||||
Arguments:
|
||||
base_path {str} -- custom base path if you are not using the system default
|
||||
|
||||
Returns:
|
||||
Account -- the default account or None if no local accounts were found
|
||||
"""
|
||||
accounts = get_local_accounts(base_path=base_path)
|
||||
if not accounts:
|
||||
return None
|
||||
|
||||
default = next((acc for acc in accounts if acc.isDefault), None)
|
||||
if not default:
|
||||
default = accounts[0]
|
||||
default.isDefault = True
|
||||
|
||||
return default
|
||||
|
||||
|
||||
class StreamWrapper:
|
||||
stream_url: str = None
|
||||
use_ssl: bool = True
|
||||
host: str = None
|
||||
stream_id: str = None
|
||||
commit_id: str = None
|
||||
object_id: str = None
|
||||
branch_name: str = None
|
||||
client: SpeckleClient = None
|
||||
|
||||
def __repr__(self):
|
||||
return f"StreamWrapper( server: {self.host}, stream_id: {self.stream_id}, type: {self.type} )"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
@property
|
||||
def type(self) -> str:
|
||||
if self.object_id:
|
||||
return "object"
|
||||
elif self.commit_id:
|
||||
return "commit"
|
||||
elif self.branch_name:
|
||||
return "branch"
|
||||
else:
|
||||
return "stream" if self.stream_id else "invalid"
|
||||
|
||||
def __init__(self, url: str) -> None:
|
||||
self.stream_url = url
|
||||
parsed = urlparse(url)
|
||||
self.host = parsed.netloc
|
||||
self.use_ssl = parsed.scheme == "https"
|
||||
segments = parsed.path.strip("/").split("/")
|
||||
|
||||
if not segments or len(segments) > 4 or len(segments) < 2:
|
||||
raise SpeckleException(
|
||||
f"Cannot parse {url} into a stream wrapper class - invalid URL provided."
|
||||
)
|
||||
|
||||
while segments:
|
||||
segment = segments.pop(0)
|
||||
if segments and segment.lower() == "streams":
|
||||
self.stream_id = segments.pop(0)
|
||||
elif segments and segment.lower() == "commits":
|
||||
self.commit_id = segments.pop(0)
|
||||
elif segments and segment.lower() == "branches":
|
||||
self.branch_name = unquote(segments.pop(0))
|
||||
elif segments and segment.lower() == "objects":
|
||||
self.object_id = segments.pop(0)
|
||||
elif segment.lower() == "globals":
|
||||
self.branch_name = "globals"
|
||||
if segments:
|
||||
self.commit_id = segments.pop(0)
|
||||
else:
|
||||
raise SpeckleException(
|
||||
f"Cannot parse {url} into a stream wrapper class - invalid URL provided."
|
||||
)
|
||||
|
||||
if not self.stream_id:
|
||||
raise SpeckleException(
|
||||
f"Cannot parse {url} into a stream wrapper class - no stream id found."
|
||||
)
|
||||
|
||||
def get_client(self) -> SpeckleClient:
|
||||
if self.client:
|
||||
return self.client
|
||||
|
||||
acct = next(
|
||||
(a for a in get_local_accounts() if self.host in a.serverInfo.url),
|
||||
None,
|
||||
)
|
||||
self.client = SpeckleClient(host=self.host, use_ssl=self.use_ssl)
|
||||
|
||||
if not acct:
|
||||
warn(f"No local account found for server {self.host}", SpeckleWarning)
|
||||
return self.client
|
||||
|
||||
self.client.authenticate(acct.token)
|
||||
return self.client
|
||||
|
||||
def get_transport(self) -> ServerTransport:
|
||||
if not self.client:
|
||||
self.get_client()
|
||||
return ServerTransport(self.client, self.stream_id)
|
||||
@@ -2,8 +2,6 @@
|
||||
# filename: stream_schema.json
|
||||
# timestamp: 2020-11-17T14:33:13+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
@@ -25,7 +23,16 @@ class Commit(BaseModel):
|
||||
authorId: Optional[str]
|
||||
authorAvatar: Optional[str]
|
||||
createdAt: Optional[str]
|
||||
sourceApplication: Optional[str]
|
||||
referencedObject: Optional[str]
|
||||
totalChildrenCount: Optional[int]
|
||||
parents: Optional[List[str]]
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Commit( id: {self.id}, message: {self.message}, referencedObject: {self.referencedObject}, authorName: {self.authorName}, createdAt: {self.createdAt} )"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class Commits(BaseModel):
|
||||
@@ -55,12 +62,6 @@ class Branches(BaseModel):
|
||||
items: List[Branch] = []
|
||||
|
||||
|
||||
class Streams(BaseModel):
|
||||
totalCount: Optional[int]
|
||||
cursor: Optional[datetime]
|
||||
items: List[Stream] = []
|
||||
|
||||
|
||||
class Stream(BaseModel):
|
||||
id: Optional[str]
|
||||
name: Optional[str]
|
||||
@@ -73,6 +74,18 @@ class Stream(BaseModel):
|
||||
commit: Optional[Commit]
|
||||
object: Optional[Object]
|
||||
|
||||
def __repr__(self):
|
||||
return f"Stream( id: {self.id}, name: {self.name}, description: {self.description}, isPublic: {self.isPublic})"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class Streams(BaseModel):
|
||||
totalCount: Optional[int]
|
||||
cursor: Optional[datetime]
|
||||
items: List[Stream] = []
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
id: Optional[str]
|
||||
@@ -84,3 +97,22 @@ class User(BaseModel):
|
||||
verified: Optional[bool]
|
||||
role: Optional[str]
|
||||
streams: Optional[Streams]
|
||||
|
||||
def __repr__(self):
|
||||
return f"User( id: {self.id}, name: {self.name}, email: {self.email}, company: {self.company} )"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class ServerInfo(BaseModel):
|
||||
name: Optional[str]
|
||||
company: Optional[str]
|
||||
url: Optional[str]
|
||||
description: Optional[str]
|
||||
adminContact: Optional[str]
|
||||
canonicalUrl: Optional[str]
|
||||
roles: Optional[List[dict]]
|
||||
scopes: Optional[List[dict]]
|
||||
authStrategies: Optional[List[dict]]
|
||||
version: Optional[str]
|
||||
@@ -0,0 +1,118 @@
|
||||
import json
|
||||
from typing import List
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.transports.sqlite import SQLiteTransport
|
||||
from specklepy.transports.server import ServerTransport
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.transports.abstract_transport import AbstractTransport
|
||||
from specklepy.serialization.base_object_serializer import BaseObjectSerializer
|
||||
|
||||
|
||||
def send(
|
||||
base: Base,
|
||||
transports: List[AbstractTransport] = [],
|
||||
use_default_cache: bool = True,
|
||||
):
|
||||
"""Sends an object via the provided transports. Defaults to the local cache.
|
||||
|
||||
Arguments:
|
||||
obj {Base} -- the object you want to send
|
||||
transports {list} -- where you want to send them
|
||||
use_default_cache {bool} -- toggle for the default cache. If set to false, it will only send to the provided transports
|
||||
|
||||
Returns:
|
||||
str -- the object id of the sent object
|
||||
"""
|
||||
if not transports and not use_default_cache:
|
||||
raise SpeckleException(
|
||||
message="You need to provide at least one transport: cannot send with an empty transport list and no default cache"
|
||||
)
|
||||
if use_default_cache:
|
||||
transports.insert(0, SQLiteTransport())
|
||||
|
||||
serializer = BaseObjectSerializer(write_transports=transports)
|
||||
|
||||
for t in transports:
|
||||
t.begin_write()
|
||||
hash, _ = serializer.write_json(base=base)
|
||||
|
||||
for t in transports:
|
||||
t.end_write()
|
||||
|
||||
return hash
|
||||
|
||||
|
||||
def receive(
|
||||
obj_id: str,
|
||||
remote_transport: AbstractTransport = None,
|
||||
local_transport: AbstractTransport = None,
|
||||
) -> Base:
|
||||
"""Receives an object from a transport.
|
||||
|
||||
Arguments:
|
||||
obj_id {str} -- the id of the object to receive
|
||||
remote_transport {Transport} -- the transport to receive from
|
||||
local_transport {Transport} -- the local cache to check for existing objects
|
||||
(defaults to `SQLiteTransport`)
|
||||
|
||||
Returns:
|
||||
Base -- the base object
|
||||
"""
|
||||
|
||||
if not local_transport:
|
||||
local_transport = SQLiteTransport()
|
||||
|
||||
serializer = BaseObjectSerializer(read_transport=local_transport)
|
||||
|
||||
# try local transport first. if the parent is there, we assume all the children are there and continue wth deserialisation using the local transport
|
||||
obj_string = local_transport.get_object(obj_id)
|
||||
if obj_string:
|
||||
return serializer.read_json(obj_string=obj_string)
|
||||
|
||||
if not remote_transport:
|
||||
raise SpeckleException(
|
||||
message="Could not find the specified object using the local transport, and you didn't provide a fallback remote from which to pull it."
|
||||
)
|
||||
|
||||
obj_string = remote_transport.copy_object_and_children(
|
||||
id=obj_id, target_transport=local_transport
|
||||
)
|
||||
|
||||
return serializer.read_json(obj_string=obj_string)
|
||||
|
||||
|
||||
def serialize(base: Base, write_transports: List[AbstractTransport] = []) -> str:
|
||||
"""
|
||||
Serialize a base object. If no write transports are provided, the object will be serialized
|
||||
without detaching or chunking any of the attributes.
|
||||
|
||||
Arguments:
|
||||
base {Base} -- the object to serialize
|
||||
write_transports {List[AbstractTransport]} -- optional: the transports to write to
|
||||
|
||||
Returns:
|
||||
str -- the serialized object
|
||||
"""
|
||||
serializer = BaseObjectSerializer(write_transports=write_transports)
|
||||
|
||||
return serializer.write_json(base)[1]
|
||||
|
||||
|
||||
def deserialize(obj_string: str, read_transport: AbstractTransport = None) -> Base:
|
||||
"""
|
||||
Deserialize a string object into a Base object. If the object contains referenced child objects that are not stored in the local db, a read transport needs to be provided in order to recompose the base with the children objects.
|
||||
|
||||
Arguments:
|
||||
obj_string {str} -- the string object to deserialize
|
||||
read_transport {AbstractTransport} -- the transport to fetch children objects from
|
||||
(defaults to SQLiteTransport)
|
||||
|
||||
Returns:
|
||||
Base -- the deserialized object
|
||||
"""
|
||||
if not read_transport:
|
||||
read_transport = SQLiteTransport()
|
||||
|
||||
serializer = BaseObjectSerializer(read_transport=read_transport)
|
||||
|
||||
return serializer.read_json(obj_string=obj_string)
|
||||
@@ -1,5 +1,5 @@
|
||||
from logging import error
|
||||
from speckle.logging.exceptions import GraphQLException, SpeckleException
|
||||
from specklepy.logging.exceptions import GraphQLException, SpeckleException
|
||||
from typing import Dict, List
|
||||
from gql.client import Client
|
||||
from gql.gql import gql
|
||||
@@ -5,9 +5,9 @@ import pkgutil
|
||||
from importlib import import_module
|
||||
|
||||
|
||||
for (_, name, _) in pkgutil.iter_modules([Path(__file__).parent]):
|
||||
for (_, name, _) in pkgutil.iter_modules(__path__):
|
||||
|
||||
imported_module = import_module("." + name, package=__name__)
|
||||
|
||||
if hasattr(imported_module, "Resource"):
|
||||
setattr(sys.modules[__name__], name, imported_module)
|
||||
setattr(sys.modules[__name__], name, imported_module)
|
||||
@@ -0,0 +1,212 @@
|
||||
from specklepy.api.resources import stream
|
||||
from typing import List, Optional
|
||||
from gql import gql
|
||||
from pydantic.main import BaseModel
|
||||
from specklepy.api.resource import ResourceBase
|
||||
from specklepy.api.models import Branch
|
||||
|
||||
NAME = "branch"
|
||||
METHODS = ["create"]
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for branches"""
|
||||
|
||||
def __init__(self, me, basepath, client) -> None:
|
||||
super().__init__(
|
||||
me=me, basepath=basepath, client=client, name=NAME, methods=METHODS
|
||||
)
|
||||
self.schema = Branch
|
||||
|
||||
def create(
|
||||
self, stream_id: str, name: str, description: str = "No description provided"
|
||||
) -> str:
|
||||
"""Create a new branch on this stream
|
||||
|
||||
Arguments:
|
||||
name {str} -- the name of the new branch
|
||||
description {str} -- a short description of the branch
|
||||
|
||||
Returns:
|
||||
id {str} -- the newly created branch's id
|
||||
"""
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
mutation BranchCreate($branch: BranchCreateInput!) {
|
||||
branchCreate(branch: $branch)
|
||||
}
|
||||
"""
|
||||
)
|
||||
params = {
|
||||
"branch": {
|
||||
"streamId": stream_id,
|
||||
"name": name,
|
||||
"description": description,
|
||||
}
|
||||
}
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type="branchCreate", parse_response=False
|
||||
)
|
||||
|
||||
def get(self, stream_id: str, name: str, commits_limit: int = 10):
|
||||
"""Get a branch by name from a stream
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream to get the branch from
|
||||
name {str} -- the name of the branch to get
|
||||
commits_limit {int} -- maximum number of commits to get
|
||||
|
||||
Returns:
|
||||
Branch -- the fetched branch with its latest commits
|
||||
"""
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
query BranchGet($stream_id: String!, $name: String!, $commits_limit: Int!) {
|
||||
stream(id: $stream_id) {
|
||||
branch(name: $name) {
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
commits (limit: $commits_limit) {
|
||||
totalCount,
|
||||
cursor,
|
||||
items {
|
||||
id,
|
||||
referencedObject,
|
||||
sourceApplication,
|
||||
totalChildrenCount,
|
||||
message,
|
||||
authorName,
|
||||
authorId,
|
||||
branchName,
|
||||
parents,
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
params = {"stream_id": stream_id, "name": name, "commits_limit": commits_limit}
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type=["stream", "branch"]
|
||||
)
|
||||
|
||||
def list(self, stream_id: str, branches_limit: int = 10, commits_limit: int = 10):
|
||||
"""Get a list of branches from a given stream
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream to get the branches from
|
||||
branches_limit {int} -- maximum number of branches to get
|
||||
commits_limit {int} -- maximum number of commits to get
|
||||
|
||||
Returns:
|
||||
List[Branch] -- the branches on the stream
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
query BranchesGet($stream_id: String!, $branches_limit: Int!, $commits_limit: Int!) {
|
||||
stream(id: $stream_id) {
|
||||
branches(limit: $branches_limit) {
|
||||
items {
|
||||
id
|
||||
name
|
||||
description
|
||||
commits(limit: $commits_limit) {
|
||||
totalCount
|
||||
items{
|
||||
id
|
||||
message
|
||||
referencedObject
|
||||
sourceApplication
|
||||
parents
|
||||
authorId
|
||||
authorName
|
||||
branchName
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
params = {
|
||||
"stream_id": stream_id,
|
||||
"branches_limit": branches_limit,
|
||||
"commits_limit": commits_limit,
|
||||
}
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type=["stream", "branches", "items"]
|
||||
)
|
||||
|
||||
def update(
|
||||
self, stream_id: str, branch_id: str, name: str = None, description: str = None
|
||||
):
|
||||
"""Update a branch
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream containing the branch to update
|
||||
branch_id {str} -- the id of the branch to update
|
||||
name {str} -- optional: the updated branch name
|
||||
description {str} -- optional: the updated branch description
|
||||
|
||||
Returns:
|
||||
bool -- True if update is successfull
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
mutation BranchUpdate($branch: BranchUpdateInput!) {
|
||||
branchUpdate(branch: $branch)
|
||||
}
|
||||
"""
|
||||
)
|
||||
params = {
|
||||
"branch": {
|
||||
"streamId": stream_id,
|
||||
"id": branch_id,
|
||||
}
|
||||
}
|
||||
|
||||
if name:
|
||||
params["branch"]["name"] = name
|
||||
if description:
|
||||
params["branch"]["description"] = description
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type="branchUpdate", parse_response=False
|
||||
)
|
||||
|
||||
def delete(self, stream_id: str, branch_id: str):
|
||||
"""Delete a branch
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream containing the branch to delete
|
||||
branch_id {str} -- the branch to delete
|
||||
|
||||
Returns:
|
||||
bool -- True if deletion is successful
|
||||
"""
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
mutation BranchDelete($branch: BranchDeleteInput!) {
|
||||
branchDelete(branch: $branch)
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
params = {"branch": {"streamId": stream_id, "id": branch_id}}
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type="branchDelete", parse_response=False
|
||||
)
|
||||
@@ -0,0 +1,187 @@
|
||||
from typing import Optional, List
|
||||
from gql import gql
|
||||
from pydantic.main import BaseModel
|
||||
from specklepy.api.resource import ResourceBase
|
||||
from specklepy.api.models import Commit
|
||||
|
||||
|
||||
NAME = "commit"
|
||||
METHODS = []
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for commits"""
|
||||
|
||||
def __init__(self, me, basepath, client) -> None:
|
||||
super().__init__(
|
||||
me=me, basepath=basepath, client=client, name=NAME, methods=METHODS
|
||||
)
|
||||
self.schema = Commit
|
||||
|
||||
def get(self, stream_id: str, commit_id: str) -> Commit:
|
||||
"""
|
||||
Gets a commit given a stream and the commit id
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the stream where we can find the commit
|
||||
commit_id {str} -- the id of the commit you want to get
|
||||
|
||||
Returns:
|
||||
Commit -- the retrieved commit object
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
query Commit($stream_id: String!, $commit_id: String!) {
|
||||
stream(id: $stream_id) {
|
||||
commit(id: $commit_id) {
|
||||
id
|
||||
referencedObject
|
||||
message
|
||||
authorId
|
||||
authorName
|
||||
authorAvatar
|
||||
createdAt
|
||||
sourceApplication
|
||||
totalChildrenCount
|
||||
parents
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
params = {"stream_id": stream_id, "commit_id": commit_id}
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type=["stream", "commit"]
|
||||
)
|
||||
|
||||
def list(self, stream_id: str, limit: int = 10) -> List[Commit]:
|
||||
"""
|
||||
Get a list of commits on a given stream
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the stream where the commits are
|
||||
limit {int} -- the maximum number of commits to fetch (default = 10)
|
||||
|
||||
Returns:
|
||||
List[Commit] -- a list of the most recent commit objects
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
query Commits($stream_id: String!, $limit: Int!) {
|
||||
stream(id: $stream_id) {
|
||||
commits(limit: $limit) {
|
||||
items {
|
||||
id
|
||||
message
|
||||
referencedObject
|
||||
authorName
|
||||
authorId
|
||||
authorName
|
||||
authorAvatar
|
||||
createdAt
|
||||
sourceApplication
|
||||
totalChildrenCount
|
||||
parents
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
params = {"stream_id": stream_id, "limit": limit}
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type=["stream", "commits", "items"]
|
||||
)
|
||||
|
||||
def create(
|
||||
self,
|
||||
stream_id: str,
|
||||
object_id: str,
|
||||
branch_name: str = "main",
|
||||
message: str = "",
|
||||
source_application: str = "python",
|
||||
parents: List[str] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Creates a commit on a branch
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the stream you want to commit to
|
||||
object_id {str} -- the hash of your commit object
|
||||
branch_name {str} -- the name of the branch to commit to (defaults to "main")
|
||||
message {str} -- optional: a message to give more information about the commit
|
||||
source_application{str} -- optional: the application from which the commit was created (defaults to "python")
|
||||
parents {List[str]} -- optional: the id of the parent commits
|
||||
|
||||
Returns:
|
||||
str -- the id of the created commit
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
mutation CommitCreate ($commit: CommitCreateInput!){ commitCreate(commit: $commit)}
|
||||
"""
|
||||
)
|
||||
params = {
|
||||
"commit": {
|
||||
"streamId": stream_id,
|
||||
"branchName": branch_name,
|
||||
"objectId": object_id,
|
||||
"message": message,
|
||||
"sourceApplication": source_application,
|
||||
}
|
||||
}
|
||||
if parents:
|
||||
params["commit"]["parents"] = parents
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type="commitCreate", parse_response=False
|
||||
)
|
||||
|
||||
def update(self, stream_id: str, commit_id: str, message: str) -> bool:
|
||||
"""
|
||||
Update a commit
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream that contains the commit you'd like to update
|
||||
commit_id {str} -- the id of the commit you'd like to update
|
||||
message {str} -- the updated commit message
|
||||
|
||||
Returns:
|
||||
bool -- True if the operation succeeded
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
mutation CommitUpdate($commit: CommitUpdateInput!){ commitUpdate(commit: $commit)}
|
||||
"""
|
||||
)
|
||||
params = {
|
||||
"commit": {"streamId": stream_id, "id": commit_id, "message": message}
|
||||
}
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type="commitUpdate", parse_response=False
|
||||
)
|
||||
|
||||
def delete(self, stream_id: str, commit_id: str) -> bool:
|
||||
"""
|
||||
Delete a commit
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream that contains the commit you'd like to delete
|
||||
commit_id {str} -- the id of the commit you'd like to delete
|
||||
|
||||
Returns:
|
||||
bool -- True if the operation succeeded
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
mutation CommitDelete($commit: CommitDeleteInput!){ commitDelete(commit: $commit)}
|
||||
"""
|
||||
)
|
||||
params = {"commit": {"streamId": stream_id, "id": commit_id}}
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type="commitDelete", parse_response=False
|
||||
)
|
||||
@@ -0,0 +1,80 @@
|
||||
from typing import Dict, List
|
||||
from gql import gql
|
||||
from graphql.language import parser
|
||||
from specklepy.api.resource import ResourceBase
|
||||
from specklepy.objects.base import Base
|
||||
|
||||
NAME = "object"
|
||||
METHODS = []
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for objects"""
|
||||
|
||||
def __init__(self, me, basepath, client) -> None:
|
||||
super().__init__(
|
||||
me=me, basepath=basepath, client=client, name=NAME, methods=METHODS
|
||||
)
|
||||
self.schema = Base
|
||||
|
||||
def get(self, stream_id: str, object_id: str) -> Base:
|
||||
"""
|
||||
Get a stream object
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream for the object
|
||||
object_id {str} -- the hash of the object you want to get
|
||||
|
||||
Returns:
|
||||
Base -- the returned Base object
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
query Object($stream_id: String!, $object_id: String!) {
|
||||
stream(id: $stream_id) {
|
||||
id
|
||||
name
|
||||
object(id: $object_id) {
|
||||
id
|
||||
speckleType
|
||||
applicationId
|
||||
createdAt
|
||||
totalChildrenCount
|
||||
data
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
params = {"stream_id": stream_id, "object_id": object_id}
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type=["stream", "object", "data"]
|
||||
)
|
||||
|
||||
def create(self, stream_id: str, objects: List[Dict]) -> str:
|
||||
"""
|
||||
Not advised - generally, you want to use `operations.send()`.
|
||||
|
||||
Create a new object on a stream. To send a base object, you can prepare it by running it through the
|
||||
`BaseObjectSerializer.traverse_base()` function to get a valid (serialisable) object to send.
|
||||
|
||||
NOTE: this does not create a commit - you can create one with `SpeckleClient.commit.create`. Dynamic fields will be located in the 'data' dict of the received `Base` object
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream you want to send the object to
|
||||
objects {List[Dict]} -- a list of base dictionary objects (NOTE: must be json serialisable)
|
||||
|
||||
Returns:
|
||||
str -- the id of the object
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
mutation ObjectCreate($object_input: ObjectCreateInput!) { objectCreate(objectInput: $object_input) }
|
||||
"""
|
||||
)
|
||||
params = {"object_input": {"streamId": stream_id, "objects": objects}}
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type="objectCreate", parse_response=False
|
||||
)
|
||||
@@ -0,0 +1,137 @@
|
||||
from typing import Dict, List
|
||||
from gql import gql
|
||||
from gql.client import Client
|
||||
from specklepy.api.models import ServerInfo
|
||||
from specklepy.api.resource import ResourceBase
|
||||
|
||||
|
||||
NAME = "server"
|
||||
METHODS = ["get", "apps"]
|
||||
|
||||
|
||||
class Resource(ResourceBase):
|
||||
"""API Access class for the server"""
|
||||
|
||||
def __init__(self, me, basepath, client) -> None:
|
||||
super().__init__(
|
||||
me=me, basepath=basepath, client=client, name=NAME, methods=METHODS
|
||||
)
|
||||
|
||||
def get(self) -> ServerInfo:
|
||||
"""Get the server info
|
||||
|
||||
Returns:
|
||||
dict -- the server info in dictionary form
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
query Server {
|
||||
serverInfo {
|
||||
name
|
||||
company
|
||||
description
|
||||
adminContact
|
||||
canonicalUrl
|
||||
version
|
||||
roles {
|
||||
name
|
||||
description
|
||||
resourceTarget
|
||||
}
|
||||
scopes {
|
||||
name
|
||||
description
|
||||
}
|
||||
authStrategies{
|
||||
id
|
||||
name
|
||||
icon
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
return self.make_request(
|
||||
query=query, return_type="serverInfo", schema=ServerInfo
|
||||
)
|
||||
|
||||
def apps(self) -> Dict:
|
||||
"""Get the apps registered on the server
|
||||
|
||||
Returns:
|
||||
dict -- a dictionary of apps registered on the server
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
query Apps {
|
||||
apps{
|
||||
id
|
||||
name
|
||||
description
|
||||
termsAndConditionsLink
|
||||
trustByDefault
|
||||
logo
|
||||
author {
|
||||
id
|
||||
name
|
||||
avatar
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
return self.make_request(query=query, return_type="apps", parse_response=False)
|
||||
|
||||
def create_token(self, name: str, scopes: List[str], lifespan: int) -> str:
|
||||
"""Create a personal API token
|
||||
|
||||
Arguments:
|
||||
scopes {List[str]} -- the scopes to grant with this token
|
||||
name {str} -- a name for your new token
|
||||
lifespan {int} -- duration before the token expires
|
||||
|
||||
Returns:
|
||||
str -- the new API token. note: this is the only time you'll see the token!
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
mutation TokenCreate($token: ApiTokenCreateInput!) {
|
||||
apiTokenCreate(token: $token)
|
||||
}
|
||||
"""
|
||||
)
|
||||
params = {"token": {"scopes": scopes, "name": name, "lifespan": lifespan}}
|
||||
|
||||
return self.make_request(
|
||||
query=query,
|
||||
params=params,
|
||||
return_type="apiTokenCreate",
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
def revoke_token(self, token: str) -> bool:
|
||||
"""Revokes (deletes) a personal API token
|
||||
|
||||
Arguments:
|
||||
token {str} -- the token to revoke (delete)
|
||||
|
||||
Returns:
|
||||
bool -- True if the token was successfully deleted
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
mutation TokenRevoke($token: String!) {
|
||||
apiTokenRevoke(token: $token)
|
||||
}
|
||||
"""
|
||||
)
|
||||
params = {"token": token}
|
||||
|
||||
return self.make_request(
|
||||
query=query,
|
||||
params=params,
|
||||
return_type="apiTokenRevoke",
|
||||
parse_response=False,
|
||||
)
|
||||
@@ -1,10 +1,7 @@
|
||||
from re import search
|
||||
from typing import Dict, List, Optional
|
||||
from pydantic import BaseModel
|
||||
from gql import gql
|
||||
from speckle.api.resource import ResourceBase
|
||||
from speckle.api.models import Stream
|
||||
from speckle.logging.exceptions import GraphQLException
|
||||
from specklepy.api.resource import ResourceBase
|
||||
from specklepy.api.models import Stream
|
||||
|
||||
NAME = "stream"
|
||||
METHODS = [
|
||||
@@ -296,3 +293,64 @@ class Resource(ResourceBase):
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type=["streams", "items"]
|
||||
)
|
||||
|
||||
def grant_permission(self, stream_id: str, user_id: str, role: str):
|
||||
"""Grant permissions to a user on a given stream
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream to grant permissions to
|
||||
user_id {str} -- the id of the user to grant permissions for
|
||||
role {str} -- the role to grant the user
|
||||
|
||||
Returns:
|
||||
bool -- True if the operation was successful
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
mutation StreamGrantPermission($permission_params: StreamGrantPermissionInput !) {
|
||||
streamGrantPermission(permissionParams: $permission_params)
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
params = {
|
||||
"permission_params": {
|
||||
"streamId": stream_id,
|
||||
"userId": user_id,
|
||||
"role": role,
|
||||
}
|
||||
}
|
||||
|
||||
return self.make_request(
|
||||
query=query,
|
||||
params=params,
|
||||
return_type="streamGrantPermission",
|
||||
parse_response=False,
|
||||
)
|
||||
|
||||
def revoke_permission(self, stream_id: str, user_id: str):
|
||||
"""Revoke permissions from a user on a given stream
|
||||
|
||||
Arguments:
|
||||
stream_id {str} -- the id of the stream to revoke permissions from
|
||||
user_id {str} -- the id of the user to revoke permissions from
|
||||
|
||||
Returns:
|
||||
bool -- True if the operation was successful
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
mutation StreamRevokePermission($permission_params: StreamRevokePermissionInput !) {
|
||||
streamRevokePermission(permissionParams: $permission_params)
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
params = {"permission_params": {"streamId": stream_id, "userId": user_id}}
|
||||
|
||||
return self.make_request(
|
||||
query=query,
|
||||
params=params,
|
||||
return_type="streamRevokePermission",
|
||||
parse_response=False,
|
||||
)
|
||||
@@ -1,9 +1,9 @@
|
||||
from typing import Callable, Dict, List, Optional, Any
|
||||
from functools import wraps
|
||||
from gql import gql
|
||||
from speckle.api.resource import ResourceBase
|
||||
from speckle.api.resources.stream import Stream
|
||||
from speckle.logging.exceptions import GraphQLException, SpeckleException
|
||||
from specklepy.api.resource import ResourceBase
|
||||
from specklepy.api.resources.stream import Stream
|
||||
from specklepy.logging.exceptions import GraphQLException, SpeckleException
|
||||
|
||||
NAME = "subscribe"
|
||||
METHODS = [
|
||||
@@ -122,4 +122,4 @@ class Resource(ResourceBase):
|
||||
if callback is not None:
|
||||
callback(res)
|
||||
else:
|
||||
return res
|
||||
return res
|
||||
@@ -1,9 +1,9 @@
|
||||
from speckle.logging.exceptions import SpeckleException
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from typing import List, Optional
|
||||
from gql import gql
|
||||
from pydantic.main import BaseModel
|
||||
from speckle.api.resource import ResourceBase
|
||||
from speckle.api.models import User
|
||||
from specklepy.api.resource import ResourceBase
|
||||
from specklepy.api.models import User
|
||||
|
||||
NAME = "user"
|
||||
METHODS = ["get"]
|
||||
@@ -30,17 +30,17 @@ class Resource(ResourceBase):
|
||||
query = gql(
|
||||
"""
|
||||
query User($id: String) {
|
||||
user(id: $id) {
|
||||
id
|
||||
email
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
profiles
|
||||
role
|
||||
}
|
||||
user(id: $id) {
|
||||
id
|
||||
email
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
profiles
|
||||
role
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
@@ -66,16 +66,16 @@ class Resource(ResourceBase):
|
||||
query = gql(
|
||||
"""
|
||||
query UserSearch($search_query: String!, $limit: Int!) {
|
||||
userSearch(query: $search_query, limit: $limit) {
|
||||
items {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
userSearch(query: $search_query, limit: $limit) {
|
||||
items {
|
||||
id
|
||||
name
|
||||
bio
|
||||
company
|
||||
avatar
|
||||
verified
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
@@ -84,3 +84,37 @@ class Resource(ResourceBase):
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type=["userSearch", "items"]
|
||||
)
|
||||
|
||||
def update(
|
||||
self, name: str = None, company: str = None, bio: str = None, avatar: str = None
|
||||
):
|
||||
"""Updates your user profile. All arguments are optional.
|
||||
|
||||
Arguments:
|
||||
name {str} -- your name
|
||||
company {str} -- the company you may or may not work for
|
||||
bio {str} -- tell us about yourself
|
||||
avatar {str} -- a nice photo of yourself
|
||||
|
||||
Returns:
|
||||
bool -- True if your profile was updated successfully
|
||||
"""
|
||||
query = gql(
|
||||
"""
|
||||
mutation UserUpdate($user: UserUpdateInput!) {
|
||||
userUpdate(user: $user)
|
||||
}
|
||||
"""
|
||||
)
|
||||
params = {"name": name, "company": company, "bio": bio, "avatar": avatar}
|
||||
|
||||
params = {"user": {k: v for k, v in params.items() if v is not None}}
|
||||
|
||||
if not params["user"]:
|
||||
return SpeckleException(
|
||||
message="You must provide at least one field to update your user profile"
|
||||
)
|
||||
|
||||
return self.make_request(
|
||||
query=query, params=params, return_type="userUpdate", parse_response=False
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
from typing import Any, List
|
||||
|
||||
|
||||
class SpeckleException(Exception):
|
||||
def __init__(self, message: str, exception: Exception = None) -> None:
|
||||
self.message = message
|
||||
self.exception = exception
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"SpeckleException: {self.message}"
|
||||
|
||||
|
||||
class SerializationException(SpeckleException):
|
||||
def __init__(self, message: str, object: Any, exception: Exception = None) -> None:
|
||||
super().__init__(message=message)
|
||||
self.object = object
|
||||
self.unhandled_type = type(object)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"SpeckleException: Could not serialize object of type {self.unhandled_type}"
|
||||
|
||||
|
||||
class GraphQLException(SpeckleException):
|
||||
def __init__(self, message: str, errors: List, data=None) -> None:
|
||||
super().__init__(message=message)
|
||||
self.errors = errors
|
||||
self.data = data
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"GraphQLException: {self.message}"
|
||||
|
||||
|
||||
class SpeckleWarning(Warning):
|
||||
def __init__(self, *args: object) -> None:
|
||||
super().__init__(*args)
|
||||
@@ -0,0 +1,5 @@
|
||||
"""Builtin Speckle object kit."""
|
||||
|
||||
from specklepy.objects.base import Base
|
||||
|
||||
__all__ = ["Base"]
|
||||
@@ -0,0 +1,253 @@
|
||||
from inspect import getattr_static
|
||||
from pydantic import BaseModel, validator
|
||||
from pydantic.main import Extra
|
||||
from typing import ClassVar, Dict, List, Optional, Any, Set, Type
|
||||
from specklepy.transports.memory import MemoryTransport
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.objects.units import get_units_from_string
|
||||
|
||||
PRIMITIVES = (int, float, str, bool)
|
||||
|
||||
|
||||
class _RegisteringBase(BaseModel):
|
||||
"""
|
||||
Private Base model for Speckle types.
|
||||
|
||||
This is an implementation detail, please do not use this outside this module.
|
||||
|
||||
This class provides automatic registration of `speckle_type` into a global,
|
||||
(class level) registry for each subclassing type.
|
||||
The type registry is a base for accurate type based (de)serialization.
|
||||
"""
|
||||
|
||||
speckle_type: ClassVar[str]
|
||||
_type_registry: ClassVar[Dict[str, Type["Base"]]] = {}
|
||||
|
||||
class Config:
|
||||
validate_assignment = True
|
||||
|
||||
@classmethod
|
||||
def get_registered_type(cls, speckle_type: str) -> Optional[Type["Base"]]:
|
||||
"""Get the registered type from the protected mapping via the `speckle_type`"""
|
||||
return cls._type_registry.get(speckle_type, None)
|
||||
|
||||
def __init_subclass__(
|
||||
cls,
|
||||
speckle_type: Optional[str] = None,
|
||||
**kwargs: Dict[str, Any],
|
||||
):
|
||||
"""
|
||||
Hook into subclass type creation.
|
||||
|
||||
This is provides a mechanism to hook into the event of the subclass type object
|
||||
initialization. This is reused to register each subclassing type into a class
|
||||
level dictionary.
|
||||
"""
|
||||
if speckle_type in cls._type_registry:
|
||||
raise ValueError(
|
||||
f"The speckle_type: {speckle_type} is already registered for type: "
|
||||
f"{cls._type_registry[speckle_type].__name__}. "
|
||||
f"Please choose a different type name."
|
||||
)
|
||||
cls.speckle_type = speckle_type or cls.__name__
|
||||
cls._type_registry[cls.speckle_type] = cls # type: ignore
|
||||
super().__init_subclass__(**kwargs)
|
||||
|
||||
|
||||
class Base(_RegisteringBase):
|
||||
id: Optional[str] = None
|
||||
totalChildrenCount: Optional[int] = None
|
||||
applicationId: Optional[str] = None
|
||||
_units: str = "m"
|
||||
_chunkable: Dict[str, int] = {} # dict of chunkable props and their max chunk size
|
||||
_chunk_size_default: int = 1000
|
||||
_detachable: Set[str] = set() # list of defined detachable props
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"{self.__class__.__name__}(id: {self.id}, "
|
||||
f"speckle_type: {self.speckle_type}, "
|
||||
f"totalChildrenCount: {self.totalChildrenCount})"
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
def __setitem__(self, name: str, value: Any) -> None:
|
||||
self.validate_prop_name(name)
|
||||
self.__dict__[name] = value
|
||||
|
||||
def __getitem__(self, name: str) -> Any:
|
||||
return self.__dict__[name]
|
||||
|
||||
def __setattr__(self, name: str, value: Any) -> None:
|
||||
"""
|
||||
Guard attribute and property set mechanism.
|
||||
|
||||
The `speckle_type` is a protected class attribute it must not be overridden.
|
||||
"""
|
||||
if name != "speckle_type":
|
||||
attr = getattr(self.__class__, name, None)
|
||||
if isinstance(attr, property):
|
||||
try:
|
||||
attr.__set__(self, value)
|
||||
except AttributeError:
|
||||
pass # the prop probably doesn't have a setter
|
||||
super().__setattr__(name, value)
|
||||
|
||||
@classmethod
|
||||
def validate_prop_name(cls, name: str) -> None:
|
||||
"""Validator for dynamic attribute names."""
|
||||
if name in {"", "@"}:
|
||||
raise ValueError("Invalid Name: Base member names cannot be empty strings")
|
||||
if name.startswith("@@"):
|
||||
raise ValueError(
|
||||
"Invalid Name: Base member names cannot start with more than one '@'",
|
||||
)
|
||||
if "." in name or "/" in name:
|
||||
raise ValueError(
|
||||
"Invalid Name: Base member names cannot contain characters '.' or '/'",
|
||||
)
|
||||
|
||||
def add_chunkable_attrs(self, **kwargs: int) -> None:
|
||||
"""
|
||||
Mark defined attributes as chunkable for serialisation
|
||||
|
||||
Arguments:
|
||||
kwargs {int} -- the name of the attribute as the keyword and the chunk size as the arg
|
||||
"""
|
||||
chunkable = {k: v for k, v in kwargs.items() if isinstance(v, int)}
|
||||
self._chunkable = dict(self._chunkable, **chunkable)
|
||||
|
||||
def add_detachable_attrs(self, names: Set[str]) -> None:
|
||||
"""
|
||||
Mark defined attributes as detachable for serialisation
|
||||
|
||||
Arguments:
|
||||
names {Set[str]} -- the names of the attributes to detach as a set of strings
|
||||
"""
|
||||
self._detachable = self._detachable.union(names)
|
||||
|
||||
@property
|
||||
def units(self):
|
||||
return self._units
|
||||
|
||||
@units.setter
|
||||
def units(self, value: str):
|
||||
self._units = get_units_from_string(value)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Convenience method to view the whole base object as a dict"""
|
||||
base_dict = self.__dict__
|
||||
for key, value in base_dict.items():
|
||||
if not value or isinstance(value, PRIMITIVES):
|
||||
continue
|
||||
else:
|
||||
base_dict[key] = self.__dict_helper(value)
|
||||
return base_dict
|
||||
|
||||
def __dict_helper(self, obj: Any) -> Any:
|
||||
if not obj or isinstance(obj, PRIMITIVES):
|
||||
return obj
|
||||
if isinstance(obj, Base):
|
||||
return self.__dict_helper(obj.__dict__)
|
||||
if isinstance(obj, (list, set)):
|
||||
return [self.__dict_helper(v) for v in obj]
|
||||
if not isinstance(obj, dict):
|
||||
raise SpeckleException(
|
||||
message=f"Could not convert to dict due to unrecognized type: {type(obj)}"
|
||||
)
|
||||
|
||||
for k, v in obj.items():
|
||||
if v and not isinstance(obj, PRIMITIVES):
|
||||
obj[k] = self.__dict_helper(v)
|
||||
return obj
|
||||
|
||||
def get_member_names(self) -> List[str]:
|
||||
"""Get all of the property names on this object, dynamic or not"""
|
||||
attrs = list(self.__dict__.keys())
|
||||
properties = [
|
||||
name
|
||||
for name in dir(self)
|
||||
if not name.startswith("_")
|
||||
and name
|
||||
!= "fields" # soon to be removed as this pydantic prop is depreciated
|
||||
and isinstance(getattr(type(self), name, None), property)
|
||||
]
|
||||
return attrs + properties
|
||||
|
||||
def get_typed_member_names(self) -> List[str]:
|
||||
"""Get all of the names of the defined (typed) properties of this object"""
|
||||
return list(self.__fields__.keys())
|
||||
|
||||
def get_dynamic_member_names(self) -> List[str]:
|
||||
"""Get all of the names of the dynamic properties of this object"""
|
||||
return list(set(self.__dict__.keys()) - set(self.__fields__.keys()))
|
||||
|
||||
def get_children_count(self) -> int:
|
||||
"""Get the total count of children Base objects"""
|
||||
parsed = []
|
||||
return 1 + self._count_descendants(self, parsed)
|
||||
|
||||
def get_id(self, decompose: bool = False) -> str:
|
||||
"""
|
||||
Gets the id (a unique hash) of this object. ⚠️ This method fully serializes the object, which in the case of large objects (with many sub-objects), has a tangible cost. Avoid using it!
|
||||
|
||||
Note: the hash of a decomposed object differs from that of a non-decomposed object
|
||||
|
||||
Arguments:
|
||||
decompose {bool} -- if True, will decompose the object in the process of hashing it
|
||||
|
||||
Returns:
|
||||
str -- the hash (id) of the fully serialized object
|
||||
"""
|
||||
from specklepy.serialization.base_object_serializer import (
|
||||
BaseObjectSerializer,
|
||||
)
|
||||
|
||||
serializer = BaseObjectSerializer()
|
||||
if decompose:
|
||||
serializer.write_transports = [MemoryTransport()]
|
||||
return serializer.traverse_base(self)[0]
|
||||
|
||||
def _count_descendants(self, base: "Base", parsed: List) -> int:
|
||||
if base in parsed:
|
||||
return 0
|
||||
parsed.append(base)
|
||||
|
||||
return sum(
|
||||
self._handle_object_count(value, parsed)
|
||||
for name, value in base.__dict__.items()
|
||||
if not name.startswith("@")
|
||||
)
|
||||
|
||||
def _handle_object_count(self, obj: Any, parsed: List) -> int:
|
||||
count = 0
|
||||
if obj is None:
|
||||
return count
|
||||
if isinstance(obj, "Base"):
|
||||
count += 1
|
||||
count += self._count_descendants(obj, parsed)
|
||||
return count
|
||||
elif isinstance(obj, list):
|
||||
for item in obj:
|
||||
if isinstance(item, "Base"):
|
||||
count += 1
|
||||
count += self._count_descendants(item, parsed)
|
||||
else:
|
||||
count += self._handle_object_count(item, parsed)
|
||||
elif isinstance(obj, dict):
|
||||
for _, value in obj.items():
|
||||
if isinstance(value, "Base"):
|
||||
count += 1
|
||||
count += self._count_descendants(value, parsed)
|
||||
else:
|
||||
count += self._handle_object_count(value, parsed)
|
||||
return count
|
||||
|
||||
class Config:
|
||||
extra = Extra.allow
|
||||
|
||||
|
||||
class DataChunk(Base, speckle_type="Speckle.Core.Models.DataChunk"):
|
||||
data: List[Any] = []
|
||||
@@ -0,0 +1,38 @@
|
||||
from specklepy.objects.geometry import Point
|
||||
from typing import List
|
||||
|
||||
from .base import Base
|
||||
|
||||
CHUNKABLE_PROPS = {
|
||||
"vertices": 100,
|
||||
"faces": 100,
|
||||
"colors": 100,
|
||||
"textureCoordinates": 100,
|
||||
"test_bases": 10,
|
||||
}
|
||||
|
||||
DETACHABLE = {"detach_this", "origin", "detached_list"}
|
||||
|
||||
|
||||
class FakeMesh(Base):
|
||||
vertices: List[float] = None
|
||||
faces: List[int] = None
|
||||
colors: List[int] = None
|
||||
textureCoordinates: List[float] = None
|
||||
test_bases: List[Base] = None
|
||||
detach_this: Base = None
|
||||
detached_list: List[Base] = None
|
||||
_origin: Point = None
|
||||
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super(FakeMesh, self).__init__(**kwargs)
|
||||
self.add_chunkable_attrs(**CHUNKABLE_PROPS)
|
||||
self.add_detachable_attrs(DETACHABLE)
|
||||
|
||||
@property
|
||||
def origin(self):
|
||||
return self._origin
|
||||
|
||||
@origin.setter
|
||||
def origin(self, value: Point):
|
||||
self._origin = value
|
||||
@@ -0,0 +1,351 @@
|
||||
from .base import Base
|
||||
from typing import Any, List
|
||||
|
||||
GEOMETRY = "Objects.Geometry."
|
||||
|
||||
|
||||
class Interval(Base, speckle_type="Objects.Primitive.Interval"):
|
||||
start: float = 0
|
||||
end: float = 0
|
||||
|
||||
def length(self):
|
||||
return abs(self.start - self.end)
|
||||
|
||||
|
||||
class Point(Base, speckle_type=GEOMETRY + "Point"):
|
||||
x: float = 0
|
||||
y: float = 0
|
||||
z: float = 0
|
||||
|
||||
def __init__(self, x: float = 0, y: float = 0, z: float = 0, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
self.x, self.y, self.z = x, y, z
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}(x: {self.x}, y: {self.y}, z: {self.z}, id: {self.id}, speckle_type: {self.speckle_type})"
|
||||
|
||||
|
||||
class Vector(Point, speckle_type=GEOMETRY + "Vector"):
|
||||
pass
|
||||
|
||||
|
||||
class ControlPoint(Point, speckle_type=GEOMETRY + "ControlPoint"):
|
||||
weight: float = None
|
||||
|
||||
|
||||
class Plane(Base, speckle_type=GEOMETRY + "Plane"):
|
||||
origin: Point = Point()
|
||||
normal: Vector = Vector()
|
||||
xdir: Vector = Vector()
|
||||
ydir: Vector = Vector()
|
||||
|
||||
|
||||
class Box(Base, speckle_type=GEOMETRY + "Box"):
|
||||
basePlane: Plane = Plane()
|
||||
ySize: Interval = Interval()
|
||||
zSize: Interval = Interval()
|
||||
xSize: Interval = Interval()
|
||||
area: float = None
|
||||
volume: float = None
|
||||
|
||||
|
||||
class Line(Base, speckle_type=GEOMETRY + "Line"):
|
||||
start: Point = Point()
|
||||
end: Point = None
|
||||
domain: Interval = None
|
||||
bbox: Box = None
|
||||
length: float = None
|
||||
|
||||
|
||||
class Arc(Base, speckle_type=GEOMETRY + "Arc"):
|
||||
radius: float = None
|
||||
startAngle: float = None
|
||||
endAngle: float = None
|
||||
angleRadians: float = None
|
||||
plane: Plane = None
|
||||
domain: Interval = None
|
||||
startPoint: Point = None
|
||||
midPoint: Point = None
|
||||
endPoint: Point = None
|
||||
bbox: Box = None
|
||||
area: float = None
|
||||
length: float = None
|
||||
|
||||
|
||||
class Circle(Base, speckle_type=GEOMETRY + "Circle"):
|
||||
radius: float = None
|
||||
plane: Plane = None
|
||||
domain: Interval = None
|
||||
bbox: Box = None
|
||||
area: float = None
|
||||
length: float = None
|
||||
|
||||
|
||||
class Ellipse(Base, speckle_type=GEOMETRY + "Ellipse"):
|
||||
firstRadius: float = None
|
||||
secondRadius: float = None
|
||||
plane: Plane = None
|
||||
domain: Interval = None
|
||||
trimDomain: Interval = None
|
||||
bbox: Box = None
|
||||
area: float = None
|
||||
length: float = None
|
||||
|
||||
|
||||
class Polyline(Base, speckle_type=GEOMETRY + "Polyline"):
|
||||
value: List[float] = None
|
||||
closed: bool = None
|
||||
domain: Interval = None
|
||||
bbox: Box = None
|
||||
area: float = None
|
||||
length: float = None
|
||||
|
||||
def __init__(self, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
self.add_chunkable_attrs(value=20000)
|
||||
|
||||
@classmethod
|
||||
def from_points(cls, points: List[Point]):
|
||||
polyline = cls()
|
||||
polyline.units = points[0].units
|
||||
for point in points:
|
||||
polyline.value.extend([point.x, point.y, point.z])
|
||||
return polyline
|
||||
|
||||
# @property
|
||||
# def value(self) -> List[float]:
|
||||
# return self._value
|
||||
|
||||
# @value.setter
|
||||
# def value(self, coords) -> None:
|
||||
# if len(coords) % 3:
|
||||
# coords.extend([0] * (3 - len(coords) % 3))
|
||||
# self._value = coords
|
||||
|
||||
def as_points(self) -> List[Point]:
|
||||
"""Converts the `value` attribute to a list of Points"""
|
||||
if not self.value:
|
||||
return
|
||||
|
||||
if len(self.value) % 3:
|
||||
raise ValueError("Points array malformed: length%3 != 0.")
|
||||
|
||||
values = iter(self.value)
|
||||
return [Point(v, next(values), next(values), units=self.units) for v in values]
|
||||
|
||||
|
||||
class Curve(Base, speckle_type=GEOMETRY + "Curve"):
|
||||
degree: int = None
|
||||
periodic: bool = None
|
||||
rational: bool = None
|
||||
points: List[float] = None
|
||||
weights: List[float] = None
|
||||
knots: List[float] = None
|
||||
domain: Interval = None
|
||||
displayValue: Polyline = None
|
||||
closed: bool = None
|
||||
bbox: Box = None
|
||||
area: float = None
|
||||
length: float = None
|
||||
|
||||
def __init__(self, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
self.add_chunkable_attrs(points=20000, weights=20000, knots=20000)
|
||||
|
||||
def as_points(self) -> List[Point]:
|
||||
"""Converts the `value` attribute to a list of Points"""
|
||||
if not self.points:
|
||||
return
|
||||
|
||||
if len(self.points) % 3:
|
||||
raise ValueError("Points array malformed: length%3 != 0.")
|
||||
|
||||
values = iter(self.points)
|
||||
return [Point(v, next(values), next(values), units=self.units) for v in values]
|
||||
|
||||
|
||||
class Polycurve(Base, speckle_type=GEOMETRY + "Polycurve"):
|
||||
segments: List[Base] = []
|
||||
domain: Interval = None
|
||||
closed: bool = None
|
||||
bbox: Box = None
|
||||
area: float = None
|
||||
length: float = None
|
||||
|
||||
|
||||
class Extrusion(Base, speckle_type=GEOMETRY + "Extrusion"):
|
||||
capped: bool = None
|
||||
profile: Base = None
|
||||
pathStart: Point = None
|
||||
pathEnd: Point = None
|
||||
pathCurve: Base = None
|
||||
pathTangent: Base = None
|
||||
profiles: List[Base] = None
|
||||
length: float = None
|
||||
area: float = None
|
||||
volume: float = None
|
||||
bbox: Box = None
|
||||
|
||||
|
||||
class Mesh(Base, speckle_type=GEOMETRY + "Mesh"):
|
||||
vertices: List[float] = None
|
||||
faces: List[int] = None
|
||||
colors: List[int] = None
|
||||
textureCoordinates: List[float] = None
|
||||
bbox: Box = None
|
||||
area: float = None
|
||||
volume: float = None
|
||||
|
||||
def __init__(self, **data) -> None:
|
||||
super().__init__(**data)
|
||||
self.add_chunkable_attrs(
|
||||
vertices=2000, faces=2000, colors=2000, textureCoordinates=2000
|
||||
)
|
||||
|
||||
|
||||
class Surface(Base, speckle_type=GEOMETRY + "Surface"):
|
||||
degreeU: int = None
|
||||
degreeV: int = None
|
||||
rational: bool = None
|
||||
area: float = None
|
||||
pointData: List[float] = None
|
||||
countU: int = None
|
||||
countV: int = None
|
||||
bbox: Box = None
|
||||
|
||||
|
||||
class BrepFace(Base, speckle_type=GEOMETRY + "BrepFace"):
|
||||
_Brep: "Brep" = None
|
||||
SurfaceIndex: int = None
|
||||
LoopIndices: List[int] = None
|
||||
OuterLoopIndex: int = None
|
||||
OrientationReversed: bool = None
|
||||
|
||||
@property
|
||||
def _outer_loop(self):
|
||||
return self._Brep.Loops[self.OuterLoopIndex]
|
||||
|
||||
@property
|
||||
def _surface(self):
|
||||
return self._Brep.Surfaces[self.SurfaceIndex]
|
||||
|
||||
@property
|
||||
def _loops(self):
|
||||
return [self._Brep.Loops[index] for index in self.LoopIndices]
|
||||
|
||||
|
||||
class BrepEdge(Base, speckle_type=GEOMETRY + "BrepEdge"):
|
||||
_Brep: "Brep" = None
|
||||
Curve3dIndex: int = None
|
||||
TrimIndices: List[int] = None
|
||||
StartIndex: int = None
|
||||
EndIndex: int = None
|
||||
ProxyCurveIsReversed: bool = None
|
||||
Domain: Interval = None
|
||||
|
||||
@property
|
||||
def _start_vertex(self):
|
||||
return self._Brep.Vertices[self.StartIndex]
|
||||
|
||||
@property
|
||||
def _end_vertex(self):
|
||||
return self._Brep.Vertices[self.EndIndex]
|
||||
|
||||
@property
|
||||
def _trims(self):
|
||||
return [self._Brep.Trims[i] for i in self.TrimIndices]
|
||||
|
||||
@property
|
||||
def _curve(self):
|
||||
return self._Brep.Curve3D[self.Curve3dIndex]
|
||||
|
||||
|
||||
class BrepLoop(Base, speckle_type=GEOMETRY + "BrepLoop"):
|
||||
_Brep: "Brep" = None
|
||||
FaceIndex: int = None
|
||||
TrimIndices: List[int] = None
|
||||
Type: str = None
|
||||
|
||||
@property
|
||||
def _face(self):
|
||||
return self._Brep.Faces[self.FaceIndex]
|
||||
|
||||
@property
|
||||
def _trims(self):
|
||||
return [self._Brep.Trims[i] for i in self.TrimIndices]
|
||||
|
||||
|
||||
class BrepTrim(Base, speckle_type=GEOMETRY + "BrepTrim"):
|
||||
_Brep: "Brep" = None
|
||||
EdgeIndex: int = None
|
||||
StartIndex: int = None
|
||||
EndIndex: int = None
|
||||
FaceIndex: int = None
|
||||
LoopIndex: int = None
|
||||
CurveIndex: int = None
|
||||
IsoStatus: int = None
|
||||
TrimType: str = None
|
||||
IsReversed: bool = None
|
||||
Domain: Interval = None
|
||||
|
||||
@property
|
||||
def _face(self):
|
||||
return self._Brep.Faces[self.FaceIndex]
|
||||
|
||||
@property
|
||||
def _loop(self):
|
||||
return self._Brep.Loops[self.LoopIndex]
|
||||
|
||||
@property
|
||||
def _edge(self):
|
||||
return self._Brep.Edges[self.EdgeIndex] if self.EdgeIndex != -1 else None
|
||||
|
||||
@property
|
||||
def _curve_2d(self):
|
||||
return self._Brep.Curve2D[self.CurveIndex]
|
||||
|
||||
|
||||
class Brep(Base, speckle_type=GEOMETRY + "Brep"):
|
||||
provenance: str = None
|
||||
bbox: Box = None
|
||||
area: float = None
|
||||
volume: float = None
|
||||
displayValue: Mesh = None
|
||||
Surfaces: List[Surface] = []
|
||||
Curve3D: List[Base] = []
|
||||
Curve2D: List[Base] = []
|
||||
Vertices: List[Point] = []
|
||||
Edges: List[BrepEdge] = []
|
||||
Loops: List[BrepLoop] = []
|
||||
Trims: List[BrepTrim] = []
|
||||
Faces: List[BrepFace] = []
|
||||
IsClosed: bool = None
|
||||
Orientation: int = 0
|
||||
|
||||
def __init__(self, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
self.add_detachable_attrs({"displayValue"})
|
||||
self.add_chunkable_attrs(
|
||||
Surfaces=200,
|
||||
Curve3D=200,
|
||||
Curve2D=200,
|
||||
Vertices=5000,
|
||||
Edges=5000,
|
||||
Loops=5000,
|
||||
Trims=5000,
|
||||
Faces=5000,
|
||||
)
|
||||
|
||||
def __setattr__(self, name: str, value: Any) -> None:
|
||||
if not value:
|
||||
return
|
||||
if name in ["Edges", "Loops", "Trims", "Faces"]:
|
||||
for val in value:
|
||||
val._Brep = self
|
||||
super().__setattr__(name, value)
|
||||
|
||||
|
||||
BrepEdge.update_forward_refs()
|
||||
BrepLoop.update_forward_refs()
|
||||
BrepTrim.update_forward_refs()
|
||||
BrepFace.update_forward_refs()
|
||||
@@ -0,0 +1,12 @@
|
||||
from .base import Base
|
||||
|
||||
OTHER = "Objects.Other."
|
||||
|
||||
|
||||
class RenderMaterial(Base, speckle_type=OTHER + "RenderMaterial"):
|
||||
name: str = None
|
||||
opacity: float = 1
|
||||
metalness: float = 0
|
||||
roughness: float = 1
|
||||
diffuse: int = -2894893 # light gray arbg
|
||||
emissive: int = -16777216 # black arbg
|
||||
@@ -0,0 +1,26 @@
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
UNITS = ["mm", "cm", "m", "in", "ft", "yd", "mi"]
|
||||
|
||||
UNITS_STRINGS = {
|
||||
"mm": ["mm", "mil", "millimeters", "millimetres"],
|
||||
"cm": ["cm", "centimetre", "centimeter", "centimetres", "centimeters"],
|
||||
"m": ["m", "meter", "meters", "metre", "metres"],
|
||||
"km": ["km", "kilometer", "kilometre", "kilometers", "kilometres"],
|
||||
"in": ["in", "inch", "inches"],
|
||||
"ft": ["ft", "foot", "feet"],
|
||||
"yd": ["yd", "yard", "yards"],
|
||||
"mi": ["mi", "mile", "miles"],
|
||||
"none": ["none", "null"],
|
||||
}
|
||||
|
||||
|
||||
def get_units_from_string(unit: str):
|
||||
unit = str.lower(unit)
|
||||
for name, alternates in UNITS_STRINGS.items():
|
||||
if unit in alternates:
|
||||
return name
|
||||
|
||||
raise SpeckleException(
|
||||
message=f"Could not understand what unit {unit} is referring to. Please enter a valid unit (eg {UNITS})."
|
||||
)
|
||||
@@ -0,0 +1,351 @@
|
||||
import json
|
||||
import hashlib
|
||||
import re
|
||||
|
||||
from uuid import uuid4
|
||||
from typing import Any, Dict, List, Tuple
|
||||
from specklepy.objects.base import Base, DataChunk
|
||||
from specklepy.logging.exceptions import SerializationException, SpeckleException
|
||||
from specklepy.transports.abstract_transport import AbstractTransport
|
||||
import specklepy.objects.geometry
|
||||
import specklepy.objects.other
|
||||
|
||||
PRIMITIVES = (int, float, str, bool)
|
||||
|
||||
|
||||
def hash_obj(obj: Any) -> str:
|
||||
return hashlib.sha256(json.dumps(obj).encode()).hexdigest()[:32]
|
||||
|
||||
|
||||
class BaseObjectSerializer:
|
||||
read_transport: AbstractTransport
|
||||
write_transports: List[AbstractTransport]
|
||||
detach_lineage: List[bool] = [] # tracks depth and whether or not to detach
|
||||
lineage: List[str] = [] # keeps track of hash chain through the object tree
|
||||
family_tree: Dict[str, Dict[str, int]] = {}
|
||||
closure_table: Dict[str, Dict[str, int]] = {}
|
||||
|
||||
def __init__(
|
||||
self, write_transports: List[AbstractTransport] = [], read_transport=None
|
||||
) -> None:
|
||||
self.write_transports = write_transports
|
||||
self.read_transport = read_transport
|
||||
|
||||
def write_json(self, base: Base):
|
||||
self.__reset_writer()
|
||||
self.detach_lineage = [True]
|
||||
hash, obj = self.traverse_base(base)
|
||||
return hash, json.dumps(obj)
|
||||
|
||||
def traverse_base(self, base: Base) -> Tuple[str, Dict]:
|
||||
"""Decomposes the given base object and builds a serializable dictionary
|
||||
|
||||
Arguments:
|
||||
base {Base} -- the base object to be decomposed and serialized
|
||||
|
||||
Returns:
|
||||
(str, dict) -- a tuple containing the hash (id) of the base object and the constructed serializable dictionary
|
||||
"""
|
||||
if not self.detach_lineage:
|
||||
self.detach_lineage = [True]
|
||||
|
||||
self.lineage.append(uuid4().hex)
|
||||
object_builder = {"id": "", "speckle_type": "Base", "totalChildrenCount": 0}
|
||||
object_builder.update(speckle_type=base.speckle_type)
|
||||
obj, props = base, base.get_member_names()
|
||||
|
||||
while props:
|
||||
prop = props.pop(0)
|
||||
value = getattr(obj, prop, None)
|
||||
chunkable = False
|
||||
detach = False
|
||||
|
||||
# skip nulls or props marked to be ignored with "__" or "_"
|
||||
if value is None or prop.startswith(("__", "_")):
|
||||
continue
|
||||
|
||||
# don't prepopulate id as this will mess up hashing
|
||||
if prop == "id":
|
||||
continue
|
||||
|
||||
# only bother with chunking and detaching if there is a write transport
|
||||
if self.write_transports:
|
||||
dynamic_chunk_match = re.match(r"^@\((\d*)\)", prop)
|
||||
if dynamic_chunk_match:
|
||||
chunk_size = dynamic_chunk_match.groups()[0]
|
||||
base._chunkable[prop] = (
|
||||
int(chunk_size) if chunk_size else base._chunk_size_default
|
||||
)
|
||||
|
||||
chunkable = prop in base._chunkable
|
||||
detach = bool(
|
||||
prop.startswith("@") or prop in base._detachable or chunkable
|
||||
)
|
||||
|
||||
# 1. handle primitives (ints, floats, strings, and bools)
|
||||
if isinstance(value, PRIMITIVES):
|
||||
object_builder[prop] = value
|
||||
continue
|
||||
|
||||
# 2. handle Base objects
|
||||
elif isinstance(value, Base):
|
||||
child_obj = self.traverse_value(value, detach=detach)
|
||||
if detach and self.write_transports:
|
||||
ref_hash = child_obj["id"]
|
||||
object_builder[prop] = self.detach_helper(ref_hash=ref_hash)
|
||||
else:
|
||||
object_builder[prop] = child_obj
|
||||
|
||||
# 3. handle chunkable props
|
||||
elif chunkable and self.write_transports:
|
||||
chunks = []
|
||||
max_size = base._chunkable[prop]
|
||||
chunk = DataChunk()
|
||||
for count, item in enumerate(value):
|
||||
if count and count % max_size == 0:
|
||||
chunks.append(chunk)
|
||||
chunk = DataChunk()
|
||||
chunk.data.append(item)
|
||||
chunks.append(chunk)
|
||||
|
||||
chunk_refs = []
|
||||
for c in chunks:
|
||||
self.detach_lineage.append(detach)
|
||||
ref_hash, _ = self.traverse_base(c)
|
||||
ref_obj = self.detach_helper(ref_hash=ref_hash)
|
||||
chunk_refs.append(ref_obj)
|
||||
object_builder[prop] = chunk_refs
|
||||
|
||||
# 4. handle all other cases
|
||||
else:
|
||||
child_obj = self.traverse_value(value, detach)
|
||||
object_builder[prop] = child_obj
|
||||
|
||||
closure = {}
|
||||
# add closures & children count to the object
|
||||
detached = self.detach_lineage.pop()
|
||||
if self.lineage[-1] in self.family_tree:
|
||||
closure = {
|
||||
ref: depth - len(self.detach_lineage)
|
||||
for ref, depth in self.family_tree[self.lineage[-1]].items()
|
||||
}
|
||||
object_builder["totalChildrenCount"] = len(closure)
|
||||
|
||||
hash = hash_obj(object_builder)
|
||||
|
||||
object_builder["id"] = hash
|
||||
if closure:
|
||||
object_builder["__closure"] = self.closure_table[hash] = closure
|
||||
|
||||
# write detached or root objects to transports
|
||||
if detached and self.write_transports:
|
||||
for t in self.write_transports:
|
||||
t.save_object(id=hash, serialized_object=json.dumps(object_builder))
|
||||
|
||||
del self.lineage[-1]
|
||||
|
||||
return hash, object_builder
|
||||
|
||||
def traverse_value(self, obj: Any, detach: bool = False) -> Any:
|
||||
"""Decomposes a given object and constructs a serializable object or dictionary
|
||||
|
||||
Arguments:
|
||||
obj {Any} -- the value to decompose
|
||||
|
||||
Returns:
|
||||
Any -- a serializable version of the given object
|
||||
"""
|
||||
if isinstance(obj, PRIMITIVES):
|
||||
return obj
|
||||
|
||||
elif isinstance(obj, (list, tuple, set)):
|
||||
if not detach:
|
||||
return [self.traverse_value(o) for o in obj]
|
||||
|
||||
detached_list = []
|
||||
for o in obj:
|
||||
if isinstance(o, Base):
|
||||
self.detach_lineage.append(detach)
|
||||
hash, _ = self.traverse_base(o)
|
||||
detached_list.append(self.detach_helper(ref_hash=hash))
|
||||
else:
|
||||
detached_list.append(self.traverse_value(o, detach))
|
||||
return detached_list
|
||||
|
||||
elif isinstance(obj, dict):
|
||||
for k, v in obj.items():
|
||||
if isinstance(v, PRIMITIVES):
|
||||
continue
|
||||
else:
|
||||
obj[k] = self.traverse_value(v)
|
||||
return obj
|
||||
|
||||
elif isinstance(obj, Base):
|
||||
self.detach_lineage.append(detach)
|
||||
_, base_obj = self.traverse_base(obj)
|
||||
return base_obj
|
||||
|
||||
else:
|
||||
try:
|
||||
return obj.dict()
|
||||
except:
|
||||
SerializationException(
|
||||
message=f"Failed to handle {type(obj)} in `BaseObjectSerializer.traverse_value`",
|
||||
object=obj,
|
||||
)
|
||||
return str(obj)
|
||||
|
||||
def detach_helper(self, ref_hash: str) -> Dict[str, str]:
|
||||
"""Helper to keep track of detached objects and their depth in the family tree and create reference objects to place in the parent object
|
||||
|
||||
Arguments:
|
||||
ref_hash {str} -- the hash of the fully traversed object
|
||||
|
||||
Returns:
|
||||
dict -- a reference object to be inserted into the given object's parent
|
||||
"""
|
||||
|
||||
for parent in self.lineage:
|
||||
if parent not in self.family_tree:
|
||||
self.family_tree[parent] = {}
|
||||
if ref_hash not in self.family_tree[parent] or self.family_tree[parent][
|
||||
ref_hash
|
||||
] > len(self.detach_lineage):
|
||||
self.family_tree[parent][ref_hash] = len(self.detach_lineage)
|
||||
|
||||
return {
|
||||
"referencedId": ref_hash,
|
||||
"speckle_type": "reference",
|
||||
}
|
||||
|
||||
def __reset_writer(self) -> None:
|
||||
"""Reinitializes the lineage, and other variables that get used during the json writing process"""
|
||||
self.detach_lineage = []
|
||||
self.lineage = []
|
||||
self.family_tree = {}
|
||||
self.closure_table = {}
|
||||
|
||||
def read_json(self, obj_string: str) -> Base:
|
||||
"""Recomposes a Base object from the string representation of the object
|
||||
|
||||
Arguments:
|
||||
obj_string {str} -- the string representation of the object
|
||||
|
||||
Returns:
|
||||
Base -- the base object with all it's children attached
|
||||
"""
|
||||
if not obj_string:
|
||||
return None
|
||||
obj = json.loads(obj_string)
|
||||
return self.recompose_base(obj=obj)
|
||||
|
||||
def recompose_base(self, obj: dict) -> Base:
|
||||
"""Steps through a base object dictionary and recomposes the base object
|
||||
|
||||
Arguments:
|
||||
obj {dict} -- the dictionary representation of the object
|
||||
|
||||
Returns:
|
||||
Base -- the base object with all its children attached
|
||||
"""
|
||||
# make sure an obj was passed and create dict if string was somehow passed
|
||||
if not obj:
|
||||
return
|
||||
if isinstance(obj, str):
|
||||
obj = json.loads(obj)
|
||||
|
||||
if "speckle_type" in obj and obj["speckle_type"] == "reference":
|
||||
obj = self.get_child(obj=obj)
|
||||
|
||||
speckle_type = obj.get("speckle_type")
|
||||
# if speckle type is not in the object definition, it is treated as a dict
|
||||
if not speckle_type:
|
||||
return obj
|
||||
|
||||
# get the registered type from base register.
|
||||
object_type = Base.get_registered_type(speckle_type)
|
||||
|
||||
# initialise the base object using `speckle_type` fall back to base if needed
|
||||
base = object_type() if object_type else Base(speckle_type=speckle_type)
|
||||
# get total children count
|
||||
if "__closure" in obj:
|
||||
if not self.read_transport:
|
||||
raise SpeckleException(
|
||||
message="Cannot resolve reference - no read transport is defined"
|
||||
)
|
||||
closure = obj.pop("__closure")
|
||||
base.totalChildrenCount = len(closure)
|
||||
|
||||
for prop, value in obj.items():
|
||||
# 1. handle primitives (ints, floats, strings, and bools) or None
|
||||
if isinstance(value, PRIMITIVES) or value is None:
|
||||
base.__setattr__(prop, value)
|
||||
continue
|
||||
|
||||
# 2. handle referenced child objects
|
||||
elif "referencedId" in value:
|
||||
ref_hash = value["referencedId"]
|
||||
ref_obj_str = self.read_transport.get_object(id=ref_hash)
|
||||
if not ref_obj_str:
|
||||
raise SpeckleException(
|
||||
f"Could not find the referenced child object of id `{ref_hash}` in the given read transport: {self.read_transport.name}"
|
||||
)
|
||||
ref_obj = json.loads(ref_obj_str)
|
||||
base.__setattr__(prop, self.recompose_base(obj=ref_obj))
|
||||
|
||||
# 3. handle all other cases (base objects, lists, and dicts)
|
||||
else:
|
||||
base.__setattr__(prop, self.handle_value(value))
|
||||
|
||||
return base
|
||||
|
||||
def handle_value(self, obj: Any):
|
||||
"""Helper for recomposing a base object by handling the dictionary representation's values
|
||||
|
||||
Arguments:
|
||||
obj {Any} -- a value from the base object dictionary
|
||||
|
||||
Returns:
|
||||
Any -- the handled value (primitive, list, dictionary, or Base)
|
||||
"""
|
||||
if not obj:
|
||||
return obj
|
||||
|
||||
if isinstance(obj, PRIMITIVES):
|
||||
return obj
|
||||
|
||||
# lists (regular and chunked)
|
||||
if isinstance(obj, list):
|
||||
obj_list = [self.handle_value(o) for o in obj]
|
||||
if (
|
||||
hasattr(obj_list[0], "speckle_type")
|
||||
and "DataChunk" in obj_list[0].speckle_type
|
||||
):
|
||||
# handle chunked lists
|
||||
data = []
|
||||
for o in obj_list:
|
||||
data.extend(o["data"])
|
||||
return data
|
||||
return obj_list
|
||||
|
||||
# bases
|
||||
if isinstance(obj, dict) and "speckle_type" in obj:
|
||||
return self.recompose_base(obj=obj)
|
||||
|
||||
# dictionaries
|
||||
if isinstance(obj, dict):
|
||||
for k, v in obj.items():
|
||||
if isinstance(v, PRIMITIVES):
|
||||
continue
|
||||
else:
|
||||
obj[k] = self.handle_value(v)
|
||||
return obj
|
||||
|
||||
def get_child(self, obj: Dict):
|
||||
ref_hash = obj["referencedId"]
|
||||
ref_obj_str = self.read_transport.get_object(id=ref_hash)
|
||||
if not ref_obj_str:
|
||||
raise SpeckleException(
|
||||
f"Could not find the referenced child object of id `{ref_hash}` in the given read transport: {self.read_transport.name}"
|
||||
)
|
||||
return json.loads(ref_obj_str)
|
||||
@@ -0,0 +1,95 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Optional, List, Dict
|
||||
from pydantic import BaseModel
|
||||
from pydantic.main import Extra
|
||||
|
||||
# __________________
|
||||
# | |
|
||||
# | this is v wip |
|
||||
# | pls be careful |
|
||||
# |__________________|
|
||||
# (\__/) ||
|
||||
# (•ㅅ•) ||
|
||||
# / づ
|
||||
|
||||
|
||||
class AbstractTransport(ABC, BaseModel):
|
||||
_name: str = "Abstract"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return type(self)._name
|
||||
|
||||
@abstractmethod
|
||||
def begin_write(self) -> None:
|
||||
"""Optional: signals to the transport that writes are about to begin."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def end_write(self) -> None:
|
||||
"""Optional: signals to the transport that no more items will need to be written."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def save_object(self, id: str, serialized_object: str) -> None:
|
||||
"""Saves the given serialized object.
|
||||
|
||||
Arguments:
|
||||
id {str} -- the hash of the object
|
||||
serialized_object {str} -- the full string representation of the object
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def save_object_from_transport(
|
||||
self, id: str, source_transport: "AbstractTransport"
|
||||
) -> None:
|
||||
"""Saves an object from the given source transport.
|
||||
|
||||
Arguments:
|
||||
id {str} -- the hash of the object
|
||||
source_transport {AbstractTransport) -- the transport through which the object can be found
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_object(self, id: str) -> Optional[str]:
|
||||
"""Gets an object. Returns `None` if the object is not found.
|
||||
|
||||
Arguments:
|
||||
id {str} -- the hash of the object
|
||||
|
||||
Returns:
|
||||
str -- the full string representation of the object (or null if no object is found)
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def has_objects(self, id_list: List[str]) -> Dict[str, bool]:
|
||||
"""Checks the presence of multiple objects.
|
||||
|
||||
Arguments:
|
||||
id_list -- List of object id to be checked
|
||||
|
||||
Returns:
|
||||
Dict[str, bool] -- keys: input ids, values: whether the transport has that object
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def copy_object_and_children(
|
||||
self, id: str, target_transport: "AbstractTransport"
|
||||
) -> str:
|
||||
"""Copies the parent object and all its children to the provided transport.
|
||||
|
||||
Arguments:
|
||||
id {str} -- the id of the object you want to copy
|
||||
target_transport {AbstractTransport} -- the transport you want to copy the object to
|
||||
Returns:
|
||||
str -- the string representation of the root object
|
||||
"""
|
||||
pass
|
||||
|
||||
class Config:
|
||||
extra = Extra.allow
|
||||
arbitrary_types_allowed = True
|
||||
@@ -0,0 +1,48 @@
|
||||
import json
|
||||
from typing import Any, List, Dict
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.transports.abstract_transport import AbstractTransport
|
||||
|
||||
|
||||
class MemoryTransport(AbstractTransport):
|
||||
_name: str = "Memory"
|
||||
objects: dict = {}
|
||||
saved_object_count: int = 0
|
||||
|
||||
def __init__(self, name=None, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
if name:
|
||||
self._name = name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"MemoryTransport(objects: {len(self.objects)})"
|
||||
|
||||
def save_object(self, id: str, serialized_object: str) -> None:
|
||||
self.objects[id] = serialized_object
|
||||
|
||||
self.saved_object_count += 1
|
||||
|
||||
def save_object_from_transport(
|
||||
self, id: str, source_transport: AbstractTransport
|
||||
) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def get_object(self, id: str) -> str or None:
|
||||
if id in self.objects:
|
||||
return self.objects[id]
|
||||
else:
|
||||
return None
|
||||
|
||||
def has_objects(self, id_list: List[str]) -> Dict[str, bool]:
|
||||
return {id: (id in self.objects) for id in id_list}
|
||||
|
||||
def begin_write(self) -> None:
|
||||
self.saved_object_count = 0
|
||||
|
||||
def end_write(self) -> None:
|
||||
pass
|
||||
|
||||
def copy_object_and_children(
|
||||
self, id: str, target_transport: AbstractTransport
|
||||
) -> str:
|
||||
raise NotImplementedError
|
||||
@@ -0,0 +1 @@
|
||||
from .server import ServerTransport
|
||||
@@ -0,0 +1,141 @@
|
||||
import json
|
||||
import logging
|
||||
import threading
|
||||
import queue
|
||||
import time
|
||||
import gzip
|
||||
|
||||
import requests
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BatchSender(object):
|
||||
def __init__(
|
||||
self,
|
||||
server_url,
|
||||
stream_id,
|
||||
token,
|
||||
max_batch_size_mb=1,
|
||||
batch_buffer_length=10,
|
||||
thread_count=4,
|
||||
):
|
||||
self.server_url = server_url
|
||||
self.stream_id = stream_id
|
||||
self._token = token
|
||||
|
||||
self.max_size = int(max_batch_size_mb * 1000 * 1000)
|
||||
self._batches = queue.Queue(batch_buffer_length)
|
||||
self._crt_batch = []
|
||||
self._crt_batch_size = 0
|
||||
|
||||
self.thread_count = thread_count
|
||||
self._send_threads = []
|
||||
self._exception = None
|
||||
|
||||
def send_object(self, id: str, obj: str):
|
||||
if not self._send_threads:
|
||||
self._create_threads()
|
||||
|
||||
crt_obj_size = len(obj)
|
||||
if not self._crt_batch or self._crt_batch_size + crt_obj_size < self.max_size:
|
||||
self._crt_batch.append((id, obj))
|
||||
self._crt_batch_size += crt_obj_size
|
||||
return
|
||||
|
||||
self._batches.put(self._crt_batch)
|
||||
self._crt_batch = [(id, obj)]
|
||||
self._crt_batch_size = crt_obj_size
|
||||
|
||||
def flush(self):
|
||||
# Add current non-complete batch
|
||||
if self._crt_batch:
|
||||
self._batches.put(self._crt_batch)
|
||||
self._crt_batch = []
|
||||
self._crt_batch_size = 0
|
||||
# Wait for queued batches to be sent
|
||||
self._batches.join()
|
||||
# End the sending threads
|
||||
self._delete_threads()
|
||||
# If there was any error, throw the first exception that occurred during upload
|
||||
if self._exception is not None:
|
||||
ex = self._exception
|
||||
self._exception = None
|
||||
raise ex
|
||||
|
||||
def _sending_thread_main(self):
|
||||
try:
|
||||
session = requests.Session()
|
||||
session.headers.update(
|
||||
{"Authorization": f"Bearer {self._token}", "Accept": "text/plain"}
|
||||
)
|
||||
|
||||
while True:
|
||||
batch = self._batches.get()
|
||||
|
||||
# None is a sentinel value, meaning the thread should exit gracefully
|
||||
if batch is None:
|
||||
self._batches.task_done()
|
||||
break
|
||||
|
||||
try:
|
||||
self._bg_send_batch(session, batch)
|
||||
except Exception as ex:
|
||||
self._exception = self._exception or ex
|
||||
LOG.error("Error sending batch of objects to server: " + str(ex))
|
||||
|
||||
self._batches.task_done()
|
||||
except Exception as ex:
|
||||
self._exception = self._exception or ex
|
||||
LOG.error("ServerTransport sending thread error: " + str(ex))
|
||||
|
||||
def _bg_send_batch(self, session, batch):
|
||||
object_ids = [obj[0] for obj in batch]
|
||||
server_has_object = session.post(
|
||||
url=f"{self.server_url}/api/diff/{self.stream_id}",
|
||||
data={"objects": json.dumps(object_ids)},
|
||||
).json()
|
||||
|
||||
new_object_ids = [x for x in object_ids if not server_has_object[x]]
|
||||
new_object_ids = set(new_object_ids)
|
||||
new_objects = [obj[1] for obj in batch if obj[0] in new_object_ids]
|
||||
|
||||
if not new_objects:
|
||||
LOG.info(f"Uploading batch of {len(batch)} objects: all objects are already in the server")
|
||||
return
|
||||
|
||||
upload_data = "[" + ",".join(new_objects) + "]"
|
||||
upload_data_gzip = gzip.compress(upload_data.encode())
|
||||
LOG.info(
|
||||
"Uploading batch of %s objects (%s new): (size: %s, compressed size: %s)"
|
||||
% (len(batch), len(new_objects), len(upload_data), len(upload_data_gzip))
|
||||
)
|
||||
|
||||
r = session.post(
|
||||
url=f"{self.server_url}/objects/{self.stream_id}",
|
||||
files={"batch-1": ("batch-1", upload_data_gzip, "application/gzip")},
|
||||
)
|
||||
if r.status_code != 201:
|
||||
LOG.warning("Upload server response: %s", r.text)
|
||||
raise SpeckleException(
|
||||
message=f"Could not save the object to the server - status code {r.status_code}"
|
||||
)
|
||||
|
||||
def _create_threads(self):
|
||||
for i in range(self.thread_count):
|
||||
t = threading.Thread(target=self._sending_thread_main, daemon=True)
|
||||
t.start()
|
||||
self._send_threads.append(t)
|
||||
|
||||
def _delete_threads(self):
|
||||
for i in range(len(self._send_threads)):
|
||||
self._batches.put(None)
|
||||
|
||||
for thread in self._send_threads:
|
||||
thread.join()
|
||||
|
||||
self._send_threads = []
|
||||
|
||||
def __del__(self):
|
||||
self._delete_threads()
|
||||
@@ -0,0 +1,129 @@
|
||||
import json
|
||||
import time
|
||||
|
||||
import requests
|
||||
|
||||
from typing import Any, Dict, List, Type
|
||||
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.transports.abstract_transport import AbstractTransport
|
||||
|
||||
from .batch_sender import BatchSender
|
||||
|
||||
|
||||
class ServerTransport(AbstractTransport):
|
||||
_name = "RemoteTransport"
|
||||
url: str = None
|
||||
stream_id: str = None
|
||||
saved_obj_count: int = 0
|
||||
session: requests.Session = None
|
||||
|
||||
def __init__(self, client: SpeckleClient, stream_id: str, **data: Any) -> None:
|
||||
super().__init__(**data)
|
||||
# TODO: replace client with account or some other auth avenue
|
||||
if not client.me:
|
||||
raise SpeckleException("The provided SpeckleClient was not authenticated.")
|
||||
self.url = client.url
|
||||
self.stream_id = stream_id
|
||||
|
||||
token = client.me["token"]
|
||||
self._batch_sender = BatchSender(
|
||||
self.url, self.stream_id, token, max_batch_size_mb=1
|
||||
)
|
||||
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update(
|
||||
{"Authorization": f"Bearer {token}", "Accept": "text/plain"}
|
||||
)
|
||||
|
||||
def begin_write(self) -> None:
|
||||
self.saved_obj_count = 0
|
||||
|
||||
def end_write(self) -> None:
|
||||
self._batch_sender.flush()
|
||||
|
||||
def save_object(self, id: str, serialized_object: str) -> None:
|
||||
self._batch_sender.send_object(id, serialized_object)
|
||||
|
||||
def save_object_from_transport(
|
||||
self, id: str, source_transport: AbstractTransport
|
||||
) -> None:
|
||||
obj_string = source_transport.get_object(id=id)
|
||||
self.save_object(id=id, serialized_object=obj_string)
|
||||
|
||||
def get_object(self, id: str) -> str:
|
||||
# endpoint = f"{self.url}/objects/{self.stream_id}/{id}/single"
|
||||
# r = self.session.get(endpoint, stream=True)
|
||||
|
||||
# _, obj = next(r.iter_lines().decode("utf-8")).split("\t")
|
||||
|
||||
# return obj
|
||||
|
||||
raise SpeckleException(
|
||||
"Getting a single object using `ServerTransport.get_object()` is not implemented. To get an object from the server, please use the `SpeckleClient.object.get()` route",
|
||||
NotImplementedError,
|
||||
)
|
||||
|
||||
def has_objects(self, id_list: List[str]) -> Dict[str, bool]:
|
||||
return {id: False for id in id_list}
|
||||
|
||||
def copy_object_and_children(
|
||||
self, id: str, target_transport: AbstractTransport
|
||||
) -> str:
|
||||
endpoint = f"{self.url}/objects/{self.stream_id}/{id}/single"
|
||||
r = self.session.get(endpoint)
|
||||
if r.encoding is None:
|
||||
r.encoding = "utf-8"
|
||||
|
||||
if r.status_code != 200:
|
||||
raise SpeckleException(
|
||||
f"Can't get object {self.stream_id}/{id}: HTTP error {r.status_code} ({r.text[:1000]})"
|
||||
)
|
||||
root_obj_serialized = r.text
|
||||
root_obj = json.loads(root_obj_serialized)
|
||||
closures = root_obj.get("__closure", {})
|
||||
|
||||
# Check which children are not already in the target transport
|
||||
children_ids = list(closures.keys())
|
||||
children_found_map = target_transport.has_objects(children_ids)
|
||||
new_children_ids = [
|
||||
id for id in children_found_map if not children_found_map[id]
|
||||
]
|
||||
|
||||
# Get the new children
|
||||
endpoint = f"{self.url}/api/getobjects/{self.stream_id}"
|
||||
r = self.session.post(
|
||||
endpoint, data={"objects": json.dumps(new_children_ids)}, stream=True
|
||||
)
|
||||
if r.encoding is None:
|
||||
r.encoding = "utf-8"
|
||||
lines = r.iter_lines(decode_unicode=True)
|
||||
|
||||
# iter through returned objects saving them as we go
|
||||
for line in lines:
|
||||
if line:
|
||||
hash, obj = line.split("\t")
|
||||
target_transport.save_object(hash, obj)
|
||||
|
||||
target_transport.save_object(id, root_obj_serialized)
|
||||
|
||||
return root_obj_serialized
|
||||
|
||||
# async def stream_res(self, endpoint: str) -> str:
|
||||
# data = b""
|
||||
# async with aiohttp.ClientSession() as session:
|
||||
# session.headers.update(
|
||||
# {
|
||||
# "Authorization": f"{self.session.headers['Authorization']}",
|
||||
# "Accept": "text/plain",
|
||||
# }
|
||||
# )
|
||||
# async with session.get(endpoint) as res:
|
||||
# while True:
|
||||
# chunk = await res.content.read(self.chunk_size)
|
||||
# if not chunk:
|
||||
# break
|
||||
# data += chunk
|
||||
|
||||
# return data.decode("utf-8")
|
||||
@@ -0,0 +1,216 @@
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import sched
|
||||
import sqlite3
|
||||
from typing import Any, List, Dict
|
||||
from appdirs import user_data_dir
|
||||
from contextlib import closing
|
||||
from specklepy.transports.abstract_transport import AbstractTransport
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
|
||||
class SQLiteTransport(AbstractTransport):
|
||||
_name = "SQLite"
|
||||
_base_path: str = None
|
||||
_root_path: str = None
|
||||
_is_writing: bool = False
|
||||
_scheduler = sched.scheduler(time.time, time.sleep)
|
||||
_polling_interval = 0.5 # seconds
|
||||
__connection: sqlite3.Connection = None
|
||||
app_name: str = ""
|
||||
scope: str = ""
|
||||
saved_obj_count: int = 0
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
base_path: str = None,
|
||||
app_name: str = None,
|
||||
scope: str = None,
|
||||
**data: Any,
|
||||
) -> None:
|
||||
super().__init__(**data)
|
||||
self.app_name = app_name or "Speckle"
|
||||
self.scope = scope or "Objects"
|
||||
self._base_path = base_path or self.__get_base_path()
|
||||
|
||||
try:
|
||||
os.makedirs(self._base_path, exist_ok=True)
|
||||
|
||||
self._root_path = os.path.join(
|
||||
os.path.join(self._base_path, f"{self.scope}.db")
|
||||
)
|
||||
self.__initialise()
|
||||
except Exception as ex:
|
||||
raise SpeckleException(
|
||||
f"SQLiteTransport could not initialise {self.scope}.db at {self._base_path}. Either provide a different `base_path` or use an alternative transport.",
|
||||
ex,
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"SQLiteTransport(app: '{self.app_name}', scope: '{self.scope}')"
|
||||
|
||||
# def __write_timer_elapsed(self):
|
||||
# print("WRITE TIMER ELAPSED")
|
||||
# proc = Process(target=_run_queue, args=(self.__queue, self._root_path))
|
||||
# proc.start()
|
||||
# proc.join()
|
||||
|
||||
def __get_base_path(self):
|
||||
# from appdirs https://github.com/ActiveState/appdirs/blob/master/appdirs.py
|
||||
# default mac path is not the one we use (we use unix path), so using special case for this
|
||||
system = sys.platform
|
||||
if system.startswith("java"):
|
||||
import platform
|
||||
|
||||
os_name = platform.java_ver()[3][0]
|
||||
if os_name.startswith("Mac"):
|
||||
system = "darwin"
|
||||
|
||||
if system != "darwin":
|
||||
return user_data_dir(appname=self.app_name, appauthor=False, roaming=True)
|
||||
|
||||
path = os.path.expanduser("~/.config/")
|
||||
return os.path.join(path, self.app_name)
|
||||
|
||||
# def __consume_queue(self):
|
||||
# if self._is_writing or self.__queue.empty():
|
||||
# return
|
||||
# print("CONSUME QUEUE")
|
||||
# self._is_writing = True
|
||||
# while not self.__queue.empty():
|
||||
# data = self.__queue.get()
|
||||
# self.save_object(data[0], data[1])
|
||||
# self._is_writing = False
|
||||
|
||||
# self._scheduler.enter(
|
||||
# delay=self._polling_interval, priority=1, action=self.__consume_queue
|
||||
# )
|
||||
# self._scheduler.run(blocking=True)
|
||||
|
||||
# def save_object(self, id: str, serialized_object: str) -> None:
|
||||
# """Adds an object to the queue and schedules it to be saved.
|
||||
|
||||
# Arguments:
|
||||
# id {str} -- the object id
|
||||
# serialized_object {str} -- the full string representation of the object
|
||||
# """
|
||||
# print("SAVE OBJECT")
|
||||
# self.__queue.put((id, serialized_object))
|
||||
|
||||
# self._scheduler.enter(
|
||||
# delay=self._polling_interval, priority=1, action=self.__consume_queue
|
||||
# )
|
||||
# self._scheduler.run(blocking=True)
|
||||
|
||||
def save_object_from_transport(
|
||||
self, id: str, source_transport: AbstractTransport
|
||||
) -> None:
|
||||
"""Adds an object from the given transport to the the local db
|
||||
|
||||
Arguments:
|
||||
id {str} -- the object id
|
||||
source_transport {AbstractTransport) -- the transport through which the object can be found
|
||||
"""
|
||||
serialized_object = source_transport.get_object(id)
|
||||
self.save_object(id, serialized_object)
|
||||
|
||||
def save_object(self, id: str, serialized_object: str) -> None:
|
||||
"""Directly saves an object into the database.
|
||||
|
||||
Arguments:
|
||||
id {str} -- the object id
|
||||
serialized_object {str} -- the full string representation of the object
|
||||
"""
|
||||
self.__check_connection()
|
||||
try:
|
||||
with closing(self.__connection.cursor()) as c:
|
||||
c.execute(
|
||||
"INSERT OR IGNORE INTO objects(hash, content) VALUES(?,?)",
|
||||
(id, serialized_object),
|
||||
)
|
||||
self.__connection.commit()
|
||||
except Exception as ex:
|
||||
raise SpeckleException(
|
||||
f"Could not save the object to the local db. Inner exception: {ex}", ex
|
||||
)
|
||||
|
||||
def get_object(self, id: str) -> str or None:
|
||||
self.__check_connection()
|
||||
with closing(self.__connection.cursor()) as c:
|
||||
row = c.execute(
|
||||
"SELECT * FROM objects WHERE hash = ? LIMIT 1", (id,)
|
||||
).fetchone()
|
||||
return row[1] if row else None
|
||||
|
||||
def has_objects(self, id_list: List[str]) -> Dict[str, bool]:
|
||||
ret = {}
|
||||
self.__check_connection()
|
||||
with closing(self.__connection.cursor()) as c:
|
||||
for id in id_list:
|
||||
row = c.execute(
|
||||
"SELECT 1 FROM objects WHERE hash = ? LIMIT 1", (id,)
|
||||
).fetchone()
|
||||
ret[id] = bool(row)
|
||||
return ret
|
||||
|
||||
def begin_write(self):
|
||||
self.saved_obj_count = 0
|
||||
|
||||
def end_write(self):
|
||||
pass
|
||||
|
||||
def copy_object_and_children(
|
||||
self, id: str, target_transport: AbstractTransport
|
||||
) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
def get_all_objects(self):
|
||||
"""Returns all the objects in the store. NOTE: do not use for large collections!"""
|
||||
self.__check_connection()
|
||||
with closing(self.__connection.cursor()) as c:
|
||||
rows = c.execute("SELECT * FROM objects").fetchall()
|
||||
return rows
|
||||
|
||||
def close(self):
|
||||
"""Close the connection to the database"""
|
||||
if self.__connection:
|
||||
self.__connection.close()
|
||||
self.__connection = None
|
||||
|
||||
def __initialise(self) -> None:
|
||||
self.__connection = sqlite3.connect(self._root_path)
|
||||
with closing(self.__connection.cursor()) as c:
|
||||
c.execute(
|
||||
""" CREATE TABLE IF NOT EXISTS objects(
|
||||
hash TEXT PRIMARY KEY,
|
||||
content TEXT
|
||||
) WITHOUT ROWID;"""
|
||||
)
|
||||
c.execute("PRAGMA journal_mode='wal';")
|
||||
c.execute("PRAGMA count_changes=OFF;")
|
||||
c.execute("PRAGMA temp_store=MEMORY;")
|
||||
self.__connection.commit()
|
||||
|
||||
def __check_connection(self):
|
||||
if not self.__connection:
|
||||
self.__connection = sqlite3.connect(self._root_path)
|
||||
|
||||
def __del__(self):
|
||||
self.__connection.close()
|
||||
|
||||
|
||||
# def _run_queue(queue: Queue, root_path: str):
|
||||
# if queue.empty():
|
||||
# return
|
||||
# print("RUN QUEUE")
|
||||
# conn = sqlite3.connect(root_path)
|
||||
# while not queue.empty():
|
||||
# data = queue.get()
|
||||
# with closing(conn.cursor()) as c:
|
||||
# c.execute(
|
||||
# "INSERT OR IGNORE INTO objects(hash, content) VALUES(?,?)",
|
||||
# (data[0], data[1]),
|
||||
# )
|
||||
# conn.commit()
|
||||
# conn.close()
|
||||
@@ -0,0 +1,105 @@
|
||||
import uuid
|
||||
import random
|
||||
import pytest
|
||||
import requests
|
||||
from specklepy.api.models import Stream
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.objects.geometry import Point
|
||||
from specklepy.objects.fakemesh import FakeMesh
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def host():
|
||||
return "localhost:3000"
|
||||
|
||||
|
||||
def seed_user(host):
|
||||
seed = uuid.uuid4().hex
|
||||
user_dict = {
|
||||
"email": f"{seed[0:7]}@spockle.com",
|
||||
"password": "$uper$3cr3tP@ss",
|
||||
"name": f"{seed[0:7]} Name",
|
||||
"company": "test spockle",
|
||||
}
|
||||
|
||||
r = requests.post(
|
||||
url=f"http://{host}/auth/local/register?challenge=pyspeckletests",
|
||||
data=user_dict,
|
||||
)
|
||||
print(r.url)
|
||||
access_code = r.url.split("access_code=")[1]
|
||||
|
||||
r_tokens = requests.post(
|
||||
url=f"http://{host}/auth/token",
|
||||
json={
|
||||
"appSecret": "spklwebapp",
|
||||
"appId": "spklwebapp",
|
||||
"accessCode": access_code,
|
||||
"challenge": "pyspeckletests",
|
||||
},
|
||||
)
|
||||
|
||||
user_dict.update(**r_tokens.json())
|
||||
|
||||
return user_dict
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def user_dict(host):
|
||||
return seed_user(host)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def second_user_dict(host):
|
||||
return seed_user(host)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def client(host, user_dict):
|
||||
client = SpeckleClient(host=host, use_ssl=False)
|
||||
client.authenticate(user_dict["token"])
|
||||
return client
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def sample_stream(client):
|
||||
stream = Stream(
|
||||
name="a sample stream for testing",
|
||||
description="a stream created for testing",
|
||||
isPublic=True,
|
||||
)
|
||||
stream.id = client.stream.create(stream.name, stream.description, stream.isPublic)
|
||||
return stream
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def mesh():
|
||||
mesh = FakeMesh()
|
||||
mesh.name = "my_mesh"
|
||||
mesh.vertices = [random.uniform(0, 10) for _ in range(1, 210)]
|
||||
mesh.faces = [i for i in range(1, 210)]
|
||||
mesh["@(100)colours"] = [random.uniform(0, 10) for _ in range(1, 210)]
|
||||
mesh["@()default_chunk"] = [random.uniform(0, 10) for _ in range(1, 210)]
|
||||
mesh.test_bases = [Base(name=f"test {i}") for i in range(1, 22)]
|
||||
mesh.detach_this = Base(name="predefined detached base")
|
||||
mesh["@detach"] = Base(name="detached base")
|
||||
mesh["@detached_list"] = [
|
||||
42,
|
||||
"some text",
|
||||
[1, 2, 3],
|
||||
Base(name="detached within a list"),
|
||||
]
|
||||
mesh.origin = Point(value=[4, 2, 0])
|
||||
return mesh
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def base():
|
||||
base = Base()
|
||||
base.name = "my_base"
|
||||
base.units = "millimetres"
|
||||
base.vertices = [random.uniform(0, 10) for _ in range(1, 120)]
|
||||
base.test_bases = [Base(name=i) for i in range(1, 22)]
|
||||
base["@detach"] = Base(name="detached base")
|
||||
return base
|
||||
@@ -0,0 +1,74 @@
|
||||
import pytest
|
||||
from specklepy.objects import Base
|
||||
from specklepy.api import operations
|
||||
from contextlib import ExitStack as does_not_raise
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"invalid_prop_name",
|
||||
[
|
||||
(""),
|
||||
("@"),
|
||||
("@@wow"),
|
||||
("this.is.bad"),
|
||||
("super/bad"),
|
||||
],
|
||||
)
|
||||
def test_empty_prop_names(invalid_prop_name: str) -> None:
|
||||
base = Base()
|
||||
with pytest.raises(ValueError):
|
||||
base[invalid_prop_name] = "🐛️"
|
||||
|
||||
|
||||
class FakeModel(Base):
|
||||
"""Just a test class type."""
|
||||
|
||||
foo: str = ""
|
||||
|
||||
|
||||
def test_new_type_registration() -> None:
|
||||
"""Test if a new subclass is registered into the type register."""
|
||||
assert Base.get_registered_type("FakeModel") == FakeModel
|
||||
assert Base.get_registered_type("🐺️") is None
|
||||
|
||||
|
||||
def test_fake_base_serialization() -> None:
|
||||
fake_model = FakeModel(foo="bar")
|
||||
|
||||
serialized = operations.serialize(fake_model)
|
||||
deserialized = operations.deserialize(serialized)
|
||||
|
||||
assert fake_model.get_id() == deserialized.get_id()
|
||||
|
||||
|
||||
def test_duplicate_speckle_type_raises_error():
|
||||
with pytest.raises(ValueError):
|
||||
|
||||
class NaughtyClass(Base, speckle_type="Base"):
|
||||
"""This class has a speckle_type that is already taken."""
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"forbidden_attribute_name, expectation",
|
||||
[
|
||||
("", pytest.raises(ValueError)),
|
||||
("@", pytest.raises(ValueError)),
|
||||
("@@", pytest.raises(ValueError)),
|
||||
("im.cheeky", pytest.raises(ValueError)),
|
||||
("im.cheeky", pytest.raises(ValueError)),
|
||||
("imgood", does_not_raise()),
|
||||
],
|
||||
)
|
||||
def test_attribute_name_validation(
|
||||
forbidden_attribute_name: str,
|
||||
expectation,
|
||||
base: Base,
|
||||
):
|
||||
with expectation:
|
||||
base[forbidden_attribute_name] = None
|
||||
|
||||
|
||||
def test_speckle_type_cannot_be_set(base: Base) -> None:
|
||||
assert base.speckle_type == "Base"
|
||||
base.speckle_type = "unset"
|
||||
assert base.speckle_type == "Base"
|
||||
@@ -0,0 +1,82 @@
|
||||
import pytest
|
||||
from specklepy.api import operations
|
||||
from specklepy.transports.server import ServerTransport
|
||||
from specklepy.api.models import Branch, Commit, Stream
|
||||
|
||||
|
||||
class TestBranch:
|
||||
@pytest.fixture(scope="module")
|
||||
def branch(self):
|
||||
return Branch(name="olive branch 🌿", description="a test branch")
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def updated_branch(self):
|
||||
return Branch(name="eucalyptus branch 🌿", description="an updated test branch")
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def stream(self, client):
|
||||
stream = Stream(
|
||||
name="a sample stream for testing",
|
||||
description="a stream created for testing",
|
||||
isPublic=True,
|
||||
)
|
||||
stream.id = client.stream.create(
|
||||
stream.name, stream.description, stream.isPublic
|
||||
)
|
||||
return stream
|
||||
|
||||
def test_branch_create(self, client, stream, branch):
|
||||
branch.id = client.branch.create(
|
||||
stream_id=stream.id, name=branch.name, description=branch.description
|
||||
)
|
||||
|
||||
assert isinstance(branch.id, str)
|
||||
|
||||
def test_branch_get(self, client, mesh, stream, branch):
|
||||
transport = ServerTransport(client=client, stream_id=stream.id)
|
||||
mesh.id = operations.send(mesh, transports=[transport])
|
||||
|
||||
client.commit.create(
|
||||
stream_id=stream.id,
|
||||
branch_name=branch.name,
|
||||
object_id=mesh.id,
|
||||
message="a commit for testing branch get",
|
||||
)
|
||||
|
||||
fetched_branch = client.branch.get(stream_id=stream.id, name=branch.name)
|
||||
|
||||
assert isinstance(fetched_branch, Branch)
|
||||
assert fetched_branch.name == branch.name
|
||||
assert fetched_branch.description == branch.description
|
||||
assert isinstance(fetched_branch.commits.items, list)
|
||||
assert isinstance(fetched_branch.commits.items[0], Commit)
|
||||
|
||||
def test_branch_list(self, client, stream, branch):
|
||||
branches = client.branch.list(stream_id=stream.id)
|
||||
print(branches)
|
||||
|
||||
assert isinstance(branches, list)
|
||||
assert len(branches) == 2
|
||||
assert isinstance(branches[0], Branch)
|
||||
assert branches[0].name == branch.name
|
||||
|
||||
def test_branch_update(self, client, stream, branch, updated_branch):
|
||||
updated = client.branch.update(
|
||||
stream_id=stream.id,
|
||||
branch_id=branch.id,
|
||||
name=updated_branch.name,
|
||||
description=updated_branch.description,
|
||||
)
|
||||
|
||||
fetched_branch = client.branch.get(
|
||||
stream_id=stream.id, name=updated_branch.name
|
||||
)
|
||||
|
||||
assert updated is True
|
||||
assert fetched_branch.name == updated_branch.name
|
||||
assert fetched_branch.description == updated_branch.description
|
||||
|
||||
def test_branch_delete(self, client, stream, branch):
|
||||
deleted = client.branch.delete(stream_id=stream.id, branch_id=branch.id)
|
||||
|
||||
assert deleted is True
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user