diff --git a/server/admin/__main__.py b/server/admin/__main__.py
index 0332c54..732097a 100644
--- a/server/admin/__main__.py
+++ b/server/admin/__main__.py
@@ -8,10 +8,14 @@ from wtforms import fields, validators
from server.admin.auth import POOL, User
from server.admin.config import app, db_sync
from server.admin.views.attachments import AttachmentView
+from server.admin.views.results import ResultsView
+from server.admin.views.vote import VotesView
from server.config import get_app_config
from server.infra.db import AsyncDB
from server.modules.attachments import AtachmentService, DBAttachmentRepository, S3StorageDriver
from server.modules.attachments.repository.models import Attachment
+from server.modules.rate.repository.models import Vote
+from server.modules.recognizer.repository.models import ResultBeerds
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
@@ -119,5 +123,7 @@ if __name__ == "__main__":
)
admin.add_view(AttachmentView(Attachment, db_sync.session))
+ admin.add_view(ResultsView(ResultBeerds, db_sync.session))
+ admin.add_view(VotesView(Vote, db_sync.session))
app.run(debug=False, host="0.0.0.0", port=8000)
diff --git a/server/admin/views/results.py b/server/admin/views/results.py
new file mode 100644
index 0000000..6388edb
--- /dev/null
+++ b/server/admin/views/results.py
@@ -0,0 +1,38 @@
+import flask_login
+from flask import flash, redirect, request, url_for
+from flask_admin.contrib.sqla import ModelView
+from markupsafe import Markup
+
+
+# Create customized model view class
+class ResultsView(ModelView):
+ can_edit = False
+ can_delete = False
+ can_create = False
+ column_default_sort = ("result.created_at", True)
+
+ def is_accessible(self):
+ """Check if current user can access admin interface"""
+ return flask_login.current_user.is_authenticated
+
+ def inaccessible_callback(self, name, **kwargs):
+ """Redirect to login if not accessible"""
+ flash("Please login to access this page.", "danger")
+ return redirect(url_for("admin.login_view", next=request.url))
+
+ @staticmethod
+ def _list_thumbnail(view, _context, model, _name):
+ data = ""
+ data += f'
'
+
+ return Markup(data)
+
+ column_formatters = {
+ "path": _list_thumbnail,
+ }
+
+ column_list = [
+ "result",
+ "path",
+ "beerd",
+ ]
diff --git a/server/admin/views/vote.py b/server/admin/views/vote.py
new file mode 100644
index 0000000..1f9db3d
--- /dev/null
+++ b/server/admin/views/vote.py
@@ -0,0 +1,38 @@
+import flask_login
+from flask import flash, redirect, request, url_for
+from flask_admin.contrib.sqla import ModelView
+from markupsafe import Markup
+
+
+class VotesView(ModelView):
+ can_edit = False
+ can_delete = False
+ can_create = False
+ column_default_sort = ("created_at", True)
+
+ def is_accessible(self):
+ """Check if current user can access admin interface"""
+ return flask_login.current_user.is_authenticated
+
+ def inaccessible_callback(self, name, **kwargs):
+ """Redirect to login if not accessible"""
+ flash("Please login to access this page.", "danger")
+ return redirect(url_for("admin.login_view", next=request.url))
+
+ @staticmethod
+ def _list_thumbnail(view, _context, model, _name):
+ data = ""
+ data += f'
'
+
+ return Markup(data)
+
+ column_formatters = {
+ "path": _list_thumbnail,
+ }
+
+ column_list = [
+ "created_at",
+ "path",
+ "beerd",
+ "rate",
+ ]
diff --git a/server/modules/descriptions/repository/models.py b/server/modules/descriptions/repository/models.py
index 18810a9..e99d900 100644
--- a/server/modules/descriptions/repository/models.py
+++ b/server/modules/descriptions/repository/models.py
@@ -17,3 +17,6 @@ class Beerds(UJsonMixin):
alias: str = field(metadata={"sa": Column(Text(), nullable=False)})
descriptions: str = field(metadata={"sa": Column(Text(), nullable=False)})
signs: dict | None = field(default=None, metadata={"sa": Column(JSON(), nullable=False)})
+
+ def __str__(self):
+ return self.name
diff --git a/server/modules/rate/repository/models.py b/server/modules/rate/repository/models.py
index b669c00..34809b9 100644
--- a/server/modules/rate/repository/models.py
+++ b/server/modules/rate/repository/models.py
@@ -9,10 +9,13 @@ from sqlalchemy import (
ForeignKeyConstraint,
String,
)
+from sqlalchemy.orm import relationship
from server.config import get_app_config
from server.infra.db.db_mapper import mapper_registry
from server.modules.rate import domain
+from server.modules.attachments.repository.attachments import Attachment
+from server.modules.descriptions.repository.models import Beerds
@mapper_registry.mapped
@@ -25,6 +28,12 @@ class Vote(UJsonMixin):
ForeignKeyConstraint(["beerd_id"], ["beerds.id"], "votes_beerd_id_fk"),
)
+ __mapper_args__ = {
+ "properties": {
+ "beerd": relationship(Beerds, foreign_keys="Vote.beerd_id"),
+ "attachment": relationship(Attachment, foreign_keys="Vote.attachment_id"),
+ }
+ }
id: str = field(metadata={"sa": Column(String(), primary_key=True, nullable=False)})
attachment_id: str = field(metadata={"sa": Column(String(), nullable=False)})
beerd_id: str = field(metadata={"sa": Column(String(), nullable=False)})
diff --git a/server/modules/recognizer/repository/models.py b/server/modules/recognizer/repository/models.py
index 5e58c38..1bd6da0 100644
--- a/server/modules/recognizer/repository/models.py
+++ b/server/modules/recognizer/repository/models.py
@@ -8,8 +8,11 @@ from sqlalchemy import (
ForeignKeyConstraint,
String,
)
+from sqlalchemy.orm import relationship
from server.infra.db.db_mapper import mapper_registry
+from server.modules.attachments.repository.attachments import Attachment
+from server.modules.descriptions.repository.models import Beerds
@mapper_registry.mapped
@@ -19,6 +22,12 @@ class Results(UJsonMixin):
__tablename__ = "recognizer_results"
__table_args__ = (ForeignKeyConstraint(["attachment_id"], ["attachments.id"], "votes_attachment_id_fk"),)
+ __mapper_args__ = {
+ "properties": {
+ "attachment": relationship(Attachment, foreign_keys="Results.attachment_id"),
+ }
+ }
+
id: str = field(metadata={"sa": Column(String(), primary_key=True, nullable=False)})
attachment_id: str = field(metadata={"sa": Column(String(), nullable=False)})
user_id: str = field(metadata={"sa": Column(String(), nullable=False)})
@@ -28,6 +37,10 @@ class Results(UJsonMixin):
metadata={"sa": Column(DateTime(timezone=True), nullable=False)},
)
+ def __str__(self):
+ formatted_date = self.created_at.strftime("%Y-%m-%d %H:%M")
+ return f"{formatted_date}, device_id: {self.device_id}, user_id: {self.user_id}"
+
@mapper_registry.mapped
@dataclass
@@ -39,6 +52,13 @@ class ResultBeerds(UJsonMixin):
ForeignKeyConstraint(["beerd_id"], ["beerds.id"], "recognizer_results_beerd_id_fk"),
)
+ __mapper_args__ = {
+ "properties": {
+ "result": relationship(Results, foreign_keys="ResultBeerds.recognizer_results_id"),
+ "beerd": relationship(Beerds, foreign_keys="ResultBeerds.beerd_id"),
+ }
+ }
+
id: str = field(metadata={"sa": Column(String(), primary_key=True, nullable=False)})
recognizer_results_id: str = field(metadata={"sa": Column(String(), nullable=False)})
beerd_id: str = field(metadata={"sa": Column(String(), nullable=False)})
diff --git a/server/static/scripts.js b/server/static/scripts.js
index e695495..701a0e9 100644
--- a/server/static/scripts.js
+++ b/server/static/scripts.js
@@ -41,7 +41,7 @@ async function SavePhoto(self) {
for (let key in json.results) {
currentBeerdName.push(json.results[key]);
if (json.description != undefined) {
- text += `