From a1e198c87e2c59170738ff5c7a320c4e7f4e03a8 Mon Sep 17 00:00:00 2001 From: Jann Stute Date: Thu, 24 Apr 2025 19:01:00 +0200 Subject: [PATCH 1/4] refactor: simplify datetime widget creation --- .../qt/widgets/preview/field_containers.py | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/tagstudio/qt/widgets/preview/field_containers.py b/src/tagstudio/qt/widgets/preview/field_containers.py index 76dc48600..93255bbdd 100644 --- a/src/tagstudio/qt/widgets/preview/field_containers.py +++ b/src/tagstudio/qt/widgets/preview/field_containers.py @@ -375,21 +375,20 @@ def write_container(self, index: int, field: BaseField, is_mixed: bool = False): ) elif field.type.type == FieldTypeEnum.DATETIME: + logger.info("[FieldContainers][write_container] Datetime Field", field=field) if not is_mixed: + container.set_title(field.type.name) + container.set_inline(False) try: - container.set_title(field.type.name) - container.set_inline(False) - # TODO: Localize this and/or add preferences. - date = dt.strptime(field.value, "%Y-%m-%d %H:%M:%S") title = f"{field.type.name} (Date)" - inner_widget = TextWidget(title, date.strftime("%D - %r")) - container.set_inner_widget(inner_widget) - except Exception: - container.set_title(field.type.name) - container.set_inline(False) + # TODO: Localize this and/or add preferences. + text = dt.strptime(field.value, "%Y-%m-%d %H:%M:%S").strftime("%D - %r") + except ValueError: title = f"{field.type.name} (Date) (Unknown Format)" - inner_widget = TextWidget(title, str(field.value)) - container.set_inner_widget(inner_widget) + text = str(field.value) + + inner_widget = TextWidget(title, text) + container.set_inner_widget(inner_widget) container.set_edit_callback() container.set_remove_callback( From 736407736d75ca52a19d58fe8e08e7d5fecd3516 Mon Sep 17 00:00:00 2001 From: Jann Stute Date: Thu, 24 Apr 2025 19:20:41 +0200 Subject: [PATCH 2/4] refactor: fix warnings --- .../qt/widgets/preview/field_containers.py | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/tagstudio/qt/widgets/preview/field_containers.py b/src/tagstudio/qt/widgets/preview/field_containers.py index 93255bbdd..7e384e326 100644 --- a/src/tagstudio/qt/widgets/preview/field_containers.py +++ b/src/tagstudio/qt/widgets/preview/field_containers.py @@ -109,8 +109,9 @@ def update_from_entry(self, entry_id: int, update_badges: bool = True): """Update tags and fields from a single Entry source.""" logger.warning("[FieldContainers] Updating Selection", entry_id=entry_id) - self.cached_entries = [self.lib.get_entry_full(entry_id)] - entry = self.cached_entries[0] + entry = self.lib.get_entry_full(entry_id) + assert entry is not None + self.cached_entries = [entry] self.update_granular(entry.tags, entry.fields, update_badges) def update_granular( @@ -177,6 +178,7 @@ def add_to_cluster(tag_id: int, p_ids: list[int] | None = None): """ tag_obj = self.lib.get_tag(tag_id) # Get full object if p_ids is None: + assert tag_obj is not None p_ids = tag_obj.parent_ids for p_id in p_ids: @@ -186,6 +188,7 @@ def add_to_cluster(tag_id: int, p_ids: list[int] | None = None): if tag_id not in cluster_map[p_id]: cluster_map[p_id].add(tag_id) p_tag = self.lib.get_tag(p_id) # Get full object + assert p_tag is not None if p_tag.parent_ids: add_to_cluster( tag_id, @@ -201,7 +204,7 @@ def add_to_cluster(tag_id: int, p_ids: list[int] | None = None): logger.info("[FieldContainers] Cluster Map", cluster_map=cluster_map) # Initialize all categories from parents. - tags_ = {self.lib.get_tag(x) for x in exhausted} + tags_ = {t for tid in exhausted if (t := self.lib.get_tag(tid)) is not None} for tag in tags_: if tag.is_category: cats[tag] = set() @@ -218,20 +221,28 @@ def add_to_cluster(tag_id: int, p_ids: list[int] | None = None): ) if final_tags := cluster_map.get(key.id, set()).union([key.id]): - cats[key] = {self.lib.get_tag(x) for x in final_tags if x in base_tag_ids} - added_ids = added_ids.union({x for x in final_tags if x in base_tag_ids}) + cats[key] = { + t + for tid in final_tags + if tid in base_tag_ids and (t := self.lib.get_tag(tid)) is not None + } + added_ids = added_ids.union({tid for tid in final_tags if tid in base_tag_ids}) # Add remaining tags to None key (general case). - cats[None] = {self.lib.get_tag(x) for x in base_tag_ids if x not in added_ids} + cats[None] = { + t + for tid in base_tag_ids + if tid not in added_ids and (t := self.lib.get_tag(tid)) is not None + } logger.info( - f"[FieldContainers] [{key}] Key cluster: None, general case!", - general_tags=cats[key], + "[FieldContainers] Key cluster: None, general case!", + general_tags=cats[None], added=added_ids, base_tag_ids=base_tag_ids, ) # Remove unused categories - empty: list[Tag] = [] + empty: list[Tag | None] = [] for k, v in list(cats.items()): if not v: empty.append(k) @@ -326,7 +337,7 @@ def write_container(self, index: int, field: BaseField, is_mixed: bool = False): ) if "pytest" in sys.modules: # for better testability - container.modal = modal + container.modal = modal # type: ignore container.set_edit_callback(modal.show) container.set_remove_callback( From ee2562bbbd73aa0cd072698a6c1ceaf58ef057ca Mon Sep 17 00:00:00 2001 From: Jann Stute Date: Thu, 24 Apr 2025 19:34:47 +0200 Subject: [PATCH 3/4] feat: text modal to enter date --- .../qt/widgets/preview/field_containers.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/tagstudio/qt/widgets/preview/field_containers.py b/src/tagstudio/qt/widgets/preview/field_containers.py index 7e384e326..c385f9066 100644 --- a/src/tagstudio/qt/widgets/preview/field_containers.py +++ b/src/tagstudio/qt/widgets/preview/field_containers.py @@ -393,7 +393,7 @@ def write_container(self, index: int, field: BaseField, is_mixed: bool = False): try: title = f"{field.type.name} (Date)" # TODO: Localize this and/or add preferences. - text = dt.strptime(field.value, "%Y-%m-%d %H:%M:%S").strftime("%D - %r") + text = dt.strptime(field.value or "", "%Y-%m-%d %H:%M:%S").strftime("%D - %r") except ValueError: title = f"{field.type.name} (Date) (Unknown Format)" text = str(field.value) @@ -401,7 +401,19 @@ def write_container(self, index: int, field: BaseField, is_mixed: bool = False): inner_widget = TextWidget(title, text) container.set_inner_widget(inner_widget) - container.set_edit_callback() + modal = PanelModal( # TODO Replace with proper date picker including timezone etc. + EditTextLine(field.value), + title=f"Edit {field.type.name} in 'YYYY-MM-DD HH:MM:SS' format", + window_title=f"Edit {field.type.name}", + save_callback=( + lambda content: ( + self.update_field(field, content), # type: ignore + self.update_from_entry(self.cached_entries[0].id), + ) + ), + ) + + container.set_edit_callback(modal.show) container.set_remove_callback( lambda: self.remove_message_box( prompt=self.remove_field_prompt(field.type.name), From 6b5210e18e6c0313ab30e0cdc9d544fe68ea19c8 Mon Sep 17 00:00:00 2001 From: Jann Stute Date: Thu, 24 Apr 2025 19:47:12 +0200 Subject: [PATCH 4/4] chore: change ignore comment to only apply to pyright not mypy --- src/tagstudio/qt/widgets/preview/field_containers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tagstudio/qt/widgets/preview/field_containers.py b/src/tagstudio/qt/widgets/preview/field_containers.py index c385f9066..b29fe580d 100644 --- a/src/tagstudio/qt/widgets/preview/field_containers.py +++ b/src/tagstudio/qt/widgets/preview/field_containers.py @@ -337,7 +337,7 @@ def write_container(self, index: int, field: BaseField, is_mixed: bool = False): ) if "pytest" in sys.modules: # for better testability - container.modal = modal # type: ignore + container.modal = modal # pyright: ignore[reportAttributeAccessIssue] container.set_edit_callback(modal.show) container.set_remove_callback(