Skip to content

Commit

Permalink
Merge pull request #4 from anexia-it/ab_moveToAnexia
Browse files Browse the repository at this point in the history
move to 'anexia' organization
  • Loading branch information
beachmachine authored Oct 21, 2022
2 parents 7f1748b + 321ccad commit 8b1bcca
Show file tree
Hide file tree
Showing 30 changed files with 484 additions and 195 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Publish package
on:
release:
types: [created]

jobs:
deploy:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.10'
architecture: 'x64'

- name: Install dependencies and package
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Build source and binary distribution package
run: |
python setup.py sdist bdist_wheel
env:
PACKAGE_VERSION: ${{ github.ref }}

- name: Check distribution package
run: |
twine check dist/*
- name: Publish distribution package
run: |
twine upload dist/*
env:
TWINE_REPOSITORY: ${{ secrets.PYPI_REPOSITORY }}
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
TWINE_NON_INTERACTIVE: yes
64 changes: 64 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Run linter and tests
on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version:
- '3.7'
- '3.8'
- '3.9'
- '3.10'
django-version:
- '3.2'
djangorestframework-version:
- '3.10'
- '3.11'
- '3.12'
exclude:
- django-version: '3.1'
djangorestframework-version: '3.10'
- django-version: '3.2'
djangorestframework-version: '3.10'

steps:
- uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies and package
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install django~=${{ matrix.django-version }}.0
pip install djangorestframework~=${{ matrix.djangorestframework-version }}.0
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 './drf_attachments' --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 './drf_attachments' --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Run tests with coverage
run: |
# prepare Django project: link all necessary data from the test project into the root directory
# Hint: Simply changing the directory does not work (leads to missing files in coverage report)
ln -s ./tests/core core
ln -s ./tests/testapp testapp
ln -s ./tests/manage.py manage.py
# run tests with coverage
coverage run \
--source='./drf_attachments' \
manage.py test
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
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.10.1
hooks:
- id: isort
args: [ "--profile", "black", "--filter-files" ]

- repo: https://github.com/psf/black
rev: 22.6.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+
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

[Unreleased]: https://github.com/anexia-it/drf-attachments/compare/1.0.0...HEAD
[1.0.0]: https://github.com/anexia-it/drf-attachments/releases/tag/1.0.0
[Unreleased]: https://github.com/anexia/drf-attachments/compare/1.0.0...HEAD
[1.0.0]: https://github.com/anexia/drf-attachments/releases/tag/1.0.0
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ If used with DRF, `django-filter` is an additional requirement.
1. Install using pip:

```shell
pip install git+https://github.com/anexia-it/drf-attachments@main
pip install git+https://github.com/anexia/drf-attachments@main
```

2. Integrate `drf_attachments` and `django_userforeignkey` into your `settings.py`
Expand Down Expand Up @@ -146,6 +146,17 @@ def attachment_context_translations():
}
```

### Auto-formatter setup
We use isort (https://github.com/pycqa/isort) and black (https://github.com/psf/black) for local auto-formatting and for linting in the CI pipeline.
The pre-commit framework (https://pre-commit.com) provides GIT hooks for these tools, so they are automatically applied before every commit.

Steps to activate:
* Install the pre-commit framework: `pip install pre-commit` (for alternative installation options see https://pre-commit.com/#install)
* Activate the framework (from the root directory of the repository): `pre-commit install`

Hint: You can also run the formatters manually at any time with the following command: `pre-commit run --all-files`


## Usage

Attachments accept any other Model as content_object and store the uploaded files in their respective directories
Expand Down Expand Up @@ -284,6 +295,26 @@ python manage.py runserver
# Admin Panel: http://localhost:8000/admin
```

## Unit Tests

See folder [tests/](tests/). Basically, all endpoints are covered with multiple
unit tests.

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
```

## ToDos

* Simplify configuration (maybe a default configuration class that can be subclasses for customizations?)
Expand Down
4 changes: 3 additions & 1 deletion drf_attachments/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ def content_object(obj):
app_label = entity._meta.app_label
model_name = entity._meta.model_name
try:
admin_url = reverse(f"admin:{app_label}_{model_name}_change", args=(entity.pk,))
admin_url = reverse(
f"admin:{app_label}_{model_name}_change", args=(entity.pk,)
)
return mark_safe(f'<a href="{admin_url}">{entity}</a>')
except NoReverseMatch:
return entity
Expand Down
22 changes: 10 additions & 12 deletions drf_attachments/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def get_callable(cls, setting_key) -> Optional[Callable]:
if not setting:
return None

module_name, callable_name = setting.rsplit('.', maxsplit=1)
module_name, callable_name = setting.rsplit(".", maxsplit=1)
module = importlib.import_module(module_name)
return getattr(module, callable_name)

Expand All @@ -51,7 +51,10 @@ def get_content_object_field(cls) -> GenericRelatedField:

@classmethod
def context_choices(
cls, include_default=True, values_list=True, translated=True,
cls,
include_default=True,
values_list=True,
translated=True,
) -> Union[List[str], Tuple[Tuple[Any, Any]]]:
"""
Extract all unique context definitions from settings "ATTACHMENT_CONTEXT_*" + "ATTACHMENT_DEFAULT_CONTEXT"
Expand All @@ -73,21 +76,16 @@ def context_choices(
def get_contexts(cls, include_default) -> Set[str]:
settings_keys = dir(settings)
return {
getattr(settings, key) for key in settings_keys if cls.__is_context_setting(key, include_default)
getattr(settings, key)
for key in settings_keys
if cls.__is_context_setting(key, include_default)
}

@staticmethod
def __is_context_setting(key, include_default) -> bool:
return (
(
key.startswith("ATTACHMENT_CONTEXT_")
and not key.endswith("_CALLABLE")
)
or (
include_default
and key == DEFAULT_CONTEXT_SETTING
)
)
key.startswith("ATTACHMENT_CONTEXT_") and not key.endswith("_CALLABLE")
) or (include_default and key == DEFAULT_CONTEXT_SETTING)

@classmethod
def translate_context(cls, context):
Expand Down
87 changes: 64 additions & 23 deletions drf_attachments/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,77 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
('contenttypes', '0002_remove_content_type_name'),
("contenttypes", "0002_remove_content_type_name"),
]

operations = [
migrations.CreateModel(
name='Attachment',
name="Attachment",
fields=[
('id',
models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True,
verbose_name='Attachment ID')),
('name', models.CharField(blank=True, max_length=255, verbose_name='name')),
('context',
models.CharField(blank=True, help_text="Additional info about the attachment's context/meaning.",
max_length=255, verbose_name='context')),
('meta', django.db.models.JSONField(
help_text='Additional info about the attachment (e.g. file meta data: mime_type, extension, size).',
verbose_name='meta')),
('file',
models.FileField(storage=storage.AttachmentFileStorage(), upload_to=storage.attachment_upload_path,
verbose_name='file')),
('object_id', models.UUIDField()),
('creation_date', models.DateTimeField(auto_now_add=True, verbose_name='Creation date')),
('last_modification_date', models.DateTimeField(auto_now=True, verbose_name='Last modification date')),
('content_type',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
unique=True,
verbose_name="Attachment ID",
),
),
(
"name",
models.CharField(blank=True, max_length=255, verbose_name="name"),
),
(
"context",
models.CharField(
blank=True,
help_text="Additional info about the attachment's context/meaning.",
max_length=255,
verbose_name="context",
),
),
(
"meta",
django.db.models.JSONField(
help_text="Additional info about the attachment (e.g. file meta data: mime_type, extension, size).",
verbose_name="meta",
),
),
(
"file",
models.FileField(
storage=storage.AttachmentFileStorage(),
upload_to=storage.attachment_upload_path,
verbose_name="file",
),
),
("object_id", models.UUIDField()),
(
"creation_date",
models.DateTimeField(
auto_now_add=True, verbose_name="Creation date"
),
),
(
"last_modification_date",
models.DateTimeField(
auto_now=True, verbose_name="Last modification date"
),
),
(
"content_type",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="contenttypes.contenttype",
),
),
],
options={
'verbose_name': 'attachment',
'verbose_name_plural': 'attachments',
'ordering': ('creation_date',),
"verbose_name": "attachment",
"verbose_name_plural": "attachments",
"ordering": ("creation_date",),
},
),
]
6 changes: 3 additions & 3 deletions drf_attachments/migrations/0002_attachment_object_id_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
class Migration(migrations.Migration):

dependencies = [
('drf_attachments', '0001_initial'),
("drf_attachments", "0001_initial"),
]

operations = [
migrations.AlterField(
model_name='attachment',
name='object_id',
model_name="attachment",
name="object_id",
field=models.CharField(db_index=True, max_length=64),
),
]
Loading

0 comments on commit 8b1bcca

Please sign in to comment.