mirror of https://github.com/logos-co/open-law.git
Merge pull request #53 from Simple2B/svyat/feat/book_stats
Svyat/feat/book_stats
This commit is contained in:
commit
dd05759b02
|
@ -1,4 +1,5 @@
|
|||
from flask_login import current_user
|
||||
from sqlalchemy import and_
|
||||
|
||||
from app import db, models as m
|
||||
from app.models.utils import BaseModel
|
||||
|
@ -17,7 +18,7 @@ class Book(BaseModel):
|
|||
owner = db.relationship("User", viewonly=True)
|
||||
stars = db.relationship("User", secondary="books_stars", back_populates="stars")
|
||||
contributors = db.relationship("BookContributor")
|
||||
versions = db.relationship("BookVersion")
|
||||
versions = db.relationship("BookVersion", order_by="asc(BookVersion.id)")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.id}: {self.label}>"
|
||||
|
@ -34,3 +35,55 @@ class Book(BaseModel):
|
|||
).first()
|
||||
if book_star:
|
||||
return True
|
||||
|
||||
@property
|
||||
def approved_comments(self):
|
||||
comments = (
|
||||
db.session.query(
|
||||
m.Comment,
|
||||
)
|
||||
.filter(
|
||||
and_(
|
||||
m.BookVersion.id == self.last_version.id,
|
||||
m.Section.version_id == m.BookVersion.id,
|
||||
m.Collection.id == m.Section.collection_id,
|
||||
m.Interpretation.section_id == m.Section.id,
|
||||
m.Comment.interpretation_id == m.Interpretation.id,
|
||||
m.Comment.approved.is_(True),
|
||||
m.Comment.is_deleted.is_(False),
|
||||
m.BookVersion.is_deleted.is_(False),
|
||||
m.Interpretation.is_deleted.is_(False),
|
||||
m.Section.is_deleted.is_(False),
|
||||
m.Collection.is_deleted.is_(False),
|
||||
),
|
||||
)
|
||||
.order_by(m.Comment.created_at.desc())
|
||||
.all()
|
||||
)
|
||||
|
||||
return comments
|
||||
|
||||
@property
|
||||
def approved_interpretations(self):
|
||||
interpretations = (
|
||||
db.session.query(
|
||||
m.Interpretation,
|
||||
)
|
||||
.filter(
|
||||
and_(
|
||||
m.BookVersion.id == self.last_version.id,
|
||||
m.Section.version_id == m.BookVersion.id,
|
||||
m.Collection.id == m.Section.collection_id,
|
||||
m.Interpretation.section_id == m.Section.id,
|
||||
m.Interpretation.approved.is_(True),
|
||||
m.BookVersion.is_deleted.is_(False),
|
||||
m.Interpretation.is_deleted.is_(False),
|
||||
m.Section.is_deleted.is_(False),
|
||||
m.Collection.is_deleted.is_(False),
|
||||
),
|
||||
)
|
||||
.order_by(m.Interpretation.created_at.desc())
|
||||
.all()
|
||||
)
|
||||
|
||||
return interpretations
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
from datetime import datetime
|
||||
|
||||
from flask_login import current_user
|
||||
|
||||
from app import db, models as m
|
||||
|
@ -13,7 +11,6 @@ class Interpretation(BaseModel):
|
|||
text = db.Column(db.Text, unique=False, nullable=False)
|
||||
approved = db.Column(db.Boolean, default=False)
|
||||
marked = db.Column(db.Boolean, default=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.now)
|
||||
|
||||
# Foreign keys
|
||||
user_id = db.Column(db.ForeignKey("users.id"))
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
</span>
|
||||
<span class="space-x-0.5 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /> </svg>
|
||||
<p>55</p>
|
||||
<p>{{ book.approved_interpretations|length }}</p>
|
||||
</span>
|
||||
<span class="space-x-0.5 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /> </svg>
|
||||
<p>55</p>
|
||||
<p>{{ book.approved_comments|length }}</p>
|
||||
</span>
|
||||
</div>
|
||||
</dd>
|
||||
|
|
|
@ -36,11 +36,11 @@
|
|||
</span>
|
||||
<span class="space-x-0.5 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /> </svg>
|
||||
<p>55</p>
|
||||
<p>{{ book.approved_interpretations|length }}</p>
|
||||
</span>
|
||||
<span class="space-x-0.5 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /> </svg>
|
||||
<p>55</p>
|
||||
<p>{{ book.approved_comments|length }}</p>
|
||||
</span>
|
||||
</div>
|
||||
</dd>
|
||||
|
|
|
@ -114,11 +114,11 @@
|
|||
</span>
|
||||
<span class="space-x-0.5 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /> </svg>
|
||||
<p>55</p>
|
||||
<p>{{ book.approved_interpretations|length }}</p>
|
||||
</span>
|
||||
<span class="space-x-0.5 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /> </svg>
|
||||
<p>55</p>
|
||||
<p>{{ book.approved_comments|length }}</p>
|
||||
</span>
|
||||
</div>
|
||||
</dd>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
<!-- prettier-ignore -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-10 h-10"> <path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" /> </svg>
|
||||
{% endif %}
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<h1 class="hidden md:inline font-extrabold text-lg dark:text-white ml-4">{{user.username}}</h1>
|
||||
<!-- prettier-ignore -->
|
||||
|
@ -33,7 +34,6 @@
|
|||
<li class="mr-2 w-full md:w-auto" role="presentation">
|
||||
<!-- prettier-ignore -->
|
||||
<button class="inline-flex p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300" id="contributions-tab" data-tabs-target="#contributions" type="button" role="tab" aria-controls="contributions" aria-selected="false"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 mr-3"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /> </svg>
|
||||
|
||||
Contributions
|
||||
</button>
|
||||
</li>
|
||||
|
@ -59,11 +59,11 @@
|
|||
</span>
|
||||
<span class="space-x-0.5 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /> </svg>
|
||||
<p>55</p>
|
||||
<p>{{ book.approved_interpretations|length }}</p>
|
||||
</span>
|
||||
<span class="space-x-0.5 flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /> </svg>
|
||||
<p>55</p>
|
||||
<p>{{ book.approved_comments|length }}</p>
|
||||
</span>
|
||||
</div>
|
||||
</dd>
|
||||
|
|
|
@ -1071,8 +1071,11 @@ def test_crud_comment(client: FlaskClient, runner: FlaskCliRunner):
|
|||
def test_access_to_settings_page(client: FlaskClient):
|
||||
_, user = login(client)
|
||||
|
||||
book_1 = m.Book(label="test", about="test").save()
|
||||
book_1 = m.Book(label="test", about="test", user_id=user.id).save()
|
||||
m.BookVersion(semver="1.0.0", book_id=book_1.id).save()
|
||||
|
||||
book_2 = m.Book(label="test", about="test", user_id=user.id).save()
|
||||
m.BookVersion(semver="1.0.0", book_id=book_2.id).save()
|
||||
|
||||
response: Response = client.get(
|
||||
f"/book/{book_1.id}/settings",
|
||||
|
@ -1080,7 +1083,6 @@ def test_access_to_settings_page(client: FlaskClient):
|
|||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert b"You are not owner of this book!" in response.data
|
||||
|
||||
response: Response = client.get(
|
||||
f"/book/{book_2.id}/settings",
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
from flask.testing import FlaskClient
|
||||
|
||||
from app import models as m
|
||||
from tests.utils import login, create_test_book
|
||||
|
||||
|
||||
def test_approved_interpretations(client: FlaskClient):
|
||||
_, user = login(client)
|
||||
create_test_book(user.id)
|
||||
|
||||
dummy_user = m.User(username="Bob").save()
|
||||
create_test_book(dummy_user.id)
|
||||
|
||||
book: m.Book = m.Book.query.first()
|
||||
|
||||
assert len(book.approved_interpretations) == 0
|
||||
|
||||
for interpretation in m.Interpretation.query.all():
|
||||
interpretation.approved = True
|
||||
interpretation.save()
|
||||
|
||||
assert len(book.approved_interpretations) == 1
|
||||
|
||||
section: m.Section = m.Section.query.first()
|
||||
assert section
|
||||
interpretation: m.Interpretation = m.Interpretation(
|
||||
section_id=section.id, label="231", text="123", approved=True
|
||||
).save()
|
||||
|
||||
assert len(book.approved_interpretations) == 2
|
||||
|
||||
interpretation.is_deleted = True
|
||||
interpretation.save()
|
||||
|
||||
assert len(book.approved_interpretations) == 1
|
||||
|
||||
collection: m.Collection = m.Collection.query.first()
|
||||
sub_collection: m.Collection = m.Collection(
|
||||
parent_id=collection.id,
|
||||
label="123",
|
||||
).save()
|
||||
section: m.Section = m.Section(
|
||||
label="123", collection_id=sub_collection.id, version_id=book.last_version.id
|
||||
).save()
|
||||
interpretation: m.Interpretation = m.Interpretation(
|
||||
section_id=section.id, label="231", text="123", approved=True
|
||||
).save()
|
||||
|
||||
assert len(book.approved_interpretations) == 2
|
||||
|
||||
sub_collection.is_deleted = True
|
||||
sub_collection.save()
|
||||
assert len(book.approved_interpretations) == 1
|
||||
|
||||
sub_collection.is_deleted = False
|
||||
sub_collection.save()
|
||||
assert len(book.approved_interpretations) == 2
|
||||
|
||||
# collection.is_deleted = True
|
||||
# collection.save()
|
||||
# assert len(book.approved_interpretations) == 0
|
||||
|
||||
|
||||
def test_approved_comments(client: FlaskClient):
|
||||
_, user = login(client)
|
||||
create_test_book(user.id)
|
||||
|
||||
dummy_user = m.User(username="Bob").save()
|
||||
create_test_book(dummy_user.id)
|
||||
|
||||
book: m.Book = m.Book.query.first()
|
||||
|
||||
assert len(book.approved_comments) == 0
|
||||
|
||||
for comment in m.Comment.query.all():
|
||||
comment.approved = True
|
||||
comment.save()
|
||||
|
||||
assert len(book.approved_comments) == 1
|
||||
|
||||
interpretation: m.Interpretation = m.Interpretation.query.first()
|
||||
assert interpretation
|
||||
comment: m.Comment = m.Comment(
|
||||
text="231", approved=True, interpretation_id=interpretation.id
|
||||
).save()
|
||||
|
||||
assert len(book.approved_comments) == 2
|
||||
|
||||
comment.is_deleted = True
|
||||
comment.save()
|
||||
|
||||
assert len(book.approved_comments) == 1
|
||||
|
||||
comment: m.Comment = m.Comment(
|
||||
text="456", approved=True, interpretation_id=interpretation.id
|
||||
).save()
|
||||
|
||||
assert len(book.approved_comments) == 2
|
||||
|
||||
interpretation.is_deleted = True
|
||||
interpretation.save()
|
||||
assert len(book.approved_comments) == 0
|
||||
|
||||
interpretation.is_deleted = False
|
||||
interpretation.save()
|
||||
assert len(book.approved_comments) == 2
|
||||
|
||||
collection: m.Collection = m.Collection.query.first()
|
||||
collection.is_deleted = True
|
||||
collection.save()
|
||||
|
||||
interpretation.is_deleted = False
|
||||
interpretation.save()
|
||||
assert len(book.approved_comments) == 0
|
|
@ -120,6 +120,7 @@ def test_profile(client):
|
|||
user_id=user.id,
|
||||
)
|
||||
book.save()
|
||||
m.BookVersion(semver="1.0.0", book_id=book.id).save()
|
||||
assert book
|
||||
|
||||
# profile page
|
||||
|
|
Loading…
Reference in New Issue