from plomlib import PlomDB, PlomException, run_server, PlomHandler
server_port = 8082
-db_path = '/home/plom/org/ledger2023.dat'
+db_path = '/home/plom/org/ledger2024.dat'
html_head = """
<style>
</form>
"""
add_free_html = """<br />
-<textarea name="booking" rows=10 cols=80>
-{% for line in lines %}{{ line }}
-{% endfor %}
-</textarea>
+<textarea name="booking" rows=10 cols=80>{% for line in lines %}{{ line }}{% if not loop.last %}
+{% endif %}{% endfor %}</textarea>
"""
add_structured_html = """
<input type="submit" name="add_taxes" value="add taxes" tabindex="5" />
elif b.date_string > date:
start_at = b.start_line
break
+ lines += [''] # DEBUG is new
return self.write_lines_in_total_lines_at(self.real_lines, start_at, lines)
def update(self, start, end, lines, date):
- total_lines = self.real_lines[:start] + self.real_lines[end:]
+ remaining_lines = self.real_lines[:start] + self.real_lines[end:]
n_original_lines = end - start
- start_at = len(total_lines)
+ start_at = len(remaining_lines)
for b in self.bookings:
if b.date_string == date:
- if start_at == len(total_lines) or b.start_line == start:
+ if start_at == len(remaining_lines) or b.start_line == start:
start_at = b.start_line
if b.start_line > start:
start_at -= n_original_lines
elif b.date_string > date:
break
- if start_at == len(total_lines):
+ # print("DEBUG update start_at", start_at, "len(remaining_lines)", len(remaining_lines), "len(self.real_lines)", len(self.real_lines), "end", end)
+ if start_at != 0 and end != len(self.real_lines) and start_at == len(remaining_lines):
+ # Add empty predecessor line if appending.
lines = [''] + lines
- return self.write_lines_in_total_lines_at(total_lines, start_at, lines)
+ return self.write_lines_in_total_lines_at(remaining_lines, start_at, lines)
def write_lines_in_total_lines_at(self, total_lines, start_at, lines):
- total_lines = total_lines[:start_at] + lines + [''] + total_lines[start_at:]
+ # total_lines = total_lines[:start_at] + lines + [''] + total_lines[start_at:]
+ total_lines = total_lines[:start_at] + lines + total_lines[start_at:]
_, _ = parse_lines(lines)
text = '\n'.join(total_lines)
self.write_db(text)
else:
line += f"\t\t"
for currency, amount in account_sums[path + node].items():
- if currency != '€' and amount > 0:
+ if currency != '€' and amount != 0:
line += f"{amount:5.2f} {currency}\t"
lines += [line]
indent += " "
if b.start_line > start:
next_booking = b
break
- start_at = next_booking.start_line + len(next_booking.lines) - (end - start) + 1
- self.make_move(start, end, start_at-1)
+ # start_at = next_booking.start_line + len(next_booking.lines) - (end - start) + 1
+ # self.make_move(start, end, start_at-1)
+ start_at = next_booking.start_line + len(next_booking.lines) - (end - start)
+ print("DEBUG", start, end, start_at)
+ self.make_move(start, end, start_at)
return redir_nth
def make_move(self, start, end, start_at):
+ # FIXME currently broken due to changed self.write_lines_in_total_lines_at, easy fix would be lines += [""] maybe?
lines = self.get_lines(start, end)
- total_lines = self.real_lines[:start-1] + self.real_lines[end:] # +1 because we reduce the original position's two empty border lines to in-between line
+ if start == 0:
+ total_lines = self.real_lines[end+1:]
+ lines = [''] + lines
+ start_at += 1
+ else:
+ total_lines = self.real_lines[:start-1] + self.real_lines[end:] # -1 because we reduce the original position's two empty limit lines to one in-between line
+ lines += ['']
self.write_lines_in_total_lines_at(total_lines, start_at, lines)
def booking_lines_from_postvars(self, postvars):
temp_bookings, _ = parse_lines(temp_lines)
for currency in temp_bookings[0].sink:
amount = temp_bookings[0].sink[currency]
- lines += [f'Assets {amount:.2f} {currency}']
+ # lines += [f'Assets {amount:.2f} {currency}']
+ lines += [f'Assets {amount} {currency}']
except PlomException:
pass
elif 'add_taxes' in postvars.keys():
postvars = parse_qs(self.rfile.read(length).decode(), keep_blank_values=1)
start = int(postvars['start'][0])
end = int(postvars['end'][0])
+ print("DEBUG start, end", start, end)
db = LedgerDB(prefix)
add_empty_line = None
lines = []
nth = db.get_nth_for_booking_of_start_line(start)
else:
new_start = db.update(start, end, lines, target_date)
+ print("DEBUG save", new_start, start, end, lines)
nth = db.get_nth_for_booking_of_start_line(new_start)
if new_start > start:
nth -= 1
return
# collect modification times of numbered .bak files
- print('DEBUG BACKUP')
+ # print('DEBUG BACKUP')
bak_prefix = f'{self.db_file}.bak.'
# backup_dates = []
mtimes_to_paths = {}
if path.startswith(os.path.basename(bak_prefix))]:
path = os.path.dirname(bak_prefix) + f'/{path}'
mod_time = os.path.getmtime(path)
- print(f'DEBUG pre-exists: {path} {mod_time}')
+ # print(f'DEBUG pre-exists: {path} {mod_time}')
mtimes_to_paths[str(datetime.fromtimestamp(mod_time))] = path
- # backup_dates += [str(datetime.fromtimestamp(mod_time))]
- for mtime in sorted(mtimes_to_paths.keys()):
- print(f'DEBUG mtimes_to_paths: {mtime}:{mtimes_to_paths[mtime]}')
+ # for mtime in sorted(mtimes_to_paths.keys()):
+ # print(f'DEBUG mtimes_to_paths: {mtime}:{mtimes_to_paths[mtime]}')
# collect what numbered .bak files to save: the older, the fewer; for each
# timedelta, keep the newest file that's older
ages_to_keep = [timedelta(minutes=4**i) for i in range(0, 8)]
- print(f'DEBUG ages_to_keep: {ages_to_keep}')
+ # print(f'DEBUG ages_to_keep: {ages_to_keep}')
now = datetime.now()
to_save = {}
for age in ages_to_keep:
limit = now - age
for mtime in reversed(sorted(mtimes_to_paths.keys())):
- print(f'DEBUG checking if {mtime} < {limit} ({now} - {age})')
- if datetime.strptime(mtime, '%Y-%m-%d %H:%M:%S.%f') < limit:
- print('DEBUG it is, adding!')
+ # print(f'DEBUG checking if {mtime} < {limit} ({now} - {age})')
+ if len(mtime) < 20:
+ mtime_test = mtime + '.000000'
+ else:
+ mtime_test = mtime
+ if datetime.strptime(mtime_test, '%Y-%m-%d %H:%M:%S.%f') < limit:
+ # print('DEBUG it is, adding!')
to_save[mtime] = mtimes_to_paths[mtime]
break
for path in [path for path in mtimes_to_paths.values()
if path not in to_save.values()]:
- print(f'DEBUG removing {path} cause not in to_save')
+ # print(f'DEBUG removing {path} cause not in to_save')
os.remove(path)
i = 0
for mtime in sorted(to_save.keys()):
source = to_save[mtime]
target = f'{bak_prefix}{i}'
- print(f'DEBUG to_save {source} -> {target}')
+ # print(f'DEBUG to_save {source} -> {target}')
if source != target:
shutil.move(source, target)
i += 1
# put copy of current state at end of bak list
- print(f'DEBUG saving current state to {bak_prefix}{i}')
+ # print(f'DEBUG saving current state to {bak_prefix}{i}')
shutil.copy(self.db_file, f'{bak_prefix}{i}')
def write_text_to_db(self, text, mode='w'):
task_rows.sort(key=lambda r: False if not r['todo'] else True, reverse=True)
elif task_sort == 'comment':
task_rows.sort(key=lambda r: '' if not r['todo'] else r['todo'].comment, reverse=True)
- return j2env.get_template('day.html').render(db=self, action=self.prefix+'/day', prev_date=prev_date_str, next_date=next_date_str, task_rows=task_rows)
+ return j2env.get_template('day.html').render(db=self, action=self.prefix+'/day', prev_date=prev_date_str, next_date=next_date_str, task_rows=task_rows, sort=task_sort)
def show_calendar(self, start_date_str, end_date_str):
self.t_filter_and = ['calendar']
days_to_show[current_date].weekday = datetime.strptime(current_date, DATE_FORMAT).strftime('%A')[:2]
return j2env.get_template('calendar.html').render(db=self, days=days_to_show, action=self.prefix+'/calendar', today=str(datetime.now())[:10], start_date=start_date_str, end_date=end_date_str)
- def show_todo(self, task_uuid, selected_date):
+ def show_todo(self, task_uuid, selected_date, referer):
+ if selected_date not in self.days.keys():
+ self.days[selected_date] = self.add_day()
if task_uuid in self.days[selected_date].todos:
todo = self.days[selected_date].todos[task_uuid]
else:
todo = self.days[selected_date].add_todo(task_uuid)
- return j2env.get_template('todo.html').render(db=self, todo=todo, action=self.prefix+'/todo')
+ return j2env.get_template('todo.html').render(db=self, todo=todo, action=self.prefix+'/todo', referer=referer)
def update_todo_mini(self, task_uuid, date, day_effort, done, importance):
+ if date not in self.days.keys():
+ self.days[date] = self.add_day()
if task_uuid in self.days[date].todos.keys():
todo = self.days[date].todos[task_uuid]
else:
todo.comment = comment
todo.day_tags = self.collect_tags(day_tags_joined, day_tags_checked)
- def show_task(self, id_):
+ def show_task(self, id_, referer=''):
task = self.tasks[id_] if id_ else self.add_task()
- return j2env.get_template('task.html').render(db=self, task=task, action=self.prefix+'/task')
+ return j2env.get_template('task.html').render(db=self, task=task, action=self.prefix+'/task', referer=referer)
def update_task(self, id_, title, default_effort, tags_joined, tags_checked, links):
task = self.tasks[id_] if id_ in self.tasks.keys() else self.add_task(id_)
del db.selected_day.todos[uuid]
if 'choose' in postvars.keys():
for i, uuid in enumerate(postvars['t_uuid']):
- uuids = postvars['choose'] + postvars['done'] if 'done' in postvars.keys() else []
- if uuid in uuids or postvars['day_effort'][i] != '' or postvars['importance'][i] != '1.0':
+ uuids = postvars['choose']
+ uuids += postvars['done'] if 'done' in postvars.keys() else []
+ if uuid in uuids or postvars['day_effort'][i] != '' or (postvars['importance'][i] not in {'1.0', ''}):
done = 'done' in postvars and uuid in postvars['done']
db.update_todo_mini(uuid, db.selected_date, postvars['day_effort'][i], done, postvars['importance'][i])
if 'day_comment' in postvars.keys():
db.selected_day.comment = postvars['day_comment'][0]
params_to_encode += [('selected_date', db.selected_date)]
- encoded_params = urlencode(params_to_encode)
- homepage = f'{parsed_url.path}?{encoded_params}'
+ if 'referer' in postvars.keys() and len(postvars['referer'][0]) > 0:
+ homepage = postvars['referer'][0]
+ else:
+ encoded_params = urlencode(params_to_encode)
+ homepage = f'{parsed_url.path}?{encoded_params}'
db.write()
self.redirect(homepage)
selected_date = t_filter_and = t_filter_not = None
hide_unchosen = hide_done = False
+ referer = params.get('referer', [''])[0]
if parsed_url.path in {app_config['prefix'] + '/day', app_config['prefix'] + '/tasks'}:
selected_date = get_param('selected_date')
if parsed_url.path in {app_config['prefix'] + '/day', app_config['prefix'] + '/tasks', app_config['prefix'] + '/task'}:
elif parsed_url.path == app_config['prefix'] + '/todo':
todo_date = params.get('date', [None])[0]
task_uuid = params.get('task', [None])[0]
- page = db.show_todo(task_uuid, todo_date)
+ page = db.show_todo(task_uuid, todo_date, referer)
elif parsed_url.path == app_config['prefix'] + '/task':
id_ = params.get('id', [None])[0]
- page = db.show_task(id_)
+ page = db.show_task(id_, referer)
elif parsed_url.path == app_config['prefix'] + '/tasks':
page = db.show_tasks()
elif parsed_url.path == app_config['prefix'] + '/add_task':
table.alternating tr:nth-child(odd) {
background-color: #ffffff;
}
+th.desc { background: linear-gradient(to bottom, white, grey); }
td.checkbox, td.number { height: 0.1em; padding: 0em; text-align: center; }
td.checkbox { width: 0.1em }
td button { height: 1.5em; padding: 0em; margin: 0em }
<input type="hidden" name="selected_date" value="{{ db.selected_date }}" />
</p>
<table class="alternating">
-<tr><th><a href="?sort=title">task</a></th><th><a href="?sort=chosen">choose?</a></th><th><a href="?sort=done">done?</a></th><th><a href="?sort=default_effort">effort</a></th><th><a href="?sort=importance">importance</a></th><th>edit?</th><th>day tags</th><th><a href="?sort=comment">comment</a></th></tr>
+<tr>
+<th {% if sort=='title' %}class="desc"{% endif %}><a href="?sort=title">task</a></th>
+<th {% if sort=='chosen' %}class="desc"{% endif %}><a href="?sort=chosen">todo</a></th>
+<th {% if sort=='done' %}class="desc"{% endif %}><a href="?sort=done">done</a></th>
+<th {% if sort=='default_effort' %}class="desc"{% endif %}><a href="?sort=default_effort">effort</a></th>
+<th {% if sort=='importance' %}class="desc"{% endif %}><a href="?sort=importance">importance</a></th>
+<th>edit?</th><th>day tags</th>
+<th {% if sort=='comment' %}class="desc"{% endif %}><a href="?sort=comment">comment</a></th></tr>
{% for row in task_rows %}
<tr>
<input name="t_uuid" value="{{ row.uuid }}" type="hidden" >
-<td><details><summary>] <a href="{{db.prefix}}/task?id={{ row.uuid }}" />{{ row.task.current_title|e }}</a></summary>tags: {% for tag in row.task.tags | sort %}<a href="{{db.prefix}}/day?t_and={{tag|e}}">{{ tag }}</a> {% endfor %}</details></td>
+<td><details><summary>] <a href="{{db.prefix}}/task?id={{ row.uuid }}&referer=day" />{{ row.task.current_title|e }}</a></summary>tags: {% for tag in row.task.tags | sort %}<a href="{{db.prefix}}/day?t_and={{tag|e}}">{{ tag }}</a> {% endfor %}</details></td>
{% if row.todo %}
<td class="checkbox"><input name="choose" type="checkbox" value="{{ row.uuid }}" checked></td>
<td class="checkbox"><input name="done" type="checkbox" value="{{ row.uuid }}" {% if row.todo.done %}checked{% endif %}></td>
<td class="number"><input class="day_effort_input" name="day_effort" type="number" step=0.1 size=8 value="{% if row.todo.day_effort is not none %}{{ row.todo.day_effort }}{% endif %}" placeholder={{ row.task.current_default_effort }} ></td>
<td class="number"><input name="importance" type="number" step=0.1 size=8 value="{{row.todo.importance}}" ></td>
-<td><a href="{{db.prefix}}/todo?task={{row.uuid}}&date={{db.selected_date}}">edit</a></td>
+<td><a href="{{db.prefix}}/todo?task={{row.uuid}}&date={{db.selected_date}}&referer=day">edit</a></td>
<td>{% for tag in row.todo.day_tags | sort %}<a href="{{db.prefix}}/tags?t_and={{tag|e}}">{{ tag }}</a> {% endfor %}</td>
<td>{{ row.todo.comment|e }}</td>
{% else %}
<td class="checkbox"><input name="done" type="checkbox" value="{{ row.uuid }}"></td>
<td class="number"><input class="day_effort_input" name="day_effort" type="number" step=0.1 size=8 placeholder={{ row.task.current_default_effort }} ></td>
<td class="number"><input name="importance" type="number" step=0.1 size=8 value="1.0" ></td>
-<td><a href="{{db.prefix}}/todo?task={{row.uuid}}&date={{db.selected_date}}">edit</a></td>
+<td><a href="{{db.prefix}}/todo?task={{row.uuid}}&date={{db.selected_date}}&referer=day">edit</a></td>
<td></td>
<td></td>
<td></td>
<form id="form_to_watch" action="{{action|e}}" method="POST">
<h3>edit task</h3>
<input type="hidden" name="id" value="{{ task.id_ }}" />
+<input type="hidden" name="referer" value="{{ referer }}" />
<table>
<tr><th>title</th><td class="input"><input name="title" type="text" value="{{ task.title|e }}" /><details><summary>history</summary><ul>{% for k,v in task.title_history.items() | sort(attribute='0', reverse=True) %}<li>{{ k }}: {{ v|e }}{% endfor %}</ul></details></td></tr>
<tr><th>default effort</th><td class="input"><input type="number" name="default_effort" value="{{ task.default_effort }}" step=0.1 size=8 required /><details><summary>history</summary><ul>{% for k,v in task.default_effort_history.items() | sort(attribute='0', reverse=True) %}<li>{{ k }}: {{ v|e }}{% endfor %}</ul></details></td></tr>
<h3>edit todo</h3>
<input type="hidden" name="task_uuid" value="{{ todo.task.id_ }}" />
<input type="hidden" name="date" value="{{ todo.day.date }}" />
+<input type="hidden" name="referer" value="{{ referer }}" />
<table>
<tr><th>task</th><td><a href="{{db.prefix}}/task?id={{ todo.task.id_ }}">{{ todo.task.title|e }}</a></td></tr>
<tr><th>default effort</th><td>{{ todo.default_effort }}</td></tr>