Система учета рабочего времени.

API Worksection

Getting a list of users: get_users

{

    "status": "ok",
    "data": [
        {
            "id": "USER_ID",
            "email": "USER_EMAIL",
            "first_name": "USER_FIRST_NAME",
            "last_name": "USER_LAST_NAME",
            "name": "USER_NAME",
            "title": "USER_POSITION",
            "avatar": "URL",
            "company": "USER_COMPANY",
            "department": "USER_DEPARTMENT"
            ...
         }
    ]
}

Getting the list of all account tasks: get_all_tasks

"status": "ok",
    "data": [
    {
            "name": "TITLE",
            "page": "/project/PROJECT_ID/TASK_ID/",
            "status": "done",
            "priority": "0..10",
            "user_from": {
                "email": "USER_EMAIL",
                "name": "USER_NAME"
            },
            "user_to": {
                "email": "USER_EMAIL",
                "name": "USER_NAME"
            },
            "date_added": "YYYY-MM-DD HH:II",
            "date_start": "YYYY-MM-DD",
            "date_end": "YYYY-MM-DD",
            "date_closed": "YYYY-MM-DD HH:II",
            "max_time": "50"
            "max_money": "100"
            "tags": "complete"
            "child": [
                {
                    "name": "SUBTASK_NAME",
                    "page": "/project/PROJECT_ID/TASK_ID/SUBTASK_ID/",
                    "status": "done",
                    "priority": "0..10",
                    "user_from": {
                        "email": "USER_EMAIL",
                        "name": "USER_NAME"
                    },
                    "user_to": {
                        "email": "USER_EMAIL",
                        "name": "USER_NAME"
                    },
                    "date_added": "YYYY-MM-DD HH:II",
                    "date_start": "YYYY-MM-DD",
                    "date_end": "YYYY-MM-DD",
                    "date_closed": "YYYY-MM-DD HH:II"
"max_time": "25"
            "max_money": "50"
            "tags": "complete"
                }
            ]
       }

Getting a list of time and costs : get_timemoney

    "time": "1:30",
    "money": "20.50",
    "date": " YYYY-MM-DD",
    "is_timer": false,

Создание папки проекта.

mkdir time-control
cd time-control

Виртуальное окружение

установка в систему нужных команд (установщик python pip и virtualenv)

sudo apt-get install python3-pip virtualenv

установка виртуального окружения в проект

virtualenv -p python3 venv
  • создается папка venv где будут requirements-ы, которую обычно игнорят в git.

Активация виртуального окружения

. ./venv/bin/activate

появляется приставка (venv) в начале коммандной строки что значит что окружение активировано и мы можем устанавливать в него необходимы пакеты

Установка Django

pip install django

Создание нового проекта

django-admin startproject djangoprj

Создание приложения

cd djangoprj
./manage.py startapp main

Прописываем название нового приложения в настройках djangoprj/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'main'
]

Запуск сервера разработки.

./manage.py runserver 9898

start page

Переопределим стартовую страницу djangoprj/urls.py.

from main.views import home_page

urlpatterns = [
    path('', home_page, name='home'),
    path('admin/', admin.site.urls),
]

Создание вьюхи в main/views.py

from django.shortcuts import render

# Create your views here.

def home_page(request):
    return render(request,'index.html')

error template page

Добавление директории с шаблонами.

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [ os.path.join(BASE_DIR, 'templates') ],
        'APP_DIRS': True,
...

Использование шаблонов изнутри приложения.

Создадим папку main/templates.

Добавим туда файл main.html где напишем:

<h1>Hello from main.html!!!</h1>

Создадим главный шаблон из templates/index.html в templates/layout.html

<html>
    <head>

    </head>
    <body>
        <h1> This is layout.html </h1>

    </body> 
</html>

Унаследуем шаблон main/templates/main.html от templates/layout.html.

{% extends 'layout.html' %}

Определим блок ‘content’ в templates/layout.html куда будет помещено содержимой дочернего шаблона.

{% block content %} {% endblock %}

welcome template page

Коннекты (settings.py)

SqlLite

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

Либы для сборки

sudo apt-get install python3 python-dev python3-dev

Postgres

apt install libpq-dev

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'name',
        'USER': 'postgres',
        'PASSWORD': '***',
        'HOST': 'localhost',
    }
}

Mysql

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', 
        'NAME': 'for-export',
        'USER': 'name',
        'PASSWORD': '***',
        'HOST': 'localhost',   # Or an IP Address that your DB is hosted on
        'PORT': '3306',
    }
}

sudo apt-get install python3-dev libmysqlclient-dev build-essential

Сссылка на API

Ссылка на проект github

Установка NodeJs в систему.

sudo npm install npm@latest -g
sudo npm cache clean -f
sudo npm install -g n
sudo n stable

Инициализация проекта

npm init

Установка bootstrap

npm install bootstrap@4.0.0-alpha.6  --save

Создадим симлинк lib на директорию node_modules в новом каталоге static.

mkdir static
cd static
ln -s ../node_modules lib

Изменим главный шаблон templates/layout.html.

<!DOCTYPE html>
<html lang="en" >
<head>
    <meta charset="utf-8" />
    <title></title>
    <link href="/static/lib/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>    
</head>
<body>
    <div class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom box-shadow">
        <h5 class="my-0 mr-md-auto font-weight-normal">Time control</h5>
        <nav class="my-2 my-md-0 mr-md-3">
          <a class="p-2 text-dark" href="#">Link 1</a>
          <a class="p-2 text-dark" href="#">Link 2</a>
        </nav>
        <a class="btn btn-outline-primary" href="#">Button</a>
      </div>


      <div class="pricing-header px-3 py-3 pt-md-5 pb-md-4 mx-auto text-center">
            {% block content %}{% endblock %}

      </div>     
</html>

Добавим путь к статике в settings.py.

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

Создание структуры каталогов для команды.

Нам необходимо создать следующую структуру каталогов и файлов в папке приложения.

main/
    management/
        __init__.py
        commands/
            __init__.py

Сделаем это с помощью bash команд

cd main
mkdir management
mkdir management/commands
touch management/__init__.py
touch management/commands/__init__.py

Создание пустой команды.

from django.core.management.base import BaseCommand, CommandError

class Command(BaseCommand):

def handle(self, *args, **options):
    print('Load Users')

Установка библиотеки requests

pip install requests

Пытаемся использовать requests.

rez = requests.get('https://google.com')
print(rez.text)

Определим глобальную переменную с ключем API в файле settings.py

API_KEY = '<secret key>'

Импорт этой переменной из любого модуля будет выглядить так:

from djangoprj.settings import API_KEY

Определим ф-цию внутри команды.

def get_users():
    action = 'get_users'
    key_str = '%s%s' % (action,API_KEY)
    hash = hashlib.md5(key_str.encode()).hexdigest()
    print('hash = %s' % hash)
    url = 'https://wezom.worksection.com/api/admin/?action=%s&hash=%s' % (action, hash)
    res = requests.get(url)
    print(res.text)

Тут мы формируем строку согласно документации API где конкатенируем название запроса с секретным ключем.

key_str = '%s%s' % (action,API_KEY)

Кодируем ее в md5 строку.

hash = hashlib.md5(key_str.encode()).hexdigest()

И посылаем запрос на адрес API, куда передаем наш хэш.

url = 'https://wezom.worksection.com/api/admin/?action=%s&hash=%s' % (action, hash)
res = requests.get(url)

Получаем результат в таком формате:

{

    "status": "ok",
    "data": [
        {
            "id": "USER_ID",
            "email": "USER_EMAIL",
            "first_name": "USER_FIRST_NAME",
            "last_name": "USER_LAST_NAME",
            "name": "USER_NAME",
            "title": "USER_POSITION",
            "avatar": "URL",
            "company": "USER_COMPANY",
            "department": "USER_DEPARTMENT"
         }
    ]
}

Чтобы с ним работать необходимо эту json строку преобразовать в питоновский словарь.

out = json.loads(res.text)

теперь можно доставать данные по ключу:

out['data']

Добавим класс модели в файл main/models.py

class WUsers(models.Model):
    name = models.CharField(max_length=250)
    email = models.CharField(max_length=250)
    first_name =  models.CharField(max_length=250)
    last_name = models.CharField(max_length=250)
    title = models.CharField(max_length=250)
    avatar = models.CharField(max_length=250)
    company = models.CharField(max_length=250)
    department = models.CharField(max_length=250)

Определим функцию для сохранения пользователей в базу, которой передадим список пользователей.

def save_users(users):
    for u in users:
        print('Saving %s' % u['name'])
        try:
            WUsers.objects.get(email=u['email'])
            print('User %s exists' % u['email'])
        except:
            nu = WUsers()
            nu.name = u['name']
            nu.email = u['email']
            nu.first_name = u['first_name']
            nu.last_name = u['last_name']
            nu.title = u['title']
            nu.avatar = u['avatar']
            nu.company = u['company']
            nu.department = u['department']
            nu.save()

Попутно проверяем пользователя на существование чтобы избежать дублирования при повторных запусках команды.

Полный код комманды:

from django.core.management.base import BaseCommand, CommandError
import requests
from djangoprj.settings import API_KEY
import hashlib
import json

from main.models import WUsers

def save_users(users):
    for u in users:
        print('Saving %s' % u['name'])
        try:
            WUsers.objects.get(email=u['email'])
            print('User %s exists' % u['email'])
        except:
            nu = WUsers()
            nu.name = u['name']
            nu.email = u['email']
            nu.first_name = u['first_name']
            nu.last_name = u['last_name']
            nu.title = u['title']
            nu.avatar = u['avatar']
            nu.company = u['company']
            nu.department = u['department']
            nu.save()

def get_users():
    action = 'get_users'
    key_str = '%s%s' % (action,API_KEY)
    hash = hashlib.md5(key_str.encode()).hexdigest()
    print('hash = %s' % hash)
    url = 'https://wezom.worksection.com/api/admin/?action=%s&hash=%s' % (action, hash)
    res = requests.get(url)
    out = json.loads(res.text)
    return out['data']


class Command(BaseCommand):

    def handle(self, *args, **options):
        print('Load users key is %s' % API_KEY)
        users = get_users()
        save_users(users)

Результат работы (содержимое базы sqlite в файле db.sqlite3)

error template page

Вывод пользователей на страницу.

Во вьюхе (main/views.py) выберем всех пользователей и передадим в шаблон третьим параметром функции render.

from .models import WUsers

def home_page(request):
    users = WUsers.objects.all()
    return render(request,'main.html',{'users': users})

Парсим массив в шаблоне (main/templates/main.html).

<h1>User list</h1>

<ul>
{% for u in users %}
    <li>{{ u.name }}</li>
{% endfor %}
</ul>

user's list page

Создание пустой команды (main/management/commands/load_tasks.py).

from django.core.management.base import BaseCommand, CommandError

class Command(BaseCommand):

def handle(self, *args, **options):
    print('Load Tasks')

Установка библиотеки requests

pip install requests

Пытаемся использовать requests.

rez = requests.get('https://google.com')
print(rez.text)

Определим глобальную переменную с ключем API в файле settings.py

API_KEY = '<secret key>'

Импорт этой переменной из любого модуля будет выглядить так:

from djangoprj.settings import API_KEY

Определим ф-цию внутри команды.

def get_tasks(self):
    action = 'get_all_tasks'
    key_str = '%s%s' % (action,API_KEY)
    hash = hashlib.md5(key_str.encode()).hexdigest()
    url = 'https://wezom.worksection.com/api/admin/?action=%s&hash=%s' % (action, hash)
    res = requests.get(url)
    out = json.loads(res.text)
    return out['data']

Используем параметр self, передавая ее в функцию т.к. эта функция определена как метод внутри класса

Тут мы формируем строку согласно документации API где конкатенируем название запроса с секретным ключем.

key_str = '%s%s' % (action,API_KEY)

Кодируем ее в md5 строку.

hash = hashlib.md5(key_str.encode()).hexdigest()

И посылаем запрос на адрес API, куда передаем наш хэш.

url = 'https://wezom.worksection.com/api/admin/?action=%s&hash=%s' % (action, hash)
res = requests.get(url)

Получаем результат в таком формате:

"status": "ok",
"data": [
   {
        "name": "TITLE",
        "page": "/project/PROJECT_ID/TASK_ID/",
        "status": "done",
        "priority": "0..10",
        "user_from": {
            "email": "USER_EMAIL",
            "name": "USER_NAME"
        },
        "user_to": {
            "email": "USER_EMAIL",
            "name": "USER_NAME"
        },
        "date_added": "YYYY-MM-DD HH:II",
        "date_start": "YYYY-MM-DD",
        "date_end": "YYYY-MM-DD",
        "date_closed": "YYYY-MM-DD HH:II",
        "max_time": "50"
        "max_money": "100"
        "tags": "complete"

Чтобы с ним работать необходимо эту json строку преобразовать в питоновский словарь.

out = json.loads(res.text)

теперь можно доставать данные по ключу:

out['data']

Добавим класс модели в файл main/models.py

class WTasks(models.Model):
    name =  models.CharField(max_length=250)
    page = models.CharField(max_length=250)
    status = models.CharField(max_length=250)
    priority = models.CharField(max_length=250)
    user_from = models.CharField(max_length=250)
    user_to = models.CharField(max_length=250)
    date_added = models.CharField(max_length=250)
    date_start = models.CharField(max_length=250)
    date_end = models.CharField(max_length=250)
    date_closed = models.CharField(max_length=250)
    max_time = models.CharField(max_length=250)
    max_money = models.CharField(max_length=250)

Определим функцию для сохранения задач в базу, которой передадим список.

Попутно проверяем пользователя на существование чтоб избежать дублирования при повторных запусках команды.

Полный код комманды:

from django.core.management.base import BaseCommand, CommandError
import requests
from djangoprj.settings import API_KEY
import hashlib
import json

from main.models import WUsers

def save_users(users):
    for u in users:
        print('Saving %s' % u['name'])
        try:
            WUsers.objects.get(email=u['email'])
            print('User %s exists' % u['email'])
        except:
            nu = WUsers()
            nu.name = u['name']
            nu.email = u['email']
            nu.first_name = u['first_name']
            nu.last_name = u['last_name']
            nu.title = u['title']
            nu.avatar = u['avatar']
            nu.company = u['company']
            nu.department = u['department']
            nu.save()

def get_users():
    action = 'get_users'
    key_str = '%s%s' % (action,API_KEY)
    hash = hashlib.md5(key_str.encode()).hexdigest()
    print('hash = %s' % hash)
    url = 'https://wezom.worksection.com/api/admin/?action=%s&hash=%s' % (action, hash)
    res = requests.get(url)
    out = json.loads(res.text)
    return out['data']


class Command(BaseCommand):

    def handle(self, *args, **options):
        print('Load users key is %s' % API_KEY)
        users = get_users()
        save_users(users)

Результат работы (содержимое базы sqlite в файле db.sqlite3)

error template page

Вывод пользователей на страницу.

Во вьюхе (main/views.py) выберем всех пользователей и передадим в шаблон третьим параметром функции render.

from .models import WUsers

def home_page(request):
    users = WUsers.objects.all()
    return render(request,'main.html',{'users': users})

Парсим массив в шаблоне (main/templates/main.html).

<h1>User list</h1>

<ul>
{% for u in users %}
    <li>{{ u.name }}</li>
{% endfor %}
</ul>

user's list page

Задача

Есть 3 файла логов, которые могут быть запрошены из роутера Mikrotik следующими коммандами:

ssh u_remote_stat@192.168.10.1 "ip dhcp-server lease print detail without-paging" > dhcplease.txt
ssh u_remote_stat@192.168.10.1 "ip firewall connection print detail without-paging" > connount.txt
ssh -l u_remote_stat 192.168.10.1 'log print detail without-paging where message~"dhcp_stat"'

Лог выдачи IP адресов по DHCP.

 time=10:04:28 topics=dhcp,info 
   message="dhcp_stat_: dhcp-server-0 deassigned 192.168.10.16 from 
        B0:EB:57:36:85:3F"

 time=10:04:28 topics=dhcp,info 
   message="dhcp_stat_: dhcp-server-0 assigned 192.168.10.16 to 
        B0:EB:57:36:85:3F"

 time=10:04:58 topics=dhcp,info 
   message="dhcp_stat_: dhcp-server-0 deassigned 192.168.8.148 from 
        F4:60:E2:F0:4C:E2"

Лог состояния сесии IP.

Flags: X - disabled, R - radius, D - dynamic, B - blocked 
 0 D address=192.168.10.251 mac-address=0C:4D:E9:D2:5A:6D 
     client-id="1:c:4d:e9:d2:5a:6d" address-lists="" server=dhcp-server-0 
     dhcp-option="" status=bound expires-after=2d23h7m last-seen=1d52m59s 
     active-address=192.168.10.251 active-mac-address=0C:4D:E9:D2:5A:6D 
     active-client-id="1:c:4d:e9:d2:5a:6d" active-server=dhcp-server-0 
     host-name="Admins-iMac"

 1 D address=192.168.10.248 mac-address=D0:50:99:95:3B:A9 address-lists="" 
     server=dhcp-server-0 dhcp-option="" status=bound 
     expires-after=3d16h29m19s last-seen=7h30m40s 
     active-address=192.168.10.248 active-mac-address=D0:50:99:95:3B:A9 
     active-server=dhcp-server-0 host-name="webmaster"

Лог сетевой активности.

 0  SAC  s  protocol=tcp src-address=192.168.8.59:1643 
            dst-address=18.203.197.172:443 
            reply-src-address=18.203.197.172:443 
            reply-dst-address=193.151.241.65:1643 tcp-state=established 
            timeout=23h59m58s orig-packets=7 orig-bytes=1 804 
            orig-fasttrack-packets=0 orig-fasttrack-bytes=0 repl-packets=8 
            repl-bytes=6 169 repl-fasttrack-packets=0 repl-fasttrack-bytes=0 
            orig-rate=0bps repl-rate=0bps

 1  SAC  s  protocol=tcp src-address=192.168.10.123:49858 
            dst-address=77.120.103.109:443 
            reply-src-address=77.120.103.109:443 
            reply-dst-address=193.151.241.65:49858 tcp-state=established 
            timeout=23h59m20s orig-packets=40 orig-bytes=12 090 
            orig-fasttrack-packets=0 orig-fasttrack-bytes=0 repl-packets=30 
            repl-bytes=10 241 repl-fasttrack-packets=0 repl-fasttrack-bytes=0 
            orig-rate=0bps repl-rate=0bps

Задачи.

  • Сделать комманду импорта этих логов в базу данных.

  • Импортировать логи с заданной частотой.

  • Создать связку мак адресов с предоставленным списком пользователей.

  • Анализировать логи на предмет сетевой активности в течении рабочего дня.

  • Выводить результаты в виде таблицы с графиками.

graph page

Создаем новое приложение

./manage.py startapp mikrotik


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'main',
    'mikrotik'
]

Модель.

class MTLogin(models.Model):
    time = models.CharField(max_length=250)
    action = models.CharField(max_length=250)
    ip = models.CharField(max_length=250)
    mac = models.CharField(max_length=250)

class MTActivity(models.Model):
    time = models.CharField(max_length=250)
    ip = models.CharField(max_length=250)
    bytes = models.CharField(max_length=250)
Задать вопрос, прокомментировать.