Skip to content

Commit

Permalink
- Added user's report system
Browse files Browse the repository at this point in the history
  • Loading branch information
onstabb committed Nov 28, 2023
1 parent 29d9919 commit fa09b69
Show file tree
Hide file tree
Showing 17 changed files with 119 additions and 39 deletions.
3 changes: 2 additions & 1 deletion src/admin/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from starlette.middleware.sessions import SessionMiddleware
from starlette_admin.contrib.mongoengine import Admin

from admin import config
from admin.auth import AdminCredentialsProvider
from admin.converter import MongoengineModelConverter
from admin.views import EventView, ContactView, UserView
Expand All @@ -14,7 +15,7 @@
admin = Admin(
templates_dir=str(DATA_PATH / "templates" / "admin"),
auth_provider=AdminCredentialsProvider(),
middlewares=[Middleware(SessionMiddleware, secret_key="ads1d21m21")]
middlewares=[Middleware(SessionMiddleware, secret_key=config.SESSION_SECRET_KEY)]
)

admin.add_view(UserView(User, converter=MongoengineModelConverter(), icon="fa-solid fa-users"))
Expand Down
4 changes: 4 additions & 0 deletions src/admin/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import os


SESSION_SECRET_KEY: str = os.getenv("SESSION_SECRET_KEY")
3 changes: 2 additions & 1 deletion src/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from events.routers import user_router as user_events_router
from likes.routers import router as likes_router
from notifications.routers import router as notifications_router

from photos.routers import router as photos_router
from reports.routers import router as reports_router
from userprofile.routers import router as profile_router
from users.routers import router as users_router

Expand All @@ -17,6 +17,7 @@
api_router.include_router(users_router, tags=['Users'], prefix='/users')
api_router.include_router(auth_router, tags=["Authorization"], prefix="")
api_router.include_router(event_router, tags=["Events"], prefix="/events")
api_router.include_router(reports_router, tags=["Reports"], prefix="/reports")

authenticated_router: APIRouter = APIRouter(prefix="/users/me", tags=["Users"])
authenticated_router.include_router(profile_router, tags=["Profile"], prefix="/profile")
Expand Down
4 changes: 4 additions & 0 deletions src/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from contacts.models import Contact
from events.models import Event
from reports.models import Report
from users.models import User


Expand All @@ -15,6 +16,9 @@ def init_db(**configuration) -> MongoClient:
Contact.register_delete_rule(User, 'respondent', mongoengine.DENY)
# User.register_delete_rule(Contact, "contacts", mongoengine.CASCADE)

Report.register_delete_rule(User, 'initiator', mongoengine.DENY)
Report.register_delete_rule(User, 'respondent', mongoengine.DENY)

Event.register_delete_rule(User, 'subscribers', mongoengine.PULL)
User.register_delete_rule(Event, 'events', mongoengine.PULL)

Expand Down
3 changes: 3 additions & 0 deletions src/reports/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@


REPORT_ADDITIONAL_INFO_MAX_LENGTH = 1024
10 changes: 10 additions & 0 deletions src/reports/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import enum


@enum.unique
class ReportReason(enum.StrEnum):
INAPPROPRIATE_BEHAVIOR = "inappropriate_behavior"
INAPPROPRIATE_CONTENT = "inappropriate_content"
FRAUD_OR_AD = "fraud_or_ad"
UNDERAGE = "underage"
OTHER = "other"
12 changes: 12 additions & 0 deletions src/reports/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from mongoengine import ReferenceField, StringField, EnumField, BooleanField

from models import BaseDocument
from reports.enums import ReportReason


class Report(BaseDocument):
initiator = ReferenceField('User')
respondent = ReferenceField('User')
reason = EnumField(ReportReason) # type: ReportReason
additional_info = StringField(required=False) # type: str
opened = BooleanField(default=True) # type: bool
15 changes: 15 additions & 0 deletions src/reports/routers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from fastapi import APIRouter

from reports import service
from reports.schemas import ReportIn
from users.dependencies import CurrentActiveUser, get_active_completed_user


router = APIRouter()


@router.post("")
def create_report(data: ReportIn, current_user: CurrentActiveUser):
respondent = get_active_completed_user(data.respondent)
new_report = service.create_report(current_user, respondent, data)
return new_report
11 changes: 11 additions & 0 deletions src/reports/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from pydantic import BaseModel, constr

from models import PydanticObjectId
from reports.config import REPORT_ADDITIONAL_INFO_MAX_LENGTH
from reports.enums import ReportReason


class ReportIn(BaseModel):
respondent: PydanticObjectId
reason: ReportReason
additional_info: constr(max_length=REPORT_ADDITIONAL_INFO_MAX_LENGTH) | None
9 changes: 9 additions & 0 deletions src/reports/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from reports.models import Report
from reports.schemas import ReportIn
from users.models import User


def create_report(initiator: User, respondent: User, report_data_in: ReportIn) -> Report:
report = Report(initiator=initiator, respondent=respondent, **report_data_in.model_dump(exclude={'respondent'}))
report.save()
return report
23 changes: 11 additions & 12 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,7 @@
import config
from authorization import config as auth_config
from database import init_db, close_db
from factories.factories import (
UserFactory,
ContactFactory,
ProfileFactory,
EventFactory,
)
from factories import factories
from location.database import geonames_db
from main import app
from photos.service import set_bucket
Expand Down Expand Up @@ -77,35 +72,39 @@ def db_config(factory_random):
def client(db_config, scheduler, city_db):
auth_config.SMS_SERVICE_DISABLED = True
set_bucket(LocalBucket())

client = AppTestClient(app=app)
yield client
client.close()


@pytest.fixture(scope="session")
def user_factory(db_config, city_db):
return UserFactory
return factories.UserFactory


@pytest.fixture(scope="session")
def contact_factory(db_config, city_db):
return ContactFactory
return factories.ContactFactory


@pytest.fixture(scope="session")
def userprofile_factory(db_config, city_db):
return ProfileFactory
return factories.ProfileFactory


@pytest.fixture(scope="session")
def event_factory(db_config, city_db):
return EventFactory
return factories.EventFactory


@pytest.fixture(scope="session")
def report_factory(db_config, city_db):
return factories.ReportFactory


@pytest.fixture(scope="function")
def user(user_factory) -> User:
user: User = user_factory.create(active=True)
user: User = user_factory.create()
return user


Expand Down
41 changes: 21 additions & 20 deletions tests/factories/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from contacts.models import Contact, Message, ContactState
from events.models import Event
from location.database import geonames_db
from reports.enums import ReportReason
from reports.models import Report
from userprofile import config as profile_config
from userprofile.enums import Gender, ResidenceLength, ResidencePlan

Expand All @@ -33,6 +35,11 @@ def build_json_dict(cls, **kwargs) -> dict:
return result


class _UsingLocationFactory(_BaseMongoEngineFactory):
city_id = factory.LazyFunction(lambda: generators.get_random_city().geonameid)
location = factory.LazyAttribute(lambda location: geonames_db.get_city(location.city_id).coordinates)


class UserFactory(_BaseMongoEngineFactory):
class Meta:
model = User
Expand All @@ -41,23 +48,11 @@ class Meta:
password = factory.LazyFunction(generators.generate_hashed_password)
profile = factory.SubFactory("tests.factories.factories.ProfileFactory",)
photo_urls = factory.List([factory.Faker("image_url"), factory.Faker("image_url")])
is_active = fuzzy.FuzzyChoice((False, False, False, True))
is_active = True
banned = False

class Params:
active = factory.Trait(
is_active=True,
banned=False
)




class LocationFactory(_BaseMongoEngineFactory):
city_id = factory.LazyFunction(lambda: generators.get_random_city().geonameid)
location = factory.LazyAttribute(lambda location: geonames_db.get_city(location.city_id).coordinates)


class ProfileFactory(LocationFactory):
class ProfileFactory(_UsingLocationFactory):
class Meta:
model = UserProfile

Expand Down Expand Up @@ -87,12 +82,9 @@ class Meta:
)

class Params:

active_dialog = factory.Trait(
initiator_state=ContactState.ESTABLISHED,
respondent_state=ContactState.ESTABLISHED,
initiator__active=True,
respondent__active=True,
messages=factory.List(
[
factory.SubFactory(
Expand All @@ -117,7 +109,7 @@ class Meta:
text = factory.Faker("paragraph", nb_sentences=4)


class EventFactory(LocationFactory):
class EventFactory(_UsingLocationFactory):
class Meta:
model = Event

Expand All @@ -129,7 +121,6 @@ class Meta:

@factory.post_generation
def subscribers(self: Event, create: bool, extracted: list[User] | None, **kwargs) -> None:

if not create:
return

Expand All @@ -140,3 +131,13 @@ def subscribers(self: Event, create: bool, extracted: list[User] | None, **kwarg

self.subscribers.extend(users)
self.save()


class ReportFactory(_BaseMongoEngineFactory):
class Meta:
model = Report

initiator = factory.SubFactory(UserFactory)
respondent = factory.SubFactory(UserFactory)
reason = factory.Iterator(ReportReason)
additional_info = factory.Faker("paragraph", nb_sentences=5)
Empty file added tests/reports/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions tests/reports/test_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from reports import service
from reports.schemas import ReportIn


def test_create_report(report_factory, user_factory):
initiator, respondent = user_factory.create_batch(size=2, )
report = report_factory.build(initiator=initiator, respondent=respondent)
report_in = ReportIn(respondent=respondent.id, reason=report.reason, additional_info=report.additional_info)

assert service.create_report(initiator, respondent, report_in)
4 changes: 2 additions & 2 deletions tests/test_candidates/test_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@


def test_get_candidates_for_user(user_factory):
user_factory.create_batch(size=5, active=True)
user = user_factory.create(active=True, profile__gender_preference=None)
user_factory.create_batch(size=5)
user = user_factory.create(profile__gender_preference=None)

result = service.get_candidates_for_user(user, limit=5)

Expand Down
4 changes: 2 additions & 2 deletions tests/test_photos/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


def test_upload_photo(user_factory, client, image_file):
user = user_factory.create(photo_urls=[], is_active=True)
user = user_factory.create(photo_urls=[])
client.bearer_token = user.token[0]
data = {"photo": image_file}

Expand All @@ -12,7 +12,7 @@ def test_upload_photo(user_factory, client, image_file):


def test_upload_photo_incorrect_index(user_factory, client, image_file):
user = user_factory.create(photo_urls=[], is_active=True)
user = user_factory.create(photo_urls=[])
client.bearer_token = user.token[0]
data = {"photo": image_file}

Expand Down
2 changes: 1 addition & 1 deletion tests/test_userprofile/test_api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@


def test_create_profile(client, user_factory, userprofile_factory):
user = user_factory.create(profile=None, active=True)
user = user_factory.create(profile=None)

client.bearer_token = user.token[0]
request_data = userprofile_factory.build_json_dict()
Expand Down

0 comments on commit fa09b69

Please sign in to comment.