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 += `
${json.results[key]} (вероятность: ${Math.round(parseFloat(key) * 100)}%)
Описание
`; + text += `
${json.results[key]} (вероятность: ${Math.round(parseFloat(key) * 100)}%)
Описание и фото
`; } else { text += `
${json.results[key]} (вероятность: ${Math.round(parseFloat(key) * 100)}%)
`; } diff --git a/server/templates/base.html b/server/templates/base.html index 259c8ff..afe88d7 100644 --- a/server/templates/base.html +++ b/server/templates/base.html @@ -45,6 +45,6 @@ {% block form %}{% endblock %} - +