From f501bf750130f1e3ce1048671dbba463c782393e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Sun, 29 Dec 2019 14:30:22 +0100 Subject: [PATCH] add initial model --- chloro/phyll/__init__.py | 0 chloro/phyll/fields.py | 46 +++++++ chloro/phyll/migrations/0001_initial.py | 43 +++++++ chloro/phyll/migrations/__init__.py | 0 chloro/phyll/models.py | 30 +++++ chloro/settings.py | 2 + setup.py | 157 ++++++++++++++++++++++++ 7 files changed, 278 insertions(+) create mode 100644 chloro/phyll/__init__.py create mode 100644 chloro/phyll/fields.py create mode 100644 chloro/phyll/migrations/0001_initial.py create mode 100644 chloro/phyll/migrations/__init__.py create mode 100644 chloro/phyll/models.py create mode 100644 setup.py diff --git a/chloro/phyll/__init__.py b/chloro/phyll/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chloro/phyll/fields.py b/chloro/phyll/fields.py new file mode 100644 index 0000000..8440e4b --- /dev/null +++ b/chloro/phyll/fields.py @@ -0,0 +1,46 @@ +# chloro - personal space +# Copyright (C) 2019 Frederic Peters +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + + +import ckeditor.fields + + +class RichTextField(ckeditor.fields.RichTextField): + def formfield(self, **kwargs): + defaults = { + 'form_class': RichTextFormField, + 'config_name': self.config_name, + 'extra_plugins': self.extra_plugins, + 'external_plugin_resources': self.external_plugin_resources, + } + defaults.update(kwargs) + return super(RichTextField, self).formfield(**defaults) + + +class RichTextFormField(ckeditor.fields.RichTextFormField): + def clean(self, value): + value = super(RichTextFormField, self).clean(value) + if settings.LANGUAGE_CODE.startswith('fr-'): + # apply some basic typographic rules + value = value.replace('« ', '«\u202f') + value = value.replace('« ', '«\u202f') + value = value.replace(' »', '\u202f»') + value = value.replace(' »', '\u202f»') + value = value.replace(' :', '\u00a0:') + value = value.replace(' ;', '\u202f;') + value = value.replace(' !', '\u202f!') + value = value.replace(' ?', '\u202f?') + return value diff --git a/chloro/phyll/migrations/0001_initial.py b/chloro/phyll/migrations/0001_initial.py new file mode 100644 index 0000000..32c3af7 --- /dev/null +++ b/chloro/phyll/migrations/0001_initial.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.17 on 2019-12-29 13:29 +from __future__ import unicode_literals + +import chloro.phyll.fields +from django.db import migrations, models +import taggit.managers + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('taggit', '0002_auto_20150616_2121'), + ] + + operations = [ + migrations.CreateModel( + name='Note', + fields=[ + ( + 'id', + models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ('title', models.CharField(max_length=150, verbose_name='Title')), + ('slug', models.SlugField(max_length=150, verbose_name='Slug')), + ('text', chloro.phyll.fields.RichTextField(blank=True, null=True, verbose_name='Text')), + ('creation_timestamp', models.DateTimeField(auto_now_add=True)), + ('last_update_timestamp', models.DateTimeField(auto_now=True)), + ( + 'tags', + taggit.managers.TaggableManager( + blank=True, + help_text='A comma-separated list of tags.', + through='taggit.TaggedItem', + to='taggit.Tag', + verbose_name='Tags', + ), + ), + ], + ), + ] diff --git a/chloro/phyll/migrations/__init__.py b/chloro/phyll/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/chloro/phyll/models.py b/chloro/phyll/models.py new file mode 100644 index 0000000..587859d --- /dev/null +++ b/chloro/phyll/models.py @@ -0,0 +1,30 @@ +# chloro - personal space +# Copyright (C) 2019 Frederic Peters +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.db import models, transaction +from django.utils.translation import ugettext_lazy as _ +from taggit.managers import TaggableManager + +from .fields import RichTextField + + +class Note(models.Model): + title = models.CharField(_('Title'), max_length=150) + slug = models.SlugField(_('Slug'), max_length=150) + text = RichTextField(_('Text'), blank=True, null=True) + tags = TaggableManager(_('Tags'), blank=True) + creation_timestamp = models.DateTimeField(auto_now_add=True) + last_update_timestamp = models.DateTimeField(auto_now=True) diff --git a/chloro/settings.py b/chloro/settings.py index b29168d..2d8e8b6 100644 --- a/chloro/settings.py +++ b/chloro/settings.py @@ -36,6 +36,8 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'taggit', + 'chloro.phyll', ] MIDDLEWARE = [ diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..abcdbf4 --- /dev/null +++ b/setup.py @@ -0,0 +1,157 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import re +import subprocess +import sys + +from setuptools.command.install_lib import install_lib as _install_lib +from distutils.command.build import build as _build +from distutils.command.sdist import sdist +from distutils.cmd import Command +from distutils.spawn import find_executable +from setuptools import setup, find_packages + + +class eo_sdist(sdist): + def run(self): + if os.path.exists('VERSION'): + os.remove('VERSION') + version = get_version() + with open('VERSION', 'w') as fd: + fd.write(version) + sdist.run(self) + if os.path.exists('VERSION'): + os.remove('VERSION') + + +def get_version(): + '''Use the VERSION, if absent generates a version with git describe, if not + tag exists, take 0.0- and add the length of the commit log. + ''' + if os.path.exists('VERSION'): + with open('VERSION', 'r') as v: + return v.read() + if os.path.exists('.git'): + p = subprocess.Popen( + ['git', 'describe', '--dirty=.dirty', '--match=v*'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + result = p.communicate()[0] + if p.returncode == 0: + result = result.decode('ascii').strip()[1:] # strip spaces/newlines and initial v + if '-' in result: # not a tagged version + real_number, commit_count, commit_hash = result.split('-', 2) + version = '%s.post%s+%s' % (real_number, commit_count, commit_hash) + else: + version = result + return version + else: + return '0.0.post%s' % len(subprocess.check_output(['git', 'rev-list', 'HEAD']).splitlines()) + return '0.0' + + +class compile_translations(Command): + description = 'compile message catalogs to MO files via django compilemessages' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + orig_dir = os.getcwd() + try: + from django.core.management import call_command + + for path, dirs, files in os.walk('chloro'): + if 'locale' not in dirs: + continue + curdir = os.getcwd() + os.chdir(os.path.realpath(path)) + call_command('compilemessages') + os.chdir(curdir) + except ImportError: + sys.stderr.write('!!! Please install Django >= 1.4 to build translations\n') + os.chdir(orig_dir) + + +class compile_scss(Command): + description = 'compile scss files into css files' + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + sass_bin = None + for program in ('sassc', 'sass'): + sass_bin = find_executable(program) + if sass_bin: + break + if not sass_bin: + raise CompileError( + 'A sass compiler is required but none was found. See sass-lang.com for choices.' + ) + + for path, dirnames, filenames in os.walk('chloro'): + for filename in filenames: + if not filename.endswith('.scss'): + continue + if filename.startswith('_'): + continue + subprocess.check_call( + [ + sass_bin, + '%s/%s' % (path, filename), + '%s/%s' % (path, filename.replace('.scss', '.css')), + ] + ) + + +class build(_build): + sub_commands = [('compile_translations', None), ('compile_scss', None)] + _build.sub_commands + + +class install_lib(_install_lib): + def run(self): + self.run_command('compile_translations') + _install_lib.run(self) + + +setup( + name='chloro', + version=get_version(), + description='Content Manager', + author='Frederic Peters', + author_email='fpeters@0d.be', + packages=find_packages(exclude=['tests']), + include_package_data=True, + scripts=('manage.py',), + classifiers=[ + 'Development Status :: 4 - Beta', + 'Environment :: Web Environment', + 'Framework :: Django', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + ], + install_requires=['django>=1.11, <1.12', 'django-ckeditor<=4.5.3', 'django-taggit',], + zip_safe=False, + cmdclass={ + 'build': build, + 'compile_scss': compile_scss, + 'compile_translations': compile_translations, + 'install_lib': install_lib, + 'sdist': eo_sdist, + }, +) -- 2.39.2