djangobible 0.0.16__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
djangobible/__init__.py CHANGED
@@ -1,9 +1,11 @@
1
- """
2
- djangobible python library.
3
-
4
- djangobible lets you easily associate Django models with Bible Scripture references and search accordingly.
5
- """
6
-
7
- __version__ = "0.0.16"
8
-
9
- from pythonbible import *
1
+ """djangobible python library.
2
+
3
+ djangobible lets you easily associate Django models with Bible Scripture references and
4
+ search accordingly.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ __version__ = "0.3.0"
10
+
11
+ from pythonbible import *
djangobible/apps.py CHANGED
@@ -1,5 +1,11 @@
1
- from django.apps import AppConfig
2
-
3
-
4
- class DjangoBibleConfig(AppConfig):
5
- name: str = "djangobible"
1
+ """The app config for the djangobible library."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from django.apps import AppConfig
6
+
7
+
8
+ class DjangoBibleConfig(AppConfig):
9
+ """The app config for the djangobible library."""
10
+
11
+ name: str = "djangobible"
djangobible/fields.py CHANGED
@@ -1,58 +1,99 @@
1
- from typing import Callable, List, Optional, Union
2
-
3
- import pythonbible as bible
4
- from django import forms
5
- from django.db import models
6
- from django.utils.functional import cached_property
7
-
8
- from .validators import validate_verse
9
-
10
-
11
- class VerseField(models.IntegerField):
12
- def get_prep_value(self, value):
13
- if value is None:
14
- return None
15
-
16
- if isinstance(value, str):
17
- validate_verse(value)
18
- value = bible.convert_references_to_verse_ids(bible.get_references(value))[
19
- 0
20
- ]
21
-
22
- return super().get_prep_value(value)
23
-
24
- @cached_property
25
- def validators(self) -> List[Callable]:
26
- return [validate_verse]
27
-
28
- def to_python(self, value: Optional[Union[int, str]]) -> Optional[str]:
29
- if value is None or isinstance(value, str):
30
- return value
31
-
32
- return bible.format_scripture_references(
33
- bible.convert_verse_ids_to_references([value])
34
- )
35
-
36
- def from_db_value(
37
- self, value: Optional[Union[int, str]], expression, connection
38
- ) -> Optional[str]:
39
- return self.to_python(value)
40
-
41
- def formfield(self, **kwargs) -> forms.Field:
42
- return super().formfield(
43
- **{
44
- "form_class": forms.CharField,
45
- **kwargs,
46
- }
47
- )
48
-
49
- def get_db_prep_value(
50
- self, value: Optional[Union[int, str]], *args, **kwargs
51
- ) -> Optional[int]:
52
- if value is None:
53
- return None
54
-
55
- if isinstance(value, int):
56
- return value
57
-
58
- return bible.convert_references_to_verse_ids(bible.get_references(value))[0]
1
+ """Custom Django model fields for djangobible."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+ from typing import Callable
7
+
8
+ import pythonbible as bible
9
+ from django import forms
10
+ from django.core.exceptions import ValidationError
11
+ from django.db import models
12
+ from django.utils.functional import cached_property
13
+
14
+ from djangobible.validators import validate_verse
15
+
16
+
17
+ class VerseField(models.Field):
18
+ """Custom Django model field to represent a Verse."""
19
+
20
+ def get_prep_value(self: VerseField, value: int | str | None) -> int:
21
+ """Validate and convert the value to a verse id int."""
22
+ if isinstance(value, str):
23
+ validate_verse(value)
24
+ value = bible.convert_references_to_verse_ids(
25
+ bible.get_references(value),
26
+ )[0]
27
+ elif isinstance(value, int) and not bible.is_valid_verse_id(value):
28
+ error_message = f"{value} is not a valid verse id."
29
+ raise ValidationError(error_message)
30
+
31
+ return super().get_prep_value(value)
32
+
33
+ @cached_property
34
+ def validators(self: VerseField) -> list[Callable]:
35
+ """Return the list of validators for this field."""
36
+ return [validate_verse]
37
+
38
+ def to_python(
39
+ self: VerseField,
40
+ value: int | str | None,
41
+ ) -> str | None:
42
+ """Convert the value into a text scripture reference."""
43
+ if value is None or isinstance(value, str):
44
+ return value
45
+
46
+ return bible.format_scripture_references(
47
+ bible.convert_verse_ids_to_references([value]),
48
+ )
49
+
50
+ def from_db_value(
51
+ self: VerseField,
52
+ value: int | str | None,
53
+ expression: Any,
54
+ connection: Any,
55
+ ) -> str | None:
56
+ """Convert the value from the DB."""
57
+ return self.to_python(value)
58
+
59
+ def formfield(
60
+ self: VerseField,
61
+ form_class: type[forms.Field] | None = None,
62
+ choices_form_class: type[forms.Field] | None = None,
63
+ **kwargs: Any,
64
+ ) -> forms.Field:
65
+ """Make sure the form field is a CharField."""
66
+ return super().formfield(
67
+ **{
68
+ "form_class": forms.CharField,
69
+ **kwargs,
70
+ },
71
+ )
72
+
73
+ def get_db_prep_save(
74
+ self: VerseField,
75
+ value: int | str | None,
76
+ **kwargs: Any,
77
+ ) -> int | None:
78
+ """Validate and convert the value to a verse id int before saving to the DB."""
79
+ if not value:
80
+ return None
81
+
82
+ if isinstance(value, str):
83
+ references = bible.get_references(value)
84
+
85
+ if verse_ids := bible.convert_references_to_verse_ids(references):
86
+ value = verse_ids[0]
87
+ else:
88
+ error_message = f"{value} does not contain a valid Scripture reference."
89
+ raise ValidationError(error_message)
90
+
91
+ if not bible.is_valid_verse_id(value):
92
+ error_message = f"{value} is not a valid verse id."
93
+ raise ValidationError(error_message)
94
+
95
+ return int(value)
96
+
97
+ def get_internal_type(self: VerseField) -> str:
98
+ """Return the internal type (IntegerField) for the VerseField field."""
99
+ return "IntegerField"
@@ -1,42 +1,43 @@
1
- # Generated by Django 3.1.2 on 2020-10-10 00:44
2
-
3
- import django.db.models.deletion
4
- from django.db import migrations, models
5
-
6
-
7
- class Migration(migrations.Migration):
8
-
9
- initial = True
10
-
11
- dependencies = [
12
- ("contenttypes", "0002_remove_content_type_name"),
13
- ]
14
-
15
- operations = [
16
- migrations.CreateModel(
17
- name="VerseRelation",
18
- fields=[
19
- (
20
- "id",
21
- models.AutoField(
22
- auto_created=True,
23
- primary_key=True,
24
- serialize=False,
25
- verbose_name="ID",
26
- ),
27
- ),
28
- ("verse", models.PositiveIntegerField()),
29
- ("object_id", models.PositiveIntegerField()),
30
- (
31
- "content_type",
32
- models.ForeignKey(
33
- on_delete=django.db.models.deletion.CASCADE,
34
- to="contenttypes.contenttype",
35
- ),
36
- ),
37
- ],
38
- options={
39
- "db_table": "verse_relation",
40
- },
41
- ),
42
- ]
1
+ # Generated by Django 3.1.2 on 2020-10-10 00:44
2
+
3
+ from __future__ import annotations
4
+
5
+ import django.db.models.deletion
6
+ from django.db import migrations, models
7
+
8
+
9
+ class Migration(migrations.Migration):
10
+ initial = True
11
+
12
+ dependencies = [
13
+ ("contenttypes", "0002_remove_content_type_name"),
14
+ ]
15
+
16
+ operations = [
17
+ migrations.CreateModel(
18
+ name="VerseRelation",
19
+ fields=[
20
+ (
21
+ "id",
22
+ models.AutoField(
23
+ auto_created=True,
24
+ primary_key=True,
25
+ serialize=False,
26
+ verbose_name="ID",
27
+ ),
28
+ ),
29
+ ("verse", models.PositiveIntegerField()),
30
+ ("object_id", models.PositiveIntegerField()),
31
+ (
32
+ "content_type",
33
+ models.ForeignKey(
34
+ on_delete=django.db.models.deletion.CASCADE,
35
+ to="contenttypes.contenttype",
36
+ ),
37
+ ),
38
+ ],
39
+ options={
40
+ "db_table": "verse_relation",
41
+ },
42
+ ),
43
+ ]
@@ -1 +0,0 @@
1
- """Database migration(s) for the djangobible library."""
djangobible/models.py CHANGED
@@ -1,62 +1,89 @@
1
- import pythonbible as bible
2
- from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
3
- from django.contrib.contenttypes.models import ContentType
4
- from django.db import models
5
-
6
-
7
- class VerseRelation(models.Model):
8
- verse = models.PositiveIntegerField(null=False, blank=False)
9
-
10
- # Below the mandatory fields for generic relation
11
- content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
12
- object_id = models.PositiveIntegerField()
13
- content_object = GenericForeignKey()
14
-
15
- class Meta:
16
- db_table = u"verse_relation"
17
-
18
- def save(self, *args, **kwargs):
19
- if not bible.is_valid_verse_id(self.verse):
20
- raise bible.InvalidVerseError(verse_id=self.verse)
21
-
22
- super(VerseRelation, self).save(*args, **kwargs)
23
-
24
-
25
- class ScriptureIndexedModelManager(models.Manager):
26
- def filter_by_verse_ids(self, verse_ids):
27
- content_type = ContentType.objects.get_for_model(self.model)
28
- verse_relations = VerseRelation.objects.filter(
29
- content_type=content_type, verse__in=verse_ids
30
- )
31
- object_ids = verse_relations.values_list("object_id", flat=True)
32
- return self.get_queryset().filter(pk__in=object_ids)
33
-
34
-
35
- class ScriptureIndexedModel(models.Model):
36
- verses = GenericRelation(VerseRelation)
37
- objects = ScriptureIndexedModelManager()
38
-
39
- class Meta:
40
- """ScriptureIndexed Model is an abstract model."""
41
-
42
- abstract = True
43
-
44
- def add_verse(self, verse_id):
45
- # will raise an error if object.id is null
46
- VerseRelation(content_object=self, verse=verse_id).save()
47
-
48
- def set_verses(self, verse_ids):
49
- # Delete any existing verse relations
50
- self.verses.all().delete()
51
-
52
- # Create new verse relation objects
53
- verse_relations = []
54
-
55
- for verse_id in verse_ids:
56
- verse_relations.append(VerseRelation(content_object=self, verse=verse_id))
57
-
58
- VerseRelation.objects.bulk_create(verse_relations)
59
-
60
- @property
61
- def verse_ids(self):
62
- return [verse_relation.verse for verse_relation in self.verses.all()]
1
+ """Custom Django managers and models for djangobible."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pythonbible as bible
6
+ from django.contrib.contenttypes.fields import GenericForeignKey
7
+ from django.contrib.contenttypes.fields import GenericRelation
8
+ from django.contrib.contenttypes.models import ContentType
9
+ from django.db import models
10
+
11
+
12
+ class VerseRelation(models.Model):
13
+ """Custom Django model for a verse relation."""
14
+
15
+ verse = models.PositiveIntegerField(null=False, blank=False)
16
+
17
+ # Below the mandatory fields for generic relation
18
+ content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
19
+ object_id = models.PositiveIntegerField()
20
+ content_object = GenericForeignKey()
21
+
22
+ class Meta:
23
+ """Set the DB table name."""
24
+
25
+ db_table = "verse_relation"
26
+
27
+ def save(
28
+ self: VerseRelation,
29
+ force_insert: bool = False,
30
+ force_update: bool = False,
31
+ using: str | None = None,
32
+ update_fields: list[str] | None = None,
33
+ ) -> None:
34
+ """Save the instance to the DB."""
35
+ if not bible.is_valid_verse_id(self.verse):
36
+ raise bible.InvalidVerseError(verse_id=self.verse)
37
+
38
+ super().save(force_insert, force_update, using, update_fields)
39
+
40
+
41
+ class ScriptureIndexedModelManager(models.Manager):
42
+ """Custom Django model manager for ScriptureIndexedModel."""
43
+
44
+ def filter_by_verse_ids(
45
+ self: ScriptureIndexedModelManager,
46
+ verse_ids: list[int] | None,
47
+ ) -> models.QuerySet:
48
+ """Return the Query Set for the objects related to the given verse ids."""
49
+ content_type = ContentType.objects.get_for_model(self.model)
50
+ verse_relations = VerseRelation.objects.filter(
51
+ content_type=content_type,
52
+ verse__in=verse_ids,
53
+ )
54
+ object_ids = verse_relations.values_list("object_id", flat=True)
55
+ return self.get_queryset().filter(pk__in=object_ids)
56
+
57
+
58
+ class ScriptureIndexedModel(models.Model):
59
+ """An abstract Django model with a many-to-many relationship for verses."""
60
+
61
+ verses = GenericRelation(VerseRelation)
62
+ objects = ScriptureIndexedModelManager()
63
+
64
+ class Meta:
65
+ """ScriptureIndexed Model is an abstract model."""
66
+
67
+ abstract = True
68
+
69
+ def add_verse(self: ScriptureIndexedModel, verse_id: int | None) -> None:
70
+ """Add a verse to the related verses."""
71
+ VerseRelation(content_object=self, verse=verse_id).save()
72
+
73
+ def set_verses(self: ScriptureIndexedModel, verse_ids: list[int] | None) -> None:
74
+ """Overwrite the related verses with the given list of verse ids."""
75
+ self.verses.all().delete()
76
+
77
+ if not verse_ids:
78
+ return
79
+
80
+ verse_relations = [
81
+ VerseRelation(content_object=self, verse=verse_id) for verse_id in verse_ids
82
+ ]
83
+
84
+ VerseRelation.objects.bulk_create(verse_relations)
85
+
86
+ @property
87
+ def verse_ids(self: ScriptureIndexedModel) -> list[int]:
88
+ """Return the list of related verse id integers."""
89
+ return [verse_relation.verse for verse_relation in self.verses.all()]
@@ -1,61 +1,61 @@
1
- from typing import Optional
2
-
3
- import pythonbible as bible
4
- from django import template
5
-
6
- register = template.Library()
7
-
8
-
9
- @register.simple_tag
10
- def verse_reference(verse_id: int, **kwargs) -> str:
11
- """For a given verse id return the formatted scripture reference string
12
-
13
- :param verse_id:
14
- :return: the scripture reference string for the given verse id
15
- """
16
- book: bible.Book
17
- chapter: int
18
- verse: int
19
- book, chapter, verse = bible.get_book_chapter_verse(verse_id)
20
-
21
- version_id: Optional[str] = kwargs.get("version")
22
-
23
- if version_id:
24
- kwargs["version"] = _get_version(version_id)
25
-
26
- return bible.format_single_reference(book, chapter, verse, chapter, verse, **kwargs)
27
-
28
-
29
- @register.simple_tag
30
- def verse_text(verse_id: int, **kwargs) -> str:
31
- """For a given verse id and version, return the verse text string
32
-
33
- :param verse_id:
34
- :return: the verse text for the given verse id and version
35
- """
36
- version_id: Optional[str] = kwargs.get("version")
37
- text: str = ""
38
-
39
- if version_id is not None:
40
- text = bible.get_verse_text(verse_id, _get_version(version_id))
41
- else:
42
- text = bible.get_verse_text(verse_id)
43
-
44
- include_verse_numbers: bool = kwargs.get("include_verse_numbers", False)
45
-
46
- if include_verse_numbers:
47
- text = f"{bible.get_verse_number(verse_id)}. {text}"
48
-
49
- return text
50
-
51
-
52
- def _get_version(version_id: str) -> Optional[bible.Version]:
53
- try:
54
- return bible.Version[version_id]
55
- except KeyError:
56
- try:
57
- return bible.Version(version_id)
58
- except ValueError:
59
- pass
60
-
61
- return None
1
+ """Custom tags for djangobible."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from contextlib import suppress
6
+ from typing import Any
7
+
8
+ import pythonbible as bible
9
+ from django import template
10
+
11
+ register = template.Library()
12
+
13
+
14
+ @register.simple_tag
15
+ def verse_reference(verse_id: int, **kwargs: Any) -> str:
16
+ """For a given verse id return the formatted scripture reference string.
17
+
18
+ :param verse_id:
19
+ :return: the scripture reference string for the given verse id
20
+ """
21
+ book: bible.Book
22
+ chapter: int
23
+ verse: int
24
+ book, chapter, verse = bible.get_book_chapter_verse(verse_id)
25
+
26
+ if version_id := kwargs.get("version"):
27
+ kwargs["version"] = _get_version(version_id) # type: ignore[arg-type,assignment]
28
+
29
+ reference = bible.NormalizedReference(book, chapter, verse, chapter, verse)
30
+
31
+ return bible.format_single_reference(reference, **kwargs)
32
+
33
+
34
+ @register.simple_tag
35
+ def verse_text(verse_id: int, **kwargs: Any) -> str:
36
+ """For a given verse id and version, return the verse text string.
37
+
38
+ :param verse_id:
39
+ :return: the verse text for the given verse id and version
40
+ """
41
+ version_id: str | None = kwargs.get("version") # type: ignore[assignment]
42
+ text: str = (
43
+ bible.get_verse_text(verse_id, _get_version(version_id))
44
+ if version_id
45
+ else bible.get_verse_text(verse_id)
46
+ )
47
+ include_verse_numbers: bool = kwargs.get("include_verse_numbers", False) # type: ignore[assignment]
48
+
49
+ return (
50
+ f"{bible.get_verse_number(verse_id)}. {text}" if include_verse_numbers else text
51
+ )
52
+
53
+
54
+ def _get_version(version_id: str) -> bible.Version | None:
55
+ try:
56
+ return bible.Version[version_id]
57
+ except KeyError:
58
+ with suppress(ValueError):
59
+ return bible.Version(version_id)
60
+
61
+ return bible.versions.DEFAULT_VERSION
djangobible/validators.py CHANGED
@@ -1,25 +1,30 @@
1
- from typing import List, Optional
2
-
3
- import pythonbible as bible
4
- from django.core.exceptions import ValidationError
5
-
6
-
7
- def validate_verse(value: Optional[str]) -> None:
8
- if value is None:
9
- return
10
-
11
- references: List[bible.NormalizedReference] = bible.get_references(value)
12
-
13
- if len(references) == 0:
14
- raise ValidationError("Not a valid reference.")
15
-
16
- if len(references) > 1:
17
- raise ValidationError("Only single verse references allowed.")
18
-
19
- verse_ids: List[int] = bible.convert_references_to_verse_ids(references)
20
-
21
- if len(verse_ids) == 0:
22
- raise ValidationError("Not a valid reference.")
23
-
24
- if len(verse_ids) > 1:
25
- raise ValidationError("Only single verse references allowed.")
1
+ """Validators for djangobible custom Fields."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from django.core.exceptions import ValidationError
6
+ from pythonbible import NormalizedReference
7
+ from pythonbible import convert_references_to_verse_ids
8
+ from pythonbible import get_references
9
+
10
+
11
+ def validate_verse(verse_value: str | None) -> None:
12
+ """Validate that the given value is a valid string representation of a verse."""
13
+ if verse_value is None:
14
+ return
15
+
16
+ references: list[NormalizedReference] = get_references(verse_value)
17
+
18
+ if not references:
19
+ error_message = "Not a valid reference."
20
+ raise ValidationError(error_message)
21
+
22
+ if len(references) > 1:
23
+ error_message = "Only single verse references allowed."
24
+ raise ValidationError(error_message)
25
+
26
+ verse_ids: list[int] = convert_references_to_verse_ids(references)
27
+
28
+ if len(verse_ids) > 1:
29
+ error_message = "Only single verse references allowed."
30
+ raise ValidationError(error_message)
@@ -0,0 +1,270 @@
1
+ Metadata-Version: 2.4
2
+ Name: djangobible
3
+ Version: 0.3.0
4
+ Author: Nathan Patton
5
+ Author-email: Nathan Patton <npatton@gmail.com>
6
+ License-Expression: MIT
7
+ Classifier: Framework :: Django
8
+ Classifier: Intended Audience :: Information Technology
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Classifier: Topic :: Software Development :: Libraries
13
+ Classifier: Topic :: Software Development
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: License :: OSI Approved :: MIT License
23
+ Requires-Dist: django>=4.2.0
24
+ Requires-Dist: pythonbible==0.14.0
25
+ Requires-Python: >=3.10
26
+ Project-URL: Source, https://github.com/avendesora/djangobible
27
+ Description-Content-Type: text/markdown
28
+
29
+ # djangobible
30
+
31
+ The djangobible library is a Django app that wraps the [pythonbible](https://github.com/avendesora/pythonbible) library and provides models, managers, and other tools to easily index an object by a scripture reference.
32
+
33
+ <table>
34
+ <tr>
35
+ <td>Latest Version</td>
36
+ <td>
37
+ <a href="https://pypi.org/project/djangobible/"><img src="https://img.shields.io/pypi/v/djangobible?color=gold&logo=pypi&logoColor=lightgray"></a>
38
+ <img src="https://img.shields.io/pypi/dm/djangobible?color=gold">
39
+ </td>
40
+ </tr>
41
+ <tr>
42
+ <td>License</td>
43
+ <td><a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-orange.svg"></a></td>
44
+ </tr>
45
+ <tr>
46
+ <td>Tests</td>
47
+ <td>
48
+ <img src="https://github.com/avendesora/djangobible/actions/workflows/tests.yml/badge.svg">
49
+ <img src="https://github.com/avendesora/djangobible/workflows/Django%20CI/badge.svg"><br/>
50
+ <a href="https://app.codacy.com/gh/avendesora/djangobible/coverage/dashboard"><img src="https://app.codacy.com/project/badge/Coverage/83a28131bf6642ed9e439344122686fc"></a>
51
+ </td>
52
+ </tr>
53
+ <tr>
54
+ <td>Code Quality</td>
55
+ <td>
56
+ <img src="https://github.com/avendesora/djangobible/workflows/CodeQL/badge.svg">
57
+ <a href="https://app.codacy.com/gh/avendesora/djangobible?utm_source=github.com&utm_medium=referral&utm_content=avendesora/djangobible&utm_campaign=Badge_Grade_Settings"><img src="https://api.codacy.com/project/badge/Grade/ca34603bdaf8446ba288430b69092093"></a><br/>
58
+ <a href="https://results.pre-commit.ci/latest/github/avendesora/djangobible/main"><img src="https://results.pre-commit.ci/badge/github/avendesora/djangobible/main.svg"></a>
59
+ <a href="https://github.com/psf/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
60
+ </td>
61
+ </tr>
62
+ <tr>
63
+ <td>Supported Python/Django Versions</td>
64
+ <td>
65
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue?logo=python&logoColor=lightgray"></a><br />
66
+ <a href="https://www.djangoproject.com/download/"><img src="https://img.shields.io/badge/Django-4.2%20%7C%205.0%20%7C%205.1%20%7C%205.2-blue"></a><br />
67
+ </td>
68
+ </tr>
69
+ </table>
70
+
71
+ ## Installation
72
+
73
+ Pip install the djangobible library.
74
+
75
+ ```shell script
76
+ pip install djangobible
77
+ ```
78
+
79
+ Add djangobible to your Django project's ``INSTALLED_APPS`` setting:
80
+
81
+ ```python
82
+ INSTALLED_APPS = [
83
+ ..., # other apps
84
+ "djangobible",
85
+ ]
86
+ ```
87
+
88
+ Run the django migrations for djangobible
89
+
90
+ ```shell script
91
+ ./manage.py migrate djangobible
92
+ ```
93
+
94
+ ### Settings
95
+
96
+ There currently are no settings (other than INSTALLED_APPS) related to the djangobible project. In the future, it would be nice to have settings that determine things like the available versions of the Bible and the default version.
97
+
98
+ Also, once support is implemented for multiple locales and languages, there could be related settings for that functionality.
99
+
100
+ ## Features
101
+
102
+ The djangobible library is a complete wrapper for the [pythonbible](https://github.com/avendesora/pythonbible) library, so importing the djangobible library as:
103
+
104
+ ```python
105
+ import djangobible as bible
106
+ ```
107
+
108
+ will provide all the same functionality as importing the pythonbible library as:
109
+
110
+ ```python
111
+ import pythonbible as bible
112
+ ```
113
+
114
+ This includes features such as:
115
+ - Searching text for Scripture references
116
+ - Converting a normalized scripture reference into a list of integer verse ids
117
+ - Converting a list of verse id integers into a list of normalized scripture references
118
+ - Converting a list of normalized scripture references into a formatted string scripture reference
119
+ - Retrieving the Biblical text (in one or more open-source or public domain versions) for a given verse ID integer
120
+
121
+ For more information, see the [pythonbible documentation](https://github.com/avendesora/pythonbible).
122
+
123
+ In addition, the djangobible library includes the following features:
124
+
125
+ ### Template Tags
126
+
127
+ There are currently two template tags provided by the djangobible library: ``verse_reference`` and ``verse_text``.
128
+
129
+ #### verse_reference
130
+
131
+ The ``verse_reference`` template tag, given a verse ID and a Bible version, returns the appropriate Scripture reference string.
132
+
133
+ For example, given ``verse_id = 1001001`` and ``version = djangobible.Version.KING_JAMES``, the following snippet from a Django template:
134
+
135
+ ```html
136
+ {% load verse_tags %}
137
+ ...
138
+ {% verse_reference verse_id version=version %}
139
+ ```
140
+
141
+ would display:
142
+
143
+ ```
144
+ Genesis 1:1
145
+ ```
146
+
147
+ The version parameter is optional, and the current default is King James, though that will ideally be configurable in the future.
148
+
149
+ There is another optional parameter, ``full_title``, which is a boolean flag to determine whether to display the long version or the short version of the book of the Bible title. It defaults to ``False``, which displays the short version. For example, given ``verse_id = 1001001`` and ``version = djangobible.Version.KING_JAMES`` and ``full_title = True``, the following snippet from a Django template:
150
+
151
+ ```html
152
+ {% load verse_tags %}
153
+ ...
154
+ {% verse_reference verse_id version=version full_title=full_title %}
155
+ ```
156
+
157
+ would display:
158
+
159
+ ```
160
+ The First Book of Moses, called Genesis 1:1
161
+ ```
162
+
163
+ #### verse_text
164
+
165
+ The ``verse_text`` template tag, given a verse ID and a Bible version, returns the appropriate text of that Bible verse.
166
+
167
+ For example, given ``verse_id = 1001001`` and ``version = djangobible.Version.KING_JAMES``, the following snippet from a Django template:
168
+
169
+ ```html
170
+ {% load verse_tags %}
171
+ ...
172
+ {% verse_text verse_id %}
173
+ ```
174
+
175
+ would display:
176
+
177
+ ```
178
+ In the beginning God created the heaven and the earth.
179
+ ```
180
+
181
+ The version parameter is optional, and the current default is King James, though that will ideally be configurable in the future.
182
+
183
+ ### One-to-many style relationships between verses and Django models
184
+
185
+ For situations where an instance of a Django model needs to be associated with a single verse, that Django model can have a field of type ``VerseField``.
186
+
187
+ For example:
188
+
189
+ ```python
190
+ from django.db import models
191
+
192
+ import djangobible as bible
193
+
194
+
195
+ class MyModel(models.Model):
196
+ ... # other fields
197
+
198
+ verse = bible.VerseField()
199
+ ```
200
+
201
+ The underlying implementation of ``VerseField`` is an ``IntegerField`` which stores the verse ID of the associated verse.
202
+
203
+ Having this custom field type provides several benefits:
204
+ - The Django admin (and Wagtail) form contains a text field for the verse rather than an integer field and allows the user to enter the Scripture reference text rather than the verse id, which they may not know. It then validates that text to ensure it references one, and only one, verse.
205
+ - As just mentioned, this allows for validation, not only that the value is one, and only one, verse id, but that it is also a valid verse id (i.e. that it represents a book, chapter, and verse of the Bible that actually exists).
206
+ - More readable query filters (e.g. ``MyModel.objects.filter(verse=1001001)`` is valid, but so is ``MyModel.objects.filter(verse="Genesis 1:1")``).
207
+
208
+ You can set the verse field with either the int verse ID or the string reference:
209
+
210
+ ```python
211
+ my_object = MyModel.objects.create(name="my object")
212
+ my_object.verse = 1001001
213
+ my_object.save()
214
+ ```
215
+
216
+ or
217
+
218
+ ```python
219
+ my_object = MyModel.objects.create(name="my object")
220
+ my_object.verse = "Genesis 1:1"
221
+ my_object.save()
222
+ ```
223
+
224
+ You can filter the objects in the query set by either the int verse ID or the string reference:
225
+
226
+ ```python
227
+ MyModel.objects.filter(verse=1001001)
228
+ ```
229
+
230
+ or
231
+
232
+ ```python
233
+ MyModel.objects.filter(verse="Genesis 1:1")
234
+ ```
235
+
236
+ In any of the above examples, if the verse is not a valid verse ID integer or string reference for a single verse, then a ``ValidationError`` will be raised.
237
+
238
+ ### Many-to-many style relationships between verses and Django models
239
+
240
+ > **WARNING**: This is still a work in progress, and this functionality does not yet exist in a stable form.
241
+
242
+ There are situations where an instance of a Django model needs to be associated with multiple verses. The current intended solution, inspired by the [django-taggit](https://github.com/jazzband/django-taggit) library, is to implement this feature in such a way that you would add this relationship to your model like:
243
+
244
+ ```python
245
+ from django.db import models
246
+
247
+ import djangobible as bible
248
+
249
+
250
+ class MyModel(models.Model):
251
+ ... # other fields
252
+
253
+ verses = bible.VerseManager()
254
+ ```
255
+
256
+ Then you could add, remove, and reference those verses with something like:
257
+
258
+ ```
259
+ >>> my_object = MyModel.objects.create(name="My Object")
260
+ >>> my_object.verses.add("Genesis 1:1-3")
261
+ >>> my_object.verses.all()
262
+ [<Verse: Genesis 1:1>, <Verse: Genesis 1:2>, <Verse: Genesis 1:3>]
263
+ >>> my_object.verses.remove("Genesis 1:2")
264
+ >>> my_object.verses.all()
265
+ [<Verse: Genesis 1:1>, <Verse: Genesis 1:3>]
266
+ >>> MyModel.objects.filter(verses__in=[1001001])
267
+ [<MyModel: My Object>]
268
+ ```
269
+
270
+ Ideally, the form field would be a text field where the user could enter a list of Scripture references (e.g. "Genesis 1:1,3-10;Psalm 119;Luke 2:1-18;John 3:16")
@@ -0,0 +1,12 @@
1
+ djangobible/__init__.py,sha256=y0MkH-8--IsNMjbM6ftQqFHZA7J9R-Z0NXc07iRKURI,230
2
+ djangobible/apps.py,sha256=lJgCh5Hj0i-nuTS_1s9HmwMNgVX-i1nFEIJkVsUbOfs,244
3
+ djangobible/fields.py,sha256=i8KYKQY1K6ZJm2FSFmirutwBWM5Xfbtnp7pDC8dLUFw,3189
4
+ djangobible/migrations/0001_initial.py,sha256=5lNyBJtwuZSchhDbxeIfiWGPcf308PGl0Mc5uCxI5c0,1200
5
+ djangobible/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ djangobible/models.py,sha256=qDCKczj9nOYauVErkae14gfRaZSs0R-1mzuN3q5KD9E,3082
7
+ djangobible/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ djangobible/templatetags/verse_tags.py,sha256=xL-ZWOugyBKM1UyfLb7Hm3RD98Quu8_PhoAHV3UueGg,1811
9
+ djangobible/validators.py,sha256=nAtbb7Epwehd8oZ7ATSWKTcKCHklZZXYGlmLoUq7gow,996
10
+ djangobible-0.3.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
11
+ djangobible-0.3.0.dist-info/METADATA,sha256=tBriLOnfPvHh6XF-mw0Nvh_LhgCQUQ3hQ3Wwa0u2quU,10306
12
+ djangobible-0.3.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: flit 3.0.0
2
+ Generator: uv 0.8.24
3
3
  Root-Is-Purelib: true
4
- Tag: py3-none-any
4
+ Tag: py3-none-any
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2020 Nathan Patton
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,69 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: djangobible
3
- Version: 0.0.16
4
- Summary: djangobible python library.
5
- Home-page: https://github.com/avendesora/django-bible
6
- License: UNKNOWN
7
- Author: Nathan Patton
8
- Author-email: npatton@gmail.com
9
- Requires-Python: >=3.7
10
- Description-Content-Type: text/markdown
11
- Classifier: License :: OSI Approved :: MIT License
12
- Requires-Dist: Django >=3.0.0
13
- Requires-Dist: pythonbible >=0.3.0
14
- Requires-Dist: defusedxml >=0.6.0 ; extra == "all"
15
- Requires-Dist: bandit >=1.7.0 ; extra == "dev"
16
- Requires-Dist: black >=20.8b1 ; extra == "dev"
17
- Requires-Dist: mypy >=0.800 ; extra == "dev"
18
- Requires-Dist: prospector >=1.3.1 ; extra == "dev"
19
- Requires-Dist: pytest-django >=4.1.0 ; extra == "test"
20
- Provides-Extra: all
21
- Provides-Extra: dev
22
- Provides-Extra: doc
23
- Provides-Extra: test
24
-
25
- # djangobible
26
-
27
- The djangobible library is a Django app that wraps the [pythonbible](https://github.com/avendesora/python-bible) library and provides models, managers, and other tools to easily index an object by a scripture reference.
28
-
29
- [![PyPI version](https://img.shields.io/pypi/v/djangobible?color=blue&logo=pypi&logoColor=lightgray)](https://pypi.org/project/djangobible/)
30
- [![license MIT](https://img.shields.io/badge/license-MIT-orange.svg)](https://opensource.org/licenses/MIT)
31
-
32
- ![Django CI](https://github.com/avendesora/django-bible/workflows/Django%20CI/badge.svg)
33
- ![CodeQL](https://github.com/avendesora/django-bible/workflows/CodeQL/badge.svg)
34
- [![Codacy Badge](https://api.codacy.com/project/badge/Grade/ca34603bdaf8446ba288430b69092093)](https://app.codacy.com/gh/avendesora/django-bible?utm_source=github.com&utm_medium=referral&utm_content=avendesora/django-bible&utm_campaign=Badge_Grade_Settings)
35
- [![Codacy Badge](https://app.codacy.com/project/badge/Coverage/83a28131bf6642ed9e439344122686fc)](https://www.codacy.com/gh/avendesora/django-bible/dashboard?utm_source=github.com&utm_medium=referral&utm_content=avendesora/django-bible&utm_campaign=Badge_Coverage)
36
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
37
-
38
- [![Python 3.9](https://img.shields.io/badge/python-3.7%20%7C%203.8%20%7C%203.9-blue?logo=python&logoColor=lightgray)](https://www.python.org/downloads/release/python-390/)
39
-
40
- ## Installation
41
-
42
- ```shell script
43
- pip install djangobible
44
- ```
45
-
46
- ### Optional Dependencies
47
-
48
- If the [defusedxml](https://github.com/tiran/defusedxml) library is installed, djangobible/pythonbible will use it to parse XML files rather than the builtin xml.etree library.
49
-
50
- To install djangobible with all optional dependencies, use the following command.
51
-
52
- ```shell script
53
- pip install djangobible[all]
54
- ```
55
-
56
- ### Python 3.6
57
-
58
- Python 3.6 is not officially supported (djangobible is only tested on Python 3.7+). However, djangobible should work on Python 3.6 if you have the dataclasses library installed:
59
-
60
- ```shell script
61
- pip install dataclasses
62
- ```
63
-
64
- If you are using Python 3.7+, the dataclasses library is included in the Python standard library, and you do not need to explicitly install the dataclasses library.
65
-
66
- ## Features
67
-
68
- coming soon...
69
-
@@ -1,13 +0,0 @@
1
- djangobible/__init__.py,sha256=WJsSnOYpaHtBZGcuUvP3q6RVhamr0h6zgoF1G2ZnDr0,205
2
- djangobible/apps.py,sha256=4gQL65CrE3l0NwXrc2FUPa4JloRDmZLTnDgY7yNHMYo,107
3
- djangobible/fields.py,sha256=qg0bl5OrYkG9L2J0Ujut0sK1V3aFb7fygawslkLaPOU,1687
4
- djangobible/models.py,sha256=Xq9oKJ7ad3H9tBdnq6pphDLg9SGgeEBpzI7cwaF3DJA,2155
5
- djangobible/validators.py,sha256=57ohY35gLnx_UX6HX0iAjzucP5PQhjy9XrmYS4Y9OpM,755
6
- djangobible/migrations/0001_initial.py,sha256=4-oLb6VPP7vgM7pQCJPrhNK38gXXsAQAjZP5DyiTy1U,1207
7
- djangobible/migrations/__init__.py,sha256=WTz0_YW5SQ15TP6YCRewyvAxEH42kbunv9TXBe0CQZM,58
8
- djangobible/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- djangobible/templatetags/verse_tags.py,sha256=4REewf7FoxwYe805HgUEY2K3h_eXqwgKZ93Qq3rn1e4,1671
10
- djangobible-0.0.16.dist-info/LICENSE,sha256=-wYMucoVWThISsSvFSvSV2tvgMozWtBuLy-gG1ztGIw,1091
11
- djangobible-0.0.16.dist-info/WHEEL,sha256=CqyTrkghQBNsEzLD3HbCSEIJ_fY58-XpoU29dUzwHSk,81
12
- djangobible-0.0.16.dist-info/METADATA,sha256=atONJqA9PUdsCbp-zl-I6yu0GX5b8o67yAWXJ1dUd8k,3075
13
- djangobible-0.0.16.dist-info/RECORD,,