├── Dockerfile ├── LICENSE ├── README.md ├── auto_auth ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── automatic ├── __init__.py ├── asgi.py ├── element │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── keywords │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── fixtures │ │ └── initial_data.json │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── management │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── settings │ ├── __init__.py │ └── common.py ├── signals.py ├── static │ ├── css │ │ ├── ak-base-style.css │ │ ├── ak-schedule.css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap.css │ │ ├── bootstrapValidator.css │ │ ├── font-awesome.css │ │ ├── font-awesome │ │ │ ├── css │ │ │ │ ├── font-awesome-ie7.css │ │ │ │ ├── font-awesome-ie7.min.css │ │ │ │ ├── font-awesome.css │ │ │ │ ├── font-awesome.css.map │ │ │ │ └── font-awesome.min.css │ │ │ ├── font │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ └── fontawesome-webfont.woff │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ ├── less │ │ │ │ ├── animated.less │ │ │ │ ├── bootstrap.less │ │ │ │ ├── bordered-pulled.less │ │ │ │ ├── core.less │ │ │ │ ├── extras.less │ │ │ │ ├── fixed-width.less │ │ │ │ ├── font-awesome-ie7.less │ │ │ │ ├── font-awesome.less │ │ │ │ ├── icons.less │ │ │ │ ├── larger.less │ │ │ │ ├── list.less │ │ │ │ ├── mixins.less │ │ │ │ ├── path.less │ │ │ │ ├── rotated-flipped.less │ │ │ │ ├── stacked.less │ │ │ │ └── variables.less │ │ │ └── scss │ │ │ │ ├── _animated.scss │ │ │ │ ├── _bootstrap.scss │ │ │ │ ├── _bordered-pulled.scss │ │ │ │ ├── _core.scss │ │ │ │ ├── _extras.scss │ │ │ │ ├── _fixed-width.scss │ │ │ │ ├── _icons.scss │ │ │ │ ├── _larger.scss │ │ │ │ ├── _list.scss │ │ │ │ ├── _mixins.scss │ │ │ │ ├── _path.scss │ │ │ │ ├── _rotated-flipped.scss │ │ │ │ ├── _stacked.scss │ │ │ │ ├── _variables.scss │ │ │ │ ├── font-awesome-ie7.scss │ │ │ │ └── font-awesome.scss │ │ ├── jquery-ui.css │ │ ├── jquery.dataTables.min.css │ │ ├── login-app.css │ │ ├── login-vendor.css │ │ ├── page-v3 │ │ │ ├── ak-master-page-v3.css │ │ │ └── ak-master-page-v3style.css │ │ └── wheelmenu.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ ├── glyphicons-halflings-regular.woff2 │ │ └── meteocons │ │ │ ├── meteocons-webfont.eot │ │ │ ├── meteocons-webfont.svg │ │ │ ├── meteocons-webfont.ttf │ │ │ └── meteocons-webfont.woff │ ├── image │ │ ├── background.jpg │ │ ├── background1.jpg │ │ ├── background2.jpg │ │ ├── favicon.ico │ │ ├── logo.png │ │ └── wx.jpg │ ├── images │ │ ├── ak-common.png │ │ ├── ak-person-icon01.png │ │ ├── ak-person-icon02.jpg │ │ ├── ak-person-icon03.png │ │ ├── ak-person-icon04.png │ │ ├── ak-person-icon05.png │ │ ├── ak-person-icon06.png │ │ ├── ak-z-icon.png │ │ ├── app.png │ │ ├── app_current.png │ │ ├── background.jpg │ │ ├── background1.jpg │ │ ├── background2.jpg │ │ ├── channel.png │ │ ├── channel_current.png │ │ ├── chat01.png │ │ ├── cloud.png │ │ ├── cloud_current.png │ │ ├── custom.png │ │ ├── custom_current.png │ │ ├── dotted.png │ │ ├── download.png │ │ ├── favicon.ico │ │ ├── home.png │ │ ├── home_current.png │ │ ├── icon.png │ │ ├── line_bg.png │ │ ├── logo-automagic.png │ │ ├── logo.png │ │ ├── select_xl.png │ │ ├── select_xl01.png │ │ ├── setting-top02.png │ │ ├── sort_asc.png │ │ ├── sort_both.png │ │ ├── sort_desc.png │ │ ├── source.png │ │ ├── source_current.png │ │ ├── statistics.png │ │ ├── statistics_current.png │ │ ├── syetem_management.png │ │ ├── syetem_management_c.png │ │ ├── system.png │ │ ├── system_current.png │ │ ├── timg.png │ │ ├── user-13.jpg │ │ ├── xiaoxi01.png │ │ ├── xiaoxi01g.png │ │ └── zkonw-back.png │ ├── js │ │ ├── automagic.js │ │ ├── back-to-top.js │ │ ├── bootstrap.min.js │ │ ├── bootstrapValidator.js │ │ ├── casemanage.js │ │ ├── common.js │ │ ├── jquery-ui.js │ │ ├── jquery.dataTables.min.js │ │ ├── jquery.min.js │ │ ├── jquery.wheelmenu.js │ │ ├── keyword.js │ │ ├── lodash.min.js │ │ └── taskmanage.js │ ├── media │ │ └── sendlog.tar │ ├── muti_select │ │ ├── css │ │ │ ├── multi.css │ │ │ └── style.css │ │ ├── jquery-1.10.2.min.js │ │ └── src │ │ │ └── MultiSelectDropList.js │ └── zTree_v3 │ │ ├── css │ │ ├── awesomeStyle │ │ │ ├── awesome.css │ │ │ ├── awesome.less │ │ │ ├── fa.less │ │ │ └── img │ │ │ │ └── loading.gif │ │ ├── demo.css │ │ ├── metroStyle │ │ │ ├── img │ │ │ │ ├── line_conn.png │ │ │ │ ├── loading.gif │ │ │ │ ├── metro.gif │ │ │ │ └── metro.png │ │ │ └── metroStyle.css │ │ └── zTreeStyle │ │ │ ├── img │ │ │ ├── diy │ │ │ │ ├── 1_close.png │ │ │ │ ├── 1_open.png │ │ │ │ ├── 2.png │ │ │ │ ├── 3.png │ │ │ │ ├── 4.png │ │ │ │ ├── 5.png │ │ │ │ ├── 6.png │ │ │ │ ├── 7.png │ │ │ │ ├── 8.png │ │ │ │ └── 9.png │ │ │ ├── line_conn.gif │ │ │ ├── loading.gif │ │ │ ├── zTreeStandard.gif │ │ │ └── zTreeStandard.png │ │ │ └── zTreeStyle.css │ │ └── js │ │ ├── jquery-1.4.4.min.js │ │ ├── jquery.ztree.all.js │ │ ├── jquery.ztree.all.min.js │ │ ├── jquery.ztree.core.js │ │ ├── jquery.ztree.core.min.js │ │ ├── jquery.ztree.excheck.js │ │ ├── jquery.ztree.excheck.min.js │ │ ├── jquery.ztree.exedit.js │ │ ├── jquery.ztree.exedit.min.js │ │ ├── jquery.ztree.exhide.js │ │ └── jquery.ztree.exhide.min.js ├── templates │ ├── 404.html │ ├── 500.html │ ├── base.html │ ├── comingsoon.html │ ├── element │ │ └── element.html │ ├── frame.html │ ├── index.html │ ├── keywords │ │ └── keyword.html │ ├── management │ │ ├── moduleadd.html │ │ ├── moduleview.html │ │ ├── productadd.html │ │ ├── productlist.html │ │ ├── productview.html │ │ ├── projectadd.html │ │ ├── projectlist.html │ │ ├── projectview.html │ │ └── syslog.html │ ├── nav.html │ ├── oauth │ │ └── userlist.html │ ├── registration │ │ └── login.html │ ├── testcase │ │ ├── caseadd.html │ │ ├── casecopy.html │ │ ├── caseedit.html │ │ ├── caselist.html │ │ └── caseview.html │ ├── testtask │ │ ├── taskadd.html │ │ ├── taskedit.html │ │ └── tasklist.html │ └── webinterface │ │ └── webinterface.html ├── testcase │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── testtask │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── urls.py ├── webinterface │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py └── wsgi.py ├── docker-compose.yml ├── init.sh ├── insertkeyword.sql ├── manage.py ├── requirements ├── base.txt └── seleniumreq.txt ├── seleniumkeyword ├── AddCase.py ├── Base.py ├── CustomKeyword.py ├── HTMLTestRunner.py ├── README.MD ├── RestApiUtil.py ├── SimulatorUtil.py ├── TestSuite.py ├── __init__.py ├── data │ └── readme.md ├── mwupgrade.py ├── popautomagic.py ├── result │ ├── highcharts.js │ ├── img.png │ ├── jquery.min.js │ └── video.jpg ├── sendlog │ ├── README.md │ ├── __init__.py │ ├── guitest.py │ ├── mysetup.py │ ├── randip.py │ ├── randomip.py │ ├── send.config │ ├── sendingdata.py │ ├── syslogc.py │ ├── tcpsendingsyslog.py │ ├── tcpsendtest.py │ ├── udpsendingsyslog.py │ └── weighted_choice.py ├── settings.py ├── testrail.py └── testraildemo.py ├── start.py └── 公众号.jpg /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | MAINTAINER ray 4 | 5 | LABEL version="2.0" by="ray" descriptio="python3.6 django 3.2.3" 6 | 7 | ENV TZ=Asia/Shanghai 8 | ENV PATH=/usr/bin:$PATH 9 | ENV DEBIAN_FRONTEND=noninteractive 10 | ENV LANG C.UTF-8 11 | 12 | 13 | RUN mkdir /opt/automagic 14 | WORKDIR /opt/automagic 15 | 16 | RUN set -x;apt-get update \ 17 | && apt-get install -y vim \ 18 | && apt-get install -y tzdata \ 19 | && apt-get install -y python3 \ 20 | && apt-get install -y python3-pip \ 21 | && pip3 install --upgrade pip 22 | 23 | COPY . /opt/automagic 24 | 25 | RUN pip3 --no-cache-dir install -r /opt/automagic/requirements/base.txt \ 26 | -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com 27 | RUN pip3 --no-cache-dir install -r /opt/automagic/requirements/seleniumreq.txt \ 28 | -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com 29 | 30 | ENTRYPOINT ["python3","start.py"] 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 自动化测试平台 2 | ## python3.8+ Django 3.2.10框架 3 | >python3.8以下版本 使用Django 3.0.5 以上版本 ,django的 /admin/后台会异常退出,不使用/admin/后台不影响,安装请注意版本 4 | 5 | ### [新用户指导使用指南](https://github.com/radiateboy/automagic/wiki) 6 | 7 | # (一)源码安装 8 | > pip3 install -r requirements/base.txt 9 | > 10 | > pip3 install -r requirements/seleniumreq.txt 11 | 12 | ### Mysql/Mariadb 数据库 automatic/settings/common.py 13 | ```python 14 | MYSQL_USERNAME = os.environ.get('MYSQL_USERNAME', 'root') 15 | MYSQL_PASSWORD = os.environ.get('MYSQL_PASSWORD', '123456') 16 | MYSQL_HOST = os.environ.get('MYSQL_HOST', 'localhost') 17 | MYSQL_PORT = os.environ.get('MYSQL_PORT', '3306') 18 | MYSQL_DBNAME = os.environ.get('MYSQL_DBNAME', 'automatic') 19 | 20 | DATABASES = { 21 | 'default': { 22 | 'ENGINE': 'django.db.backends.mysql', 23 | 'NAME': MYSQL_DBNAME, 24 | 'USER': MYSQL_USERNAME, 25 | 'PASSWORD': MYSQL_PASSWORD, 26 | 'HOST': MYSQL_HOST, 27 | 'PORT': MYSQL_PORT, 28 | } 29 | } 30 | ``` 31 | #### 初始化并启动服务 32 | 33 | ```shell 34 | python3 start.py 35 | ``` 36 | 37 | 另:内置关键字 在wiki #关键字创建# 页面(可以了解一下) 38 | 39 | _http://127.0.0.1:8000_ 访问登录即可 40 | 41 | 默认管理员用户:admin, 密码:admin@123 42 | # (二)docker安装 43 | ## 方法一: 命令安装启动 44 | ```shell script 45 | docker pull tsbc520/automagic:2.0 46 | ``` 47 | 启动docker容器: 48 | ```shell script 49 | docker run -d -p 8000:8000 \ 50 | -e MYSQL_HOST=192.168.10.167 \ 51 | -e MYSQL_PORT=3306 \ 52 | -e MYSQL_DBNAME=automatic \ 53 | -e MYSQL_USERNAME=root \ 54 | -e MYSQL_PASSWORD=123456 \ 55 | tsbc520/automagic:2.0 56 | ``` 57 | 58 | ## 方法二: docker-compose 59 | ```shell script 60 | docker-compose up 61 | ``` 62 | 63 | ## 如何执行测试脚本 64 | [点击查看如何执行测试](https://github.com/radiateboy/automagic/wiki/Seleniumkeyword%E4%BB%8B%E7%BB%8D) 65 | ## 公众号 66 | 扫一扫关注公众号 67 | 68 | ![开源优测](https://gitee.com/tsbc/automagic/raw/master/%E5%85%AC%E4%BC%97%E5%8F%B7.jpg) 69 | -------------------------------------------------------------------------------- /auto_auth/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/auto_auth/__init__.py -------------------------------------------------------------------------------- /auto_auth/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /auto_auth/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AutoAuthConfig(AppConfig): 5 | name = 'auto_auth' 6 | -------------------------------------------------------------------------------- /auto_auth/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django import forms 3 | from django.contrib.auth.models import User 4 | from django.contrib.auth.forms import UserCreationForm 5 | from django.utils.translation import ugettext_lazy as _ 6 | 7 | from auto_auth.models import UserActivationKey 8 | 9 | class RegistrationForm(UserCreationForm): 10 | email = forms.EmailField() 11 | 12 | class Meta: 13 | model = User 14 | fields = ("username",) 15 | 16 | def clean_email(self): 17 | email = self.cleaned_data['email'] 18 | try: 19 | User.objects.get(email=email) 20 | except User.DoesNotExist: 21 | return email 22 | raise forms.ValidationError( 23 | _("A user with that email already exists.")) 24 | 25 | def save(self, commit=True): 26 | user = super(RegistrationForm, self).save(commit=False) 27 | user.email = self.cleaned_data['email'] 28 | user.is_active = False 29 | user.set_password(self.cleaned_data["password1"]) 30 | 31 | if User.objects.filter(is_superuser=True).count() == 0: 32 | user.is_superuser = True 33 | 34 | if commit: 35 | user.save() 36 | # initiate_user_with_default_setups(user) 37 | return user 38 | 39 | def set_activation_key(self): 40 | return UserActivationKey.set_random_key_for_user(user=self.instance) 41 | -------------------------------------------------------------------------------- /auto_auth/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.2 on 2020-01-15 10:02 2 | 3 | from django.conf import settings 4 | import django.contrib.auth.models 5 | import django.contrib.auth.validators 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | import django.utils.timezone 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | initial = True 14 | 15 | dependencies = [ 16 | ('auth', '0011_update_proxy_permissions'), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name='User', 22 | fields=[ 23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 | ('password', models.CharField(max_length=128, verbose_name='password')), 25 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 26 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 27 | ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), 28 | ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')), 29 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), 30 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), 31 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 32 | ('realname', models.CharField(blank=True, max_length=50, null=True, verbose_name='真实姓名')), 33 | ('mobile', models.CharField(blank=True, max_length=11, null=True, verbose_name='电话号码')), 34 | ('email', models.EmailField(max_length=255, unique=True, verbose_name='邮箱')), 35 | ('dept', models.CharField(choices=[('测试', '测试'), ('开发', '开发')], default='测试', max_length=100, verbose_name='部门')), 36 | ('is_active', models.BooleanField(default=True, verbose_name='激活状态')), 37 | ('is_admin', models.BooleanField(default=False, verbose_name='是否管理员')), 38 | ('testrailuser', models.CharField(blank=True, max_length=50, null=True, verbose_name='TestRail用户名')), 39 | ('testrailpass', models.CharField(blank=True, max_length=50, null=True, verbose_name='TestRail密码')), 40 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), 41 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), 42 | ], 43 | options={ 44 | 'verbose_name': 'user', 45 | 'verbose_name_plural': 'users', 46 | 'abstract': False, 47 | }, 48 | managers=[ 49 | ('objects', django.contrib.auth.models.UserManager()), 50 | ], 51 | ), 52 | migrations.CreateModel( 53 | name='UserActivationKey', 54 | fields=[ 55 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 56 | ('activation_key', models.CharField(blank=True, max_length=64, null=True)), 57 | ('key_expires', models.DateTimeField(blank=True, null=True)), 58 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 59 | ], 60 | ), 61 | ] 62 | -------------------------------------------------------------------------------- /auto_auth/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/auto_auth/migrations/__init__.py -------------------------------------------------------------------------------- /auto_auth/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import datetime 4 | import secrets 5 | 6 | from django.db import models 7 | from django.conf import settings 8 | from django.contrib.auth.models import BaseUserManager, AbstractUser 9 | 10 | class UserActivationKey(models.Model): 11 | user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) 12 | activation_key = models.CharField(max_length=64, null=True, blank=True) 13 | key_expires = models.DateTimeField(null=True, blank=True) 14 | 15 | @classmethod 16 | def set_random_key_for_user(cls, user, force=False): 17 | activation_key = secrets.token_hex() 18 | 19 | # Create and save their profile 20 | user_activation_key, created = cls.objects.get_or_create(user=user) 21 | if created or force: 22 | user_activation_key.activation_key = activation_key 23 | user_activation_key.key_expires = datetime.datetime.today() + datetime.timedelta(7) 24 | user_activation_key.save() 25 | 26 | return user_activation_key 27 | 28 | 29 | class MyUserManager(BaseUserManager): 30 | # def current_time(self): 31 | # """get current time """ 32 | # from datetime import datetime 33 | # return datetime.now().strftime("%Y-%m-%d") 34 | 35 | def create_user(self, username, email, password): 36 | """ 37 | Creates and saves a User with the given email, date of 38 | birth and password. 39 | """ 40 | 41 | if not username: 42 | raise ValueError('username is unique') 43 | user = self.model(username=username, email=self.normalize_email(email)) 44 | user.set_password(password) 45 | user.save(using=self._db) 46 | return user 47 | 48 | def create_superuser(self, username, email, password): 49 | """ 50 | Creates and saves a superuser with the given email, password. 51 | """ 52 | user = self.create_user(username, email, password) 53 | user.is_admin = True 54 | user.is_staff = True 55 | user.save(using=self._db) 56 | return user 57 | 58 | 59 | class User(AbstractUser): 60 | Dept_Choice = ( 61 | ('测试', '测试'), 62 | ('开发', '开发'), 63 | ) 64 | realname = models.CharField(max_length=50, verbose_name="真实姓名", null=True, blank=True, editable=True) 65 | mobile = models.CharField(max_length=11, verbose_name="电话号码", null=True, blank=True, editable=True) 66 | email = models.EmailField(verbose_name='邮箱', max_length=255, unique=True) 67 | dept = models.CharField(verbose_name=u'部门', choices=Dept_Choice, default='测试', max_length=100) 68 | is_active = models.BooleanField(default=True, verbose_name='激活状态') 69 | is_admin = models.BooleanField(default=False, verbose_name='是否管理员') 70 | testrailuser = models.CharField(max_length=50, verbose_name="TestRail用户名", null=True, blank=True, editable=True) 71 | testrailpass = models.CharField(max_length=50, verbose_name="TestRail密码", null=True, blank=True, editable=True) 72 | def get_full_name(self): 73 | # The user is identified by their email address 74 | return self.username 75 | 76 | def get_short_name(self): 77 | # The user is identified by their email address 78 | return self.username 79 | 80 | def __unicode__(self): # __unicode__ on Python 2 81 | return self.username 82 | 83 | def has_perm(self, perm, obj=None): 84 | "Does the user have a specific permission?" 85 | # Simplest possible answer: Yes, always 86 | return True 87 | 88 | def has_module_perms(self, app_label): 89 | "Does the user have permissions to view the app `app_label`?" 90 | # Simplest possible answer: Yes, always 91 | return True -------------------------------------------------------------------------------- /auto_auth/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /auto_auth/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.conf.urls import url 4 | from django.urls import reverse_lazy 5 | from django.contrib.auth import views as contrib_auth_views 6 | from django.contrib.auth.decorators import login_required 7 | from auto_auth import views 8 | 9 | urlpatterns = [ 10 | url(r'^(?P[\w.@+-]+)/profile/$', views.profile, 11 | name='auto-profile'), 12 | 13 | url(r'^register/$', views.register, name='auto-register'), 14 | 15 | url(r'^confirm/(?P[A-Za-z0-9\-]+)/$', views.confirm, 16 | name='auto-confirm'), 17 | 18 | url(r'^user/add/$', views.add_user, name='adduser'), 19 | url(r'^user/list/$', login_required(views.UserListIndex.as_view()), name='userlist'), 20 | url(r'^user/update/$', views.update_user, name='userupdate'), 21 | url(r'^user/del/(?P\d+)/$', views.del_user, name='userdel'), 22 | url(r'^setedit/user/$', views.set_edit_user, name='setedituser'), 23 | 24 | url(r'^login/$', views.LoginViewWithCustomTemplate.as_view(), name='auto-login'), 25 | url(r'^logout/$', 26 | contrib_auth_views.LogoutView.as_view(next_page=reverse_lazy('auto-login')), 27 | name='auto-logout'), 28 | 29 | url(r'^passwordreset/$', contrib_auth_views.PasswordResetView.as_view(), 30 | name='auto-password_reset'), 31 | url(r'^passwordreset/done/$', contrib_auth_views.PasswordResetDoneView.as_view(), 32 | name='password_reset_done'), 33 | url(r'^passwordreset/confirm/(?P[0-9A-Za-z]+)-(?P.+)/$', 34 | contrib_auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'), 35 | url(r'^passwordreset/complete/$', 36 | contrib_auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'), 37 | ] 38 | -------------------------------------------------------------------------------- /automatic/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = '2.0' 3 | 4 | import pymysql 5 | pymysql.install_as_MySQLdb() 6 | -------------------------------------------------------------------------------- /automatic/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for automatic project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'automatic.settings.common') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /automatic/element/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/element/__init__.py -------------------------------------------------------------------------------- /automatic/element/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | 5 | from automatic.element import models 6 | 7 | 8 | class ElementAdmin(admin.ModelAdmin): 9 | list_display = (id, 'descr', 'projectid', 'moduleid', 'locmode', 'location', 'createat', 'createtime', 'updateat', 'updatetime') 10 | search_fields = ('keyword', 'kwdescr') 11 | 12 | 13 | admin.site.register(models.Element, ElementAdmin) -------------------------------------------------------------------------------- /automatic/element/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ElementConfig(AppConfig): 5 | name = 'automatic.element' 6 | -------------------------------------------------------------------------------- /automatic/element/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2020-01-08 6 | """ 7 | from django import forms 8 | from automatic.element.models import Element 9 | from django.forms import ModelForm, Textarea, Select, TextInput 10 | 11 | 12 | class FormElement(forms.ModelForm): 13 | class Meta: 14 | model = Element 15 | fields = ('descr','projectid','moduleid','locmode','location') 16 | widgets = {'locmode': Select(attrs={'class':'ak-left ac-aselect','required':''}), 17 | 'descr':TextInput(attrs={'class':'form-control','placeholder':'请输入元素描述','required':''}), 18 | 'location': TextInput(attrs={'class': 'form-control','placeholder':'(如:id_username)','required':''}), 19 | } -------------------------------------------------------------------------------- /automatic/element/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.2 on 2020-01-15 10:02 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('management', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Element', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('descr', models.CharField(max_length=100)), 21 | ('locmode', models.CharField(blank=True, choices=[('id', 'id'), ('name', 'name'), ('css selector', 'css selector'), ('xpath', 'xpath'), ('class_name', 'class name'), ('tag_name', 'tag name'), ('link_text', 'link text'), ('portial_link_text', 'portial link text')], max_length=32, null=True)), 22 | ('location', models.CharField(blank=True, max_length=200, null=True)), 23 | ('createtime', models.DateTimeField(auto_now_add=True)), 24 | ('createat', models.CharField(blank=True, max_length=32, null=True)), 25 | ('updatetime', models.DateTimeField(auto_now=True)), 26 | ('updateat', models.CharField(blank=True, max_length=32, null=True)), 27 | ('moduleid', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='management.Module')), 28 | ('projectid', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='management.Project')), 29 | ], 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /automatic/element/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/element/migrations/__init__.py -------------------------------------------------------------------------------- /automatic/element/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2020-01-06 6 | """ 7 | from django.db import models 8 | from automatic.management.models import Project, Module 9 | # Create your models here. 10 | 11 | class Element(models.Model): 12 | Element_Choice = ( 13 | ('id','id'), 14 | ('name','name'), 15 | ('css selector','css selector'), 16 | ('xpath','xpath'), 17 | ('class name','class name'), 18 | ('tag name','tag name'), 19 | ('link text','link text'), 20 | ('portial link_text','portial link text') 21 | ) 22 | projectid = models.ForeignKey(Project, editable=True, on_delete=models.DO_NOTHING) 23 | moduleid = models.ForeignKey(Module, editable=True, on_delete=models.DO_NOTHING) 24 | descr = models.CharField(max_length=100, editable=True) 25 | locmode = models.CharField(max_length=32, choices=Element_Choice, null=True, blank=True, editable=True) 26 | location = models.CharField(max_length=200, null=True, blank=True, editable=True) 27 | createtime = models.DateTimeField(auto_now_add=True) 28 | createat = models.CharField(max_length=32, null=True, blank=True, editable=True) 29 | updatetime = models.DateTimeField(auto_now=True) 30 | updateat = models.CharField(max_length=32, null=True, blank=True, editable=True) 31 | 32 | def __unicode__(self): 33 | return self.descr 34 | -------------------------------------------------------------------------------- /automatic/element/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /automatic/element/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.contrib.auth.decorators import login_required 3 | from automatic.element import views 4 | 5 | urlpatterns = [ 6 | url(r'list/$', login_required(views.ElementListIndex.as_view()), name='elementlist'), 7 | url(r'add/$', views.add_element, name='elementadd'), 8 | url(r'update/$', views.update_element, name='elementupdate'), 9 | url(r'del/(?P\d+)/$', views.del_element, name='elementdel'), 10 | url(r'get/$', views.get_element, name='getelement'), 11 | url(r'setedit/$', views.set_edit_element, name='seteditelement'), 12 | ] -------------------------------------------------------------------------------- /automatic/keywords/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/keywords/__init__.py -------------------------------------------------------------------------------- /automatic/keywords/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | 5 | from automatic.keywords import models 6 | 7 | 8 | class KeywordAdmin(admin.ModelAdmin): 9 | list_display = (id, 'keyword', 'kwdescr','createat', 'createtime', 'updateat', 'updatetime') 10 | search_fields = ('keyword', 'kwdescr') 11 | 12 | 13 | admin.site.register(models.Keyword, KeywordAdmin) -------------------------------------------------------------------------------- /automatic/keywords/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class KeywordsConfig(AppConfig): 5 | name = 'automatic.keywords' 6 | -------------------------------------------------------------------------------- /automatic/keywords/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.2 on 2020-01-15 10:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Keyword', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('productid', models.IntegerField(blank=True, null=True, verbose_name='所属产品')), 19 | ('keyword', models.CharField(max_length=32, unique=True)), 20 | ('kwdescr', models.TextField(blank=True, null=True)), 21 | ('createtime', models.DateTimeField(auto_now_add=True)), 22 | ('createat', models.CharField(blank=True, max_length=32, null=True)), 23 | ('updatetime', models.DateTimeField(auto_now=True)), 24 | ('updateat', models.CharField(blank=True, max_length=32, null=True)), 25 | ], 26 | options={ 27 | 'ordering': ['productid'], 28 | }, 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /automatic/keywords/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/keywords/migrations/__init__.py -------------------------------------------------------------------------------- /automatic/keywords/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2020-01-06 6 | """ 7 | from django.db import models 8 | # Create your models here. 9 | 10 | 11 | class Keyword(models.Model): 12 | productid = models.IntegerField(verbose_name='所属产品', null=True, blank=True, editable=True) 13 | keyword = models.CharField(max_length=32, unique=True) 14 | kwdescr = models.TextField(null=True,blank=True,editable=True) 15 | createtime = models.DateTimeField(auto_now_add=True) 16 | createat = models.CharField(max_length=32, null=True, blank=True, editable=True) 17 | updatetime = models.DateTimeField(auto_now=True) 18 | updateat = models.CharField(max_length=32, null=True, blank=True, editable=True) 19 | 20 | def __unicode__(self): 21 | return self.keyword 22 | 23 | class Meta: 24 | ordering = ["productid"] -------------------------------------------------------------------------------- /automatic/keywords/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /automatic/keywords/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.contrib.auth.decorators import login_required 3 | from automatic.keywords import views 4 | 5 | urlpatterns = [ 6 | url(r'list/$', login_required(views.KeyWordListIndex.as_view()), name='keywordlist'), 7 | url(r'add/$', views.add_keyword, name='keywordadd'), 8 | url(r'update/$', views.update_keyword, name='keywordupdate'), 9 | url(r'del/(?P\d+)/$', views.del_keyword, name='keyworddel'), 10 | url(r'get/$', views.get_keyword, name='getkeyword'), 11 | url(r'setedit/$', views.set_edit_keyword, name='seteditelement'), 12 | ] -------------------------------------------------------------------------------- /automatic/keywords/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2020-01-08 6 | """ 7 | import json 8 | 9 | from django.utils import timezone 10 | from django.contrib.auth.decorators import login_required 11 | from django.shortcuts import render, redirect, get_object_or_404 12 | from django.http import HttpResponse,HttpResponseRedirect 13 | from django.views.decorators.csrf import csrf_exempt 14 | 15 | from automatic.element.forms import * 16 | from django.views.generic import ListView 17 | from automatic.element.models import * 18 | from django.db.models import Q 19 | from django.urls import reverse 20 | from automatic.management.models import Product, Project, UserAndProduct 21 | from automatic.keywords.models import Keyword 22 | 23 | 24 | class KeyWordListIndex(ListView): 25 | context_object_name = 'keywordlist' 26 | template_name = 'keywords/keyword.html' 27 | paginate_by = 10 28 | keywordsum = 0 29 | model = Keyword 30 | http_method_names = [u'get'] 31 | 32 | def get_queryset(self): 33 | keywordlist = Keyword.objects.all().order_by('-pk') 34 | keyword = self.request.GET.get('keyword') 35 | if keyword: 36 | keywordlist = keywordlist.filter(Q(keyword__icontains=keyword)|Q(kwdescr__icontains=keyword)) 37 | self.keywordsum = len(keywordlist) 38 | return keywordlist 39 | 40 | def get_context_data(self, **kwargs): 41 | context = super(KeyWordListIndex,self).get_context_data(**kwargs) 42 | context['userandproduct'] = UserAndProduct.objects.all() 43 | context['productlist'] = Product.objects.all() 44 | context['keywordsum'] = self.keywordsum 45 | return context 46 | 47 | 48 | @login_required() 49 | def add_keyword(request): 50 | if request.method == 'POST': 51 | name = request.POST['keyword'] 52 | descr = request.POST['kwdescr'] 53 | productid = request.POST['productid'] 54 | createat = request.user.username 55 | updateat = request.user.username 56 | keyword = Keyword(productid=productid, keyword=name, kwdescr=descr, createat=createat, updateat=updateat) 57 | try: 58 | keyword.save() 59 | except Exception as e: 60 | return HttpResponse(e) 61 | return HttpResponse('添加关键字成功。') 62 | else: 63 | return HttpResponse('添加关键字失败。') 64 | 65 | 66 | @csrf_exempt 67 | @login_required() 68 | def update_keyword(request): 69 | if request.method == 'POST': 70 | id = request.POST['keywordid'] 71 | name = request.POST['keyword'] 72 | descr = request.POST['kwdescr'] 73 | productid = request.POST['productname'] 74 | updateat = request.user.username 75 | updatetime = timezone.now() 76 | k = Keyword.objects.filter(id=int(id)) 77 | k.update(productid=productid, keyword=name, kwdescr=descr, updateat=updateat, updatetime=updatetime) 78 | return HttpResponse('修改关键字成功。') 79 | else: 80 | return HttpResponse('修改关键字失败。') 81 | 82 | 83 | @login_required() 84 | def del_keyword(request, id): 85 | keyword = get_object_or_404(Keyword, pk=int(id)) 86 | keyword.delete() 87 | return HttpResponseRedirect(reverse('keywordlist')) 88 | 89 | 90 | @login_required() 91 | def get_keyword(request): 92 | keywordlist = [] 93 | productid = request.GET['productid'] 94 | kwlist = Keyword.objects.filter(Q(productid=productid)|Q(productid=0)) 95 | for i in kwlist: 96 | keywordinfo = {} 97 | keywordinfo['key'] = i.id 98 | keywordinfo['kwdescr'] = i.kwdescr 99 | keywordinfo['keyword'] = i.keyword 100 | keywordinfo['productid'] = i.productid 101 | keywordlist.append(keywordinfo) 102 | return HttpResponse(json.dumps(keywordlist)) 103 | 104 | 105 | @login_required() 106 | def set_edit_keyword(request): 107 | keywordid = request.GET['keywordid'] 108 | kw = Keyword.objects.get(pk=keywordid) 109 | keywordinfo = {} 110 | keywordinfo['id'] = kw.pk 111 | keywordinfo['descr'] = kw.kwdescr 112 | keywordinfo['name'] = kw.keyword 113 | keywordinfo['productid'] = kw.productid 114 | keywordlist = [keywordinfo] 115 | return HttpResponse(json.dumps(keywordlist)) -------------------------------------------------------------------------------- /automatic/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/management/__init__.py -------------------------------------------------------------------------------- /automatic/management/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | 5 | from automatic.management import models 6 | 7 | 8 | class ProductAdmin(admin.ModelAdmin): 9 | list_display = (id, 'name', 'isenabled', 'descr','createat', 'createtime', 'updateat', 'updatetime') 10 | search_fields = ('name','descr') 11 | 12 | 13 | class ProjectAdmin(admin.ModelAdmin): 14 | list_display = (id, 'name', 'isenabled','version','descr','createat', 'createtime', 'updateat', 'updatetime') 15 | search_fields = ('name','descr','version') 16 | 17 | 18 | class ModuleAdmin(admin.ModelAdmin): 19 | list_display = (id, 'name', 'isenabled', 'createat', 'createtime', 'updateat', 'updatetime') 20 | search_fields = ('name',) 21 | 22 | 23 | admin.site.register(models.Product, ProductAdmin) 24 | admin.site.register(models.Project, ProjectAdmin) 25 | admin.site.register(models.Module, ModuleAdmin) -------------------------------------------------------------------------------- /automatic/management/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ManagementConfig(AppConfig): 5 | name = 'automatic.management' 6 | -------------------------------------------------------------------------------- /automatic/management/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.2 on 2020-01-15 10:02 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Product', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('name', models.CharField(max_length=32, unique=True, verbose_name='产品名称')), 22 | ('isenabled', models.BooleanField(blank=True, default=True, verbose_name='产品状态')), 23 | ('descr', models.TextField(blank=True, null=True, verbose_name='产品描述')), 24 | ('createtime', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')), 25 | ('createat', models.CharField(blank=True, max_length=32, null=True, verbose_name='创建者')), 26 | ('updatetime', models.DateTimeField(auto_now=True, null=True, verbose_name='更新时间')), 27 | ('updateat', models.CharField(blank=True, max_length=32, null=True, verbose_name='更新者')), 28 | ('sortby', models.IntegerField(blank=True, default=0, null=True, verbose_name='排序')), 29 | ], 30 | options={ 31 | 'ordering': ['-sortby'], 32 | }, 33 | ), 34 | migrations.CreateModel( 35 | name='UserAndProduct', 36 | fields=[ 37 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 38 | ('productname', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='management.Product')), 39 | ('username', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)), 40 | ], 41 | ), 42 | migrations.CreateModel( 43 | name='Project', 44 | fields=[ 45 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 46 | ('name', models.CharField(max_length=32, unique=True, verbose_name='项目名称')), 47 | ('version', models.CharField(blank=True, max_length=32, null=True, verbose_name='版本')), 48 | ('isenabled', models.BooleanField(default=True, verbose_name='状态')), 49 | ('descr', models.TextField(blank=True, null=True, verbose_name='项目描述')), 50 | ('createtime', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')), 51 | ('createat', models.CharField(blank=True, max_length=32, null=True, verbose_name='创建者')), 52 | ('updatetime', models.DateTimeField(auto_now=True, null=True, verbose_name='更新时间')), 53 | ('updateat', models.CharField(blank=True, max_length=32, null=True, verbose_name='更新者')), 54 | ('sortby', models.IntegerField(blank=True, default=0, null=True, verbose_name='排序')), 55 | ('productid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='management.Product', verbose_name='产品名称')), 56 | ], 57 | options={ 58 | 'ordering': ['-sortby'], 59 | }, 60 | ), 61 | migrations.CreateModel( 62 | name='Module', 63 | fields=[ 64 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 65 | ('name', models.CharField(max_length=32, verbose_name='模块名称')), 66 | ('isenabled', models.BooleanField(default=True, verbose_name='状态')), 67 | ('createtime', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 68 | ('createat', models.CharField(blank=True, max_length=32, null=True, verbose_name='创建者')), 69 | ('updatetime', models.DateTimeField(auto_now=True, verbose_name='更新时间')), 70 | ('updateat', models.CharField(blank=True, max_length=32, null=True, verbose_name='更新者')), 71 | ('sortby', models.IntegerField(blank=True, default=0, null=True, verbose_name='排序')), 72 | ('projectid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='management.Project', verbose_name='所属项目')), 73 | ], 74 | options={ 75 | 'ordering': ['-sortby'], 76 | }, 77 | ), 78 | ] 79 | -------------------------------------------------------------------------------- /automatic/management/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/management/migrations/__init__.py -------------------------------------------------------------------------------- /automatic/management/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2020-01-06 6 | """ 7 | from __future__ import unicode_literals 8 | 9 | import datetime 10 | from django.db import models 11 | from auto_auth.models import User 12 | # Create your models here. 13 | 14 | 15 | class Product(models.Model): 16 | name = models.CharField(max_length=32, verbose_name='产品名称', unique=True) 17 | # version = models.CharField(max_length=32) 18 | isenabled = models.BooleanField(default=True, blank=True, verbose_name='产品状态') 19 | descr = models.TextField(null=True, blank=True, verbose_name='产品描述') 20 | createtime = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name='创建时间') 21 | createat = models.CharField(max_length=32, null=True, blank=True, editable=True, verbose_name='创建者') 22 | updatetime = models.DateTimeField(auto_now=True,null=True, blank=True, verbose_name='更新时间') 23 | updateat = models.CharField(max_length=32, null=True, blank=True, editable=True, verbose_name='更新者') 24 | sortby = models.IntegerField(null=True, blank=True, editable=True, default=0, verbose_name='排序') 25 | 26 | def __unicode__(self): 27 | return self.name 28 | 29 | def save(self, *args, **kwargs): 30 | if not self.id: 31 | self.createtime = datetime.datetime.now() 32 | self.updatetime = datetime.datetime.now() 33 | 34 | super(Product, self).save(*args, **kwargs) 35 | 36 | class Meta: 37 | ordering = ["-sortby"] 38 | 39 | 40 | class Project(models.Model): 41 | productid = models.ForeignKey(Product, verbose_name='产品名称', on_delete=models.CASCADE) 42 | name = models.CharField(max_length=32, unique=True, verbose_name='项目名称') 43 | version = models.CharField(max_length=32, null=True, blank=True, editable=True, verbose_name='版本') 44 | isenabled = models.BooleanField(default=True, verbose_name='状态') 45 | descr = models.TextField(null=True, blank=True, editable=True,verbose_name='项目描述') 46 | createtime = models.DateTimeField(auto_now_add=True, null=True, blank=True, editable=True,verbose_name='创建时间') 47 | createat = models.CharField( max_length=32, null=True, blank=True, editable=True, verbose_name='创建者') 48 | updatetime = models.DateTimeField(auto_now=True,null=True, blank=True, verbose_name='更新时间') 49 | updateat = models.CharField(max_length=32, null=True, blank=True, editable=True, verbose_name='更新者') 50 | sortby = models.IntegerField(null=True, blank=True, editable=True, default=0, verbose_name='排序') 51 | 52 | def __unicode__(self): 53 | return self.name 54 | 55 | class Meta: 56 | ordering = ["-sortby"] 57 | 58 | 59 | class Module(models.Model): 60 | projectid = models.ForeignKey(Project, verbose_name='所属项目', on_delete=models.CASCADE) 61 | name = models.CharField(max_length=32, verbose_name='模块名称') 62 | isenabled = models.BooleanField(default=True, verbose_name='状态') 63 | createtime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') 64 | createat = models.CharField(max_length=32 ,null=True, blank=True, editable=True, verbose_name='创建者') 65 | updatetime = models.DateTimeField(auto_now=True, verbose_name='更新时间') 66 | updateat = models.CharField(max_length=32, null=True, blank=True, editable=True, verbose_name='更新者') 67 | sortby = models.IntegerField(null=True, blank=True, editable=True, default=0, verbose_name='排序') 68 | 69 | def __unicode__(self): 70 | return self.name 71 | 72 | class Meta: 73 | ordering = ["-sortby"] 74 | 75 | class UserAndProduct(models.Model): 76 | username = models.ForeignKey(User, on_delete=models.DO_NOTHING) 77 | productname = models.ForeignKey(Product, on_delete=models.DO_NOTHING) 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /automatic/management/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /automatic/management/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.contrib.auth.decorators import login_required 3 | from automatic.management import views 4 | 5 | urlpatterns = [ 6 | url(r'^product/add/$', views.add_product, name='productadd'), 7 | url(r'^product/view/(?P\d+)', views.view_product, name='productview'), 8 | url(r'^product/list/$', login_required(views.ProductListIndex.as_view()), name='productlist'), 9 | url(r'^product/update/$', views.update_product, name='productupdate'), 10 | url(r'^product/del/(?P\d+)/$', views.del_product, name='productdel'), 11 | url(r'^project/add/$', views.add_project, name='projectadd'), 12 | url(r'^project/list/$', login_required(views.ProjectListIndex.as_view()), name='projectlist'), 13 | url(r'^project/view/(?P\d+)', views.view_project, name='projectview'), 14 | url(r'^project/update/$', views.update_project, name='projectupdate'), 15 | url(r'^project/del/(?P\d+)/$', views.del_project, name='projectdel'), 16 | url(r'^module/add/$', views.add_module, name='moduleadd'), 17 | # url(r'^module/list/$', login_required(views.ModuleListIndex.as_view()), name='modulelist'), 18 | url(r'^module/update/$', views.update_module, name='moduleupdate'), 19 | url(r'^module/del/(?P\d+)/$', views.del_module, name='moduledel'), 20 | url(r'^get/project/$', views.get_project, name='getproject'), 21 | url(r'^get/module/$', views.get_module, name='getmodule'), 22 | url(r'^get/connecteduser/$', views.get_connected_user, name='getconnecteduser'), 23 | url(r'^product/user/$', views.product_user, name='productuser'), 24 | url(r'^get/moduleList/$', views.get_module_list, name='getmodulelist'), 25 | url(r'^setedit/product/$', views.set_edit_product, name='seteditproduct'), 26 | url(r'^setedit/project/$', views.set_edit_project, name='seteditproject'), 27 | url(r'^setedit/module/$', views.set_edit_module, name='seteditmodule'), 28 | url(r'^syslog/home/$', views.page_syslog, name='toolsyslog'), 29 | url(r'^snmp/home/$', views.comingsoon, name='toolsnmp'), 30 | ] -------------------------------------------------------------------------------- /automatic/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/settings/__init__.py -------------------------------------------------------------------------------- /automatic/signals.py: -------------------------------------------------------------------------------- 1 | from django.dispatch import Signal 2 | 3 | USER_REGISTERED_SIGNAL = Signal(providing_args=['user']) -------------------------------------------------------------------------------- /automatic/static/css/bootstrapValidator.css: -------------------------------------------------------------------------------- 1 | /** 2 | * BootstrapValidator (http://bootstrapvalidator.com) 3 | * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3 4 | * 5 | * @author http://twitter.com/nghuuphuoc 6 | * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc 7 | * @license MIT 8 | */ 9 | 10 | .bv-form .help-block { 11 | margin-bottom: 0; 12 | } 13 | .bv-form .tooltip-inner { 14 | text-align: left; 15 | } 16 | .nav-tabs li.bv-tab-success > a { 17 | color: #3c763d; 18 | } 19 | .nav-tabs li.bv-tab-error > a { 20 | color: #a94442; 21 | } 22 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/font/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/css/font-awesome/font/FontAwesome.otf -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/font/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/css/font-awesome/font/fontawesome-webfont.eot -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/css/font-awesome/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/font/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/css/font-awesome/font/fontawesome-webfont.woff -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/css/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/css/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/css/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/css/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/css/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/bootstrap.less: -------------------------------------------------------------------------------- 1 | /* BOOTSTRAP SPECIFIC CLASSES 2 | * -------------------------- */ 3 | 4 | /* Bootstrap 2.0 sprites.less reset */ 5 | [class^="icon-"], 6 | [class*=" icon-"] { 7 | display: inline; 8 | width: auto; 9 | height: auto; 10 | line-height: normal; 11 | vertical-align: baseline; 12 | background-image: none; 13 | background-position: 0% 0%; 14 | background-repeat: repeat; 15 | margin-top: 0; 16 | } 17 | 18 | /* more sprites.less reset */ 19 | .icon-white, 20 | .nav-pills > .active > a > [class^="icon-"], 21 | .nav-pills > .active > a > [class*=" icon-"], 22 | .nav-list > .active > a > [class^="icon-"], 23 | .nav-list > .active > a > [class*=" icon-"], 24 | .navbar-inverse .nav > .active > a > [class^="icon-"], 25 | .navbar-inverse .nav > .active > a > [class*=" icon-"], 26 | .dropdown-menu > li > a:hover > [class^="icon-"], 27 | .dropdown-menu > li > a:hover > [class*=" icon-"], 28 | .dropdown-menu > .active > a > [class^="icon-"], 29 | .dropdown-menu > .active > a > [class*=" icon-"], 30 | .dropdown-submenu:hover > a > [class^="icon-"], 31 | .dropdown-submenu:hover > a > [class*=" icon-"] { 32 | background-image: none; 33 | } 34 | 35 | 36 | /* keeps Bootstrap styles with and without icons the same */ 37 | .btn, .nav { 38 | [class^="icon-"], 39 | [class*=" icon-"] { 40 | // display: inline; 41 | &.icon-large { line-height: .9em; } 42 | &.icon-spin { display: inline-block; } 43 | } 44 | } 45 | .nav-tabs, .nav-pills { 46 | [class^="icon-"], 47 | [class*=" icon-"] { 48 | &, &.icon-large { line-height: .9em; } 49 | } 50 | } 51 | .btn { 52 | [class^="icon-"], 53 | [class*=" icon-"] { 54 | &.pull-left, &.pull-right { 55 | &.icon-2x { margin-top: .18em; } 56 | } 57 | &.icon-spin.icon-large { line-height: .8em; } 58 | } 59 | } 60 | .btn.btn-small { 61 | [class^="icon-"], 62 | [class*=" icon-"] { 63 | &.pull-left, &.pull-right { 64 | &.icon-2x { margin-top: .25em; } 65 | } 66 | } 67 | } 68 | .btn.btn-large { 69 | [class^="icon-"], 70 | [class*=" icon-"] { 71 | margin-top: 0; // overrides bootstrap default 72 | &.pull-left, &.pull-right { 73 | &.icon-2x { margin-top: .05em; } 74 | } 75 | &.pull-left.icon-2x { margin-right: .2em; } 76 | &.pull-right.icon-2x { margin-left: .2em; } 77 | } 78 | } 79 | 80 | /* Fixes alignment in nav lists */ 81 | .nav-list [class^="icon-"], 82 | .nav-list [class*=" icon-"] { 83 | line-height: inherit; 84 | } 85 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .@{fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/core.less: -------------------------------------------------------------------------------- 1 | /* FONT AWESOME CORE 2 | * -------------------------- */ 3 | 4 | [class^="icon-"], 5 | [class*=" icon-"] { 6 | .icon-FontAwesome(); 7 | } 8 | 9 | [class^="icon-"]:before, 10 | [class*=" icon-"]:before { 11 | text-decoration: inherit; 12 | display: inline-block; 13 | speak: none; 14 | } 15 | 16 | /* makes the font 33% larger relative to the icon container */ 17 | .icon-large:before { 18 | vertical-align: -10%; 19 | font-size: 4/3em; 20 | } 21 | 22 | /* makes sure icons active on rollover in links */ 23 | a { 24 | [class^="icon-"], 25 | [class*=" icon-"] { 26 | display: inline; 27 | } 28 | } 29 | 30 | /* increased font size for icon-large */ 31 | [class^="icon-"], 32 | [class*=" icon-"] { 33 | &.icon-fixed-width { 34 | display: inline-block; 35 | width: 16/14em; 36 | text-align: right; 37 | padding-right: 4/14em; 38 | &.icon-large { 39 | width: 20/14em; 40 | } 41 | } 42 | } 43 | 44 | .icons-ul { 45 | margin-left: @icons-li-width; 46 | list-style-type: none; 47 | 48 | > li { position: relative; } 49 | 50 | .icon-li { 51 | position: absolute; 52 | left: -@icons-li-width; 53 | width: @icons-li-width; 54 | text-align: center; 55 | line-height: inherit; 56 | } 57 | } 58 | 59 | // allows usage of the hide class directly on font awesome icons 60 | [class^="icon-"], 61 | [class*=" icon-"] { 62 | &.hide { 63 | display: none; 64 | } 65 | } 66 | 67 | .icon-muted { color: @iconMuted; } 68 | .icon-light { color: @iconLight; } 69 | .icon-dark { color: @iconDark; } 70 | 71 | // Icon Borders 72 | // ------------------------- 73 | 74 | .icon-border { 75 | border: solid 1px @borderColor; 76 | padding: .2em .25em .15em; 77 | .border-radius(3px); 78 | } 79 | 80 | // Icon Sizes 81 | // ------------------------- 82 | 83 | .icon-2x { 84 | font-size: 2em; 85 | &.icon-border { 86 | border-width: 2px; 87 | .border-radius(4px); 88 | } 89 | } 90 | .icon-3x { 91 | font-size: 3em; 92 | &.icon-border { 93 | border-width: 3px; 94 | .border-radius(5px); 95 | } 96 | } 97 | .icon-4x { 98 | font-size: 4em; 99 | &.icon-border { 100 | border-width: 4px; 101 | .border-radius(6px); 102 | } 103 | } 104 | 105 | .icon-5x { 106 | font-size: 5em; 107 | &.icon-border { 108 | border-width: 5px; 109 | .border-radius(7px); 110 | } 111 | } 112 | 113 | 114 | // Floats & Margins 115 | // ------------------------- 116 | 117 | // Quick floats 118 | .pull-right { float: right; } 119 | .pull-left { float: left; } 120 | 121 | [class^="icon-"], 122 | [class*=" icon-"] { 123 | &.pull-left { 124 | margin-right: .3em; 125 | } 126 | &.pull-right { 127 | margin-left: .3em; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/extras.less: -------------------------------------------------------------------------------- 1 | /* EXTRAS 2 | * -------------------------- */ 3 | 4 | /* Stacked and layered icon */ 5 | .icon-stack(); 6 | 7 | /* Animated rotating icon */ 8 | .icon-spin { 9 | display: inline-block; 10 | -moz-animation: spin 2s infinite linear; 11 | -o-animation: spin 2s infinite linear; 12 | -webkit-animation: spin 2s infinite linear; 13 | animation: spin 2s infinite linear; 14 | } 15 | 16 | /* Prevent stack and spinners from being taken inline when inside a link */ 17 | a .icon-stack, 18 | a .icon-spin { 19 | display: inline-block; 20 | text-decoration: none; 21 | } 22 | 23 | @-moz-keyframes spin { 24 | 0% { -moz-transform: rotate(0deg); } 25 | 100% { -moz-transform: rotate(359deg); } 26 | } 27 | @-webkit-keyframes spin { 28 | 0% { -webkit-transform: rotate(0deg); } 29 | 100% { -webkit-transform: rotate(359deg); } 30 | } 31 | @-o-keyframes spin { 32 | 0% { -o-transform: rotate(0deg); } 33 | 100% { -o-transform: rotate(359deg); } 34 | } 35 | @-ms-keyframes spin { 36 | 0% { -ms-transform: rotate(0deg); } 37 | 100% { -ms-transform: rotate(359deg); } 38 | } 39 | @keyframes spin { 40 | 0% { transform: rotate(0deg); } 41 | 100% { transform: rotate(359deg); } 42 | } 43 | 44 | /* Icon rotations and mirroring */ 45 | .icon-rotate-90:before { 46 | -webkit-transform: rotate(90deg); 47 | -moz-transform: rotate(90deg); 48 | -ms-transform: rotate(90deg); 49 | -o-transform: rotate(90deg); 50 | transform: rotate(90deg); 51 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); 52 | } 53 | 54 | .icon-rotate-180:before { 55 | -webkit-transform: rotate(180deg); 56 | -moz-transform: rotate(180deg); 57 | -ms-transform: rotate(180deg); 58 | -o-transform: rotate(180deg); 59 | transform: rotate(180deg); 60 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); 61 | } 62 | 63 | .icon-rotate-270:before { 64 | -webkit-transform: rotate(270deg); 65 | -moz-transform: rotate(270deg); 66 | -ms-transform: rotate(270deg); 67 | -o-transform: rotate(270deg); 68 | transform: rotate(270deg); 69 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); 70 | } 71 | 72 | .icon-flip-horizontal:before { 73 | -webkit-transform: scale(-1, 1); 74 | -moz-transform: scale(-1, 1); 75 | -ms-transform: scale(-1, 1); 76 | -o-transform: scale(-1, 1); 77 | transform: scale(-1, 1); 78 | } 79 | 80 | .icon-flip-vertical:before { 81 | -webkit-transform: scale(1, -1); 82 | -moz-transform: scale(1, -1); 83 | -ms-transform: scale(1, -1); 84 | -o-transform: scale(1, -1); 85 | transform: scale(1, -1); 86 | } 87 | 88 | /* ensure rotation occurs inside anchor tags */ 89 | a { 90 | .icon-rotate-90, .icon-rotate-180, .icon-rotate-270, .icon-flip-horizontal, .icon-flip-vertical { 91 | &:before { display: inline-block; } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 3.2.1 3 | * the iconic font designed for Bootstrap 4 | * ------------------------------------------------------------------------------ 5 | * The full suite of pictographic icons, examples, and documentation can be 6 | * found at http://fontawesome.io. Stay up to date on Twitter at 7 | * http://twitter.com/fontawesome. 8 | * 9 | * License 10 | * ------------------------------------------------------------------------------ 11 | * - The Font Awesome font is licensed under SIL OFL 1.1 - 12 | * http://scripts.sil.org/OFL 13 | * - Font Awesome CSS, LESS, and SASS files are licensed under MIT License - 14 | * http://opensource.org/licenses/mit-license.html 15 | * - Font Awesome documentation licensed under CC BY 3.0 - 16 | * http://creativecommons.org/licenses/by/3.0/ 17 | * - Attribution is no longer required in Font Awesome 3.0, but much appreciated: 18 | * "Font Awesome by Dave Gandy - http://fontawesome.io" 19 | * 20 | * Author - Dave Gandy 21 | * ------------------------------------------------------------------------------ 22 | * Email: dave@fontawesome.io 23 | * Twitter: http://twitter.com/byscuits 24 | * Work: Lead Product Designer @ Kyruus - http://kyruus.com 25 | */ 26 | 27 | @import "variables.less"; 28 | @import "mixins.less"; 29 | @import "path.less"; 30 | @import "core.less"; 31 | @import "bootstrap.less"; 32 | @import "extras.less"; 33 | @import "icons.less"; 34 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .icon(@icon) { 5 | .icon-FontAwesome(); 6 | content: @icon; 7 | } 8 | 9 | .icon-FontAwesome() { 10 | font-family: FontAwesome; 11 | font-weight: normal; 12 | font-style: normal; 13 | text-decoration: inherit; 14 | -webkit-font-smoothing: antialiased; 15 | *margin-right: .3em; // fixes ie7 issues 16 | } 17 | 18 | .border-radius(@radius) { 19 | -webkit-border-radius: @radius; 20 | -moz-border-radius: @radius; 21 | border-radius: @radius; 22 | } 23 | 24 | .icon-stack(@width: 2em, @height: 2em, @top-font-size: 1em, @base-font-size: 2em) { 25 | .icon-stack { 26 | position: relative; 27 | display: inline-block; 28 | width: @width; 29 | height: @height; 30 | line-height: @width; 31 | vertical-align: -35%; 32 | [class^="icon-"], 33 | [class*=" icon-"] { 34 | display: block; 35 | text-align: center; 36 | position: absolute; 37 | width: 100%; 38 | height: 100%; 39 | font-size: @top-font-size; 40 | line-height: inherit; 41 | *line-height: @height; 42 | } 43 | .icon-stack-base { 44 | font-size: @base-font-size; 45 | *line-height: @height / @base-font-size; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{FontAwesomePath}/fontawesome-webfont.eot?v=@{FontAwesomeVersion}'); 7 | src: url('@{FontAwesomePath}/fontawesome-webfont.eot?#iefix&v=@{FontAwesomeVersion}') format('embedded-opentype'), 8 | url('@{FontAwesomePath}/fontawesome-webfont.woff?v=@{FontAwesomeVersion}') format('woff'), 9 | url('@{FontAwesomePath}/fontawesome-webfont.ttf?v=@{FontAwesomeVersion}') format('truetype'), 10 | url('@{FontAwesomePath}/fontawesome-webfont.svg#fontawesomeregular?v=@{FontAwesomeVersion}') format('svg'); 11 | // src: url('@{FontAwesomePath}/FontAwesome.otf') format('opentype'); // used when developing fonts 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/_bootstrap.scss: -------------------------------------------------------------------------------- 1 | /* BOOTSTRAP SPECIFIC CLASSES 2 | * -------------------------- */ 3 | 4 | /* Bootstrap 2.0 sprites.less reset */ 5 | [class^="icon-"], 6 | [class*=" icon-"] { 7 | display: inline; 8 | width: auto; 9 | height: auto; 10 | line-height: normal; 11 | vertical-align: baseline; 12 | background-image: none; 13 | background-position: 0% 0%; 14 | background-repeat: repeat; 15 | margin-top: 0; 16 | } 17 | 18 | /* more sprites.less reset */ 19 | .icon-white, 20 | .nav-pills > .active > a > [class^="icon-"], 21 | .nav-pills > .active > a > [class*=" icon-"], 22 | .nav-list > .active > a > [class^="icon-"], 23 | .nav-list > .active > a > [class*=" icon-"], 24 | .navbar-inverse .nav > .active > a > [class^="icon-"], 25 | .navbar-inverse .nav > .active > a > [class*=" icon-"], 26 | .dropdown-menu > li > a:hover > [class^="icon-"], 27 | .dropdown-menu > li > a:hover > [class*=" icon-"], 28 | .dropdown-menu > .active > a > [class^="icon-"], 29 | .dropdown-menu > .active > a > [class*=" icon-"], 30 | .dropdown-submenu:hover > a > [class^="icon-"], 31 | .dropdown-submenu:hover > a > [class*=" icon-"] { 32 | background-image: none; 33 | } 34 | 35 | 36 | /* keeps Bootstrap styles with and without icons the same */ 37 | .btn, .nav { 38 | [class^="icon-"], 39 | [class*=" icon-"] { 40 | // display: inline; 41 | &.icon-large { line-height: .9em; } 42 | &.icon-spin { display: inline-block; } 43 | } 44 | } 45 | .nav-tabs, .nav-pills { 46 | [class^="icon-"], 47 | [class*=" icon-"] { 48 | &, &.icon-large { line-height: .9em; } 49 | } 50 | } 51 | .btn { 52 | [class^="icon-"], 53 | [class*=" icon-"] { 54 | &.pull-left, &.pull-right { 55 | &.icon-2x { margin-top: .18em; } 56 | } 57 | &.icon-spin.icon-large { line-height: .8em; } 58 | } 59 | } 60 | .btn.btn-small { 61 | [class^="icon-"], 62 | [class*=" icon-"] { 63 | &.pull-left, &.pull-right { 64 | &.icon-2x { margin-top: .25em; } 65 | } 66 | } 67 | } 68 | .btn.btn-large { 69 | [class^="icon-"], 70 | [class*=" icon-"] { 71 | margin-top: 0; // overrides bootstrap default 72 | &.pull-left, &.pull-right { 73 | &.icon-2x { margin-top: .05em; } 74 | } 75 | &.pull-left.icon-2x { margin-right: .2em; } 76 | &.pull-right.icon-2x { margin-left: .2em; } 77 | } 78 | } 79 | 80 | /* Fixes alignment in nav lists */ 81 | .nav-list [class^="icon-"], 82 | .nav-list [class*=" icon-"] { 83 | line-height: inherit; 84 | } 85 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/_core.scss: -------------------------------------------------------------------------------- 1 | /* FONT AWESOME CORE 2 | * -------------------------- */ 3 | 4 | [class^="icon-"], 5 | [class*=" icon-"] { 6 | @include icon-FontAwesome(); 7 | } 8 | 9 | [class^="icon-"]:before, 10 | [class*=" icon-"]:before { 11 | text-decoration: inherit; 12 | display: inline-block; 13 | speak: none; 14 | } 15 | 16 | /* makes the font 33% larger relative to the icon container */ 17 | .icon-large:before { 18 | vertical-align: -10%; 19 | font-size: (4em/3); 20 | } 21 | 22 | /* makes sure icons active on rollover in links */ 23 | a { 24 | [class^="icon-"], 25 | [class*=" icon-"] { 26 | display: inline; 27 | } 28 | } 29 | 30 | /* increased font size for icon-large */ 31 | [class^="icon-"], 32 | [class*=" icon-"] { 33 | &.icon-fixed-width { 34 | display: inline-block; 35 | width: (16em/14); 36 | text-align: right; 37 | padding-right: (4em/14); 38 | &.icon-large { 39 | width: (20em/14); 40 | } 41 | } 42 | } 43 | 44 | .icons-ul { 45 | margin-left: $icons-li-width; 46 | list-style-type: none; 47 | 48 | > li { position: relative; } 49 | 50 | .icon-li { 51 | position: absolute; 52 | left: -$icons-li-width; 53 | width: $icons-li-width; 54 | text-align: center; 55 | line-height: inherit; 56 | } 57 | } 58 | 59 | // allows usage of the hide class directly on font awesome icons 60 | [class^="icon-"], 61 | [class*=" icon-"] { 62 | &.hide { 63 | display: none; 64 | } 65 | } 66 | 67 | .icon-muted { color: $iconMuted; } 68 | .icon-light { color: $iconLight; } 69 | .icon-dark { color: $iconDark; } 70 | 71 | // Icon Borders 72 | // ------------------------- 73 | 74 | .icon-border { 75 | border: solid 1px $borderColor; 76 | padding: .2em .25em .15em; 77 | @include border-radius(3px); 78 | } 79 | 80 | // Icon Sizes 81 | // ------------------------- 82 | 83 | .icon-2x { 84 | font-size: 2em; 85 | &.icon-border { 86 | border-width: 2px; 87 | @include border-radius(4px); 88 | } 89 | } 90 | .icon-3x { 91 | font-size: 3em; 92 | &.icon-border { 93 | border-width: 3px; 94 | @include border-radius(5px); 95 | } 96 | } 97 | .icon-4x { 98 | font-size: 4em; 99 | &.icon-border { 100 | border-width: 4px; 101 | @include border-radius(6px); 102 | } 103 | } 104 | 105 | .icon-5x { 106 | font-size: 5em; 107 | &.icon-border { 108 | border-width: 5px; 109 | @include border-radius(7px); 110 | } 111 | } 112 | 113 | 114 | // Floats & Margins 115 | // ------------------------- 116 | 117 | // Quick floats 118 | .pull-right { float: right; } 119 | .pull-left { float: left; } 120 | 121 | [class^="icon-"], 122 | [class*=" icon-"] { 123 | &.pull-left { 124 | margin-right: .3em; 125 | } 126 | &.pull-right { 127 | margin-left: .3em; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/_extras.scss: -------------------------------------------------------------------------------- 1 | /* EXTRAS 2 | * -------------------------- */ 3 | 4 | /* Stacked and layered icon */ 5 | @include icon-stack(); 6 | 7 | /* Animated rotating icon */ 8 | .icon-spin { 9 | display: inline-block; 10 | -moz-animation: spin 2s infinite linear; 11 | -o-animation: spin 2s infinite linear; 12 | -webkit-animation: spin 2s infinite linear; 13 | animation: spin 2s infinite linear; 14 | } 15 | 16 | /* Prevent stack and spinners from being taken inline when inside a link */ 17 | a .icon-stack, 18 | a .icon-spin { 19 | display: inline-block; 20 | text-decoration: none; 21 | } 22 | 23 | @-moz-keyframes spin { 24 | 0% { -moz-transform: rotate(0deg); } 25 | 100% { -moz-transform: rotate(359deg); } 26 | } 27 | @-webkit-keyframes spin { 28 | 0% { -webkit-transform: rotate(0deg); } 29 | 100% { -webkit-transform: rotate(359deg); } 30 | } 31 | @-o-keyframes spin { 32 | 0% { -o-transform: rotate(0deg); } 33 | 100% { -o-transform: rotate(359deg); } 34 | } 35 | @-ms-keyframes spin { 36 | 0% { -ms-transform: rotate(0deg); } 37 | 100% { -ms-transform: rotate(359deg); } 38 | } 39 | @keyframes spin { 40 | 0% { transform: rotate(0deg); } 41 | 100% { transform: rotate(359deg); } 42 | } 43 | 44 | /* Icon rotations and mirroring */ 45 | .icon-rotate-90:before { 46 | -webkit-transform: rotate(90deg); 47 | -moz-transform: rotate(90deg); 48 | -ms-transform: rotate(90deg); 49 | -o-transform: rotate(90deg); 50 | transform: rotate(90deg); 51 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); 52 | } 53 | 54 | .icon-rotate-180:before { 55 | -webkit-transform: rotate(180deg); 56 | -moz-transform: rotate(180deg); 57 | -ms-transform: rotate(180deg); 58 | -o-transform: rotate(180deg); 59 | transform: rotate(180deg); 60 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); 61 | } 62 | 63 | .icon-rotate-270:before { 64 | -webkit-transform: rotate(270deg); 65 | -moz-transform: rotate(270deg); 66 | -ms-transform: rotate(270deg); 67 | -o-transform: rotate(270deg); 68 | transform: rotate(270deg); 69 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); 70 | } 71 | 72 | .icon-flip-horizontal:before { 73 | -webkit-transform: scale(-1, 1); 74 | -moz-transform: scale(-1, 1); 75 | -ms-transform: scale(-1, 1); 76 | -o-transform: scale(-1, 1); 77 | transform: scale(-1, 1); 78 | } 79 | 80 | .icon-flip-vertical:before { 81 | -webkit-transform: scale(1, -1); 82 | -moz-transform: scale(1, -1); 83 | -ms-transform: scale(1, -1); 84 | -o-transform: scale(1, -1); 85 | transform: scale(1, -1); 86 | } 87 | 88 | /* ensure rotation occurs inside anchor tags */ 89 | a { 90 | .icon-rotate-90, .icon-rotate-180, .icon-rotate-270, .icon-flip-horizontal, .icon-flip-vertical { 91 | &:before { display: inline-block; } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin icon($icon) { 5 | @include icon-FontAwesome(); 6 | content: $icon; 7 | } 8 | 9 | @mixin icon-FontAwesome() { 10 | font-family: FontAwesome; 11 | font-weight: normal; 12 | font-style: normal; 13 | text-decoration: inherit; 14 | -webkit-font-smoothing: antialiased; 15 | *margin-right: .3em; // fixes ie7 issues 16 | } 17 | 18 | @mixin border-radius($radius) { 19 | -webkit-border-radius: $radius; 20 | -moz-border-radius: $radius; 21 | border-radius: $radius; 22 | } 23 | 24 | @mixin icon-stack($width: 2em, $height: 2em, $top-font-size: 1em, $base-font-size: 2em) { 25 | .icon-stack { 26 | position: relative; 27 | display: inline-block; 28 | width: $width; 29 | height: $height; 30 | line-height: $width; 31 | vertical-align: -35%; 32 | [class^="icon-"], 33 | [class*=" icon-"] { 34 | display: block; 35 | text-align: center; 36 | position: absolute; 37 | width: 100%; 38 | height: 100%; 39 | font-size: $top-font-size; 40 | line-height: inherit; 41 | *line-height: $height; 42 | } 43 | .icon-stack-base { 44 | font-size: $base-font-size; 45 | *line-height: #{$height / $base-font-size}em; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$FontAwesomePath}/fontawesome-webfont.eot?v=#{$FontAwesomeVersion}'); 7 | src: url('#{$FontAwesomePath}/fontawesome-webfont.eot?#iefix&v=#{$FontAwesomeVersion}') format('embedded-opentype'), 8 | url('#{$FontAwesomePath}/fontawesome-webfont.woff?v=#{$FontAwesomeVersion}') format('woff'), 9 | url('#{$FontAwesomePath}/fontawesome-webfont.ttf?v=#{$FontAwesomeVersion}') format('truetype'), 10 | url('#{$FontAwesomePath}/fontawesome-webfont.svg#fontawesomeregular?v=#{$FontAwesomeVersion}') format('svg'); 11 | // src: url('#{$FontAwesomePath}/FontAwesome.otf') format('opentype'); // used when developing fonts 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /automatic/static/css/font-awesome/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 3.2.1 3 | * the iconic font designed for Bootstrap 4 | * ------------------------------------------------------------------------------ 5 | * The full suite of pictographic icons, examples, and documentation can be 6 | * found at http://fontawesome.io. Stay up to date on Twitter at 7 | * http://twitter.com/fontawesome. 8 | * 9 | * License 10 | * ------------------------------------------------------------------------------ 11 | * - The Font Awesome font is licensed under SIL OFL 1.1 - 12 | * http://scripts.sil.org/OFL 13 | * - Font Awesome CSS, LESS, and SASS files are licensed under MIT License - 14 | * http://opensource.org/licenses/mit-license.html 15 | * - Font Awesome documentation licensed under CC BY 3.0 - 16 | * http://creativecommons.org/licenses/by/3.0/ 17 | * - Attribution is no longer required in Font Awesome 3.0, but much appreciated: 18 | * "Font Awesome by Dave Gandy - http://fontawesome.io" 19 | * 20 | * Author - Dave Gandy 21 | * ------------------------------------------------------------------------------ 22 | * Email: dave@fontawesome.io 23 | * Twitter: http://twitter.com/byscuits 24 | * Work: Lead Product Designer @ Kyruus - http://kyruus.com 25 | */ 26 | 27 | @import "variables"; 28 | @import "mixins"; 29 | @import "path"; 30 | @import "core"; 31 | @import "bootstrap"; 32 | @import "extras"; 33 | @import "icons"; 34 | -------------------------------------------------------------------------------- /automatic/static/css/wheelmenu.css: -------------------------------------------------------------------------------- 1 | /* Required Stylesheets */ 2 | 3 | a { 4 | text-decoration: none; 5 | } 6 | 7 | .wheel-button { 8 | position: relative; 9 | } 10 | 11 | .wheel { 12 | margin: 0; 13 | padding: 0; 14 | list-style: none; 15 | width: 260px; /* this will determine the diameter of the circle */ 16 | height: 200px; /* this will determine the diameter of the circle */ 17 | visibility: hidden; 18 | position: relative; 19 | display: none; 20 | } 21 | 22 | .wheel li { 23 | overflow: hidden; 24 | float: left; 25 | } 26 | 27 | .wheel li a { 28 | display: block; 29 | } 30 | 31 | .wheel-button, .wheel-button:visited { 32 | line-height: 35px; 33 | font-weight: bold; 34 | font-size: 36px; 35 | background: #df4727; 36 | padding: 10px 11px; 37 | text-align: center; 38 | border-radius: 50px; 39 | width: 60px; 40 | height: 60px; 41 | color: white; 42 | display: block; 43 | margin: 70px auto 20px; 44 | border: 3px solid #92311e; 45 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); 46 | -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); 47 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); 48 | } 49 | 50 | .wheel-button:hover { 51 | color: white; 52 | } 53 | 54 | .wheel-button.ne { 55 | border-color: white; 56 | background: #35b0ee; 57 | color: #0277bd; 58 | position: absolute; 59 | bottom: 50px; 60 | left: 0px; 61 | } 62 | 63 | .wheel-button.nw { 64 | border-color: white; 65 | background-color: #E67E22; 66 | color: #FFFC44; 67 | position: absolute; 68 | bottom: 10px; 69 | right: 10px; 70 | } 71 | 72 | .wheel-button span, .wheel span { 73 | position: relative; 74 | -moz-transition: all 1s ease; 75 | -webkit-transition: all 1s ease; 76 | -o-transition: all 1s ease; 77 | transition: all 1s ease; 78 | display: block; 79 | } 80 | 81 | .wheel-button.active span { 82 | transform: rotate(135deg); 83 | -ms-transform: rotate(135deg); /* IE 9 */ 84 | -webkit-transform: rotate(135deg); /* Safari and Chrome */ 85 | } 86 | 87 | .wheel-button.ne span{ 88 | font-size:28px; 89 | line-height:32px; 90 | font-family: sans-serif; 91 | } 92 | .wheel li a, .wheel li a:visited { 93 | background: rgba(0, 0, 0, 0.65); 94 | border-radius: 50px; 95 | font-weight: bold; 96 | padding: 10px; 97 | text-align: center; 98 | width: 40px; 99 | height: 40px; 100 | border: 1px solid black; 101 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), inset 0 1px 1px rgba(255, 255, 255, 0.5); 102 | -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), inset 0 1px 1px rgba(255, 255, 255, 0.5); 103 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), inset 0 1px 1px rgba(255, 255, 255, 0.5); 104 | color: white; 105 | -moz-transition: all 0.25s ease; 106 | -webkit-transition: all 0.25s ease; 107 | -o-transition: all 0.25s ease; 108 | transition: all 0.25s ease; 109 | } 110 | 111 | .wheel li a:hover { 112 | background: rgba(0, 0, 0, 0.8); 113 | } -------------------------------------------------------------------------------- /automatic/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /automatic/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /automatic/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /automatic/static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /automatic/static/fonts/meteocons/meteocons-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/fonts/meteocons/meteocons-webfont.eot -------------------------------------------------------------------------------- /automatic/static/fonts/meteocons/meteocons-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/fonts/meteocons/meteocons-webfont.ttf -------------------------------------------------------------------------------- /automatic/static/fonts/meteocons/meteocons-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/fonts/meteocons/meteocons-webfont.woff -------------------------------------------------------------------------------- /automatic/static/image/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/image/background.jpg -------------------------------------------------------------------------------- /automatic/static/image/background1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/image/background1.jpg -------------------------------------------------------------------------------- /automatic/static/image/background2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/image/background2.jpg -------------------------------------------------------------------------------- /automatic/static/image/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/image/favicon.ico -------------------------------------------------------------------------------- /automatic/static/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/image/logo.png -------------------------------------------------------------------------------- /automatic/static/image/wx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/image/wx.jpg -------------------------------------------------------------------------------- /automatic/static/images/ak-common.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/ak-common.png -------------------------------------------------------------------------------- /automatic/static/images/ak-person-icon01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/ak-person-icon01.png -------------------------------------------------------------------------------- /automatic/static/images/ak-person-icon02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/ak-person-icon02.jpg -------------------------------------------------------------------------------- /automatic/static/images/ak-person-icon03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/ak-person-icon03.png -------------------------------------------------------------------------------- /automatic/static/images/ak-person-icon04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/ak-person-icon04.png -------------------------------------------------------------------------------- /automatic/static/images/ak-person-icon05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/ak-person-icon05.png -------------------------------------------------------------------------------- /automatic/static/images/ak-person-icon06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/ak-person-icon06.png -------------------------------------------------------------------------------- /automatic/static/images/ak-z-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/ak-z-icon.png -------------------------------------------------------------------------------- /automatic/static/images/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/app.png -------------------------------------------------------------------------------- /automatic/static/images/app_current.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/app_current.png -------------------------------------------------------------------------------- /automatic/static/images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/background.jpg -------------------------------------------------------------------------------- /automatic/static/images/background1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/background1.jpg -------------------------------------------------------------------------------- /automatic/static/images/background2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/background2.jpg -------------------------------------------------------------------------------- /automatic/static/images/channel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/channel.png -------------------------------------------------------------------------------- /automatic/static/images/channel_current.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/channel_current.png -------------------------------------------------------------------------------- /automatic/static/images/chat01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/chat01.png -------------------------------------------------------------------------------- /automatic/static/images/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/cloud.png -------------------------------------------------------------------------------- /automatic/static/images/cloud_current.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/cloud_current.png -------------------------------------------------------------------------------- /automatic/static/images/custom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/custom.png -------------------------------------------------------------------------------- /automatic/static/images/custom_current.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/custom_current.png -------------------------------------------------------------------------------- /automatic/static/images/dotted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/dotted.png -------------------------------------------------------------------------------- /automatic/static/images/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/download.png -------------------------------------------------------------------------------- /automatic/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/favicon.ico -------------------------------------------------------------------------------- /automatic/static/images/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/home.png -------------------------------------------------------------------------------- /automatic/static/images/home_current.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/home_current.png -------------------------------------------------------------------------------- /automatic/static/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/icon.png -------------------------------------------------------------------------------- /automatic/static/images/line_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/line_bg.png -------------------------------------------------------------------------------- /automatic/static/images/logo-automagic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/logo-automagic.png -------------------------------------------------------------------------------- /automatic/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/logo.png -------------------------------------------------------------------------------- /automatic/static/images/select_xl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/select_xl.png -------------------------------------------------------------------------------- /automatic/static/images/select_xl01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/select_xl01.png -------------------------------------------------------------------------------- /automatic/static/images/setting-top02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/setting-top02.png -------------------------------------------------------------------------------- /automatic/static/images/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/sort_asc.png -------------------------------------------------------------------------------- /automatic/static/images/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/sort_both.png -------------------------------------------------------------------------------- /automatic/static/images/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/sort_desc.png -------------------------------------------------------------------------------- /automatic/static/images/source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/source.png -------------------------------------------------------------------------------- /automatic/static/images/source_current.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/source_current.png -------------------------------------------------------------------------------- /automatic/static/images/statistics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/statistics.png -------------------------------------------------------------------------------- /automatic/static/images/statistics_current.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/statistics_current.png -------------------------------------------------------------------------------- /automatic/static/images/syetem_management.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/syetem_management.png -------------------------------------------------------------------------------- /automatic/static/images/syetem_management_c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/syetem_management_c.png -------------------------------------------------------------------------------- /automatic/static/images/system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/system.png -------------------------------------------------------------------------------- /automatic/static/images/system_current.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/system_current.png -------------------------------------------------------------------------------- /automatic/static/images/timg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/timg.png -------------------------------------------------------------------------------- /automatic/static/images/user-13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/user-13.jpg -------------------------------------------------------------------------------- /automatic/static/images/xiaoxi01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/xiaoxi01.png -------------------------------------------------------------------------------- /automatic/static/images/xiaoxi01g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/xiaoxi01g.png -------------------------------------------------------------------------------- /automatic/static/images/zkonw-back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/images/zkonw-back.png -------------------------------------------------------------------------------- /automatic/static/js/back-to-top.js: -------------------------------------------------------------------------------- 1 | //** jQuery Scroll to Top Control script- (c) Dynamic Drive DHTML code library: http://www.dynamicdrive.com. 2 | //** Available/ usage terms at http://www.dynamicdrive.com (March 30th, 09') 3 | //** v1.1 (April 7th, 09'): 4 | //** 1) Adds ability to scroll to an absolute position (from top of page) or specific element on the page instead. 5 | //** 2) Fixes scroll animation not working in Opera. 6 | 7 | 8 | var scrolltotop={ 9 | //startline: Integer. Number of pixels from top of doc scrollbar is scrolled before showing control 10 | //scrollto: Keyword (Integer, or "Scroll_to_Element_ID"). How far to scroll document up when control is clicked on (0=top). 11 | setting: {startline:100, scrollto: 0, scrollduration:1000, fadeduration:[500, 100]}, 12 | controlHTML: '', // //HTML for control, which is auto wrapped in DIV w/ ID="topcontrol" 13 | controlattrs: {offsetx:5, offsety:5}, //offset of control relative to right/ bottom of window corner 14 | anchorkeyword: '#top', //Enter href value of HTML anchors on the page that should also act as "Scroll Up" links 15 | 16 | state: {isvisible:false, shouldvisible:false}, 17 | 18 | scrollup:function(){ 19 | if (!this.cssfixedsupport) //if control is positioned using JavaScript 20 | this.$control.css({opacity:0}) //hide control immediately after clicking it 21 | var dest=isNaN(this.setting.scrollto)? this.setting.scrollto : parseInt(this.setting.scrollto) 22 | if (typeof dest=="string" && jQuery('#'+dest).length==1) //check element set by string exists 23 | dest=jQuery('#'+dest).offset().top 24 | else 25 | dest=0 26 | this.$body.animate({scrollTop: dest}, this.setting.scrollduration); 27 | }, 28 | 29 | keepfixed:function(){ 30 | var $window=jQuery(window) 31 | var controlx=$window.scrollLeft() + $window.width() - this.$control.width() - this.controlattrs.offsetx 32 | var controly=$window.scrollTop() + $window.height() - this.$control.height() - this.controlattrs.offsety 33 | this.$control.css({left:controlx+'px', top:controly+'px'}) 34 | }, 35 | 36 | togglecontrol:function(){ 37 | var scrolltop=jQuery(window).scrollTop() 38 | if (!this.cssfixedsupport) 39 | this.keepfixed() 40 | this.state.shouldvisible=(scrolltop>=this.setting.startline)? true : false 41 | if (this.state.shouldvisible && !this.state.isvisible){ 42 | this.$control.stop().animate({opacity:1}, this.setting.fadeduration[0]) 43 | this.state.isvisible=true 44 | } 45 | else if (this.state.shouldvisible==false && this.state.isvisible){ 46 | this.$control.stop().animate({opacity:0}, this.setting.fadeduration[1]) 47 | this.state.isvisible=false 48 | } 49 | }, 50 | 51 | init:function(){ 52 | jQuery(document).ready(function($){ 53 | var mainobj=scrolltotop 54 | var iebrws=document.all 55 | mainobj.cssfixedsupport=!iebrws || iebrws && document.compatMode=="CSS1Compat" && window.XMLHttpRequest //not IE or IE7+ browsers in standards mode 56 | mainobj.$body=(window.opera)? (document.compatMode=="CSS1Compat"? $('html') : $('body')) : $('html,body') 57 | mainobj.$control=$('
'+mainobj.controlHTML+'
') 58 | .css({position:mainobj.cssfixedsupport? 'fixed' : 'absolute', bottom:mainobj.controlattrs.offsety, right:mainobj.controlattrs.offsetx, opacity:0, cursor:'pointer'}) 59 | .attr({title:'Scroll Back to Top'}) 60 | .click(function(){mainobj.scrollup(); return false}) 61 | .appendTo('body') 62 | if (document.all && !window.XMLHttpRequest && mainobj.$control.text()!='') //loose check for IE6 and below, plus whether control contains any text 63 | mainobj.$control.css({width:mainobj.$control.width()}) //IE6- seems to require an explicit width on a DIV containing text 64 | mainobj.togglecontrol() 65 | $('a[href="' + mainobj.anchorkeyword +'"]').click(function(){ 66 | mainobj.scrollup() 67 | return false 68 | }) 69 | $(window).bind('scroll resize', function(e){ 70 | mainobj.togglecontrol() 71 | }) 72 | }) 73 | } 74 | } 75 | 76 | scrolltotop.init() -------------------------------------------------------------------------------- /automatic/static/js/common.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * RENAISSANCE - Responsive Admin Theme 4 | * version 1.3.0 5 | * 6 | */ 7 | 8 | var datetime = null, 9 | date = null; 10 | 11 | var update = function () { 12 | date = moment(new Date()) 13 | datetime.html(date.format('h:mm A')); 14 | }; 15 | 16 | $(window).on('load', function(){ 17 | //Preloader 18 | setTimeout(function(){ 19 | $('.preloader').fadeOut(100); 20 | }, 500); 21 | }); 22 | 23 | 24 | // check if browser support HTML5 local storage 25 | function localStorageSupport() { 26 | return (('localStorage' in window) && window['localStorage'] !== null) 27 | } 28 | 29 | 30 | //Personal working platform Sidebar 31 | 32 | $("li.perwork-btn").click(function(){ 33 | 34 | $(this).toggleClass("active").siblings().removeClass("active"); 35 | 36 | var currentEle=$(this); 37 | 38 | var siblingsElel=currentEle.siblings("li.members-btn"); 39 | 40 | $.each(siblingsElel,function(index,ele){ 41 | $("#"+$(ele).data("href")).removeClass('members-sidebar-open'); 42 | if($("#"+$(ele).data("href")).hasClass('dropdown-menu')){ 43 | $("#"+$(ele).data("href")).attr("aria-expanded","false"); 44 | $(ele).removeClass("open").removeClass("active"); 45 | } 46 | }); 47 | 48 | cta($(this)[0], $("#"+currentEle.data("href"))[0], {relativeToWindow: true}, function () { 49 | if($("#"+currentEle.data("href")).hasClass('dropdown-menu')){ 50 | $("#"+currentEle.data("href")).attr("aria-expanded","true"); 51 | currentEle.toggleClass("open"); 52 | }else{ 53 | $("#"+currentEle.data("href")).toggleClass('members-sidebar-open'); 54 | } 55 | }); 56 | 57 | $(currentEle.data("close")).click(function(){ 58 | $("#"+currentEle.data("href")).removeClass('members-sidebar-open'); 59 | currentEle.removeClass('active'); 60 | }); 61 | return false; 62 | 63 | }); 64 | 65 | -------------------------------------------------------------------------------- /automatic/static/js/keyword.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Ray on 16-11-11. 3 | */ 4 | 5 | /* 点击关键字编辑按钮 */ 6 | function setkeywordValue(id){ 7 | 8 | $.ajax({ 9 | type:"GET", 10 | data:{'keywordid':id}, 11 | url: "/func/keyword/setedit/", 12 | cache: false, 13 | dataType:'json', 14 | 15 | success: function(result,TextStatus) { 16 | if (result.length >0){ 17 | for(var i=0; i'); 28 | var $selvalue = $(''); 29 | $ipt.width(options.width - 8);//设定文本框宽度 30 | var $this = $(this); 31 | $this.width(options.width); 32 | $ipt.appendTo($this); 33 | $selvalue.appendTo($this); 34 | 35 | //创建 下拉选项 36 | 37 | //1.下拉选项包裹 38 | var $container = $('
'); 39 | 40 | //2.创建 全选和确认按钮 top层 41 | var $top = $('
');//外层div包裹 42 | var $all = $('');//全选 43 | var $btn = $(''); 44 | $all.appendTo($top); 45 | $btn.appendTo($top); 46 | 47 | //3.下拉中的内容 content层 48 | var $content = $('
');//外层div包裹 49 | var count = options.data.length; 50 | var h = ( (count * 22) > parseInt(options.maxheight) ) ? options.maxheight : "'" + count * 22 + "'"; 51 | $content.height(h); 52 | for(var i = count-1; i >= 0; i--){ 53 | 54 | var $list = $('

'); 55 | $list.appendTo($content); 56 | } 57 | 58 | //4把top层和content层加到$container下 59 | $top.appendTo($container); 60 | $content.appendTo($container); 61 | 62 | //把$container加到$(this)下 63 | $container.appendTo($this); 64 | 65 | 66 | //js Effect 67 | var $dropList = $content.children().children('input'); 68 | 69 | $all.change(function (){//点击all 70 | 71 | var opt_arr = []; 72 | var opt_arry = []; 73 | $dropList.each(function (){ 74 | if($all.is(':checked')){ 75 | $(this)[0].checked = 'checked'; 76 | opt_arr.push($(this).val()); 77 | opt_arry.push($(this).next().text()); 78 | }else{ 79 | $(this)[0].checked = ''; 80 | opt_arr=[]; 81 | opt_arry= []; 82 | } 83 | }); 84 | 85 | $ipt.val(opt_arry.join(';')); 86 | $selvalue.val(opt_arr.join(';')); 87 | }); 88 | 89 | $container.addClass('hidden');//开始隐藏 90 | 91 | $ipt.focus(function (){//文本框处于编辑 92 | $container.removeClass('hidden'); 93 | $this.addClass('multi_select_focus'); 94 | }); 95 | 96 | $btn.click(function (){//点击 ok按钮 97 | $container.addClass('hidden'); 98 | $this.removeClass('multi_select_focus'); 99 | }); 100 | 101 | 102 | $dropList.change(function (){//勾选选项 103 | var opt_arr = []; 104 | var opt_arry = []; 105 | $dropList.each(function (){ 106 | if ($(this).is(':checked')){ 107 | opt_arr.push($(this).val()); 108 | opt_arry.push($(this).next().text()); 109 | } 110 | 111 | }); 112 | var $dropList_selected = $content.children().children('input:checked'); 113 | $selvalue.val(opt_arr.join(';')); 114 | $ipt.val(opt_arry.join(';')); 115 | var o = $all[0]; 116 | var n1 = $dropList_selected.length; 117 | var n2 = $dropList.length; 118 | o.checked = (n1 === n2) ? 'checked' : ''; 119 | }); 120 | }); 121 | }, 122 | }); 123 | })(jQuery); -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/awesomeStyle/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/awesomeStyle/img/loading.gif -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/demo.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { 2 | margin: 0;padding: 0;border: 0;outline: 0;font-weight: inherit;font-style: inherit;font-size: 100%;font-family: inherit;vertical-align: baseline;} 3 | body {color: #2f332a;font: 15px/21px Arial, Helvetica, simsun, sans-serif;background: #f0f6e4 \9;} 4 | h1, h2, h3, h4, h5, h6 {color: #2f332a;font-weight: bold;font-family: Helvetica, Arial, sans-serif;padding-bottom: 5px;} 5 | h1 {font-size: 24px;line-height: 34px;text-align: center;} 6 | h2 {font-size: 14px;line-height: 24px;padding-top: 5px;} 7 | h6 {font-weight: normal;font-size: 12px;letter-spacing: 1px;line-height: 24px;text-align: center;} 8 | a {color:#3C6E31;text-decoration: underline;} 9 | a:hover {background-color:#3C6E31;color:white;} 10 | input.radio {margin: 0 2px 0 8px;} 11 | input.radio.first {margin-left:0;} 12 | input.empty {color: lightgray;} 13 | code {color: #2f332a;} 14 | .highlight_red {color:#A60000;} 15 | .highlight_green {color:#A7F43D;} 16 | li {list-style: circle;font-size: 12px;} 17 | li.title {list-style: none;} 18 | ul.list {margin-left: 17px;} 19 | 20 | div.content_wrap {width: 600px;height:380px;} 21 | div.content_wrap div.left{float: left;width: 250px;} 22 | div.content_wrap div.right{float: right;width: 340px;} 23 | div.zTreeDemoBackground {width:250px;height:362px;text-align:left;} 24 | 25 | ul.ztree {margin-top: 10px;border: 1px solid #617775;background: #f0f6e4;width:220px;height:360px;overflow-y:scroll;overflow-x:auto;} 26 | ul.log {border: 1px solid #617775;background: #f0f6e4;width:300px;height:170px;overflow: hidden;} 27 | ul.log.small {height:45px;} 28 | ul.log li {color: #666666;list-style: none;padding-left: 10px;} 29 | ul.log li.dark {background-color: #E3E3E3;} 30 | 31 | /* ruler */ 32 | div.ruler {height:20px; width:220px; background-color:#f0f6e4;border: 1px solid #333; margin-bottom: 5px; cursor: pointer} 33 | div.ruler div.cursor {height:20px; width:30px; background-color:#3C6E31; color:white; text-align: right; padding-right: 5px; cursor: pointer} -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/metroStyle/img/line_conn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/metroStyle/img/line_conn.png -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/metroStyle/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/metroStyle/img/loading.gif -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/metroStyle/img/metro.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/metroStyle/img/metro.gif -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/metroStyle/img/metro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/metroStyle/img/metro.png -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/diy/1_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/diy/1_close.png -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/diy/1_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/diy/1_open.png -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/diy/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/diy/2.png -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/diy/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/diy/3.png -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/diy/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/diy/4.png -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/diy/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/diy/5.png -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/diy/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/diy/6.png -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/diy/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/diy/7.png -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/diy/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/diy/8.png -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/diy/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/diy/9.png -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/line_conn.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/line_conn.gif -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/loading.gif -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/zTreeStandard.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/zTreeStandard.gif -------------------------------------------------------------------------------- /automatic/static/zTree_v3/css/zTreeStyle/img/zTreeStandard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/static/zTree_v3/css/zTreeStyle/img/zTreeStandard.png -------------------------------------------------------------------------------- /automatic/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'frame.html' %} 2 | {% block title %}Automagic{% endblock %} 3 | {% block slideshow %} 4 |
5 | 6 |
7 | {% endblock %} -------------------------------------------------------------------------------- /automatic/templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends 'frame.html' %} 2 | {% block title %}Automagic{% endblock %} 3 | {% block slideshow %} 4 |
5 | 6 |
7 | {% endblock %} -------------------------------------------------------------------------------- /automatic/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}Default Title{% endblock %} - Ray 6 | 7 | 8 | {% include 'nav.html' %} 9 | 10 | {% block content %} 11 |
这里是默认内容,所有继承自这个模板的,如果不覆盖就显示这里的默认内容。
12 | {% for key,value in info_dict.items %} 13 | {{ key }}:{{ value }}
14 | {% endfor %} 15 | 16 | (zhandian):{{ info_dict.site }}
17 | (neirong):{{ info_dict.content }}
18 | {% endblock %} 19 | add product 20 | 21 | 22 | -------------------------------------------------------------------------------- /automatic/templates/comingsoon.html: -------------------------------------------------------------------------------- 1 | {% extends 'frame.html' %} 2 | {% block title %}Automagic{% endblock %} 3 | {% block slideshow %} 4 |
5 | 6 |
7 | {% endblock %} -------------------------------------------------------------------------------- /automatic/templates/management/moduleadd.html: -------------------------------------------------------------------------------- 1 | {% extends 'nav.html' %} 2 | {% block title %}Module Add{% endblock %} 3 | 4 | {% block slideshow %} 5 |


6 |
7 | {% csrf_token %} 8 | 9 | {{ moduleform.as_table }} 10 |
11 | 12 | 13 |
14 | 15 | {% endblock %} -------------------------------------------------------------------------------- /automatic/templates/management/moduleview.html: -------------------------------------------------------------------------------- 1 | {% extends 'nav.html' %} 2 | {% block title %}Module View{% endblock %} 3 | 4 | {% block slideshow %} 5 |


6 | 7 | {% if errors %} 8 |
    9 | {% for error in errors %} 10 |
  • {{error}}
  • 11 | {% endfor %} 12 |
13 | {% endif %} 14 | 15 | 16 |

{{module.name}}

17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
ISENABLED:{{ module.isenabled }}
{{ module.createat }}:{{ module.createtime }}
{{ module.updateat }}:{{ module.updatetime }}
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | {% for module in modulelist %} 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 59 | 60 | {% endfor %} 61 | 62 |
NO.Module NameIsEnabledCreate AtCreate TimeUpdate AtUpdate Time
{{ forloop.counter }}{{ module.name }}{{ module.isenabled }}{{ module.createat }}{{ module.createtime }}{{ module.createat }}{{ module.updatetime }} 56 | Edit 57 | Delete 58 |
63 |

64 | 65 | {% endblock %} -------------------------------------------------------------------------------- /automatic/templates/management/productadd.html: -------------------------------------------------------------------------------- 1 | {% extends 'nav.html' %} 2 | {% block title %}Product Add{% endblock %} 3 | 4 | {% block slideshow %} 5 |


6 |
7 | {% csrf_token %} 8 | 9 | {{ productform.as_table }} 10 |
11 | 12 | 13 |
14 | {% endblock %} -------------------------------------------------------------------------------- /automatic/templates/management/projectadd.html: -------------------------------------------------------------------------------- 1 | {% extends 'nav.html' %} 2 | {% block title %}Project Add{% endblock %} 3 | 4 | {% block slideshow %} 5 |


6 |
7 | {% csrf_token %} 8 | 9 | {{ projectform.as_table }} 10 |
11 | 12 | 13 |
14 | 15 | {% endblock %} -------------------------------------------------------------------------------- /automatic/templates/management/projectlist.html: -------------------------------------------------------------------------------- 1 | {% extends 'nav.html' %} 2 | {% block title %}Project List{% endblock %} 3 | {% block slideshow %} 4 |


5 |
6 |
7 |
8 |
9 | 15 |
16 |
17 | 20 |
21 |
22 |
23 | 24 | 25 | 26 | 27 |
28 |
29 | {# #} 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | {% for project in projectlist %} 50 | 51 | 52 | 53 | 54 | 55 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 72 | 73 | {% endfor %} 74 | 75 |
NO.产品名称项目名称版本状态项目描述排序创建者创建时间更新者更新时间
{{ forloop.counter }}{{ project.productid }} {{ project.name }}{{ project.version }} 56 | {% if project.isenabled == True %} 57 | 启 用 58 | {% else %} 59 | 停 用 60 | {% endif %} 61 | {{ project.descr }}{{ project.sortby }}{{ project.createat }}{{ project.createtime|date:'Y-m-d H:i:s' }}{{ project.createat }}{{ project.updatetime|date:'Y-m-d H:i:s' }} 69 | 编辑 70 | 删除 71 |
76 |
77 | {% if is_paginated %} 78 |
79 | 97 |
98 | {% endif %} 99 |
100 | {% endblock %} 101 | -------------------------------------------------------------------------------- /automatic/templates/management/syslog.html: -------------------------------------------------------------------------------- 1 | {% extends 'frame.html' %} 2 | {% block title %}Automagic{% endblock %} 3 | {% block slideshow %} 4 | {% load static %} 5 |
6 |
7 |
8 | {# 000000000000000#} 9 | Syslog 10 |
11 |
12 | 13 | 14 |
15 |
16 |
17 |

流量回放模式(Linux)

18 | 通过回放Syslog流进行发送syslog(UDP支持源地址伪装)|sendingdata.py 19 |
usage:
20 | sendingdata.py [-h] [-sip SRCIP]
21 | [-dip DSTIP] [-p PROTOCOL]
22 | [-dport DPORT] [-c COUNT]
23 | [-f FILE] [-t THREAD]
24 | [-s SPEED]
25 | optional arguments:
26 | -h, --help    show this help message
27 | -sip SRCIP    src ip addr
28 | -dip DSTIP    dst ip addr
29 | -p PROTOCOL   protocol udp or tcp
30 | -dport DPORT  send port
31 | -c COUNT      packets count
32 | -f FILE       packets file
33 | -t THREAD     thread number
34 | -s SPEED      send speed (s)
35 |                 
36 |
37 |
38 |

UDP Socket

39 | 通过UDP Socket发送syslog事件|udpsendingsyslog.py 40 |
Usge:
41 | python sendlog -f [filepath] -c [cycles]
42 | -t [sleep time]
43 | -d [Receiver Host] -p [Receiver port]
44 | Details:
45 | -C	[开启无限循环]
46 | -f	[指派日志文件路径]
47 | -c	[指定循环次数]
48 | -t	[指定日志发送间隔时间 单位:秒]
49 | -d	[指定日志接收IP地址]
50 | -p	[指定日志接收端口(整数)]
51 | -v	[查看工具版本]
52 | -h	[查看帮助]
53 |                 
54 |
55 |
56 |

TCP Socket

57 | 通过TCP Socket发送syslog事件|tcpsendingsyslog.py 58 |
usage:
59 | tcpsendingsyslog.py [-h] [-host HOST]
60 | [-port DPORT] [-c COUNT]
61 | 
62 | optional arguments:
63 |   -h, --help   show this help message and exit
64 |   -host HOST   receive host ip
65 |   -port DPORT  send port
66 |   -c COUNT     count number
67 |
68 |
69 |
70 | 71 | 72 |
73 | {% endblock %} -------------------------------------------------------------------------------- /automatic/templates/testcase/caselist.html: -------------------------------------------------------------------------------- 1 | {% extends 'nav.html' %} 2 | {% block title %}Case List{% endblock %} 3 | {% block slideshow %} 4 |


5 |
6 | {% if caselist %} 7 |
8 |
9 |
10 | 16 |
17 |
18 | 24 |
25 |
26 | 31 |
32 |
33 | 34 |
35 | 36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | {% for case in caselist %} 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 67 | 68 | {% endfor %} 69 | 70 |
Case IDModule NameCase DescrCase StatusCreate AtCreate TimeUpdateAtUpdate Time
{{ case.id }}{{ case.moduleid.name }} {{ case.casedesc }}{{ case.isenabled }}{{ case.createat }}{{ case.createtime|date:'Y-m-d H:i:s' }}{{ case.createat }}{{ case.updatetime|date:'Y-m-d H:i:s' }} 64 | Edit 65 | Delete 66 |
71 |
72 | {% if is_paginated %} 73 |
74 |
    75 |
  • «
  • 76 | {% if page_obj.has_previous %} 77 |
  • 上一页
  • 78 | {% else %} 79 | 80 | {% endif %} 81 | {% for i in page_obj.paginator.page_range %} 82 |
  • {{ i }}
  • 83 | {% endfor %} 84 | {% if page_obj.has_next %} 85 |
  • 下一页
  • 86 | {% else %} 87 | 88 | {% endif %} 89 |
  • »
  • 90 |
  • This is {{ page_obj.number }}
  • 91 |
92 |
93 | {% endif %} 94 | {% else %} 95 |

No msg!!!

96 | {% endif %} 97 |
98 | {% endblock %} 99 | -------------------------------------------------------------------------------- /automatic/testcase/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/testcase/__init__.py -------------------------------------------------------------------------------- /automatic/testcase/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | 5 | from automatic.testcase import models 6 | 7 | 8 | class CaseAdmin(admin.ModelAdmin): 9 | list_display = (id, 'casedesc', 'isenabled','issmoke', 'projectid', 'createat', 'createtime', 'updateat', 'updatetime') 10 | search_fields = ('casedesc', 'projectid') 11 | 12 | 13 | class StepAdmin(admin.ModelAdmin): 14 | list_display = (id, 'caseid', 'stepid', 'descr', 'keywordid', 'elementid', 'inputtext') 15 | search_fields = ('descr',) 16 | 17 | 18 | admin.site.register(models.Case, CaseAdmin) 19 | admin.site.register(models.Step, StepAdmin) -------------------------------------------------------------------------------- /automatic/testcase/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class TestcaseConfig(AppConfig): 5 | name = 'automatic.testcase' 6 | -------------------------------------------------------------------------------- /automatic/testcase/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2020-01-06 6 | """ 7 | from django import forms 8 | from automatic.testcase.models import * 9 | 10 | 11 | class FormCase(forms.ModelForm): 12 | class Meta: 13 | model = Case 14 | fields = ('casedesc','testrailcaseid','projectid','moduleid','isenabled','dependent') 15 | 16 | 17 | class FormStep(forms.ModelForm): 18 | class Meta: 19 | model = Step 20 | fields = ('caseid', 'descr', 'keywordid','elementid','inputtext') -------------------------------------------------------------------------------- /automatic/testcase/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.2 on 2020-01-15 10:02 2 | 3 | import django.core.validators 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import re 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('element', '0001_initial'), 15 | ('management', '0001_initial'), 16 | ('keywords', '0001_initial'), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name='Case', 22 | fields=[ 23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 | ('testrailcaseid', models.CharField(blank=True, max_length=12, null=True)), 25 | ('casedesc', models.CharField(max_length=255, verbose_name='Title')), 26 | ('isenabled', models.BooleanField(default=True)), 27 | ('issmoke', models.BooleanField(default=False)), 28 | ('dependent', models.CharField(blank=True, max_length=8, null=True)), 29 | ('debuginfo', models.CharField(blank=True, max_length=9999, null=True)), 30 | ('createtime', models.DateTimeField(auto_now_add=True)), 31 | ('createat', models.CharField(blank=True, max_length=32, null=True)), 32 | ('updatetime', models.DateTimeField(auto_now_add=True)), 33 | ('updateat', models.CharField(blank=True, max_length=32, null=True)), 34 | ('moduleid', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='management.Module')), 35 | ('projectid', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='management.Project')), 36 | ], 37 | ), 38 | migrations.CreateModel( 39 | name='Caseset', 40 | fields=[ 41 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 42 | ('descr', models.CharField(max_length=200)), 43 | ('caseid', models.CharField(max_length=255, validators=[django.core.validators.RegexValidator(re.compile('^\\d+(?:\\,\\d+)*\\Z'), code='invalid', message='Enter only digits separated by commas.')])), 44 | ('isenabled', models.BooleanField(default=True)), 45 | ('createtime', models.DateTimeField(auto_now_add=True)), 46 | ('createat', models.CharField(blank=True, max_length=32, null=True)), 47 | ('updatetime', models.DateTimeField(auto_now=True)), 48 | ('updateat', models.CharField(blank=True, max_length=32, null=True)), 49 | ], 50 | ), 51 | migrations.CreateModel( 52 | name='Step', 53 | fields=[ 54 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 55 | ('stepid', models.IntegerField(blank=True, null=True)), 56 | ('descr', models.CharField(blank=True, max_length=200, null=True)), 57 | ('inputtext', models.CharField(blank=True, max_length=200, null=True)), 58 | ('createtime', models.DateTimeField(auto_now_add=True)), 59 | ('createat', models.CharField(blank=True, max_length=32, null=True)), 60 | ('updatetime', models.DateTimeField(auto_now=True)), 61 | ('updateat', models.CharField(blank=True, max_length=32, null=True)), 62 | ('caseid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='testcase.Case')), 63 | ('elementid', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='element.Element')), 64 | ('keywordid', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='keywords.Keyword')), 65 | ], 66 | ), 67 | ] 68 | -------------------------------------------------------------------------------- /automatic/testcase/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/testcase/migrations/__init__.py -------------------------------------------------------------------------------- /automatic/testcase/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2020-01-06 6 | """ 7 | from django.db import models 8 | from automatic.management.models import Project, Module 9 | from automatic.keywords.models import Keyword 10 | from automatic.element.models import Element 11 | # from django.core.validators import validate_comma_separated_integer_list 12 | # Create your models here. 13 | 14 | 15 | class Case(models.Model): 16 | projectid = models.ForeignKey(Project, on_delete=models.DO_NOTHING) 17 | moduleid = models.ForeignKey(Module, on_delete=models.DO_NOTHING) 18 | testrailcaseid = models.CharField(max_length=12,null=True, blank=True, editable=True) 19 | casedesc = models.CharField(max_length=255, verbose_name="Title") 20 | isenabled = models.BooleanField(default=True) 21 | issmoke = models.BooleanField(default=False) 22 | dependent = models.CharField(max_length=8,null=True, blank=True, editable=True) 23 | debuginfo = models.CharField(max_length=9999, null=True, blank=True, editable=True) 24 | createtime = models.DateTimeField(auto_now_add=True) 25 | createat = models.CharField(max_length=32,null=True, blank=True, editable=True) 26 | updatetime = models.DateTimeField(auto_now_add=True) 27 | updateat = models.CharField(max_length=32,null=True, blank=True, editable=True) 28 | 29 | def __unicode__(self): 30 | return self.casedesc 31 | 32 | # class Caseset(models.Model): 33 | # descr = models.CharField(max_length=200) 34 | # caseid = models.CharField(validators=[validate_comma_separated_integer_list],max_length=255) 35 | # isenabled = models.BooleanField(default=True) 36 | # createtime = models.DateTimeField(auto_now_add=True) 37 | # createat = models.CharField(max_length=32, null=True, blank=True, editable=True) 38 | # updatetime = models.DateTimeField(auto_now=True) 39 | # updateat = models.CharField(max_length=32, null=True, blank=True, editable=True) 40 | # 41 | # def __unicode__(self): 42 | # return self.descr 43 | 44 | 45 | class Step(models.Model): 46 | caseid = models.ForeignKey(Case, on_delete=models.CASCADE) 47 | stepid = models.IntegerField(null=True, blank=True, editable=True) 48 | descr = models.CharField(max_length=200, null=True, blank=True, editable=True) 49 | keywordid = models.ForeignKey(Keyword, on_delete=models.DO_NOTHING) 50 | elementid = models.ForeignKey(Element,null=True, blank=True, on_delete=models.DO_NOTHING) 51 | inputtext = models.CharField(max_length=200, null=True, blank=True, editable=True) 52 | createtime = models.DateTimeField(auto_now_add=True) 53 | createat = models.CharField(max_length=32, null=True, blank=True, editable=True) 54 | updatetime = models.DateTimeField(auto_now=True) 55 | updateat = models.CharField(max_length=32, null=True, blank=True, editable=True) 56 | 57 | def __unicode__(self): 58 | return self.descr 59 | -------------------------------------------------------------------------------- /automatic/testcase/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /automatic/testcase/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.urls import reverse_lazy 3 | from django.contrib.auth import views as contrib_auth_views 4 | from django.contrib.auth.decorators import login_required 5 | from automatic.testcase import views 6 | 7 | urlpatterns = [ 8 | url(r'^add/$', views.add_case, name='caseadd'), 9 | url(r'^list/$|^$|^index/$', login_required(views.CaseListIndex.as_view()), name='caselist'), 10 | url(r'^view/(?P\d+)/$', views.view_case, name='caseview'), 11 | url(r'^update/(?P\d+)/$', views.update_case, name='caseupdate'), 12 | url(r'^copy/(?P\d+)/$', views.copy_case, name='copycase'), 13 | url(r'^del/(?P\d+)/$', views.del_case, name='casedel'), 14 | url(r'^run/$', views.run_case, name='runcase'), 15 | url(r'^caselist/$', views.get_caselist, name='getcase'), 16 | url(r'^step/del/(?P\d+)/$', views.del_step, name='stepdel'), 17 | ] -------------------------------------------------------------------------------- /automatic/testtask/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/testtask/__init__.py -------------------------------------------------------------------------------- /automatic/testtask/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | 5 | from automatic.testtask import models 6 | 7 | 8 | class KeywordAdmin(admin.ModelAdmin): 9 | list_display = (id, 'taskname', 'tasktype','status', 'issmoke', 'createat', 'createtime', 'updateat', 'updatetime') 10 | search_fields = ('taskname', 'tasktype') 11 | 12 | 13 | class CodelistAdmin(admin.ModelAdmin): 14 | list_display = (id, 'codename', 'codedescr', 'codevalue', 'createat', 'createtime', 'updateat', 'updatetime') 15 | search_fields = ('codename',) 16 | 17 | 18 | class TaskhistoryAdmin(admin.ModelAdmin): 19 | list_display = (id, 'taskid', 'tasktype', 'taskname', 'case_tag_all', 'case_tag_pass', 'case_tag_fail', 'case_tag_error') 20 | search_fields = ('taskid', 'taskname') 21 | 22 | 23 | admin.site.register(models.Task, KeywordAdmin) 24 | admin.site.register(models.Codelist, CodelistAdmin) 25 | admin.site.register(models.Taskhistory, TaskhistoryAdmin) -------------------------------------------------------------------------------- /automatic/testtask/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class TesttaskConfig(AppConfig): 5 | name = 'automatic.testtask' 6 | -------------------------------------------------------------------------------- /automatic/testtask/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.2 on 2020-01-15 10:02 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ('management', '0001_initial'), 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Task', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('taskname', models.CharField(max_length=255, verbose_name='任务描述')), 23 | ('tasktype', models.CharField(blank=True, choices=[('1', '执行用例'), ('2', '用例同步'), ('3', '关联Jenkins')], max_length=32)), 24 | ('status', models.SmallIntegerField(default=0, verbose_name='任务状态')), 25 | ('issmoke', models.BooleanField(default=False)), 26 | ('testrailsuites', models.CharField(blank=True, max_length=8, null=True, verbose_name='TestRail测试集ID')), 27 | ('testrailrunid', models.CharField(blank=True, max_length=8, null=True, verbose_name='TestRail执行ID')), 28 | ('testsectionid', models.CharField(blank=True, max_length=8, null=True, verbose_name='TestRail用例节点ID')), 29 | ('jenkins_server_url', models.CharField(blank=True, max_length=100, null=True, verbose_name='JenkinsServer')), 30 | ('user_id', models.CharField(blank=True, max_length=32, null=True, verbose_name='JenkinsUserid')), 31 | ('api_token', models.CharField(blank=True, max_length=32, null=True, verbose_name='JenkinsApitoken')), 32 | ('build_name', models.CharField(blank=True, max_length=32, null=True, verbose_name='JenkinsBuildName')), 33 | ('caselist', models.CharField(max_length=10240, verbose_name='用例列表')), 34 | ('createtime', models.DateTimeField(auto_now_add=True)), 35 | ('createat', models.CharField(blank=True, max_length=32, null=True)), 36 | ('updatetime', models.DateTimeField(auto_now=True)), 37 | ('updateat', models.CharField(blank=True, max_length=32, null=True)), 38 | ('projectid', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='management.Project')), 39 | ], 40 | ), 41 | migrations.CreateModel( 42 | name='Taskhistory', 43 | fields=[ 44 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 45 | ('tasktype', models.CharField(blank=True, max_length=32)), 46 | ('taskname', models.CharField(max_length=255, verbose_name='任务描述')), 47 | ('case_tag_all', models.CharField(blank=True, max_length=8, null=True)), 48 | ('case_tag_pass', models.CharField(blank=True, max_length=8, null=True)), 49 | ('case_tag_fail', models.CharField(blank=True, max_length=8, null=True)), 50 | ('case_tag_error', models.CharField(blank=True, max_length=8, null=True)), 51 | ('starttime', models.DateTimeField(blank=True)), 52 | ('exectime', models.CharField(blank=True, max_length=32, null=True)), 53 | ('reporturl', models.CharField(max_length=255, null=True, verbose_name='report Url')), 54 | ('build_name', models.CharField(blank=True, max_length=32, null=True)), 55 | ('build_number', models.CharField(blank=True, max_length=8, null=True)), 56 | ('taskid', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='testtask.Task')), 57 | ('userid', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)), 58 | ], 59 | ), 60 | migrations.CreateModel( 61 | name='Codelist', 62 | fields=[ 63 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 64 | ('codename', models.CharField(max_length=32)), 65 | ('codedescr', models.CharField(max_length=255)), 66 | ('codevalue', models.CharField(max_length=255)), 67 | ('createtime', models.DateTimeField(auto_now_add=True)), 68 | ('createat', models.CharField(blank=True, max_length=32, null=True)), 69 | ('updatetime', models.DateTimeField(auto_now=True)), 70 | ('updateat', models.CharField(blank=True, max_length=32, null=True)), 71 | ('taskid', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='testtask.Task')), 72 | ], 73 | ), 74 | ] 75 | -------------------------------------------------------------------------------- /automatic/testtask/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/testtask/migrations/__init__.py -------------------------------------------------------------------------------- /automatic/testtask/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2020-01-06 6 | """ 7 | from django.db import models 8 | from automatic.management.models import Project, User 9 | # Create your models here. 10 | 11 | 12 | class Task(models.Model): 13 | Task_Choice = ( 14 | ('1', '执行用例'), 15 | ('2', '用例同步'), 16 | ('3', '关联Jenkins'), 17 | ) 18 | taskname = models.CharField(max_length=255,verbose_name="任务描述") 19 | tasktype = models.CharField(max_length=32, choices=Task_Choice, blank=True, editable=True) 20 | status = models.SmallIntegerField(default=0, verbose_name='任务状态') 21 | issmoke = models.BooleanField(default=False) 22 | testrailsuites = models.CharField(max_length=8, verbose_name='TestRail测试集ID', null=True, blank=True, editable=True) 23 | testrailrunid = models.CharField(max_length=8,verbose_name='TestRail执行ID',null=True,blank=True, editable=True) 24 | testsectionid = models.CharField(max_length=8, verbose_name='TestRail用例节点ID',null=True,blank=True, editable=True) 25 | projectid = models.ForeignKey(Project, on_delete=models.DO_NOTHING) 26 | jenkins_server_url = models.CharField(max_length=100, verbose_name='JenkinsServer', null=True, blank=True, editable=True) 27 | user_id = models.CharField(max_length=32, verbose_name='JenkinsUserid', null=True, blank=True, editable=True) 28 | api_token = models.CharField(max_length=32, verbose_name='JenkinsApitoken', null=True, blank=True, editable=True) 29 | build_name = models.CharField(max_length=32, verbose_name='JenkinsBuildName', null=True, blank=True, editable=True) 30 | caselist = models.CharField(max_length=10240, verbose_name='用例列表') 31 | createtime = models.DateTimeField(auto_now_add=True) 32 | createat = models.CharField(max_length=32, null=True, blank=True, editable=True) 33 | updatetime = models.DateTimeField(auto_now=True) 34 | updateat = models.CharField(max_length=32, null=True, blank=True, editable=True) 35 | 36 | def __unicode__(self): 37 | return self.taskname 38 | 39 | class Codelist(models.Model): 40 | taskid = models.ForeignKey(Task, on_delete=models.DO_NOTHING) 41 | codename = models.CharField(max_length=32) 42 | codedescr = models.CharField(max_length=255) 43 | codevalue = models.CharField(max_length=255) 44 | createtime = models.DateTimeField(auto_now_add=True) 45 | createat = models.CharField(max_length=32, null=True, blank=True, editable=True) 46 | updatetime = models.DateTimeField(auto_now=True) 47 | updateat = models.CharField(max_length=32, null=True, blank=True, editable=True) 48 | 49 | def __unicode__(self): 50 | return self.codename 51 | 52 | class Taskhistory(models.Model): 53 | taskid = models.ForeignKey(Task, on_delete=models.DO_NOTHING) 54 | userid = models.ForeignKey(User, on_delete=models.DO_NOTHING) 55 | tasktype = models.CharField(max_length=32, blank=True, editable=True) 56 | taskname = models.CharField(max_length=255, verbose_name="任务描述") 57 | case_tag_all = models.CharField(max_length=8, null=True, blank=True, editable=True) 58 | case_tag_pass = models.CharField(max_length=8, null=True, blank=True, editable=True) 59 | case_tag_fail = models.CharField(max_length=8, null=True, blank=True, editable=True) 60 | case_tag_error = models.CharField(max_length=8, null=True, blank=True, editable=True) 61 | starttime = models.DateTimeField(blank=True, editable=True) 62 | exectime = models.CharField(max_length=32, null=True, blank=True, editable=True) 63 | reporturl = models.CharField(max_length=255, verbose_name="report Url", null=True) 64 | build_name = models.CharField(max_length=32, null=True, blank=True, editable=True) 65 | build_number = models.CharField(max_length=8, null=True, blank=True, editable=True) 66 | 67 | def __unicode__(self): 68 | return self.pk -------------------------------------------------------------------------------- /automatic/testtask/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /automatic/testtask/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.contrib.auth.decorators import login_required 3 | from automatic.testtask import views 4 | 5 | urlpatterns = [ 6 | url(r'add/$', views.add_task, name='taskadd'), 7 | url(r'list/$', login_required(views.TaskListIndex.as_view()), name='tasklist'), 8 | url(r'settreetask/$', views.set_tree_task, name='settreetask'), 9 | url(r'update/(?P\d+)/$', views.update_task, name='taskupdate'), 10 | url(r'del/(?P\d+)/$', views.del_task, name='taskdel'), 11 | url(r'run/$', views.run_task, name='runtask'), 12 | url(r'taskhistory/$', views.get_task_history, name='gettaskhistroy'), 13 | ] -------------------------------------------------------------------------------- /automatic/urls.py: -------------------------------------------------------------------------------- 1 | """automatic URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | # from django.urls import path 18 | from django.conf.urls import include, url 19 | from auto_auth import urls as auth_urls 20 | from automatic.testcase import urls as case_urls 21 | from automatic.management import urls as mgt_urls 22 | from automatic.element import urls as ele_urls 23 | from automatic.keywords import urls as keywords_urls 24 | from automatic.testtask import urls as task_urls 25 | from automatic.webinterface import urls as interf_urls 26 | from auto_auth.views import index 27 | 28 | urlpatterns = [ 29 | url('^$', index), 30 | url('^index/', index), 31 | url('admin/', admin.site.urls), 32 | url(r'^account/', include(auth_urls)), 33 | 34 | url(r'^setting/', include(mgt_urls)), 35 | 36 | url(r'^func/case/', include(case_urls)), 37 | 38 | url(r'^func/element/', include(ele_urls)), 39 | 40 | url(r'^func/keyword/', include(keywords_urls)), 41 | 42 | url(r'^func/task/', include(task_urls)), 43 | 44 | url(r'^interface/', include(interf_urls)), 45 | 46 | 47 | 48 | # url(r'^interf/web/home/$', login_required(interface.WebinterfaceListIndex.as_view()), name='webinterface'), 49 | # url(r'^interf/get/response/$', interface.get_response, name='getresponse'), 50 | # url(r'^tools/syslog/home/$', automate.page_syslog, name='toolsyslog'), 51 | # url(r'^tools/snmp/home/$', automate.comingsoon, name='toolsnmp'), 52 | # url(r'^accounts/', include(oidc_urls)), 53 | ] 54 | -------------------------------------------------------------------------------- /automatic/webinterface/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/webinterface/__init__.py -------------------------------------------------------------------------------- /automatic/webinterface/admin.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib import admin 3 | # Register your models here. 4 | from automatic.webinterface import models 5 | 6 | class WebinterfaceAdmin(admin.ModelAdmin): 7 | list_display = (id , 'projectid','moduleid', 'descr', 'isenabled','url', 'method') 8 | search_fields = ('descr',) 9 | 10 | def save_model(self, request, obj, form, change): 11 | if change: # change 12 | obj_original = self.model.objects.get(pk=obj.pk) 13 | else: # add 14 | obj_original = None 15 | 16 | obj.user = request.user 17 | obj.save() 18 | 19 | def get_search_results(self, request, queryset, search_term): 20 | queryset, use_distinct = super(WebinterfaceAdmin, self).get_search_results(request, queryset, search_term) 21 | try: 22 | search_term_as_int = int(search_term) 23 | queryset |= self.model.objects.filter(age=search_term_as_int) 24 | except: 25 | pass 26 | return queryset, use_distinct 27 | 28 | class WebresponseAdmin(admin.ModelAdmin): 29 | list_display = (id , 'webinterfaceid','params', 'exectime', 'expected','actual', 'status_code', 'Response_content') 30 | search_fields = ('webinterfaceid',) 31 | 32 | admin.site.register(models.Webinterface,WebinterfaceAdmin) 33 | admin.site.register(models.Webresponse, WebresponseAdmin) -------------------------------------------------------------------------------- /automatic/webinterface/apps.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.apps import AppConfig 4 | 5 | 6 | class WebinterfaceConfig(AppConfig): 7 | name = 'automatic.webinterface' 8 | -------------------------------------------------------------------------------- /automatic/webinterface/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.2 on 2020-01-15 10:02 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('management', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Webinterface', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('testrailcaseid', models.CharField(blank=True, max_length=12, null=True)), 21 | ('descr', models.CharField(max_length=255, verbose_name='Title')), 22 | ('isenabled', models.BooleanField(default=True)), 23 | ('url', models.CharField(blank=True, max_length=1024)), 24 | ('method', models.CharField(blank=True, choices=[('POST', 'post'), ('GET', 'get'), ('OPTIONS', 'options'), ('HEAD', 'head'), ('PUT', 'put'), ('PATCH', 'patch'), ('DELETE', 'delete')], max_length=12)), 25 | ('headers', models.CharField(blank=True, default=None, max_length=1024, null=True)), 26 | ('cookies', models.CharField(blank=True, default=None, max_length=1024, null=True)), 27 | ('data', models.TextField(blank=True, default=None, null=True)), 28 | ('files', models.CharField(blank=True, default=None, max_length=1024, null=True)), 29 | ('auth', models.CharField(blank=True, default=None, max_length=1024, null=True)), 30 | ('timeout', models.CharField(blank=True, default=None, max_length=8, null=True)), 31 | ('allow_redirects', models.CharField(blank=True, default=None, max_length=1024, null=True)), 32 | ('proxies', models.CharField(blank=True, default=None, max_length=1024, null=True)), 33 | ('verify', models.CharField(blank=True, default=None, max_length=1024, null=True)), 34 | ('stream', models.CharField(blank=True, default=None, max_length=1024, null=True)), 35 | ('cert', models.CharField(blank=True, default=None, max_length=1024, null=True)), 36 | ('debuginfo', models.TextField(blank=True, null=True)), 37 | ('createtime', models.DateTimeField(auto_now_add=True)), 38 | ('createat', models.CharField(blank=True, max_length=32, null=True)), 39 | ('updatetime', models.DateTimeField(auto_now_add=True)), 40 | ('updateat', models.CharField(blank=True, max_length=32, null=True)), 41 | ('moduleid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='management.Module')), 42 | ('projectid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='management.Project')), 43 | ], 44 | ), 45 | migrations.CreateModel( 46 | name='Webresponse', 47 | fields=[ 48 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 49 | ('params', models.TextField(blank=True, null=True)), 50 | ('exectime', models.CharField(blank=True, max_length=16)), 51 | ('expected', models.CharField(blank=True, max_length=255, null=True)), 52 | ('actual', models.CharField(blank=True, max_length=255, null=True)), 53 | ('status_code', models.CharField(blank=True, max_length=4)), 54 | ('Response_content', models.TextField(blank=True)), 55 | ('createtime', models.DateTimeField(auto_now_add=True)), 56 | ('createat', models.CharField(blank=True, max_length=32, null=True)), 57 | ('updatetime', models.DateTimeField(auto_now_add=True)), 58 | ('updateat', models.CharField(blank=True, max_length=32, null=True)), 59 | ('webinterfaceid', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='webinterface.Webinterface')), 60 | ], 61 | ), 62 | ] 63 | -------------------------------------------------------------------------------- /automatic/webinterface/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/automatic/webinterface/migrations/__init__.py -------------------------------------------------------------------------------- /automatic/webinterface/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2020-01-08 6 | """ 7 | 8 | from __future__ import unicode_literals 9 | from django.db import models 10 | from automatic.management.models import Project, Module 11 | # Create your models here. 12 | 13 | class Webinterface(models.Model): 14 | Method_Choice = ( 15 | ('POST','post'), 16 | ('GET','get'), 17 | ('OPTIONS','options'), 18 | ('HEAD','head'), 19 | ('PUT','put'), 20 | ('PATCH','patch'), 21 | ('DELETE','delete'), 22 | ) 23 | projectid = models.ForeignKey(Project, on_delete=models.CASCADE) 24 | moduleid = models.ForeignKey(Module, on_delete=models.CASCADE) 25 | testrailcaseid = models.CharField(max_length=12,null=True, blank=True, editable=True) 26 | descr = models.CharField(max_length=255, verbose_name="Title") 27 | isenabled = models.BooleanField(default=True) 28 | url = models.CharField(max_length=1024, blank=True, editable=True) 29 | method = models.CharField(max_length=12, choices=Method_Choice, blank=True, editable=True) 30 | headers = models.CharField(max_length=1024, default=None,null=True, blank=True, editable=True) 31 | cookies = models.CharField(max_length=1024, default=None,null=True, blank=True, editable=True) 32 | data = models.TextField(null=True, blank=True, default=None,editable=True) 33 | files = models.CharField(max_length=1024, default=None,null=True, blank=True, editable=True) 34 | auth = models.CharField(max_length=1024, default=None,null=True, blank=True, editable=True) 35 | timeout = models.CharField(max_length=8, default=None,null=True, blank=True, editable=True) 36 | allow_redirects = models.CharField(max_length=1024, default=None,null=True, blank=True, editable=True) 37 | proxies = models.CharField(max_length=1024, default=None,null=True, blank=True, editable=True) 38 | verify = models.CharField(max_length=1024, default=None,null=True, blank=True, editable=True) 39 | stream = models.CharField(max_length=1024, default=None,null=True, blank=True, editable=True) 40 | cert = models.CharField(max_length=1024, default=None,null=True, blank=True, editable=True) 41 | debuginfo = models.TextField(null=True, blank=True, editable=True) 42 | createtime = models.DateTimeField(auto_now_add=True) 43 | createat = models.CharField(max_length=32,null=True, blank=True, editable=True) 44 | updatetime = models.DateTimeField(auto_now_add=True) 45 | updateat = models.CharField(max_length=32,null=True, blank=True, editable=True) 46 | 47 | def __unicode__(self): 48 | return self.descr 49 | 50 | 51 | class Webresponse(models.Model): 52 | webinterfaceid = models.ForeignKey(Webinterface, on_delete=models.CASCADE) 53 | params = models.TextField(null=True, blank=True, editable=True) 54 | exectime = models.CharField(max_length=16, null=False, blank=True) 55 | expected = models.CharField(max_length=255, null=True, blank=True, editable=True) 56 | actual = models.CharField(max_length=255, null=True, blank=True, editable=True) 57 | status_code = models.CharField(max_length=4,null=False, blank=True) 58 | Response_content = models.TextField(blank=True) 59 | createtime = models.DateTimeField(auto_now_add=True) 60 | createat = models.CharField(max_length=32,null=True, blank=True, editable=True) 61 | updatetime = models.DateTimeField(auto_now_add=True) 62 | updateat = models.CharField(max_length=32,null=True, blank=True, editable=True) 63 | 64 | def __unicode__(self): 65 | return str(self.id) -------------------------------------------------------------------------------- /automatic/webinterface/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /automatic/webinterface/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.contrib.auth.decorators import login_required 3 | from automatic.webinterface import views 4 | 5 | urlpatterns = [ 6 | url(r'list/$', login_required(views.WebinterfaceListIndex.as_view()), name='webinterface'), 7 | url(r'response/$', views.get_response, name='getresponse'), 8 | ] -------------------------------------------------------------------------------- /automatic/webinterface/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | from automatic.webinterface.models import * 3 | from django.views.generic import ListView 4 | from automatic.management.models import UserAndProduct, Project, Module 5 | from django.db.models import Q 6 | from django.contrib.auth.decorators import login_required 7 | import json 8 | # Create your views here. 9 | 10 | 11 | class WebinterfaceListIndex(ListView): 12 | context_object_name = 'caselist' 13 | template_name = 'webinterface/webinterface.html' 14 | paginate_by = 10 15 | # raw_sql = "select * from autoplat_case where projectid_id in (select id from autoplat_project where productid_id in (select productname_id from autoplat_userandproduct where username_id=3));" 16 | casesum = 0 17 | model = Webinterface 18 | http_method_names = [u'get'] 19 | 20 | def get_queryset(self): 21 | prodcutid = UserAndProduct.objects.filter(username = self.request.user).values('productname') 22 | caselist = Webinterface.objects.filter(projectid__in=Project.objects.filter(productid__in=prodcutid).values('id')).order_by('-pk') 23 | prodcutid = self.request.GET.get('check_productname') 24 | projectid = self.request.GET.get('projectid') 25 | moduleid = self.request.GET.get('moduleid') 26 | casestatus = self.request.GET.get('casestatus') 27 | keyword = self.request.GET.get('keyword') 28 | if prodcutid and int(prodcutid): 29 | caselist = caselist.filter(projectid__in=Project.objects.filter(productid=prodcutid).values('id')) 30 | if projectid: 31 | caselist = caselist.filter(projectid=projectid) 32 | if moduleid: 33 | caselist = caselist.filter(moduleid=moduleid) 34 | if casestatus: 35 | caselist = caselist.filter(isenabled=casestatus) 36 | if keyword: 37 | caselist = caselist.filter(Q(pk__icontains=keyword)|Q(descr=keyword)|Q(createat__icontains=keyword)) 38 | self.casesum = len(caselist) 39 | return caselist 40 | 41 | def get_context_data(self, **kwargs): 42 | projectid = self.request.GET.get('projectid') 43 | context = super(WebinterfaceListIndex,self).get_context_data(**kwargs) 44 | # namelist = Case.objects.values('casedesc').annotate() 45 | # context['casedesc'] = namelist 46 | context['projectlist'] = Project.objects.all().order_by('-sortby') 47 | context['modulelist'] = Module.objects.all().order_by('-sortby') 48 | # context['productlist'] = Product.objects.all().order_by('-sortby') 49 | context['userandproduct'] = UserAndProduct.objects.all() 50 | # context['casedata'] = Webresponse.objects.all() 51 | context['casesum'] = self.casesum 52 | return context 53 | 54 | @login_required() 55 | def get_response(request): 56 | responsedata = [] 57 | webinterfaceid = request.GET['webinterfaceid'] 58 | datalist = Webresponse.objects.filter(webinterfaceid=webinterfaceid) 59 | for i in datalist: 60 | responseinfo = {} 61 | responseinfo['id'] = i.id 62 | responseinfo['params'] = i.params 63 | responseinfo['exectime'] = i.exectime 64 | responseinfo['expected'] = i.expected 65 | responseinfo['actual'] = i.actual 66 | responseinfo['status_code'] = i.status_code 67 | responseinfo['Response_content'] = i.Response_content 68 | responsedata.append(responseinfo) 69 | return HttpResponse(json.dumps(responsedata)) -------------------------------------------------------------------------------- /automatic/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for automatic project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'automatic.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.0' 2 | services: 3 | mariadb: 4 | image: tsbc520/automagic:2.0 5 | container_name: "automagic" 6 | restart: always 7 | environment: 8 | MYSQL_USERNAME: "root" 9 | MYSQL_PASSWORD: "123456" 10 | MYSQL_HOST: "192.168.10.167" 11 | MYSQL_PORT: 3306 12 | MYSQL_DBNAME: "autodb" 13 | ports: 14 | - "8000:8000" -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo '生成Django表结构....' 5 | python3 manage.py makemigrations 6 | echo 'Django表结构更新完成.' 7 | 8 | echo '创建表结构....' 9 | python3 manage.py migrate 10 | echo '表结构创建完成.' 11 | 12 | echo '创建Django超级用户' 13 | echo "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser('admin', 'admin@automagic.cn', 'admin@123')" | python3 manage.py shell 14 | echo '创建超级用户admin,密码:admin@123 完成' 15 | -------------------------------------------------------------------------------- /insertkeyword.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `keywords_keyword` VALUES ('2', 0, 'click', '点击', now(), 'admin', now(), 'admin'); 2 | INSERT INTO `keywords_keyword` VALUES ('3', 0, 'InputText', '输入文本', now(), 'admin', now(), 'admin'); 3 | INSERT INTO `keywords_keyword` VALUES ('4', 0, 'assert', '通用断言', now(), 'admin', now(), 'admin'); 4 | INSERT INTO `keywords_keyword` VALUES ('5', 0, 'navigate', '页面跳转', now(), 'admin', now(), 'admin'); 5 | INSERT INTO `keywords_keyword` VALUES ('6', 0, 'sleep', '等待[n]秒', now(), 'admin', now(), 'admin'); 6 | INSERT INTO `keywords_keyword` VALUES ('7', 0, 'switchframe', '切换iframe', now(), 'admin', now(), 'admin'); 7 | INSERT INTO `keywords_keyword` VALUES ('8', 0, 'defaultframe', '返回默认Frame', now(), 'admin', now(), 'admin'); 8 | INSERT INTO `keywords_keyword` VALUES ('9', 0, 'assertTrue', '验证元素存在', now(), 'admin', now(), 'admin'); 9 | INSERT INTO `keywords_keyword` VALUES ('10', 0, 'closeBrowser', '关闭浏览器', now(), 'admin', now(), 'admin'); 10 | INSERT INTO `keywords_keyword` VALUES ('11', 0, 'select', '下拉选择框input:[value]', now(), 'admin', now(), 'admin'); 11 | INSERT INTO `keywords_keyword` VALUES ('12', 0, 'assertUrl', '验证当前页面Url地址[input:期望值]', now(), 'admin', now(), 'admin'); 12 | INSERT INTO `keywords_keyword` VALUES ('14', 0, 'uploadfile', '上传文件', now(), 'admin', now(), 'admin'); 13 | INSERT INTO `keywords_keyword` VALUES ('15', 0, 'ssh', 'ssh访问设备[host,port,user,pass,cmd]', now(), 'admin', now(), 'admin'); 14 | INSERT INTO `keywords_keyword` VALUES ('16', 0, 'assertFalse', '验证元素不存在', now(), 'admin', now(), 'admin'); 15 | INSERT INTO `keywords_keyword` VALUES ('17', 0, 'jscript', '执行javascript脚本', now(), 'admin', now(), 'admin'); 16 | INSERT INTO `keywords_keyword` VALUES ('18', 0, 'moveScroll', '移动滚动条到某元素位置', now(), 'admin', now(), 'admin'); 17 | INSERT INTO `keywords_keyword` VALUES ('19', 0, 'checkclick', '循环勾选一组复选框 [location:父级元素]', now(), 'admin', now(), 'admin'); 18 | INSERT INTO `keywords_keyword` VALUES ('20', 0, 'selectText', '下拉选择框input:[Text]', now(), 'admin', now(), 'admin'); 19 | INSERT INTO `keywords_keyword` VALUES ('21', 0, 'clicks', '点击一组元素中第n个元素input:[n]', now(), 'admin', now(), 'admin'); 20 | INSERT INTO `keywords_keyword` VALUES ('22', 0, 'timestamp', '文本框输入当前时间戳', now(), 'admin', now(), 'admin'); 21 | INSERT INTO `keywords_keyword` VALUES ('23', 0, 'submit', '表单提交', now(), 'admin', now(), 'admin'); 22 | INSERT INTO `keywords_keyword` VALUES ('24', 0, 'isEnabled', '验证元素是否置灰', now(), 'admin', now(), 'admin'); 23 | INSERT INTO `keywords_keyword` VALUES ('25', 0, 'Notassert', '通用断言【反向】', now(), 'admin', now(), 'admin'); 24 | INSERT INTO `keywords_keyword` VALUES ('26', 0, 'untilshow', '通过监听元素可用,验证页面加载状态', now(), 'admin', now(), 'admin'); 25 | INSERT INTO `keywords_keyword` VALUES ('27', 0, 'exists_file', '验证data目录中某个文件是否存在', now(), 'admin', now(), 'admin'); 26 | INSERT INTO `keywords_keyword` VALUES ('28', 0, 'udpsend', 'udp协议发送syslog日志,使用参数看工具syslog', now(), 'admin', now(), 'admin'); 27 | INSERT INTO `keywords_keyword` VALUES ('29', 0, 'refresh', '页面刷新', now(), 'admin', now(), 'admin'); -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'automatic.settings.common') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /requirements/base.txt: -------------------------------------------------------------------------------- 1 | django==3.2.18 2 | pyMySql==1.0.2 3 | testrail==0.3.13 4 | python-jenkins==1.7.0 5 | requests==2.25.1 6 | nameko==2.14.0 7 | -------------------------------------------------------------------------------- /requirements/seleniumreq.txt: -------------------------------------------------------------------------------- 1 | selenium==3.141.0 2 | paramiko==2.10.1 3 | scapy==2.4.3 -------------------------------------------------------------------------------- /seleniumkeyword/CustomKeyword.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2017-01-19 6 | """ 7 | from Base import * 8 | 9 | 10 | ################################################################################ 11 | # 关键字函数必须用Aciton.add_action(keyword)装饰起进行装饰 12 | # 函数的形参必须为4个:action_object(Aciton对象), step_desc(步骤描述信息,可用于记录log), 13 | # value(需要输入的值,多个输入值用逗号隔开), loc(元素的定位信息) 14 | # 函数正常退出时不能有返回值(使用默认的返回值None),出错时返回字符串(记录出错信息) 15 | ################################################################################ 16 | 17 | 18 | 19 | ############################################################################################## 20 | # # 21 | # 自定义关键字 START # 22 | # # 23 | ############################################################################################## 24 | 25 | @Action.add_action('keau1000_login') 26 | def action_login(action_object, step_desc, value, loc): 27 | """ 28 | 构造登录使用的公用方法 29 | """ 30 | print(value) 31 | username, password = value.split(',') 32 | action_object.find_element(('id', 'login_txtUserName')).clear() 33 | action_object.find_element(('id', 'login_txtUserName')).send_keys(username) 34 | action_object.find_element(('id', 'login_txtUserPassword')).clear() 35 | action_object.find_element(('id', 'login_txtUserPassword')).send_keys(password) 36 | # action_object.find_element(('id', 'login_text_verifycode')).clear() 37 | # action_object.find_element(('id', 'login_text_verifycode')).send_keys(VerifyNo) 38 | action_object.find_element(('id', 'login_btnLogin')).click() 39 | time.sleep(0.5) 40 | try: 41 | action_object.find_element(('css', 'button.btn-guidestyle.btn-g-over')).click() 42 | except: 43 | pass 44 | 45 | 46 | @Action.add_action('keau1000_reset') 47 | def action_reset(action_object, step_desc, value, loc): 48 | """ 49 | reset platform 50 | """ 51 | # print loc, value 52 | # username, password = value.split(',') 53 | url = action_object.driver.current_url 54 | hostname = re.search('\d+\.\d+\.\d+\.\d+', url).group(0) 55 | action_object.find_element(('css', 'ul.nav-list li:nth-child(8) span')).click() 56 | time.sleep(0.5) 57 | action_object.find_element(('css', 'ul.nav-list li:nth-child(8) div li:nth-child(2)')).click() 58 | time.sleep(0.5) 59 | action_object.find_element(('id', 'system_system_btnResetDevice')).click() 60 | time.sleep(30) 61 | for i in range(180): 62 | print(i + 1,) 63 | action_object.driver.get("https://" + hostname) 64 | if action_object.isElementExsit(('id', 'login_txtUserName')): 65 | break 66 | time.sleep(1) 67 | action_object.find_element(('id', 'login_txtUserName')).clear() 68 | action_object.find_element(('id', 'login_txtUserName')).send_keys('admin') 69 | action_object.find_element(('id', 'login_txtUserPassword')).clear() 70 | action_object.find_element(('id', 'login_txtUserPassword')).send_keys('admin@123') 71 | # action_object.find_element(('id', 'login_text_verifycode')).clear() 72 | # action_object.find_element(('id', 'login_text_verifycode')).send_keys(VerifyNo) 73 | action_object.find_element(('id', 'login_btnLogin')).click() 74 | action_object.find_element(('css', 'div.row-setting label[for="device_centralize_rabMode1"]')).click() 75 | action_object.find_element(('id', 'device_centralize_btnMode')).click() 76 | time.sleep(30) 77 | for i in range(180): 78 | print(i + 1,) 79 | action_object.driver.get("https://" + hostname) 80 | if action_object.isElementExsit(('id', 'login_txtUserName')): 81 | break 82 | time.sleep(1) 83 | action_object.find_element(('id', 'login_txtUserName')).clear() 84 | action_object.find_element(('id', 'login_txtUserName')).send_keys('admin') 85 | action_object.find_element(('id', 'login_txtUserPassword')).clear() 86 | action_object.find_element(('id', 'login_txtUserPassword')).send_keys('admin@123') 87 | # action_object.find_element(('id', 'login_text_verifycode')).clear() 88 | # action_object.find_element(('id', 'login_text_verifycode')).send_keys(VerifyNo) 89 | action_object.find_element(('id', 'login_btnLogin')).click() 90 | time.sleep(1) 91 | # action_object.find_element(('css', 'button.btn-guidestyle.btn-g-over')).click() 92 | if action_object.isElementExsit(('id', 'spanSystemTime')): 93 | print("reset succesful.") 94 | else: 95 | return "reset failed!" 96 | 97 | # # 98 | # 自定义关键字 END # 99 | ############################################################################################## 100 | -------------------------------------------------------------------------------- /seleniumkeyword/README.MD: -------------------------------------------------------------------------------- 1 | # Seleniumkeyword介绍 # 2 | 3 | seleniumkword是客户端执行脚本,AutoMagic的执行效果展示主要靠它来体现,它的执行依附于web平台存储的用例和场景数据,seleniumkeyword可以不需要部署在服务器端,只要它能够连接到服务器端的数据库,在本地部署即可执行。(这样也方便我们做分布式执行测试用例) 4 | 5 | ## 安装运行环境 ## 6 | 7 | 在安装python、selenium、 mysqldb 的环境下运行 8 | 9 | ## 目录脚本说明 ## 10 | 11 | **TestSuite.py 测试用例执行脚本引擎是 ,它可以通过参数实现不同的执行方式** 12 | 13 | >-t [taskid] 指定执行的AutoMagic任务 14 | 15 | >-u [userid] 指定执行脚本的用户 16 | 17 | >-r [runid] 执行测试用例脚本要同步的TestRailRunid,它只有在执行 -t 参数时才会生效。 18 | 19 | >-c [caseid] 指定要执行的单个用例的编号 20 | 21 | >-p [projectid] 指定要执行的项目id,会依次执行项目中所有模块和用例状态是启用的所有用例 22 | 23 | >-b [browser] 指定执行用例过程所使用浏览器,默认使用chrome浏览器,也可在执行case过程中通过openbrowser关键字重置 24 | 25 | >-v [1] 指定是否启用录屏功能(仅支持Linux系统,需要安装recordmydesktop录屏软件) 26 | 27 | >-d [1] 钉钉消息接口,启用后可以将测试结果发送的钉钉群中 28 | 29 | Exp: 30 | 31 | ``` python TestSuite.py -t 1 -u tsbc -r 1433 -b chrome -v 1``` 32 | 33 | **CustomKeyword.py 是我们自定义关键字维护脚本。** 34 | 35 | >自定义关键字示例: 36 | ```python 37 | @Action.add_action('InputText') 38 | def action_InputText(action_object, step_desc, value, loc): 39 | """ 40 | 文本框输入内容 41 | :param action_object: 42 | :param step_desc: 43 | :param value: text 44 | :param loc: 45 | :return: 46 | """ 47 | print loc, value 48 | action_object.send_keys(loc, value) 49 | ``` 50 | 51 | **AddCase.py 同步用例到TestRail到脚本** 52 | 53 | >-t [taskid] 指定执行同步的AutoMagic任务 54 | 55 | >-u [userid] 指定执行脚本的用户 56 | 57 | >-s [sctionid] 执行同步case到TestRail的那个用例集(section_id代表所在用例集) 58 | 59 | **HTMLTestRunner.py 生成报告的脚本文件** 60 | 61 | **RestApiUtil.py、RestApiUtil.py 是发包MW模拟安全事件和发送pcap发包接口** 62 | 63 | **testrail.py TestRail的接口API** 64 | 65 | **result目录: 为报告接口目录** 66 | >在生成的每日报告目录中会生成用例执行过程日志文件 67 | 68 | **data目录: 执行上传附件关键字,附件存放目录** 69 | 70 | **sendlog目录: 通过tcp、udp 514 发送Syslog 的应用** 71 | 72 | -------------------------------------------------------------------------------- /seleniumkeyword/RestApiUtil.py: -------------------------------------------------------------------------------- 1 | # -*-coding:utf-8-*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2016-09-03 6 | """ 7 | import requests 8 | import json 9 | 10 | 11 | def call_post(url_post, para_dict): 12 | data = json.dumps(para_dict) 13 | req = requests.post(url_post, data=data) 14 | if req.status_code != 200: 15 | print("Failed: HTTP error code: %s" % req.status_code) 16 | return None 17 | else: 18 | return req.text 19 | 20 | 21 | def call_get(url_get): 22 | response = requests.get(url_get) 23 | if response.status_code != 200: 24 | print("Failed: HTTP error code: %s" % response.status_code) 25 | return None 26 | else: 27 | return response.text 28 | 29 | 30 | if "__main__" == __name__: 31 | pass 32 | -------------------------------------------------------------------------------- /seleniumkeyword/SimulatorUtil.py: -------------------------------------------------------------------------------- 1 | # -*-coding:utf-8-*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2016-09-03 6 | """ 7 | from RestApiUtil import call_get, call_post 8 | 9 | 10 | def sendFlowData(simulatorIP, testServerIP, type): 11 | url_post = "http://"+simulatorIP+"/api/v1/kafka/send/flowdata" 12 | para_dict = {"mw_ip": testServerIP} 13 | if "modbus" == type: 14 | para_dict["filename"] = "modbus.flowData2" 15 | elif "all" == type: 16 | para_dict["filename"] = "all.flowData2" 17 | 18 | call_post(url_post, para_dict) 19 | 20 | def sendTrafoFlowData(simulatorIP, trafoServerIp, type, timeoutSeconds=None): 21 | url_post = "http://" + simulatorIP + "/api/v1/trafo/run" 22 | para_dict = {} 23 | para_dict = {"trafo_ip": trafoServerIp, 24 | "trafo_usr": "test", 25 | "trafo_passwd": "admin@123", 26 | "debug_level": "1"} 27 | if timeoutSeconds is None: 28 | para_dict["timer"] = '120' 29 | else: 30 | para_dict["timer"] = timeoutSeconds 31 | confFileName = "eth1-2_58_" + type + ".json" 32 | para_dict["conf_file"] = confFileName 33 | call_post(url_post, para_dict) 34 | 35 | def sendAuditFlowData(simulatorIP, testServerIP, type): 36 | url_post = "http://"+simulatorIP+"/api/v1/kafka/send/networkanalysis" 37 | para_dict = {} 38 | para_dict["dest_ip"] = testServerIP 39 | 40 | fileNameDict = {"telnet": "telnet.telnetAccountingData", 41 | "http": "http.httpAccountingData", 42 | "ftp": "ftp.ftpAccountingData", 43 | "smtp": "smtp.smtpAccountingData", 44 | "pop3": "pop3.pop3AccountingData"} 45 | para_dict["filename"] = fileNameDict[type] 46 | call_post(url_post, para_dict) 47 | 48 | def sendEventAndIncident(simulatorIP, testServerIP, dpisn, type): 49 | url_post = '' 50 | para_dict = {"dest_ip": testServerIP, 51 | "box_id": dpisn} 52 | if 'incident' == type: 53 | url_post = "http://" + simulatorIP + "/api/v1/kafka/send/incident" 54 | para_dict["filename"] = "whitelist.alert2" 55 | else: 56 | url_post = "http://" + simulatorIP + "/api/v1/kafka/send/boxevent" 57 | if "event1" == type: 58 | ports = [True, True, False, False] 59 | else: 60 | ports = [True, True, True, True] 61 | para_dict["ports"] = ports 62 | 63 | call_post(url_post, para_dict) 64 | 65 | def generateDpiLog(simulatorIP, testServerIP, dpisn, type): 66 | url_post = '' 67 | para_dict = {"dest_ip": testServerIP, 68 | "box_id": dpisn} 69 | 70 | if 'dpilogin' == type: 71 | url_post = "http://"+simulatorIP+"/api/v1/kafka/send/dpiuserlogin" 72 | para_dict["user_ip"] = "10.0.10.199" 73 | para_dict["reason"] = "DpiUserLogin Auto Test" 74 | para_dict["user"] = "Auto Tester" 75 | para_dict["result"] = 1 76 | elif 'dpicmdlog' == type: 77 | url_post = "http://" + simulatorIP + "/api/v1/kafka/send/dpicmdlog"; 78 | para_dict["cmd"] = "DpiCmdLog Auto Test" 79 | para_dict["user_ip"] = "10.0.10.199" 80 | para_dict["user"] ="Auto Tester" 81 | para_dict["result"] = 0 82 | elif 'dpifwlog' == type: 83 | url_post = "http://" + simulatorIP + "/api/v1/kafka/send/dpifwlog" 84 | para_dict["reason"] = "DpiFwLog Auto Test" 85 | 86 | call_post(url_post, para_dict) 87 | 88 | if '__main__' == __name__: 89 | #sendFlowData('127.0.0.1:4000', '192.168.1.135', 'all') 90 | for i in xrange(1): 91 | #sendEventAndIncident('192.168.116.3:4000', '172.18.51.111', 'ZB0202C400000092', 'incident') 92 | sendTrafoFlowData('192.168.110.77:4000', '192.168.110.77', 'dnp3', timeoutSeconds=None) 93 | # sendFlowData('192.168.116.3:4000', '192.168.110.114', 'modbus') 94 | -------------------------------------------------------------------------------- /seleniumkeyword/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/seleniumkeyword/__init__.py -------------------------------------------------------------------------------- /seleniumkeyword/data/readme.md: -------------------------------------------------------------------------------- 1 | ### 浏览器文件下载默认目录 -------------------------------------------------------------------------------- /seleniumkeyword/popautomagic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2017-03-01 6 | """ 7 | 8 | import requests, json 9 | 10 | 11 | def sendAutoMagic(url, data): 12 | 13 | headers = {"Content-Type": "application/json"} 14 | requests.post(url, data=data, headers=headers) 15 | 16 | 17 | # call_post(url_post, data_text) 18 | 19 | 20 | if '__main__' == __name__: 21 | #MWQA 22 | url_post = "https://oapi.dingtalk.com/robot/send?access_token=8d36288c964c024ca2e5e53a45faddde0adcb2c6c638e9a59a16096f5d866715" 23 | #Test 24 | #url_post = "https://oapi.dingtalk.com/robot/send?access_token=e0c23e2f9242783f0ad34ccf21197a41f663c9b63ddc87fc386722654914c7ee" 25 | 26 | data_text = json.dumps({ 27 | "msgtype": "text", 28 | "text": { 29 | "content": "圈人" 30 | }, 31 | "at": { 32 | "atMobiles": [ 33 | "18217516787",#wenjuang.wang 34 | "15891390680" #yongbo.he 35 | ], 36 | "isAtAll": False 37 | } 38 | }) 39 | 40 | data_link = json.dumps({"msgtype": "link", 41 | "link": { 42 | "text": "这个即将发布的新版本,创始人陈航(花名“无招”)称它为“红树林”。而在此之前,每当面临重大升级,产品经理们都会取一个应景的代号,这一次,为什么是“红树林”?", 43 | "title": "时代的火车向前开", "picUrl": "", 44 | "messageUrl": "https://mp.weixin.qq.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI"} 45 | }) 46 | 47 | count_num, pass_num, fail_num, error_num = 631, 328, 101, 202 48 | data_markdown = json.dumps({ 49 | "msgtype": "markdown", 50 | "markdown": { 51 | "title": "AutoMagic TestReprot", 52 | "text": "![screenshot](http://192.168.110.65/static/images/automagic.png) \n" + 53 | ">[AutoMagic TestReprot](http://jenkinsm.acorn-net.com/TestResults/CornerStone/2017-02-19/2017-02-19-00_55_46_result.html) \n\n " + 54 | ">执行CASE" + str(count_num) + "个,Pass" + str(pass_num) + "个,Fail" + str(fail_num) + "个,Error" + str( 55 | error_num) + "个\n" 56 | } 57 | }) 58 | data_md = json.dumps({ 59 | "msgtype": "markdown", 60 | "markdown": { 61 | "title": "Script Update", 62 | "text": "![screenshot](http://192.168.110.65/static/images/automagic.png) \n" + 63 | ">[192.168.115.1](\\192.168.115.1) \n\n " + 64 | ">sendingdata.py 脚本更新\n" + 65 | ">1.添加 -t 参数调整线程数量,默认为1\n" 66 | ">2.添加 -s 参数调整发包速度(单位是秒)可以是小数\n" 67 | } 68 | }) 69 | data_test = '''{"msgtype": "text", "text": {"content": "我就是我,颜色不一样的烟火!!!"}}''' 70 | sendAutoMagic(url_post, data_md) 71 | -------------------------------------------------------------------------------- /seleniumkeyword/result/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/seleniumkeyword/result/img.png -------------------------------------------------------------------------------- /seleniumkeyword/result/video.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/seleniumkeyword/result/video.jpg -------------------------------------------------------------------------------- /seleniumkeyword/sendlog/README.md: -------------------------------------------------------------------------------- 1 | #流量回放模式(Linux) 2 | 3 | ##通过回放Syslog流进行发送syslog(UDP支持源地址伪装)|sendingdata.py 4 | 5 | **usage:**
6 | sendingdata.py [-h] [-sip SRCIP] [-dip DSTIP] [-p PROTOCOL] [-dport DPORT]
[-c COUNT] [-f FILE] [-t THREAD] [-s SPEED] 7 | 8 | **optional arguments:**
9 | -h, --help show this help message
10 | -sip SRCIP src ip addr
11 | -dip DSTIP dst ip addr
12 | -p PROTOCOL protocol udp or tcp
13 | -dport DPORT send port
14 | -c COUNT packets count
15 | -f FILE packets file
16 | -t THREAD thread number
17 | -s SPEED send speed (s)
18 | 19 | #UDP Socket 20 | ##通过UDP Socket发送syslog事件|udpsendingsyslog.py 21 | **Usge:**
22 | python sendlog -f [filepath] -c [cycles] -t [sleep time] 23 | -d [Receiver Host] -p [Receiver port] 24 | 25 | **Details:**
26 | -C [开启无限循环]
27 | -f [指派日志文件路径]
28 | -c [指定循环次数]
29 | -t [指定日志发送间隔时间 单位:秒]
30 | -d [指定日志接收IP地址]
31 | -p [指定日志接收端口(整数)]
32 | -v [查看工具版本]
33 | -h [查看帮助]
34 | 35 | #TCP Socket 36 | ##通过TCP Socket发送syslog事件|tcpsendingsyslog.py 37 | **usage:**
38 | tcpsendingsyslog.py [-h] [-host HOST] [-port DPORT] [-c COUNT]
39 | 40 | **optional arguments:**
41 | -h, --help show this help message and exit
42 | -host HOST receive host ip
43 | -port DPORT send port
44 | -c COUNT count number
45 | -------------------------------------------------------------------------------- /seleniumkeyword/sendlog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/seleniumkeyword/sendlog/__init__.py -------------------------------------------------------------------------------- /seleniumkeyword/sendlog/mysetup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # mysetup.py 3 | 4 | from distutils.core import setup 5 | import py2exe 6 | 7 | 8 | """ 9 | Py2exe打包程序 10 | 执行:python mysetup.py py2exe 11 | """ 12 | 13 | 14 | setup(console=["syslogc.py"]) 15 | 16 | 17 | -------------------------------------------------------------------------------- /seleniumkeyword/sendlog/randip.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | 4 | __author__ = 'Ray' 5 | 6 | import random 7 | import struct 8 | from scapy.all import * 9 | from scapy.layers.inet import IP, UDP 10 | 11 | SOURCE = ['.'.join((str(random.randint(1, 254)) for _ in range(4))) for _ in range(100)] 12 | 13 | print SOURCE[1] 14 | data = struct.pack('=BHI', 0x12, 20, 1000) 15 | pkt = IP(src=SOURCE[1], dst='192.168.110.178') / UDP(sport=12345, dport=514) / data 16 | send(pkt, inter=1, count=5) 17 | -------------------------------------------------------------------------------- /seleniumkeyword/sendlog/randomip.py: -------------------------------------------------------------------------------- 1 | __author__ = 'ray' 2 | 3 | import random 4 | import socket 5 | import struct 6 | from configparser import ConfigParser 7 | 8 | # cf = ConfigParser.ConfigParser() 9 | # cf.read('send.config') 10 | # ip = cf.get('ipnet', 'ip') 11 | # 12 | # RANDOM_IP_POOL=['10.0.1.10/24'] 13 | def get_random_ip(RANDOM_IP_POOL): 14 | str_ip = RANDOM_IP_POOL[random.randint(0, len(RANDOM_IP_POOL) - 1)] 15 | # print str_ip 16 | str_ip_addr = str_ip.split('/')[0] 17 | # print str_ip_addr 18 | str_ip_mask = str_ip.split('/')[1] 19 | # print str_ip_mask 20 | ip_addr = struct.unpack('>I', socket.inet_aton(str_ip_addr))[0] 21 | mask = 0x0 22 | for i in range(31, 31 - int(str_ip_mask), -1): 23 | mask = mask | ( 1 << i) 24 | ip_addr_min = ip_addr & (mask & 0xffffffff) 25 | ip_addr_max = ip_addr | (~mask & 0xffffffff) 26 | # print socket.inet_ntoa(struct.pack('>I', random.randint(ip_addr_min, ip_addr_max))) 27 | return socket.inet_ntoa(struct.pack('>I', random.randint(ip_addr_min, ip_addr_max))) 28 | 29 | def get_random_mac(): 30 | maclist = [] 31 | for i in xrange(6): 32 | randstr = "".join(random.sample('0123456789abcdef',2)) 33 | maclist.append(randstr) 34 | randmac = ":".join(maclist) 35 | return randmac 36 | if __name__ == '__main__': 37 | # __get_random_ip(RANDOM_IP_POOL) 38 | get_random_mac() -------------------------------------------------------------------------------- /seleniumkeyword/sendlog/send.config: -------------------------------------------------------------------------------- 1 | [mesg] 2 | sip = 192.168.110.1/24 3 | sport = 1200,65535 4 | dip = 192.168.110.254/32 5 | dport = 21, 22, 23, 25, 80, 139, 443, 2000, 3306, 8080 6 | level = 5, 10, 15, 20, 50 7 | 8 | [receiver] 9 | host = 192.168.110.118 10 | port = 514 11 | -------------------------------------------------------------------------------- /seleniumkeyword/sendlog/sendingdata.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2017-02-27 6 | """ 7 | 8 | from scapy.all import * 9 | from scapy.layers.inet import IP,UDP,TCP 10 | import threading 11 | import argparse 12 | 13 | srcip = '192.168.110.249' 14 | dstip = '192.168.110.234' 15 | data = '''<188> 2017-02-27 17:29:30 SECURITY VUL host tsbc type 常规 mintype 轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击"''' 16 | # pkt = IP(src=srcip, dst=dstip)/UDP(sport=12345,dport=514)/data 17 | # send(pkt,count=80000) 18 | class Controller(object): 19 | count = 1 20 | @classmethod 21 | def init(cls): 22 | cls.args = get_args() 23 | cls.thread_stop = False 24 | 25 | def get_args(): 26 | ''' 27 | 解析命令行参数 28 | :return: 命令行参数命名空间 29 | ''' 30 | parser = argparse.ArgumentParser() 31 | parser.add_argument('-sip', action='store', dest='srcip', type=str, help='src ip addr') 32 | parser.add_argument('-dip', action='store', dest='dstip', type=str, help='dst ip addr') 33 | parser.add_argument('-p', action='store', dest='protocol', type=str, help='protocol udp or tcp') 34 | parser.add_argument('-dport', action='store', dest='dport', type=str, help='send port') 35 | parser.add_argument('-c', action='store', dest='count', type=str, help='packets count') 36 | parser.add_argument('-f', action='store', dest='file', type=str, help='packets file') 37 | parser.add_argument('-t', action='store', dest='thread', type=str, help='thread number') 38 | parser.add_argument('-s', action='store', dest='speed', type=str, help='send speed (s)') 39 | rst = parser.parse_args() 40 | return rst 41 | 42 | def run(srcip, dstip,data,protocol,dport,count,speed): 43 | 44 | if protocol.upper() == 'UDP': 45 | pkt = IP(src=srcip, dst=dstip)/UDP(sport=12345,dport=dport)/data 46 | send(pkt,inter=speed, count=count) 47 | if protocol.upper() == 'TCP': 48 | pkt = IP(src=srcip, dst=dstip)/TCP(sport=12345, dport=dport) / data 49 | send(pkt, inter=speed, count=count) 50 | 51 | def sendpkgdata(): 52 | srcip = Controller.args.srcip 53 | dstip = Controller.args.dstip 54 | data = '''<188> 2017-02-27 17:29:30 SECURITY VUL host tsbc type 常规 mintype 轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击轻蠕虫 id CN-CVE name 洪泛攻击 desc "容易受到ARP攻击"''' 55 | if Controller.args.protocol is None: 56 | protocol = 'udp' 57 | else: 58 | protocol = Controller.args.protocol 59 | if Controller.args.dport is None: 60 | dport = 514 61 | else: 62 | dport = Controller.args.dport 63 | if Controller.args.count is None: 64 | count = 1 65 | else: 66 | count = int(Controller.args.count) 67 | if Controller.args.speed is None: 68 | speed = 0 69 | else: 70 | speed = float(Controller.args.speed) 71 | run(srcip, dstip, data, protocol, dport, count, speed) 72 | 73 | 74 | 75 | if "__main__" == __name__: 76 | # 初始化控制类对象:实例化浏览器实例 77 | Controller.init() 78 | if Controller.args.thread is None: 79 | thread = 1 80 | else: 81 | thread = int(Controller.args.thread) 82 | tlst = [] 83 | 84 | for i in xrange(thread): 85 | th = threading.Thread(sendpkgdata()) 86 | tlst.append(th) 87 | 88 | for i in tlst: 89 | i.start() -------------------------------------------------------------------------------- /seleniumkeyword/sendlog/syslogc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'Ray' 3 | 4 | """ 5 | Python syslog 发包工具. 6 | mail:tsbc@vip.qq.com 7 | 2016-05-05 8 | """ 9 | 10 | import socket 11 | import threading 12 | from configparser import ConfigParser 13 | import random 14 | import randomip 15 | import weighted_choice 16 | import time 17 | 18 | class send: 19 | def __init__(self, message, host, port): 20 | self.message = message 21 | self.host = host 22 | self.port = port 23 | self.FACILITY = { 24 | 'kern': 0, 'user': 1, 'mail': 2, 'daemon': 3, 25 | 'auth': 4, 'syslog': 5, 'lpr': 6, 'news': 7, 26 | 'uucp': 8, 'cron': 9, 'authpriv': 10, 'ftp': 11, 27 | 'local0': 16, 'local1': 17, 'local2': 18, 'local3': 19, 28 | 'local4': 20, 'local5': 21, 'local6': 22, 'local7': 23, 29 | } 30 | 31 | self.LEVEL = { 32 | 'emerg': 0, 'alert':1, 'crit': 2, 'err': 3, 33 | 'warning': 4, 'notice': 5, 'info': 6, 'debug': 7 34 | } 35 | weights = [0.03, 0.04, 0.05, 0.07, 0.1, 0.2, 0.5, 0.01] #随机产生日志级别的概率 36 | 37 | if 'success' in self.message: 38 | self.levl = self.LEVEL['info'] 39 | elif 'failed' in self.message: 40 | self.levl = self.LEVEL['warning'] 41 | elif 'permit' in self.message: 42 | self.levl = self.LEVEL['info'] 43 | elif 'deny' in self.message: 44 | self.levl = self.LEVEL['warning'] 45 | else: 46 | self.levl = weighted_choice.weighted_choice_sub(weights) 47 | # self.levl = random.choice(self.LEVEL.keys()) 48 | def run(self): 49 | 50 | """ 51 | Send syslog UDP packet to given host and port. 52 | """ 53 | BUFSIZE = 4096 54 | ADDR = (self.host,self.port) 55 | # sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 56 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 57 | try: 58 | sock.connect(ADDR) 59 | except Exception as e: 60 | print("Connect Error", e) 61 | return False 62 | 63 | data = '<%d>%s' % (self.levl + self.FACILITY['local7']*8, self.message) 64 | print(data) 65 | # sock.sendto(data, (self.host, self.port)) 66 | while True: 67 | try: 68 | sock.send(data) 69 | data = sock.recv(BUFSIZE) 70 | if not data: 71 | break 72 | print("Server:",data) 73 | except Exception as e: 74 | print("Error", e) 75 | sock.close() 76 | 77 | if __name__ == '__main__': 78 | # 从配置文件读取数据 79 | cf = ConfigParser.ConfigParser() 80 | cf.read('send.config') 81 | host = cf.get('receiver', 'host').split(',') # 获取接收log服务主机 82 | port = int(cf.get('receiver', 'port')) 83 | sip = randomip.get_random_ip([cf.get('mesg', 'sip')]) # 随机生成源IP 84 | dip = randomip.get_random_ip([cf.get('mesg', 'dip')]) # 随机生成目的IP 85 | sportstr = tuple(cf.get('mesg', 'sport').split(',')) # 随机生成源端口 86 | sport = str(random.randint(int(sportstr[0]), int(sportstr[1]))) 87 | dportstr = cf.get('mesg', 'dport').split(',') # 随机生成目的端口 88 | dport = random.choice(dportstr) 89 | 90 | # 获取系统当前时间 91 | now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) 92 | nowstr = time.strftime('%b %d %H:%M:%S', time.localtime()) 93 | 94 | '''H3C交换机''' 95 | h3clog1 = nowstr + ' 2000 H3C %%10IFNET/3/LINK_UPDOWN(l): GigabitEthernet1/0/7 link status is UP.' 96 | h3clog2 = nowstr + ' 2000 H3C %%10SHELL/4/LOGOUT(t): Trap 1.3.6.1.4.1.25506.2.2.1.1.3.0.2: logout from VTY' 97 | threadh3c1 = send(h3clog1, host[0], port) 98 | threadh3c2 = send(h3clog2, host[0], port) 99 | 100 | '''Cisco路由器''' 101 | # 管理员操作 102 | csicolog1 = '29: *' + nowstr + '.' + str( 103 | random.randint(1, 999)) + ': %SYS-5-CONFIG_I: Configured from console by admin on vty0 (10.0.1.49)' 104 | print(csicolog1) 105 | threadcisco1 = send(csicolog1, host[0], port) 106 | 107 | threadcisco1.run() -------------------------------------------------------------------------------- /seleniumkeyword/sendlog/tcpsendingsyslog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'Ray' 3 | 4 | """ 5 | socket tcp send 发包工具. 6 | mail:tsbc@vip.qq.com 7 | 2017-03-03 8 | """ 9 | 10 | import sys 11 | 12 | import socket 13 | import threading 14 | import argparse 15 | 16 | class Controller(object): 17 | count = 1 18 | @classmethod 19 | def init(cls): 20 | cls.args = get_args() 21 | cls.thread_stop = False 22 | 23 | def get_args(): 24 | ''' 25 | 解析命令行参数 26 | :return: 命令行参数命名空间 27 | ''' 28 | parser = argparse.ArgumentParser() 29 | parser.add_argument('-host', action='store', dest='host', type=str, help='receive host ip') 30 | parser.add_argument('-port', action='store', dest='dport', type=str, help='send port') 31 | parser.add_argument('-c', action='store', dest='count', type=str, help='count number') 32 | # parser.add_argument('-f', action='store', dest='file', type=str, help='packets file') 33 | # parser.add_argument('-t', action='store', dest='thread', type=str, help='thread number') 34 | rst = parser.parse_args() 35 | return rst 36 | 37 | def run(host,port,data,count): 38 | address = (host, port) 39 | for i in range(count): 40 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 41 | s.connect(address) 42 | s.send(data) 43 | # source = s.recv(1024) 44 | # print 'the data received is', source 45 | s.close() 46 | 47 | 48 | def senddata(): 49 | data = '''<188> 2017-02-27 17:29:30 %SYS-5-CONFIG_I: Configured from console by admin on vty0 (10.0.1.49)''' 50 | if Controller.args.host is None: 51 | print("Receiver IP(host) is Null!") 52 | return False 53 | else: 54 | host = Controller.args.host 55 | if Controller.args.dport is None: 56 | port = 514 57 | else: 58 | port = int(Controller.args.dport) 59 | if Controller.args.count is None: 60 | count = 1 61 | else: 62 | count = int(Controller.args.count) 63 | run(host, port, data, count) 64 | 65 | 66 | 67 | if "__main__" == __name__: 68 | # 初始化控制类对象:实例化浏览器实例 69 | Controller.init() 70 | senddata() 71 | # if Controller.args.thread is None: 72 | # thread = 1 73 | # else: 74 | # thread = int(Controller.args.thread) 75 | # tlst = [] 76 | # 77 | # for i in xrange(thread): 78 | # th = threading.Thread(senddata()) 79 | # tlst.append(th) 80 | # 81 | # for i in tlst: 82 | # i.start() -------------------------------------------------------------------------------- /seleniumkeyword/sendlog/tcpsendtest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'Ray' 3 | 4 | """ 5 | socket tcp send 发包工具. 6 | mail:tsbc@vip.qq.com 7 | 2017-04-01 8 | """ 9 | 10 | import socket,time 11 | 12 | address = ('192.168.110.158', 1470) 13 | data = '''<188> 2017-02-27 17:29:30 SECURITY VUL host tsbc type desc "jiangpeng.chen@acorn-net.com" ''' 14 | for i in xrange(100): 15 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 16 | s.connect_ex(address) 17 | print s.getsockname() 18 | print s.getpeername() 19 | s.sendall(data+"["+str(1)+"]") 20 | # print s.recv(1024) 21 | time.sleep(1) 22 | s.close() 23 | -------------------------------------------------------------------------------- /seleniumkeyword/sendlog/weighted_choice.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'Ray' 3 | 4 | """ 5 | 通过概率产生随机值 6 | mail:tsbc@vip.qq.com 7 | 2016-05-05 8 | """ 9 | 10 | import random 11 | from collections import Counter 12 | 13 | def weighted_choice_sub(weights): 14 | rnd = random.random() * sum(weights) 15 | for i, w in enumerate(weights): 16 | rnd -= w 17 | if rnd < 0: 18 | # print w 19 | return i 20 | 21 | weights = [0.03, 0.04, 0.05, 0.07, 0.1, 0.2, 0.5, 0.01] 22 | 23 | # weighted_choice_sub(weights) 24 | 25 | # count =Counter() 26 | # 27 | # for x in xrange(10000): 28 | # index = weighted_choice_sub(weights) 29 | # count[index] += 1 30 | # 31 | # count_sum = sum(count.values()) 32 | # 33 | # for key, value in count.iteritems(): 34 | # print float(value)/count_sum -------------------------------------------------------------------------------- /seleniumkeyword/settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | __author__ = 'Ray' 4 | mail:tsbc@vip.qq.com 5 | 2020-01-14 6 | seleniumkeyword settings configure 7 | """ 8 | 9 | DB_HOST = '127.0.0.1' 10 | DB_PORT = 3306 11 | DB_NAME = 'automatic' 12 | DB_USER = 'root' 13 | DB_PASS = '123456' 14 | 15 | TESTRAIL_URL = 'http://172.17.3.70/testrail/' -------------------------------------------------------------------------------- /seleniumkeyword/testrail.py: -------------------------------------------------------------------------------- 1 | # 2 | # TestRail API binding for Python 2.x (API v2, available since 3 | # TestRail 3.0) 4 | # 5 | # Learn more: 6 | # 7 | # http://docs.gurock.com/testrail-api2/start 8 | # http://docs.gurock.com/testrail-api2/accessing 9 | # 10 | # Copyright Gurock Software GmbH. See license.md for details. 11 | # 12 | 13 | import urllib2, json, base64 14 | 15 | class APIClient: 16 | def __init__(self, base_url): 17 | self.user = '' 18 | self.password = '' 19 | if not base_url.endswith('/'): 20 | base_url += '/' 21 | self.__url = base_url + 'index.php?/api/v2/' 22 | 23 | # 24 | # Send Get 25 | # 26 | # Issues a GET request (read) against the API and returns the result 27 | # (as Python dict). 28 | # 29 | # Arguments:0 30 | # 31 | # uri The API method to call including parameters 32 | # (e.g. get_case/1) 33 | # 34 | def send_get(self, uri): 35 | return self.__send_request('GET', uri, None) 36 | 37 | # 38 | # Send POST 39 | # 40 | # Issues a POST request (write) against the API and returns the result 41 | # (as Python dict). 42 | # 43 | # Arguments: 44 | # 45 | # uri The API method to call including parameters 46 | # (e.g. add_case/1) 47 | # data The data to submit as part of the request (as 48 | # Python dict, strings must be UTF-8 encoded) 49 | # 50 | def send_post(self, uri, data): 51 | return self.__send_request('POST', uri, data) 52 | 53 | def __send_request(self, method, uri, data): 54 | url = self.__url + uri 55 | request = urllib2.Request(url) 56 | if (method == 'POST'): 57 | request.add_data(json.dumps(data)) 58 | auth = base64.b64encode('%s:%s' % (self.user, self.password)) 59 | request.add_header('Authorization', 'Basic %s' % auth) 60 | request.add_header('Content-Type', 'application/json') 61 | 62 | e = None 63 | try: 64 | response = urllib2.urlopen(request).read() 65 | except urllib2.HTTPError as e: 66 | response = e.read() 67 | 68 | if response: 69 | result = json.loads(response) 70 | else: 71 | result = {} 72 | 73 | if e != None: 74 | if result and 'error' in result: 75 | error = '"' + result['error'] + '"' 76 | else: 77 | error = 'No additional error message received' 78 | raise APIError('TestRail API returned HTTP %s (%s)' % 79 | (e.code, error)) 80 | 81 | return result 82 | 83 | class APIError(Exception): 84 | pass 85 | -------------------------------------------------------------------------------- /seleniumkeyword/testraildemo.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from pprint import pprint 3 | 4 | __author__ = 'ray' 5 | 6 | import testrail 7 | 8 | client = testrail.APIClient('http://172.17.3.70/testrail/') 9 | client.user = 'jiangpeng.chen@acorn-net.com' 10 | client.password = '123qwe' 11 | 12 | """ 更新Testrun 结果 13 | 14 | add_result_for_case/[runid]/[caseid] 15 | status_id: 16 | { 17 | 1 : Passed 18 | 4 : Retest 19 | 5 : Failed 20 | } 21 | 22 | """ 23 | result = client.send_post( 24 | 'add_result_for_case/1047/1036925', 25 | {'status_id': 1, 'comment': 'AutoMagic Flag.' } 26 | ) 27 | # pprint(result) 28 | 29 | 30 | """ 获取用例信息 31 | 32 | get_case/[caseid] 33 | """ 34 | case = client.send_get('get_case/1036925') 35 | # pprint(case) 36 | 37 | 38 | """ 获取用例节点信息 39 | 40 | """ 41 | section = client.send_get('get_section/45752') 42 | # pprint(section) 43 | 44 | 45 | """ 添加测试用例 46 | send_post('add_case/[sectionid]',casedata) 47 | """ 48 | casedata = { 49 | "title":"AutoMagic 测试使用admin用户登录。", 50 | "template_id":"1", 51 | "type_id":"1", 52 | "priority_id":"4", 53 | "custom_caseversion_id":"8", 54 | "custom_automation_status":"2", 55 | "custom_steps": "Step1 跳转到登录页面:http://192.168.110.111\n Step2 输入登录用户名 admin \n Step3 输入登录密码 admin@123\n Step4 点击登录按钮" 56 | } 57 | add_case = client.send_post('add_case/45752',casedata) 58 | # pprint(add_case) 59 | 60 | 61 | update_case = client.send_post('update_case/1107930',casedata) 62 | pprint(update_case) -------------------------------------------------------------------------------- /start.py: -------------------------------------------------------------------------------- 1 | import pymysql 2 | import os 3 | from automatic.settings.common import MYSQL_HOST, MYSQL_PORT, MYSQL_USERNAME, MYSQL_PASSWORD, MYSQL_DBNAME 4 | 5 | conn = pymysql.connect(host=MYSQL_HOST, port=int(MYSQL_PORT), user=MYSQL_USERNAME, passwd=MYSQL_PASSWORD) 6 | cur = conn.cursor() 7 | 8 | if not cur.execute("SELECT * FROM information_schema.SCHEMATA where SCHEMA_NAME='" + MYSQL_DBNAME + "'"): 9 | print("创建数据库 [" + MYSQL_DBNAME + "]...") 10 | sql_create_db = "create database " + MYSQL_DBNAME + " DEFAULT CHARSET utf8 COLLATE utf8_general_ci" 11 | cur.execute(sql_create_db) 12 | print("创建数据库完成.") 13 | os.system("bash ./init.sh") 14 | print("导入关键字数据") 15 | sql_list = [] 16 | with open('insertkeyword.sql', "r") as f: 17 | lines = f.readlines() 18 | for line in lines: 19 | line = line.replace('\n', '') 20 | sql_list.append(line) 21 | _con = pymysql.connect(host=MYSQL_HOST, port=int(MYSQL_PORT), user=MYSQL_USERNAME, passwd=MYSQL_PASSWORD, 22 | database=MYSQL_DBNAME, cursorclass=pymysql.cursors.DictCursor) 23 | with _con: 24 | with _con.cursor() as _cur: 25 | for sql in sql_list: 26 | try: 27 | _cur.execute(sql) 28 | print(sql) 29 | except: 30 | _con.rollback() 31 | _con.commit() 32 | _cur.close() 33 | 34 | cur.close() 35 | print("service start ...") 36 | os.system("python3 manage.py runserver 0.0.0.0:8000") 37 | -------------------------------------------------------------------------------- /公众号.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radiateboy/automagic/2c0f40bf9dadca49b950ec02f2c9257a9e8f4786/公众号.jpg --------------------------------------------------------------------------------