Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #4

Merged
merged 19 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@

# Required
version: 2
build:
os: ubuntu-20.04
tools:
python: "3.8"

# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py

# Optionally set the version of Python and requirements required to build your docs
python:
version: 3.8
install:
- requirements: docs/requirements.txt
9 changes: 9 additions & 0 deletions docs/firm_info/admin.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.. _intro_firm-admin:

=====
Admin
=====

.. automodule:: firm_info.admin
:members: UniqueModelAdmin
:exclude-members: has_add_permission
9 changes: 9 additions & 0 deletions docs/firm_info/exceptions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.. _intro_firm-info_exceptions:

==========
Exceptions
==========

.. automodule:: firm_info.exceptions
:members:
:exclude-members:
9 changes: 9 additions & 0 deletions docs/firm_info/factories.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.. _intro_firm-factories:

=========
Factories
=========

.. automodule:: firm_info.factories
:members:
:exclude-members:
10 changes: 10 additions & 0 deletions docs/firm_info/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ Django firm info

models.rst

admin.rst

factories.rst

settings.rst

serializers.rst

templatetags.rst

exceptions.rst

managers.rst
9 changes: 9 additions & 0 deletions docs/firm_info/managers.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.. _intro_firm-info_managers:

=======
Manager
=======

.. autoclass:: firm_info.managers.SingletonManager
:members:
:exclude-members: create
2 changes: 1 addition & 1 deletion docs/firm_info/models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ Models
======

.. automodule:: firm_info.models
:members: FirmContact, Link, SocialSharing, Tracking, AppsBanner,
:members:
:exclude-members: DoesNotExist, MultipleObjectsReturned
2 changes: 1 addition & 1 deletion docs/firm_info/serializers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ Serializers
===========

.. automodule:: firm_info.serializers
:members: SerializeFirmError, _format_address, serialize_firm_info, serialize_firm_social, serialize_firm_description, serialize_firm_social_sharing, serialize_firm_apps_banner
:members:
:exclude-members: DoesNotExist, MultipleObjectsReturned
9 changes: 9 additions & 0 deletions docs/firm_info/settings.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.. _intro_firm-settings:

========
Settings
========

.. automodule:: firm_info.settings
:members:
:exclude-members:
2 changes: 1 addition & 1 deletion docs/firm_info/templatetags.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ Templatetags
============

.. automodule:: firm_info.templatetags.firm_info
:members: firm_contact, firm_social_links, firm_description, firm_logos, firm_social_shares, firm_tag_analytic, app_banner
:members:
:exclude-members: DoesNotExist, MultipleObjectsReturned
15 changes: 15 additions & 0 deletions docs/history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@
History
=======

0.1.5 (2024-05-30)
------------------

Changes
~~~~~~~
* [CHG] Added global context accessibility in template tags (Ticket #5289453) [Samy Saad]


Other
~~~~~
* [CHG] Removed dj4.0 and dj4.1 from tox tests [Samy Saad]
* [FIX] Added default autofield in settings [Samy Saad]
* [FIX] Fixed rtd build [Samy Saad]
* [DOC] Updated doc [Samy Saad]

0.1.4 (2023-09-21)
------------------

Expand Down
44 changes: 28 additions & 16 deletions firm_info/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,31 @@
from .models import AppsBanner, FirmContact, Link, SocialSharing, Tracking


class UniqueModelAdmin(admin.ModelAdmin):
"""
A custom ModelAdmin that restricts the addition of model instances to only one.

This admin class overrides the default add permission to ensure that only one
instance of the associated model can exist at any given time.
If an instance already exists, it prohibits adding new instances.
"""

def has_add_permission(self, request):
existing_count = self.model.objects.count()
if existing_count == 0:
return super().has_add_permission(request)
else:
return False

def clean(self):
existing_count = self.model.objects.count()
if existing_count > 1:
# raise validation error if there is more than one firm contact
raise ValidationError(
_("Only one {} instance allowed.").format(self.model.__name__)
)


@admin.register(Link)
class LinkAdmin(admin.ModelAdmin):
pass
Expand All @@ -17,31 +42,18 @@ class LinkInline(admin.TabularInline):


@admin.register(FirmContact)
class ClientContactAdmin(admin.ModelAdmin):
class ClientContactAdmin(UniqueModelAdmin):
inlines = [LinkInline]
formfield_overrides = SmartModelAdmin.formfield_overrides

def has_add_permission(self, request):
existing_count = FirmContact.objects.count()
if existing_count == 0:
return super().has_add_permission(request)
else:
return False

def clean(self):
existing_count = FirmContact.objects.count()
if existing_count > 1:
# raise validation error if there is more than one firm contact
raise ValidationError(_("Only one FirmContact instance allowed."))


@admin.register(SocialSharing)
class SocialSharingAdmin(admin.ModelAdmin):
class SocialSharingAdmin(UniqueModelAdmin):
pass


@admin.register(Tracking)
class TrackingAdmin(admin.ModelAdmin):
class TrackingAdmin(UniqueModelAdmin):
pass


Expand Down
8 changes: 3 additions & 5 deletions firm_info/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""


class MyAppBaseException(Exception):
class FirmInfoException(Exception):
"""
Exception base.
Expand All @@ -13,8 +13,6 @@ class MyAppBaseException(Exception):
pass


class DummyError(MyAppBaseException):
"""
Dummy exception sample to raise from your code.
"""
class SerializeFirmError(FirmInfoException):
"""Exception raised when serializing firm data encounters an error."""
pass
69 changes: 67 additions & 2 deletions firm_info/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
from django.core.files import File

import factory
from firm_info.models import Tracking

from .models import AppsBanner
from firm_info.models import (
AppsBanner,
FirmContact,
SocialSharing,
Tracking
)


def create_image_file(filename=None, size=(100, 100), color="blue",
Expand Down Expand Up @@ -83,13 +87,21 @@ class Meta:
model = Tracking


def get_application_type(choice):
return choice[0]


class AppsBannerFactory(factory.django.DjangoModelFactory):
"""
Factory to create instance of a AppsBanner.
"""

title = factory.Faker("text", max_nb_chars=150)
description = factory.Faker("text", max_nb_chars=150)
application_type = factory.Iterator(
AppsBanner.APPS_CHOICES,
getter=get_application_type
)

class Meta:
model = AppsBanner
Expand All @@ -104,3 +116,56 @@ def image(self):
"""

return create_image_file()


class FirmContactFactory(factory.django.DjangoModelFactory):
"""
Factory to create instance of a FirmContact.
"""

phone_number = factory.Faker("phone_number")
email = factory.Faker("email")
address = factory.Faker("street_address")
postal_code = factory.Faker("postcode")
city = factory.Faker("city")
country = factory.Faker("country")
baseline = factory.Faker("text", max_nb_chars=255)
short_description = factory.Faker("text")

class Meta:
model = FirmContact

@factory.lazy_attribute
def logo(self):
return create_image_file()

@factory.lazy_attribute
def logo_invert(self):
return create_image_file()

@factory.lazy_attribute
def favicon(self):
return create_image_file()


class SocialSharingFactory(factory.django.DjangoModelFactory):
"""
Factory to create instance of a AppsBanner.
"""

og_twitter_site = factory.Faker("text", max_nb_chars=100)
og_description = factory.Faker("text", max_nb_chars=180)

class Meta:
model = SocialSharing

@factory.lazy_attribute
def og_image(self):
"""
Fill file field with generated image.

Returns:
django.core.files.File: File object.
"""

return create_image_file()
17 changes: 15 additions & 2 deletions firm_info/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@
from django.utils.translation import gettext_lazy as _


SINGLETON_ERROR = _("Model {model_name} has already one instance")


class SingletonManager(models.Manager):
"""
A manager to ensure that only one instance of the model exists.

This manager overrides the `create` method to enforce a singleton pattern
on the associated model. If an instance of the model already exists,
attempting to create another instance will raise a `ValueError`.

Methods:
create(**kwargs): Creates a new instance of the model if none exists.
Raises `ValueError` if an instance already exists.
"""
def create(self, **kwargs):
if self.model.objects.exists():
error_message = _("Model {model_name} has already one instance")
raise ValueError(
error_message.format(model_name=self.model._meta.verbose_name)
SINGLETON_ERROR.format(model_name=self.model._meta.verbose_name)
)
return super().create(**kwargs)
12 changes: 6 additions & 6 deletions firm_info/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ class FirmContact(models.Model):
"""
Represents the contact information for a firm.

Args:
models.Model: The base model class provided by Django.

Attributes:
phone_number (CharField): The phone number of the firm.
email (EmailField): The email address of the firm.
Expand Down Expand Up @@ -131,6 +128,7 @@ class SocialSharing(models.Model):
og_image (SmartMediaField): The OG image for social media sharing.
og_description (TextField): The OG description for social media sharing.
og_twitter_site (CharField): The OG Twitter site for social media sharing.
objects (SingletonManager): The manager for the FirmContact model.
"""

og_image = SmartMediaField(
Expand All @@ -154,6 +152,8 @@ class SocialSharing(models.Model):
verbose_name=_("OG Twitter Site"),
)

objects = SingletonManager()

class Meta:
verbose_name = _("Social media share")
verbose_name_plural = _("Social media shares")
Expand All @@ -180,6 +180,7 @@ class Tracking(models.Model):

Attributes:
tag_analytic (CharField): The tag analytic for tracking.
objects (SingletonManager): The manager for the FirmContact model.
"""

tag_analytic = models.CharField(
Expand All @@ -190,6 +191,8 @@ class Tracking(models.Model):
verbose_name=_("Tag Analytic"),
)

objects = SingletonManager()

class Meta:
verbose_name = _("Tracking")
verbose_name_plural = _("Tracks")
Expand All @@ -199,9 +202,6 @@ class AppsBanner(models.Model):
"""
Represents an app banner in the Django firm_info models.

Args:
models.Model: The base model class provided by Django.

Attributes:
APPS_CHOICES (list): A list of tuples representing the available choices for
the application type.
Expand Down
Loading
Loading