Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

💽 Import data functionality #114

Merged
merged 21 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
fd435d8
wip(data-import): created UI to import files
mrfelipemartins Sep 6, 2023
94cd579
Merge branch 'godot-4.x' into data-import
mrfelipemartins Sep 6, 2023
68673bd
feat(data-import): add possibility to import and merge pandora data f…
mrfelipemartins Sep 6, 2023
7a18a72
fix(data-import): accidental visibility change
mrfelipemartins Sep 6, 2023
7dd3058
fix(data): update data.pandora to uncompressed version
mrfelipemartins Sep 6, 2023
66c158b
Merge branch 'godot-4.x' into data-import
mrfelipemartins Sep 6, 2023
9d75e88
tests(data-import): add unit tests
mrfelipemartins Sep 6, 2023
c22a7e7
feat(data-import): fixed conflicts and added confirmations before imp…
mrfelipemartins Sep 6, 2023
84282db
Merge branch 'godot-4.x' into data-import
mrfelipemartins Sep 6, 2023
e8826e5
fix(data-import): fix vailidations
mrfelipemartins Sep 7, 2023
450b65c
Merge branch 'godot-4.x' into data-import
mrfelipemartins Sep 7, 2023
9eeeadf
Merge branch 'godot-4.x' into data-import
bitbrain Sep 7, 2023
cbaa462
Merge branch 'godot-4.x' into data-import
mrfelipemartins Sep 14, 2023
d6b52cf
Merge branch 'godot-4.x' into data-import
mrfelipemartins Sep 14, 2023
143043a
refactor
mrfelipemartins Sep 14, 2023
68781bc
move import tests to api
mrfelipemartins Sep 15, 2023
0fa1153
update tests
mrfelipemartins Sep 15, 2023
cb003e2
Merge branch 'godot-4.x' into data-import
mrfelipemartins Sep 15, 2023
a450de3
done?
mrfelipemartins Sep 15, 2023
9963666
done?
mrfelipemartins Sep 15, 2023
92993f7
:prayge:
mrfelipemartins Sep 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 40 additions & 1 deletion addons/pandora/api.gd
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ const ScriptUtil = preload("res://addons/pandora/util/script_util.gd")

signal data_loaded
signal data_loaded_failure
signal entity_added(entity: PandoraEntity)
signal entity_added(entity:PandoraEntity)
signal import_success(imported_count: int)
signal import_failed(reason: String)
signal import_calculation_ended(import_info: Dictionary)
signal import_calculation_failed(reason: String)
signal import_progress


var _context_manager: PandoraContextManager
Expand All @@ -27,6 +32,7 @@ func _enter_tree() -> void:
self._id_generator = PandoraIDGenerator.new()
self._entity_backend = PandoraEntityBackend.new(_id_generator)
self._entity_backend.entity_added.connect(func(entity): entity_added.emit(entity))
self._entity_backend.import_progress.connect(func(): import_progress.emit())
load_data()


Expand Down Expand Up @@ -145,6 +151,39 @@ func save_data() -> void:

EntityIdFileGenerator.regenerate_id_files(get_all_roots())

func calculate_import_data(path: String) -> int:
var imported_data = _storage._load_from_file(path)
var total_items = 0
if not imported_data.has("_entity_data"):
import_calculation_failed.emit("Provided file is invalid or is corrupted.")
else:
total_items = imported_data["_entity_data"]["_categories"].size() + imported_data["_entity_data"]["_entities"].size() + imported_data["_entity_data"]["_properties"].size()
if total_items == 0:
import_calculation_failed.emit("Provided file is empty.")
else:
import_calculation_ended.emit({
"total_categories": imported_data["_entity_data"]["_categories"].size(),
"total_entities": imported_data["_entity_data"]["_entities"].size(),
"total_properties": imported_data["_entity_data"]["_properties"].size(),
"path": path,
})
return total_items


func import_data(path: String) -> int:
var imported_data = _storage._load_from_file(path)
if not imported_data.has("_entity_data"):
import_failed.emit("Provided file is invalid or is corrupted.")
return 0

var imported_count = _entity_backend.import_data(imported_data["_entity_data"])
if imported_count == -1:
import_failed.emit("No data found in file.")
return 0

import_success.emit(imported_count)

return imported_count

func is_loaded() -> bool:
return _loaded
Expand Down
69 changes: 68 additions & 1 deletion addons/pandora/backend/entity_backend.gd
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ enum DropSection {
## Emitted when an entity (or category) gets created
signal entity_added(entity:PandoraEntity)

## Emitted when progress is made during data import
signal import_progress

# entity id -> PandoraEntity
var _entities:Dictionary = {}
Expand Down Expand Up @@ -354,6 +356,65 @@ func save_data() -> Dictionary:
"_properties": _serialize_data(_properties)
}

func import_data(data: Dictionary) -> int:
mrfelipemartins marked this conversation as resolved.
Show resolved Hide resolved
var imported_count: int = 0

# Deserialize imported data
var imported_categories: Dictionary = _deserialize_categories(data["_categories"])

for key in imported_categories:
if not _categories.has(imported_categories[key]._id):
_categories[key] = imported_categories[key]
import_progress.emit()
imported_count += 1

var imported_entities: Dictionary = _deserialize_entities(data["_entities"])

for key in imported_entities:
if not _entities.has(imported_entities[key]._id):
_entities[key] = imported_entities[key]
import_progress.emit()
imported_count += 1

var imported_properties: Dictionary = _deserialize_properties(data["_properties"])

for key in imported_properties:
if not _properties.has(imported_properties[key]._id):
_properties[key] = imported_properties[key]
import_progress.emit()
imported_count += 1

for key in _categories:
var category = _categories[key] as PandoraCategory
if category._category_id:
if not _categories.has(category._category_id):
push_error("Pandora error: category " + category._category_id + " on category does not exist")
return -1
var parent = _categories[category._category_id] as PandoraCategory
if not parent._children.has(category):
parent._children.append(category)
for key in _entities:
var entity = _entities[key] as PandoraEntity
if not _categories.has(entity._category_id):
push_error("Pandora error: category " + entity._category_id + " on entity does not exist")
return -1
var category = _categories[entity._category_id] as PandoraCategory
if not category._children.has(entity):
category._children.append(entity)
for key in _properties:
var property = _properties[key] as PandoraProperty
if not _categories.has(property._category_id):
push_error("Pandora error: category " + property._category_id + " on property does not exist")
return -1
var category = _categories[property._category_id] as PandoraCategory
if not category._properties.has(property):
category._properties.append(property)

for root_category in _root_categories:
_propagate_properties(root_category)

return imported_count


func _deserialize_entities(data:Array) -> Dictionary:
var dict = {}
Expand Down Expand Up @@ -386,7 +447,13 @@ func _deserialize_categories(data:Array) -> Dictionary:
dict[category._id] = category
if category._category_id == "":
# If category has no parent, it's a root category
_root_categories.append(category)
var root_category_exists = false
for existing_category in _root_categories:
if existing_category._id == category._id:
root_category_exists = true
break
if not root_category_exists:
_root_categories.append(category)
return dict


Expand Down
1 change: 1 addition & 0 deletions addons/pandora/icons/Import.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions addons/pandora/icons/Import.svg.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://3w10txrbpwmx"
path="res://.godot/imported/Import.svg-0c1716999ac5f94e8eb4af2cc2a46781.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://addons/pandora/icons/Import.svg"
dest_files=["res://.godot/imported/Import.svg-0c1716999ac5f94e8eb4af2cc2a46781.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ func _ready() -> void:


func popup() -> void:
visible = true
if tween and tween.is_running():
tween.stop()
self_modulate.a = 1.0
tween = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_CUBIC)
tween.finished.connect(func(): visible = false)
tween.tween_property(self, "self_modulate:a", 0.0, 1.5)\
.set_delay(2.0);
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ shadow_color = Color(0, 0.627451, 0.772549, 0.14902)
shadow_offset = Vector2(0, 0)

[node name="SaveLabel" type="Label"]
self_modulate = Color(1, 1, 1, 0)
theme_override_colors/font_color = Color(0.564706, 0.92549, 0, 1)
text = "Data saved!"
label_settings = SubResource("LabelSettings_ue3du")
Expand Down
24 changes: 24 additions & 0 deletions addons/pandora/ui/components/progress_bar/progress_bar.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@tool
class_name ImportProgressBar
extends MarginContainer

@onready var progress_bar = $ProgressBar

var total_steps: int = 100

# Called when the node enters the scene tree for the first time.
func _ready():
visible = false

func init(total_steps: int):
total_steps = total_steps
progress_bar.step = progress_bar.max_value / total_steps
visible = true

func advance():
progress_bar.value += progress_bar.step

func finish():
total_steps = 100
progress_bar.step = 0.01
visible = false
20 changes: 20 additions & 0 deletions addons/pandora/ui/components/progress_bar/progress_bar.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[gd_scene load_steps=3 format=3 uid="uid://dibfkp6i5uvgi"]

[ext_resource type="Script" path="res://addons/pandora/ui/components/progress_bar/progress_bar.gd" id="1_ifnan"]

[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_uy0ts"]
bg_color = Color(0.278431, 0.701961, 0.360784, 1)

[node name="ProgressContainer" type="MarginContainer"]
visible = false
offset_right = 100.0
offset_bottom = 37.0
theme_override_constants/margin_left = 5
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 5
script = ExtResource("1_ifnan")

[node name="ProgressBar" type="ProgressBar" parent="."]
layout_mode = 2
theme_override_styles/fill = SubResource("StyleBoxFlat_uy0ts")
53 changes: 53 additions & 0 deletions addons/pandora/ui/editor/import_dialog/import_dialog.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
@tool
extends Control

signal import_started(import_count: int)
signal import_ended(data: Array[PandoraEntity])

@onready var notification_dialog = %NotificationDialog
@onready var confirmation_dialog = %ConfirmationDialog
@onready var file_dialog = %FileDialog

func _ready():
file_dialog.file_selected.connect(_import_file)
Pandora.import_success.connect(self._on_import_success)
Pandora.import_failed.connect(self._on_import_failed)
Pandora.import_calculation_ended.connect(self._on_import_calculation_ended)
Pandora.import_calculation_failed.connect(self._on_import_calculation_failed)

func open():
file_dialog.popup_centered()

func _import_file(path: String) -> void:
Pandora.calculate_import_data(path)

func _on_import_calculation_failed(reason: String) -> void:
notification_dialog.title = "Import Failed!"
notification_dialog.dialog_text = reason
notification_dialog.popup_centered()

func _on_import_calculation_ended(import_info: Dictionary) -> void:
confirmation_dialog.title = "Confirm Import"
confirmation_dialog.dialog_text = "Found " + str(import_info["total_categories"]) + " Categories with " + str(import_info["total_entities"]) + " Entities. Would you like to proceed?"
confirmation_dialog.confirmed.connect(func(): self._start_import(import_info))
confirmation_dialog.popup_centered()

func _start_import(import_info: Dictionary) -> void:
import_started.emit(int(import_info["total_categories"]) + int(import_info["total_entities"]) + int(import_info["total_properties"]))
Pandora.import_data(import_info["path"])



func _on_import_success(imported_count: int = 0) -> void:
var data:Array[PandoraEntity] = []
data.assign(Pandora.get_all_roots())
notification_dialog.title = "Import Finished!"
notification_dialog.dialog_text = str(imported_count) + " records imported successfully!"
notification_dialog.popup_centered()
import_ended.emit(data)


func _on_import_failed(reason: String) -> void:
notification_dialog.title = "Import Failed!"
notification_dialog.dialog_text = reason
notification_dialog.popup_centered()
36 changes: 36 additions & 0 deletions addons/pandora/ui/editor/import_dialog/import_dialog.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[gd_scene load_steps=2 format=3 uid="uid://ceboo5esbe2ld"]

[ext_resource type="Script" path="res://addons/pandora/ui/editor/import_dialog/import_dialog.gd" id="1_4evpu"]

[node name="ImportDialog" type="Control"]
layout_mode = 3
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0
script = ExtResource("1_4evpu")

[node name="FileDialog" type="FileDialog" parent="."]
unique_name_in_owner = true
title = "Open a File"
initial_position = 1
size = Vector2i(800, 600)
transient = false
ok_button_text = "Open"
file_mode = 0
access = 2
filters = PackedStringArray("*.pandora")

[node name="NotificationDialog" type="AcceptDialog" parent="."]
unique_name_in_owner = true
title = "Import Finished!"
initial_position = 1
size = Vector2i(274, 100)
transient = false
dialog_text = "0 records imported successfully!"

[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."]
unique_name_in_owner = true
title = "Confirm Import"
initial_position = 1
size = Vector2i(504, 100)
dialog_text = "Found 10 Categories with 4 Entities. Would you like to proceed?"