Compare commits
	
		
			5 commits
		
	
	
		
			74ed6516b4
			...
			fea33eb62e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| fea33eb62e | |||
| be682f4754 | |||
| 83ae35006b | |||
| 1bc94bfd0a | |||
| 547a65e19f | 
					 10 changed files with 207 additions and 131 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -1,2 +1,2 @@ | ||||||
| *.pyc | *.pyc | ||||||
| *.db | *.db* | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								src/tt/constants.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/tt/constants.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | 
 | ||||||
|  | SPECIAL_DATES_PIECES = { | ||||||
|  |     "future": (3000,1,1), | ||||||
|  |     "unclassified": (1999,1,1), | ||||||
|  | } | ||||||
|  | SPECIAL_DATES_DISPLAY = { | ||||||
|  |     "3000-01-01": "[#333333]future[/]", | ||||||
|  |     "1999-01-01": "[#cccccc]unclassified[/]", | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @ -4,7 +4,7 @@ from peewee import fn | ||||||
| from peewee import Case, Value | from peewee import Case, Value | ||||||
| from ..db import db, Task, SavedSearch | from ..db import db, Task, SavedSearch | ||||||
| from .. import config | from .. import config | ||||||
| 
 | from ..constants import SPECIAL_DATES_PIECES | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_task(item_id: int) -> Task: | def get_task(item_id: int) -> Task: | ||||||
|  | @ -14,7 +14,7 @@ def get_task(item_id: int) -> Task: | ||||||
| def add_task( | def add_task( | ||||||
|     text: str, |     text: str, | ||||||
|     status: str, |     status: str, | ||||||
|     due: datetime | None = None, |     due: datetime | str = SPECIAL_DATES_PIECES["unclassified"], | ||||||
|     type: str = "", |     type: str = "", | ||||||
|     project: str = "", |     project: str = "", | ||||||
| ) -> Task: | ) -> Task: | ||||||
|  | @ -84,8 +84,8 @@ def get_tasks( | ||||||
|         query = query.where(fn.Lower(Task.text).contains(search_text.lower())) |         query = query.where(fn.Lower(Task.text).contains(search_text.lower())) | ||||||
|     if statuses: |     if statuses: | ||||||
|         query = query.where(Task.status.in_(statuses)) |         query = query.where(Task.status.in_(statuses)) | ||||||
|     #if projects: |     if projects: | ||||||
|     #     query = query.where(Task.project.in_(projects)) |          query = query.where(Task.project.in_(projects)) | ||||||
| 
 | 
 | ||||||
|     sort_expressions = _parse_sort_string(sort, statuses) |     sort_expressions = _parse_sort_string(sort, statuses) | ||||||
|     query = query.order_by(*sort_expressions) |     query = query.order_by(*sort_expressions) | ||||||
|  |  | ||||||
							
								
								
									
										89
									
								
								src/tt/tui/columns.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/tt/tui/columns.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,89 @@ | ||||||
|  | import datetime | ||||||
|  | from ..utils import ( | ||||||
|  |     get_color_enum, | ||||||
|  |     get_colored_date, | ||||||
|  | ) | ||||||
|  | from .modals import ChoiceModal, DateModal | ||||||
|  | 
 | ||||||
|  | class NotifyValidationError(Exception): | ||||||
|  |     """will notify and continue if raised""" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ELLIPSIS = "…" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class TableColumnConfig: | ||||||
|  |     def __init__( | ||||||
|  |         self, | ||||||
|  |         field: str, | ||||||
|  |         display_name: str, | ||||||
|  |         *, | ||||||
|  |         default=None, | ||||||
|  |         enable_editor=False, | ||||||
|  |         filterable=True, | ||||||
|  |         read_only=False, | ||||||
|  |     ): | ||||||
|  |         self.field = field | ||||||
|  |         self.display_name = display_name | ||||||
|  |         self.default = default | ||||||
|  |         self.enable_editor = enable_editor | ||||||
|  |         self.filterable = filterable | ||||||
|  |         self.read_only = read_only | ||||||
|  | 
 | ||||||
|  |     def preprocess(self, val): | ||||||
|  |         return val  # no-op | ||||||
|  | 
 | ||||||
|  |     def start_change(self, app, current_value): | ||||||
|  |         if current_value.endswith(ELLIPSIS): | ||||||
|  |             app.action_start_edit() | ||||||
|  |         else: | ||||||
|  |             # default edit mode | ||||||
|  |             app._show_input("edit", current_value) | ||||||
|  | 
 | ||||||
|  |     def format_for_display(self, val): | ||||||
|  |         val = str(val) | ||||||
|  |         if "\n" in val: | ||||||
|  |             val = val.split("\n")[0] + ELLIPSIS | ||||||
|  |         return val | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class EnumColumnConfig(TableColumnConfig): | ||||||
|  |     def __init__(self, field: str, display_name: str, enum, **kwargs): | ||||||
|  |         super().__init__(field, display_name, **kwargs) | ||||||
|  |         self.enum = enum | ||||||
|  | 
 | ||||||
|  |     def preprocess(self, val): | ||||||
|  |         if val in self.enum: | ||||||
|  |             return val | ||||||
|  |         else: | ||||||
|  |             raise NotifyValidationError(f"Invalid value {val}. Use: {list(self.enum)}") | ||||||
|  | 
 | ||||||
|  |     def format_for_display(self, val): | ||||||
|  |         return get_color_enum(val, self.enum) | ||||||
|  | 
 | ||||||
|  |     def start_change(self, app, current_value): | ||||||
|  |         # a weird hack? pass app here and correct modal gets pushed | ||||||
|  |         app.push_screen(ChoiceModal(self.enum, current_value), app.apply_change) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DateColumnConfig(TableColumnConfig): | ||||||
|  |     def preprocess(self, val): | ||||||
|  |         try: | ||||||
|  |             return datetime.datetime.strptime(val, "%Y-%m-%d") | ||||||
|  |         except ValueError: | ||||||
|  |             raise NotifyValidationError("Invalid date format. Use YYYY-MM-DD") | ||||||
|  | 
 | ||||||
|  |     def format_for_display(self, val): | ||||||
|  |         return get_colored_date(val) | ||||||
|  | 
 | ||||||
|  |     def start_change(self, app, current_value): | ||||||
|  |         app.push_screen(DateModal(current_value), app.apply_change) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def get_col_cls(field_type): | ||||||
|  |     return { | ||||||
|  |         "text": TableColumnConfig, | ||||||
|  |         "enum": EnumColumnConfig, | ||||||
|  |         "date": DateColumnConfig, | ||||||
|  |     }[field_type] | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| import datetime |  | ||||||
| from textual.app import App | from textual.app import App | ||||||
| from textual.widgets import ( | from textual.widgets import ( | ||||||
|     DataTable, |     DataTable, | ||||||
|  | @ -13,86 +12,10 @@ from ..utils import ( | ||||||
|     remove_rich_tag, |     remove_rich_tag, | ||||||
|     filter_to_string, |     filter_to_string, | ||||||
|     get_text_from_editor, |     get_text_from_editor, | ||||||
|     get_color_enum, |  | ||||||
|     get_colored_date, |  | ||||||
| ) | ) | ||||||
| from .keymodal import KeyModal | from .keymodal import KeyModal | ||||||
| from .modals import ChoiceModal, DateModal, ConfirmModal | from .modals import ConfirmModal | ||||||
| 
 | from .columns import get_col_cls | ||||||
| 
 |  | ||||||
| class NotifyValidationError(Exception): |  | ||||||
|     """will notify and continue if raised""" |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ELLIPSIS = "…" |  | ||||||
| 
 |  | ||||||
| class TableColumnConfig: |  | ||||||
|     def __init__( |  | ||||||
|         self, |  | ||||||
|         field: str, |  | ||||||
|         display_name: str, |  | ||||||
|         *, |  | ||||||
|         default=None, |  | ||||||
|         enable_editor=False, |  | ||||||
|         filterable=True, |  | ||||||
|         read_only=False, |  | ||||||
|     ): |  | ||||||
|         self.field = field |  | ||||||
|         self.display_name = display_name |  | ||||||
|         self.default = default |  | ||||||
|         self.enable_editor = enable_editor |  | ||||||
|         self.filterable = filterable |  | ||||||
|         self.read_only = read_only |  | ||||||
| 
 |  | ||||||
|     def preprocess(self, val): |  | ||||||
|         return val  # no-op |  | ||||||
| 
 |  | ||||||
|     def start_change(self, app, current_value): |  | ||||||
|         if current_value.endswith(ELLIPSIS): |  | ||||||
|             app.action_start_edit() |  | ||||||
|         else: |  | ||||||
|             # default edit mode |  | ||||||
|             app._show_input("edit", current_value) |  | ||||||
| 
 |  | ||||||
|     def format_for_display(self, val): |  | ||||||
|         val = str(val) |  | ||||||
|         if "\n" in val: |  | ||||||
|             val = val.split("\n")[0] + ELLIPSIS |  | ||||||
|         return val |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class EnumColumnConfig(TableColumnConfig): |  | ||||||
|     def __init__(self, field: str, display_name: str, enum, **kwargs): |  | ||||||
|         super().__init__(field, display_name, **kwargs) |  | ||||||
|         self.enum = enum |  | ||||||
| 
 |  | ||||||
|     def preprocess(self, val): |  | ||||||
|         if val in self.enum: |  | ||||||
|             return val |  | ||||||
|         else: |  | ||||||
|             raise NotifyValidationError(f"Invalid value {val}. Use: {list(self.enum)}") |  | ||||||
| 
 |  | ||||||
|     def format_for_display(self, val): |  | ||||||
|         return get_color_enum(val, self.enum) |  | ||||||
| 
 |  | ||||||
|     def start_change(self, app, current_value): |  | ||||||
|         # a weird hack? pass app here and correct modal gets pushed |  | ||||||
|         app.push_screen(ChoiceModal(self.enum, current_value), app.apply_change) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class DateColumnConfig(TableColumnConfig): |  | ||||||
|     def preprocess(self, val): |  | ||||||
|         try: |  | ||||||
|             return datetime.strptime(val, "%Y-%m-%d") |  | ||||||
|         except ValueError: |  | ||||||
|             raise NotifyValidationError("Invalid date format. Use YYYY-MM-DD") |  | ||||||
| 
 |  | ||||||
|     def format_for_display(self, val): |  | ||||||
|         return get_colored_date(val) |  | ||||||
| 
 |  | ||||||
|     def start_change(self, app, current_value): |  | ||||||
|         app.push_screen(DateModal(current_value), app.apply_change) |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -187,11 +110,7 @@ class TableEditor(App): | ||||||
|         # set up columns |         # set up columns | ||||||
|         for col in view["columns"]: |         for col in view["columns"]: | ||||||
|             field_type = col.get("field_type", "text") |             field_type = col.get("field_type", "text") | ||||||
|             field_cls = { |             field_cls = get_col_cls(field_type) | ||||||
|                 "text": TableColumnConfig, |  | ||||||
|                 "enum": EnumColumnConfig, |  | ||||||
|                 "date": DateColumnConfig, |  | ||||||
|             }[field_type] |  | ||||||
|             field_name = col["field_name"] |             field_name = col["field_name"] | ||||||
|             display_name = col.get("display_name", field_name.title()) |             display_name = col.get("display_name", field_name.title()) | ||||||
|             default = col.get("default") |             default = col.get("default") | ||||||
|  |  | ||||||
|  | @ -37,6 +37,7 @@ class KeyModal(ModalScreen): | ||||||
|         for binding in self.app.BINDINGS: |         for binding in self.app.BINDINGS: | ||||||
|             if binding[0] not in ["h", "j", "k", "l", "g", "G", "escape"]: |             if binding[0] not in ["h", "j", "k", "l", "g", "G", "escape"]: | ||||||
|                 table.add_row(binding[0], binding[2]) |                 table.add_row(binding[0], binding[2]) | ||||||
|  |         # TODO: MRO? | ||||||
| 
 | 
 | ||||||
|         yield Static("tt keybindings", classes="title") |         yield Static("tt keybindings", classes="title") | ||||||
|         yield Static(table) |         yield Static(table) | ||||||
|  |  | ||||||
|  | @ -1,8 +1,12 @@ | ||||||
|  | import datetime | ||||||
| from textual.screen import ModalScreen | from textual.screen import ModalScreen | ||||||
| from textual.binding import Binding | from textual.binding import Binding | ||||||
| from textual.widgets import RadioSet, RadioButton, Label | from textual.widgets import Label | ||||||
| from .. import config | from textual.containers import Horizontal, Vertical | ||||||
|  | from textual.reactive import reactive | ||||||
| from ..utils import get_color_enum | from ..utils import get_color_enum | ||||||
|  | from ..constants import SPECIAL_DATES_PIECES | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class ConfirmModal(ModalScreen): | class ConfirmModal(ModalScreen): | ||||||
|     CSS = """ |     CSS = """ | ||||||
|  | @ -42,42 +46,56 @@ class ChoiceModal(ModalScreen): | ||||||
|     ChoiceModal Label { |     ChoiceModal Label { | ||||||
|         height: 1; |         height: 1; | ||||||
|     } |     } | ||||||
|  |     ChoiceModal Label#selected { | ||||||
|  |         background: white; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     BINDINGS = [ |     BINDINGS = [ | ||||||
|         ("j,tab", "cursor_down", "Down"), |         ("j,tab", "cursor_down", "Down"), | ||||||
|         ("k", "cursor_up", "Up"), |         ("k,shift+tab", "cursor_up", "Up"), | ||||||
|         Binding("enter", "select", "Select", priority=False), |         Binding("enter", "select", "Select", priority=True), | ||||||
|         ("escape", "cancel", "cancel"), |         ("escape", "cancel", "cancel"), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     def __init__(self, enum, selected): |     def __init__(self, enum, selected): | ||||||
|         self._enum = enum |         self._enum = enum | ||||||
|         self.selected = selected |         self.enum_by_idx = list(self._enum) | ||||||
|  |         # selection index | ||||||
|  |         self.sel_idx = 0 | ||||||
|  |         # convert value back to index for initial selection | ||||||
|  |         for idx, e in enumerate(self._enum): | ||||||
|  |             if e.value == selected: | ||||||
|  |                 self.sel_idx = idx | ||||||
|  |                 break | ||||||
|         super().__init__() |         super().__init__() | ||||||
| 
 | 
 | ||||||
|     def compose(self): |     def compose(self): | ||||||
|         yield RadioSet( |         for idx, e in enumerate(self._enum): | ||||||
|             *[ |             yield Label( | ||||||
|                 RadioButton( |                 ("> " if idx == self.sel_idx else "  ") + | ||||||
|                     get_color_enum(e.value, config.STATUSES), value=self.selected == str(e.value) |                      get_color_enum(e.value, self._enum), | ||||||
|                 ) |                 classes="selected" if idx == self.sel_idx else "", | ||||||
|                 for e in self._enum |  | ||||||
|             ] |  | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|  |     def _move_cursor(self, dir): | ||||||
|  |         labels = self.query(Label) | ||||||
|  |         # reset old | ||||||
|  |         labels[self.sel_idx].update("  " + get_color_enum(self.enum_by_idx[self.sel_idx], self._enum)) | ||||||
|  |         # move cursor | ||||||
|  |         self.sel_idx = (self.sel_idx + dir) % len(self._enum) | ||||||
|  |         # reset new | ||||||
|  |         labels[self.sel_idx].update("> " + get_color_enum(self.enum_by_idx[self.sel_idx], self._enum)) | ||||||
|  | 
 | ||||||
|     def action_cursor_down(self): |     def action_cursor_down(self): | ||||||
|         self.query_one(RadioSet).action_next_button() |         self._move_cursor(1) | ||||||
| 
 | 
 | ||||||
|     def action_cursor_up(self): |     def action_cursor_up(self): | ||||||
|         self.query_one(RadioSet).action_previous_button() |         self._move_cursor(-1) | ||||||
| 
 | 
 | ||||||
|     def action_select(self): |     async def action_select(self): | ||||||
|         rs = self.query_one(RadioSet) |         self.dismiss(self.enum_by_idx[self.sel_idx]) | ||||||
|         # TODO: this doesn't work |  | ||||||
|         #rs.action_toggle_button() |  | ||||||
|         pressed = rs.pressed_button |  | ||||||
|         self.dismiss(str(pressed.label)) |  | ||||||
| 
 | 
 | ||||||
|     def action_cancel(self): |     def action_cancel(self): | ||||||
|         self.app.pop_screen() |         self.app.pop_screen() | ||||||
|  | @ -86,37 +104,71 @@ class ChoiceModal(ModalScreen): | ||||||
| class DateModal(ModalScreen): | class DateModal(ModalScreen): | ||||||
|     CSS = """ |     CSS = """ | ||||||
|     DateModal { |     DateModal { | ||||||
|         layout: horizontal; |  | ||||||
|         align: center middle; |         align: center middle; | ||||||
|         background: $primary 30%; |         background: $primary 30%; | ||||||
|     } |     } | ||||||
|  |     DateModal Vertical { | ||||||
|  |         border: double teal; | ||||||
|  |         height: 10; | ||||||
|  |         width: 50; | ||||||
|  |     } | ||||||
|  |     DateModal Horizonal { | ||||||
|  |     } | ||||||
|     DateModal Label { |     DateModal Label { | ||||||
|         border: solid grey; |         border: solid white; | ||||||
|  |         align: center middle; | ||||||
|     } |     } | ||||||
|     DateModal Label.selected-date { |     DateModal Label.selected-date { | ||||||
|         border: solid green; |         border: solid green; | ||||||
|     } |     } | ||||||
|  |     DateModal Label.hints { | ||||||
|  |         border: solid grey; | ||||||
|  |         height: 4; | ||||||
|  |     } | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     BINDINGS = [ |     BINDINGS = [ | ||||||
|         ("j", "cursor_down", "Down"), |         ("j", "cursor_down", "Down"), | ||||||
|         ("k", "cursor_up", "Up"), |         ("k", "cursor_up", "Up"), | ||||||
|         ("h,tab", "cursor_left", "Left"), |         ("h,shift+tab", "cursor_left", "Left"), | ||||||
|         ("l", "cursor_right", "Right"), |         ("l,tab", "cursor_right", "Right"), | ||||||
|  |         ("f", "future", "Future"), | ||||||
|  |         ("t", "today", "Today"), | ||||||
|  |         ("u", "unclassified", "Unclassified"), | ||||||
|         Binding("enter", "select", "Select", priority=True), |         Binding("enter", "select", "Select", priority=True), | ||||||
|         ("escape", "cancel", "cancel"), |         ("escape", "cancel", "cancel"), | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|  |     pieces = reactive([0, 0, 0], recompose=True) | ||||||
|  | 
 | ||||||
|     def __init__(self, date): |     def __init__(self, date): | ||||||
|         self.pieces = [int(p) for p in date.split("-")] |  | ||||||
|         self.selected = 1  # start on month |  | ||||||
|         super().__init__() |         super().__init__() | ||||||
|  |         if date in SPECIAL_DATES_PIECES: | ||||||
|  |             self.pieces = list(SPECIAL_DATES_PIECES[date]) | ||||||
|  |         elif date: | ||||||
|  |             self.pieces = [int(p) for p in date.split("-")] | ||||||
|  |         else: | ||||||
|  |             self.action_today() | ||||||
|  |         self.selected = 1  # start on month | ||||||
| 
 | 
 | ||||||
|     def compose(self): |     def compose(self): | ||||||
|         for idx, piece in enumerate(self.pieces): |         with Vertical(): | ||||||
|             yield Label( |             with Horizontal(): | ||||||
|                 str(piece), classes="selected-date" if idx == self.selected else "" |                 yield Label(f"{self.pieces[0]}") | ||||||
|             ) |                 yield Label(f"{self.pieces[1]}", classes="selected-date") | ||||||
|  |                 yield Label(f"{self.pieces[2]}") | ||||||
|  |             yield Label("""(h/j/k/l) move (enter) confirm (esc) quit | ||||||
|  | (p)ast (t)oday (f)uture""", classes="hints") | ||||||
|  | 
 | ||||||
|  |     def action_future(self): | ||||||
|  |         self.pieces = list(SPECIAL_DATES_PIECES["future"]) | ||||||
|  | 
 | ||||||
|  |     def action_unclassified(self): | ||||||
|  |         self.pieces = list(SPECIAL_DATES_PIECES["unclassified"]) | ||||||
|  | 
 | ||||||
|  |     def action_today(self): | ||||||
|  |         today = datetime.date.today() | ||||||
|  |         self.pieces = [today.year, today.month, today.day] | ||||||
| 
 | 
 | ||||||
|     def action_cursor_left(self): |     def action_cursor_left(self): | ||||||
|         # cycle Y/M/D |         # cycle Y/M/D | ||||||
|  | @ -152,8 +204,7 @@ class DateModal(ModalScreen): | ||||||
|         if cur_value > self.max_for(self.selected): |         if cur_value > self.max_for(self.selected): | ||||||
|             cur_value = 1 |             cur_value = 1 | ||||||
|         self.pieces[self.selected] = cur_value |         self.pieces[self.selected] = cur_value | ||||||
|         cur_label = self.query("Label")[self.selected] |         self.mutate_reactive(DateModal.pieces) | ||||||
|         cur_label.update(str(cur_value)) |  | ||||||
| 
 | 
 | ||||||
|     def action_cursor_down(self): |     def action_cursor_down(self): | ||||||
|         self._move_piece(-1) |         self._move_piece(-1) | ||||||
|  | @ -178,8 +229,7 @@ class DateModal(ModalScreen): | ||||||
|             event.prevent_default() |             event.prevent_default() | ||||||
| 
 | 
 | ||||||
|     def action_select(self): |     def action_select(self): | ||||||
|         date = "-".join(str(p) for p in self.pieces) |         self.dismiss("-".join(str(p) for p in self.pieces)) | ||||||
|         self.dismiss(date) |  | ||||||
| 
 | 
 | ||||||
|     def action_cancel(self): |     def action_cancel(self): | ||||||
|         self.app.pop_screen() |         self.app.pop_screen() | ||||||
|  |  | ||||||
|  | @ -56,10 +56,8 @@ class TT(TableEditor): | ||||||
|     def refresh_items(self): |     def refresh_items(self): | ||||||
|         items = get_tasks( |         items = get_tasks( | ||||||
|             self.search_query, |             self.search_query, | ||||||
|             projects=self.filters.get("project", "").split(","), |             projects=self._filters_to_list("project"), | ||||||
|             statuses=self.filters.get("status", "").split(",") |             statuses=self._filters_to_list("status"), | ||||||
|             if "status" in self.filters |  | ||||||
|             else None, |  | ||||||
|             sort=self.sort_string, |             sort=self.sort_string, | ||||||
|         ) |         ) | ||||||
|         for item in items: |         for item in items: | ||||||
|  | @ -68,6 +66,12 @@ class TT(TableEditor): | ||||||
|                 key=str(item.id), |                 key=str(item.id), | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|  |     def _filters_to_list(self, key): | ||||||
|  |         filters = self.filters.get(key) | ||||||
|  |         if filters: | ||||||
|  |             return filters.split(",") | ||||||
|  |         else: | ||||||
|  |             return None | ||||||
| 
 | 
 | ||||||
| def run(default_view): | def run(default_view): | ||||||
|     app = TT(default_view) |     app = TT(default_view) | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import os | ||||||
| import datetime | import datetime | ||||||
| import tempfile | import tempfile | ||||||
| import subprocess | import subprocess | ||||||
|  | from .constants import SPECIAL_DATES_DISPLAY | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def filter_to_string(filters, search_query): | def filter_to_string(filters, search_query): | ||||||
|  | @ -41,6 +42,8 @@ def get_colored_date(date: datetime.date) -> str: | ||||||
|     if not isinstance(date, datetime.date): |     if not isinstance(date, datetime.date): | ||||||
|         return "" |         return "" | ||||||
|     as_str = date.strftime("%Y-%m-%d") |     as_str = date.strftime("%Y-%m-%d") | ||||||
|  |     if as_str in SPECIAL_DATES_DISPLAY: | ||||||
|  |         return SPECIAL_DATES_DISPLAY[as_str] | ||||||
|     today = datetime.date.today() |     today = datetime.date.today() | ||||||
|     if date.date() < today: |     if date.date() < today: | ||||||
|         return f"[#eeeeee on #dd1111]{as_str}[/]" |         return f"[#eeeeee on #dd1111]{as_str}[/]" | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								tt.toml
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								tt.toml
									
									
									
									
									
								
							|  | @ -28,7 +28,7 @@ values = [ | ||||||
| 
 | 
 | ||||||
| [[views]] | [[views]] | ||||||
| name = "tasks" | name = "tasks" | ||||||
| sort = "due" | sort = "due,status" | ||||||
| 
 | 
 | ||||||
| [views.filters] | [views.filters] | ||||||
| status = "wip,blocked,zero" | status = "wip,blocked,zero" | ||||||
|  | @ -40,7 +40,7 @@ read_only = true | ||||||
| [[views.columns]] | [[views.columns]] | ||||||
| field_name = "text" | field_name = "text" | ||||||
| display_name = "Task" | display_name = "Task" | ||||||
| default = "new taskz" | default = "new task" | ||||||
| editor = true | editor = true | ||||||
| 
 | 
 | ||||||
| [[views.columns]] | [[views.columns]] | ||||||
|  | @ -56,7 +56,7 @@ default = "" | ||||||
| [[views.columns]] | [[views.columns]] | ||||||
| field_name = "due" | field_name = "due" | ||||||
| field_type = "date" | field_type = "date" | ||||||
| default = "" | default = "1999-01-01" | ||||||
| 
 | 
 | ||||||
| [[views.columns]] | [[views.columns]] | ||||||
| field_name = "project" | field_name = "project" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue