Skip to content

Commit

Permalink
SIANXKE-347: Intial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
anx-abruckner committed Oct 20, 2023
1 parent 5e3b61b commit fe0078b
Show file tree
Hide file tree
Showing 27 changed files with 556 additions and 12 deletions.
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.idea
.vscode
.env
.DS_Store
.pytest_cache
.coverage
coverage.xml
*.egg-info
*.pyc
*.pyo
*.sqlite*
12 changes: 12 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
repos:
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
args: [ "--profile", "black", "--filter-files" ]

- repo: https://github.com/psf/black
rev: 23.10.0 # Replace by any tag/version: https://github.com/psf/black/tags
hooks:
- id: black
language_version: python3 # Should be a command that runs python3.6.2+
16 changes: 4 additions & 12 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [x.y.z] - YYYY-MM-DD
## [1.0.0]

### Added
- Lorem ipsum dolor sit amet

### Deprecated
- Nothing.

### Removed
- Nothing.
- Initial setup

### Fixed
- Nothing.
[Unreleased]: https://github.com/anexia/django-generic-contact/compare/1.0.0...HEAD
[1.0.0]: https://github.com/anexia/django-generic-contact/releases/tag/1.0.0
92 changes: 92 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,98 @@

Django module to store contact request in a structured yet generic manner within the database.

### Installation

1. Install using pip:

```shell
pip install django-generic-contact
```

2. Integrate `django_generic_contact` into your `settings.py`

```python
INSTALLED_APPS = [
# ...
'django_generic_contact',
# ...
]
```

### Usage

The package provides you with a `Contact` model which expects `data` (dict) and the `name` of the requester,
an optional `message` can be added:

```
from django_generic_contact.models import Contact
contact = Contact.objects.create(
name="Mr. Tester",
message="Please contact me via email or phone.",
data={
"email": "[email protected]",
"phone": "123456",
}
)
```

#### JSON Schema validation

The contents of `data` will be validated against a [json schema](https://json-schema.org/) defined in your project's
`settings.py` (if provided). If needed you can define `GENERIC_CONTACT_DATA_SCHEMA` to check all relevant input
according to the expected structure, e.g.:

```
GENERIC_CONTACT_DATA_SCHEMA = {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"email": {"type": "string", "format": "email"},
"phone": {"type": "integer"},
},
}
```

See more examples of `GENERIC_CONTACT_DATA_SCHEMA` in `tests/testapp/tests/test_model.py`.

#### Customizing the Contact model
The base model `GenericContact` only requires the `data`. Thus you can let your own models inherit from this and extend
it according to your project's needs, e.g.:

```
from django_generic_contact.models import GenericContact
class CustomContact(GenericContact):
birth_date = models.Datetime(_("birth date"))
zip = models.CharField(_("zip"))
```

## Unit Tests

See folder [tests/](tests/). The provided tests cover these criteria:
* success:
* Contact model instance creation
* project's jsonschema validation
* failure:
* project's jsonschema validation
* exemplary custom jsonschema validation

Follow below instructions to run the tests.
You may exchange the installed Django and DRF versions according to your requirements.
:warning: Depending on your local environment settings you might need to explicitly call `python3` instead of `python`.
```bash
# install dependencies
python -m pip install --upgrade pip
pip install -r requirements.txt

# setup environment
pip install -e .

# run tests
cd tests && python manage.py test
```

### Contributing

Contributions are welcomed! Read the [Contributing Guide](CONTRIBUTING.md) for more information.
Expand Down
22 changes: 22 additions & 0 deletions django_generic_contact/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Schema configuration

The `data` field on the `Contact` model can be optionally extended by providing a Schema according to
[jsonschema](https://json-schema.org/)

```
GENERIC_CONTACT_DATA_SCHEMA = {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"email": {
"format": "email"
},
},
"unevaluatedProperties": {
"type": ["integer", "string"]
}
}
```

The app makes use of the [python implementation](https://python-jsonschema.readthedocs.io/en/stable/) of jsonschema
and supports format checking according to the library implementation.
Empty file.
24 changes: 24 additions & 0 deletions django_generic_contact/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from django import forms
from django.contrib import admin
from django_generic_contact.models import Contact
from django_generic_contact.utils import get_help_text, get_validators


class ContactAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ContactAdminForm, self).__init__(*args, **kwargs)
self.fields["data"].help_text = get_help_text()
self.fields["data"].validators = get_validators()

class Meta:
model = Contact
exclude = ()


@admin.register(Contact)
class ContactAdmin(admin.ModelAdmin):
list_display = [
"creation_date",
"name",
]
form = ContactAdminForm
6 changes: 6 additions & 0 deletions django_generic_contact/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class DjangoGenericContactConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "django_generic_contact"
26 changes: 26 additions & 0 deletions django_generic_contact/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 3.2.8 on 2021-10-05 14:13

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = []

operations = [
migrations.CreateModel(
name="Contact",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("creation_date", models.DateTimeField(auto_now_add=True, verbose_name="Creation date")),
("data", models.JSONField(default=dict, verbose_name="meta data")),
("name", models.CharField(max_length=255, verbose_name="name")),
("message", models.TextField(verbose_name="message")),
],
options={
"abstract": False,
},
),
]
18 changes: 18 additions & 0 deletions django_generic_contact/migrations/0002_alter_contact_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.8 on 2021-10-07 08:08

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("django_generic_contact", "0001_initial"),
]

operations = [
migrations.AlterField(
model_name="contact",
name="message",
field=models.TextField(blank=True, verbose_name="message"),
),
]
Empty file.
30 changes: 30 additions & 0 deletions django_generic_contact/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _

GENERIC_CONTACT_DATA_SCHEMA = getattr(settings, "GENERIC_CONTACT_DATA_SCHEMA", {})


class GenericContact(models.Model):

creation_date = models.DateTimeField(
verbose_name=_("Creation date"),
blank=False,
null=False,
auto_now_add=True,
)

data = models.JSONField(
_("meta data"),
default=dict,
)

class Meta:
abstract = True


class Contact(GenericContact):

name = models.CharField(_("name"), max_length=255)

message = models.TextField(_("message"), blank=True)
11 changes: 11 additions & 0 deletions django_generic_contact/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django.utils.translation import gettext_lazy as _
from django_generic_contact.models import GENERIC_CONTACT_DATA_SCHEMA
from django_generic_contact.validators import JSONSchemaValidator


def get_help_text():
return _("Meta data according to Schema: {schema}").format(schema=GENERIC_CONTACT_DATA_SCHEMA)


def get_validators():
return [JSONSchemaValidator(limit_value=GENERIC_CONTACT_DATA_SCHEMA)]
11 changes: 11 additions & 0 deletions django_generic_contact/validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import jsonschema
from django.core.exceptions import ValidationError
from django.core.validators import BaseValidator


class JSONSchemaValidator(BaseValidator):
def compare(self, value, schema):
try:
jsonschema.validate(value, schema, format_checker=jsonschema.draft202012_format_checker)
except jsonschema.exceptions.ValidationError as e:
raise ValidationError(str(e))
15 changes: 15 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Package and package dependencies
-e .

# Development dependencies
setuptools>=68.2.2,<68.3
wheel>=0.41.2,<0.42
twine>=4.0.2,<4.1
coverage>=7.3.2,<7.4

# Linters and formatters
pre-commit>=3.5.0,<3.6

# TestApp dependencies
django>=3.2,<4.3
jsonschema>=4.19.1,<4.20
40 changes: 40 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import os

from setuptools import find_packages, setup

with open(os.path.join(os.path.dirname(__file__), "README.md")) as readme:
README = readme.read()

# allow setup.py to be run from any path
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))

setup(
name="django-generic-contact",
version=os.getenv("PACKAGE_VERSION", "1.0.0").replace("refs/tags/", ""),
packages=find_packages(),
include_package_data=True,
license="MIT License",
description="Django module to store contact request in a structured yet generic manner.",
long_description=README,
long_description_content_type="text/markdown",
url="https://github.com/anexia/django-generic-contact",
author="Alexandra Bruckner",
author_email="[email protected]",
install_requires=[],
classifiers=[
"Development Status :: 5 - Production/Stable",
"Framework :: Django",
"Framework :: Django :: 3.2",
"Framework :: Django :: 4.1",
"Framework :: Django :: 4.2",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
],
)
Empty file added tests/core/__init__.py
Empty file.
Loading

0 comments on commit fe0078b

Please sign in to comment.