personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #125 from kognova/codex/add-admin-view-with-utilities

Add admin utilities view

authored by

Jer Miller and committed by
GitHub
b6a64752 6b294e7b

+154 -2
+9
dream/__init__.py
··· 20 20 modify_entity_in_file, 21 21 update_top_entry, 22 22 ) 23 + from .views import admin as admin_view 23 24 from .views import calendar as calendar_view 24 25 from .views import chat as chat_view 25 26 from .views import entities as entities_view ··· 62 63 send_message = chat_view.send_message 63 64 search_page = search_view.search_page 64 65 import_page = import_page_view.import_page 66 + admin_page = admin_view.admin_page 67 + reindex = admin_view.reindex 68 + refresh_summary = admin_view.refresh_summary 69 + reload_entities_view = admin_view.reload_entities_view 65 70 entities_data = entities_view.entities_data 66 71 api_top_generate = entities_view.api_top_generate 67 72 api_top_update = entities_view.api_top_update ··· 88 93 "calendar_occurrences", 89 94 "login", 90 95 "logout", 96 + "admin_page", 97 + "reindex", 98 + "refresh_summary", 99 + "reload_entities_view", 91 100 "format_date", 92 101 "modify_entity_in_file", 93 102 "modify_entity_file",
+27
dream/templates/admin.html
··· 1 + {% extends 'base.html' %} 2 + {% set title = 'Admin' %} 3 + {% set active = 'admin' %} 4 + {% block head %} 5 + <style> 6 + body { font-family: sans-serif; margin:0; } 7 + .container { max-width:600px; margin:0 auto; padding:1em; } 8 + button { padding:8px 16px; margin:0.5em 0; } 9 + pre { background:#f5f5f5; padding:0.5em; } 10 + </style> 11 + {% endblock %} 12 + {% block body %} 13 + <div class="container"> 14 + <h1>Admin</h1> 15 + <button id="reindexBtn">Reindex</button> 16 + <button id="summaryBtn">Refresh Summary</button> 17 + <button id="entitiesBtn">Reload Entities</button> 18 + <pre id="status"></pre> 19 + </div> 20 + <script> 21 + function post(url){return fetch(url,{method:'POST'}).then(r=>r.json()).then(d=>{document.getElementById('status').textContent=JSON.stringify(d,null,2);});} 22 + 23 + document.getElementById('reindexBtn').onclick=()=>post('{{ url_for('admin.reindex') }}'); 24 + document.getElementById('summaryBtn').onclick=()=>post('{{ url_for('admin.refresh_summary') }}'); 25 + document.getElementById('entitiesBtn').onclick=()=>post('{{ url_for('admin.reload_entities_view') }}'); 26 + </script> 27 + {% endblock %}
+1
dream/templates/base.html
··· 14 14 <a href="{{ url_for('import_view.import_page') }}" class="{% if active=='import' %}current{% endif %}">📥</a> 15 15 <a href="{{ url_for('calendar.calendar_page') }}" class="{% if active=='calendar' %}current{% endif %}">📅</a> 16 16 <a href="{{ url_for('entities.entities') }}" class="{% if active=='entities' %}current{% endif %}">📑</a> 17 + <a href="{{ url_for('admin.admin_page') }}" class="{% if active=='admin' %}current{% endif %}">⚙️</a> 17 18 <a href="{{ url_for('review.logout') }}" title="Logout">🔒</a> 18 19 </div> 19 20 {% block body %}{% endblock %}
+10 -2
dream/views/__init__.py
··· 4 4 5 5 from flask import Flask 6 6 7 - from . import calendar, entities, home, search 7 + from . import admin, calendar, entities, home, search 8 8 9 9 chat_view = import_module(".chat", __name__) 10 10 ··· 12 12 13 13 14 14 def register_views(app: Flask) -> None: 15 - for bp in [home.bp, search.bp, entities.bp, calendar.bp, chat_view.bp, import_view.bp]: 15 + for bp in [ 16 + home.bp, 17 + search.bp, 18 + entities.bp, 19 + calendar.bp, 20 + admin.bp, 21 + chat_view.bp, 22 + import_view.bp, 23 + ]: 16 24 app.register_blueprint(bp)
+51
dream/views/admin.py
··· 1 + from __future__ import annotations 2 + 3 + from typing import Any 4 + 5 + from flask import Blueprint, jsonify, render_template 6 + 7 + from think.indexer import ( 8 + load_cache, 9 + save_cache, 10 + scan_entities, 11 + scan_occurrences, 12 + scan_ponders, 13 + ) 14 + from think.journal_stats import JournalStats 15 + 16 + from .. import state 17 + from ..views.entities import reload_entities 18 + 19 + bp = Blueprint("admin", __name__, template_folder="../templates") 20 + 21 + 22 + @bp.route("/admin") 23 + def admin_page() -> str: 24 + return render_template("admin.html", active="admin") 25 + 26 + 27 + @bp.route("/admin/api/reindex", methods=["POST"]) 28 + def reindex() -> Any: 29 + journal = state.journal_root 30 + cache = load_cache(journal) 31 + changed = False 32 + changed |= scan_entities(journal, cache) 33 + changed |= scan_ponders(journal, cache) 34 + changed |= scan_occurrences(journal, cache) 35 + if changed: 36 + save_cache(journal, cache) 37 + return jsonify({"status": "ok", "changed": bool(changed)}) 38 + 39 + 40 + @bp.route("/admin/api/summary", methods=["POST"]) 41 + def refresh_summary() -> Any: 42 + js = JournalStats() 43 + js.scan(state.journal_root) 44 + js.save_markdown(state.journal_root) 45 + return jsonify({"status": "ok"}) 46 + 47 + 48 + @bp.route("/admin/api/reload_entities", methods=["POST"]) 49 + def reload_entities_view() -> Any: 50 + reload_entities() 51 + return jsonify({"status": "ok"})
+56
tests/test_admin_view.py
··· 1 + import importlib 2 + 3 + 4 + def test_admin_page(tmp_path): 5 + review = importlib.import_module("dream") 6 + review.journal_root = str(tmp_path) 7 + with review.app.test_request_context("/admin"): 8 + html = review.admin_page() 9 + assert "Admin" in html 10 + 11 + 12 + def test_admin_actions(monkeypatch, tmp_path): 13 + review = importlib.import_module("dream") 14 + review.journal_root = str(tmp_path) 15 + called = {} 16 + 17 + monkeypatch.setattr("dream.views.admin.load_cache", lambda j: {}) 18 + monkeypatch.setattr( 19 + "dream.views.admin.save_cache", lambda j, c: called.setdefault("save", True) 20 + ) 21 + monkeypatch.setattr( 22 + "dream.views.admin.scan_entities", lambda j, c: called.setdefault("entities", True) or True 23 + ) 24 + monkeypatch.setattr( 25 + "dream.views.admin.scan_ponders", lambda j, c: called.setdefault("ponders", True) or True 26 + ) 27 + monkeypatch.setattr( 28 + "dream.views.admin.scan_occurrences", lambda j, c: called.setdefault("occ", True) or True 29 + ) 30 + 31 + with review.app.test_request_context("/admin/api/reindex", method="POST"): 32 + resp = review.reindex() 33 + assert resp.json["status"] == "ok" 34 + assert called == {"entities": True, "ponders": True, "occ": True, "save": True} 35 + 36 + called.clear() 37 + monkeypatch.setattr( 38 + "dream.views.admin.JournalStats.scan", lambda self, j: called.setdefault("scan", True) 39 + ) 40 + monkeypatch.setattr( 41 + "dream.views.admin.JournalStats.save_markdown", 42 + lambda self, j: called.setdefault("save", True), 43 + ) 44 + with review.app.test_request_context("/admin/api/summary", method="POST"): 45 + resp = review.refresh_summary() 46 + assert resp.json["status"] == "ok" 47 + assert called == {"scan": True, "save": True} 48 + 49 + called.clear() 50 + monkeypatch.setattr( 51 + "dream.views.admin.reload_entities", lambda: called.setdefault("reload", True) 52 + ) 53 + with review.app.test_request_context("/admin/api/reload_entities", method="POST"): 54 + resp = review.reload_entities_view() 55 + assert resp.json["status"] == "ok" 56 + assert called == {"reload": True}