Browse Source

Начало разработки

develop
Golikov Ivan 5 years ago
parent
commit
ead42dca3f
15 changed files with 617 additions and 0 deletions
  1. +2
    -0
      .gitignore
  2. +18
    -0
      cross_journals.sublime-project
  3. +0
    -0
      cross_journals/__init__.py
  4. +113
    -0
      cross_journals/settings.py
  5. +21
    -0
      cross_journals/urls.py
  6. +16
    -0
      cross_journals/wsgi.py
  7. +0
    -0
      journal/__init__.py
  8. +3
    -0
      journal/admin.py
  9. +5
    -0
      journal/apps.py
  10. +171
    -0
      journal/migrations/0001_initial.py
  11. +0
    -0
      journal/migrations/__init__.py
  12. +240
    -0
      journal/models.py
  13. +3
    -0
      journal/tests.py
  14. +3
    -0
      journal/views.py
  15. +22
    -0
      manage.py

+ 2
- 0
.gitignore View File

@@ -0,0 +1,2 @@
__pycache__/
cross_journals.sublime-workspace

+ 18
- 0
cross_journals.sublime-project View File

@@ -0,0 +1,18 @@
{
"build_systems":
[
{
"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
"name": "Anaconda Python Builder",
"selector": "source.python",
"shell_cmd": "\"python\" -u \"$file\""
}
],
"folders":
[
{
"path": ".",
"folder_exclude_patterns": ["__pycache__", "migrations"]
}
]
}

+ 0
- 0
cross_journals/__init__.py View File


+ 113
- 0
cross_journals/settings.py View File

@@ -0,0 +1,113 @@
"""
Django settings for cross_journals project.

Generated by 'django-admin startproject' using Django 1.11.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

SECRET_KEY = os.environ['DJANGO_CJ_SECRET_KEY']
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
'journal.apps.JournalConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'cross_journals.urls'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]

WSGI_APPLICATION = 'cross_journals.wsgi.application'


# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': os.environ['DJANGO_CJ_DB'],
'USER': os.environ['DJANGO_CJ_DB_USER'],
'PASSWORD': os.environ['DJANGO_CJ_DB_PASS'],
'HOST': os.environ['DJANGO_CJ_DB_HOST'],
}
}

AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]


# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/

LANGUAGE_CODE = 'ru-ru'

TIME_ZONE = 'Europe/Moscow'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

STATIC_URL = '/static/'

+ 21
- 0
cross_journals/urls.py View File

@@ -0,0 +1,21 @@
"""cross_journals URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin

urlpatterns = [
url(r'^admin/', admin.site.urls),
]

+ 16
- 0
cross_journals/wsgi.py View File

@@ -0,0 +1,16 @@
"""
WSGI config for cross_journals project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cross_journals.settings")

application = get_wsgi_application()

+ 0
- 0
journal/__init__.py View File


+ 3
- 0
journal/admin.py View File

@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.

+ 5
- 0
journal/apps.py View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig


class JournalConfig(AppConfig):
name = 'journal'

+ 171
- 0
journal/migrations/0001_initial.py View File

@@ -0,0 +1,171 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2017-04-28 11:31
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Building',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('number', models.CharField(max_length=5, unique=True, verbose_name='номер')),
('letter', models.CharField(blank=True, max_length=2, verbose_name='литера')),
],
options={
'verbose_name': 'корпус',
'verbose_name_plural': 'корпуса',
},
),
migrations.CreateModel(
name='Cabinet',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('number', models.CharField(max_length=15, unique=True, verbose_name='номер')),
],
options={
'verbose_name': 'шкаф',
'verbose_name_plural': 'шкафы',
},
),
migrations.CreateModel(
name='CrossPoint',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
),
migrations.CreateModel(
name='Location',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('cabinet', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='journal.Cabinet', verbose_name='шкаф')),
],
options={
'verbose_name': 'расположение',
'verbose_name_plural': 'расположения',
},
),
migrations.CreateModel(
name='PBX',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('manufacturer', models.CharField(choices=[('asterisk', 'Asterisk'), ('avaya', 'Avaya'), ('m-200', 'М-200'), ('multicom', 'Мультиком'), ('panasonic', 'Panasonic')], max_length=10, verbose_name='производитель')),
('model', models.CharField(max_length=40, verbose_name='модель')),
],
options={
'verbose_name': 'АТС',
'verbose_name_plural': 'АТС',
},
),
migrations.CreateModel(
name='Room',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('room', models.CharField(max_length=45, verbose_name='помещение')),
('building', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rooms', to='journal.Building', verbose_name='корпус')),
],
options={
'verbose_name': 'помещение',
'verbose_name_plural': 'помещения',
},
),
migrations.CreateModel(
name='Extension',
fields=[
('crosspoint_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='journal.CrossPoint')),
('number', models.SmallIntegerField(verbose_name='номер')),
],
options={
'verbose_name': 'распределение',
'verbose_name_plural': 'распределения',
},
bases=('journal.crosspoint',),
),
migrations.CreateModel(
name='PBXPort',
fields=[
('crosspoint_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='journal.CrossPoint')),
('number', models.CharField(blank=True, max_length=20, verbose_name='номер порта')),
('type', models.CharField(choices=[('sip', 'SIP'), ('analog', 'Аналоговый'), ('pri', 'E1')], default='analog', max_length=10, verbose_name='тип порта')),
('subscriber_number', models.PositiveSmallIntegerField(blank=True, null=True, unique=True, verbose_name='абонентский номер')),
('description', models.CharField(blank=True, max_length=150, verbose_name='описание')),
('pbx', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.PBX', verbose_name='АТС')),
],
options={
'verbose_name': 'порт АТС',
'verbose_name_plural': 'порты АТС',
},
bases=('journal.crosspoint',),
),
migrations.CreateModel(
name='Phone',
fields=[
('crosspoint_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='journal.CrossPoint')),
],
options={
'verbose_name': 'телефон',
'verbose_name_plural': 'телефоны',
},
bases=('journal.crosspoint',),
),
migrations.CreateModel(
name='Subscriber',
fields=[
('crosspoint_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='journal.CrossPoint')),
('first_name', models.CharField(max_length=30, verbose_name='имя')),
('last_name', models.CharField(max_length=40, verbose_name='фамилия')),
('patronymic', models.CharField(blank=True, max_length=35, verbose_name='отчество')),
('phone', models.ManyToManyField(related_name='subscribers', to='journal.Phone', verbose_name='телефоны')),
],
options={
'verbose_name': 'абонент',
'verbose_name_plural': 'абоненты',
},
bases=('journal.crosspoint',),
),
migrations.CreateModel(
name='Trunk',
fields=[
('crosspoint_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='journal.CrossPoint')),
('number', models.SmallIntegerField(verbose_name='номер')),
],
options={
'verbose_name': 'магистраль',
'verbose_name_plural': 'магистрали',
},
bases=('journal.crosspoint',),
),
migrations.AddField(
model_name='location',
name='room',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='journal.Room', verbose_name='помещение'),
),
migrations.AddField(
model_name='crosspoint',
name='destination',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='incoming', to='journal.CrossPoint', verbose_name='направление'),
),
migrations.AddField(
model_name='crosspoint',
name='location',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Location', verbose_name='расположение'),
),
migrations.AddField(
model_name='cabinet',
name='room',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journal.Room', verbose_name='расположение'),
),
migrations.AlterUniqueTogether(
name='room',
unique_together=set([('building', 'room')]),
),
]

+ 0
- 0
journal/migrations/__init__.py View File


+ 240
- 0
journal/models.py View File

@@ -0,0 +1,240 @@
from django.db import models
from django.core.exceptions import ValidationError


class Building(models.Model):
number = models.CharField(verbose_name='номер', max_length=5, unique=True)
letter = models.CharField(verbose_name='литера', max_length=2, blank=True)

def __str__(self):
if self.letter:
result = 'Корпус {} (литера {})'.format(self.number, self.letter)
else:
result = 'Корпус {}'.format(self.number)

return result

class Meta:
verbose_name = 'корпус'
verbose_name_plural = 'корпуса'


class Room(models.Model):
building = models.ForeignKey(Building,
verbose_name='корпус',
related_name='rooms')
room = models.CharField(verbose_name='помещение', max_length=45)

class Meta:
verbose_name = "помещение"
verbose_name_plural = "помещения"

unique_together = ('building', 'room',)

def __str__(self):
return '{}, {}'.format(self.building, self.room)


class Cabinet(models.Model):
room = models.ForeignKey(
Room,
verbose_name='расположение',
on_delete=models.CASCADE,
)
number = models.CharField(verbose_name='номер', max_length=15, unique=True)

def __str__(self):
return 'Шкаф {}, {}'.format(self.number, self.room)

class Meta:
verbose_name = 'шкаф'
verbose_name_plural = 'шкафы'


class Location(models.Model):
cabinet = models.OneToOneField(Cabinet,
verbose_name='шкаф',
blank=True,
null=True,
unique=True)
room = models.OneToOneField(Room,
verbose_name='помещение',
blank=True,
null=True,
unique=True)

def __str__(self):
result = self.cabinet if self.cabinet else self.room

return str(result)

def clean(self):
if self.cabinet and self.room:
raise ValidationError('Нельзя выбирать и шкаф, и помещение одновременно')
elif not self.cabinet and not self.room:
raise ValidationError('Выберите либо шкаф, либо помещение')

class Meta:
verbose_name = 'расположение'
verbose_name_plural = 'расположения'


class CrossPoint(models.Model):
location = models.ForeignKey(Location,
verbose_name='расположение')
destination = models.ForeignKey('self',
verbose_name='направление',
related_name='incoming',
null=True,
blank=True,
)

def __str__(self):
try:
pbxport = PBXPort.objects.get(crosspoint_ptr=self.pk)
except models.ObjectDoesNotExist as e:
pbxport = None
try:
extension = Extension.objects.get(crosspoint_ptr=self.pk)
except models.ObjectDoesNotExist as e:
extension = None
try:
trunk = Trunk.objects.get(crosspoint_ptr=self.pk)
except models.ObjectDoesNotExist as e:
trunk = None
try:
phone = Phone.objects.get(crosspoint_ptr=self.pk)
except models.ObjectDoesNotExist as e:
phone = None

if pbxport:
return str(pbxport)
elif extension:
return str(extension)
elif trunk:
return str(trunk)
elif phone:
return str(phone)


class PBX(models.Model):
MANUFACTURERS = (
('asterisk', 'Asterisk'),
('avaya', 'Avaya'),
('m-200', 'М-200'),
('multicom', 'Мультиком'),
('panasonic', 'Panasonic'),
)

manufacturer = models.CharField(verbose_name='производитель',
choices=MANUFACTURERS,
max_length=10)
model = models.CharField(verbose_name='модель', max_length=40)

def __str__(self):
return '{} {}'.format(self.get_manufacturer_display(), self.model)

class Meta:
verbose_name = 'АТС'
verbose_name_plural = 'АТС'


class PBXPort(CrossPoint):
PORT_TYPES = (
('sip', 'SIP'),
('analog', 'Аналоговый'),
('pri', 'E1'),
)
pbx = models.ForeignKey(PBX, verbose_name='АТС')
number = models.CharField(verbose_name='номер порта', max_length=20, blank=True)
type = models.CharField(verbose_name='тип порта',
choices=PORT_TYPES,
default='analog',
max_length=10)
subscriber_number = models.PositiveSmallIntegerField(verbose_name='абонентский номер',
blank=True,
null=True,
unique=True)
description = models.CharField(verbose_name='описание', max_length=150, blank=True)

def __str__(self):
return '{}: {} (порт {}, {})'.format(self.pbx,
self.subscriber_number,
self.number,
self.get_type_display())

class Meta:
verbose_name = 'порт АТС'
verbose_name_plural = 'порты АТС'


class PunchBlock(CrossPoint):
number = models.SmallIntegerField(verbose_name='номер')

def __str__(self):
return '{}/{}'.format(self.number, self.location.cabinet.number)

def __init__(self, *args, **kwargs):
super(PunchBlock, self).__init__(*args, **kwargs)
self._meta.get_field('location').limit_choices_to = {
'cabinet__isnull': False,
}

class Meta:
abstract = True

verbose_name = 'плинт'
verbose_name_plural = 'плинты'


class Trunk(PunchBlock):
def __str__(self):
return 'М' + super().__str__()

class Meta:
verbose_name = 'магистраль'
verbose_name_plural = 'магистрали'


class Extension(PunchBlock):
def __str__(self):
return 'Р' + super().__str__()

class Meta:
verbose_name = 'распределение'
verbose_name_plural = 'распределения'


class Phone(CrossPoint):
def __str__(self):
return 'Телефон'

class Meta:
verbose_name = 'телефон'
verbose_name_plural = 'телефоны'


class Subscriber(CrossPoint):
first_name = models.CharField(verbose_name='имя', max_length=30)
last_name = models.CharField(verbose_name='фамилия', max_length=40)
patronymic = models.CharField(verbose_name='отчество', max_length=35, blank=True)
phone = models.ManyToManyField(Phone,
verbose_name='телефоны',
related_name='subscribers')

class Meta:
verbose_name = 'абонент'
verbose_name_plural = 'абоненты'

def __str__(self):
return '{} {}.{}.'.format(self.last_name,
self.first_name[0],
self.patronymic[0]
)

def __init__(self, *args, **kwargs):
super(Subscriber, self).__init__(*args, **kwargs)
print('init')
self._meta.get_field('location').limit_choices_to = {
'room__isnull': False,
}

+ 3
- 0
journal/tests.py View File

@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.

+ 3
- 0
journal/views.py View File

@@ -0,0 +1,3 @@
from django.shortcuts import render

# Create your views here.

+ 22
- 0
manage.py View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cross_journals.settings")
try:
from django.core.management import execute_from_command_line
except ImportError:
# The above import may fail for some other reason. Ensure that the
# issue is really that Django is missing to avoid masking other
# exceptions on Python 2.
try:
import django
except ImportError:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
)
raise
execute_from_command_line(sys.argv)

Loading…
Cancel
Save