| 20 | component.from_json(element) |
| 21 | target_list.append(component) |
| 22 | class ProjectBody: |
| 23 | class BaseProjectBodyComponent(ABC): |
| 24 | def __init__(self, **entries): |
| 25 | # Attributes every object needs to have: |
| 26 | self.id = None |
| 27 | # Update attributes from entries dict: |
| 28 | self.__dict__.update(entries) |
| 29 | @abstractmethod |
| 30 | def from_json(self, data: dict): |
| 31 | pass |
| 32 | @abstractmethod |
| 33 | def to_json(self): |
| 34 | pass |
| 35 | def _generate_new_id(self): |
| 36 | """ |
| 37 | Generates a new id and updates the id. |
| 38 | Warning: |
| 39 | When done on Block objects, the next_id attribute of the parent block and the parent_id attribute of the next block will NOT be updated by this method. |
| 40 | """ |
| 41 | self.id = ''.join(random.choices(string.ascii_letters + string.digits, k=20)) |
| 42 | class Block(BaseProjectBodyComponent): |
| 43 | # Thanks to @MonkeyBean2 for some scripts |
| 44 | def from_json(self, data: dict): |
| 45 | self.opcode = data["opcode"] # The name of the block |
| 46 | self.next_id = data.get("next", None) # The id of the block attached below this block |
| 47 | self.parent_id = data.get("parent", None) # The id of the block that this block is attached to |
| 48 | self.input_data = data.get("inputs", None) # The blocks inside of the block (if the block is a loop or an if clause for example) |
| 49 | self.fields = data.get("fields", None) # The values inside the block's inputs |
| 50 | self.shadow = data.get("shadow", False) # Whether the block is displayed with a shadow |
| 51 | self.topLevel = data.get("topLevel", False) # Whether the block has no parent |
| 52 | self.mutation = data.get("mutation", None) # For custom blocks |
| 53 | self.x = data.get("x", None) # x position if topLevel |
| 54 | self.y = data.get("y", None) # y position if topLevel |
| 55 | def to_json(self): |
| 56 | output = {"opcode": self.opcode, "next": self.next_id, "parent": self.parent_id, "inputs": self.input_data, |
| 57 | "fields": self.fields, "shadow": self.shadow, "topLevel": self.topLevel, |
| 58 | "mutation": self.mutation, "x": self.x, "y": self.y} |
| 59 | return {k: v for k, v in output.items() if v} |
| 60 | def attached_block(self): |
| 61 | return self.sprite.block_by_id(self.next_id) |
| 62 | def previous_block(self): |
| 63 | return self.sprite.block_by_id(self.parent_id) |
| 64 | def top_level_block(self): |
| 65 | block = self |
| 66 | return block |
| 67 | def previous_chain(self): |
| 68 | # to implement: a method that detects circular block chains (to make sure this method terminates) |
| 69 | chain = [] |
| 70 | block = self |
| 71 | while block.parent_id is not None: |
| 72 | block = block.previous_block() |
| 73 | chain.insert(0, block) |
| 74 | return chain |
| 75 | def attached_chain(self): |
| 76 | chain = [] |
| 77 | block = self |
| 78 | while block.next_id is not None: |
| 79 | block = block.attached_block() |
no outgoing calls
no test coverage detected