学习并复刻《基于知识图谱的农医对话系统》(七)

七.搭建Web系统—水稻疾病在线问诊平台

1.Django框架学习

任务描述

在本任务中使用Django框架创建第一个项目,学习Django框架的基本使用方法,为接下来搭建”水稻疾病在线问诊平台“做好准备。

知识点:Django框架的基本概念、Django创建项目

重    点:Django创建项目

难    点:Django创建项目

内    容:1. Django简介

              2. MVC 与 MTV模型

              3. 安装Django

              4. 使用Django 创建第一个项目

1. Django 简介

     基本介绍

     Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架。

     使用 Django,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 Django 本身基于 MVC 模型,即 Model(模型)+ View(视图)+ Controller(控制器)设计模式,MVC 模式使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。

     MVC 优势:

  • 低耦合
  • 开发快捷
  • 部署方便
  • 可重用性高
  • 维护成本低
  • ...

     Python 加 Django 是快速开发、设计、部署网站的最佳组合。

     特点

  • 强大的数据库功能
  • 自带强大的后台功能
  • 优雅的网址
2. MVC 与 MTV模型

     MVC 模型

     MVC 模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

     MVC 以一种插件式的、松耦合的方式连接在一起。

  • 模型(M)- 编写程序应有的功能,负责业务对象与数据库的映射(ORM)。
  • 视图(V)- 图形界面,负责与用户的交互(页面)。
  • 控制器(C)- 负责转发请求,对请求进行处理。

     简易图:

     用户操作流程图:

     MTV 模型

     Django 的 MTV 模式本质上和 MVC 是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django 的 MTV 分别是指:

  • M 表示模型(Model):编写程序应有的功能,负责业务对象与数据库的映射(ORM)。
  • T 表示模板 (Template):负责如何把页面(html)展示给用户。
  • V 表示视图(View):负责业务逻辑,并在适当时候调用 Model和 Template。

     除了以上三层之外,还需要一个 URL 分发器,它的作用是将一个个 URL 的页面请求分发给不同的 View 处理,View 再调用相应的 Model 和 Template,MTV 的响应模式如下所示:

     简易图:

     用户操作流程图:

     解析:

     用户通过浏览器向我们的服务器发起一个请求(request),这个请求会去访问视图函数:

  • a.如果不涉及到数据调用,那么这个时候视图函数直接返回一个模板也就是一个网页给用户。
  • b.如果涉及到数据调用,那么视图函数调用模型,模型去数据库查找数据,然后逐级返回。

     视图函数把返回的数据填充到模板中空格,最后返回网页给用户。

3. 安装Django

     在终端执行以下命令安装Django:

pip install Django -i https://pypi.tuna.tsinghua.edu.cn/simple

     安装过程如下图所示:

     安装过程中显示已存在Django,主要是因为在安装doccano工具时已安装过Django框架。

4. 使用Django 创建第一个项目

     打开终端,使用 django-admin 来创建在Ai_Farmer_Doctor/目录下创建 HelloWorld 项目,执行命令如下:

cd ~/Desktop/Ai_Farmer_Doctor/
django-admin startproject HelloWorld

     在终端输入命令如下图所示:

     创建完成后,HelloWorld项目的目录结构:

     目录说明:

  • HelloWorld: 项目的容器。
  • manage.py: 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。
  • HelloWorld/__init__.py: 一个空文件,告诉 Python 该目录是一个 Python 包。
  • HelloWorld/asgi.py: 一个 ASGI 兼容的 Web 服务器的入口,以便运行你的项目。
  • HelloWorld/settings.py: 该 Django 项目的设置/配置。
  • HelloWorld/urls.py: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"。
  • HelloWorld/wsgi.py: 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。

     接下来,进入 HelloWorld 目录输入以下命令,启动服务器:

python manage.py runserver 0.0.0.0:8000

     命令启动过程如下图所示:

     在浏览器中访问http://localhost:8000/,进入页面如下图所示:

     视图和 URL 配置

     在先前创建的 HelloWorld 目录下的 HelloWorld 目录新建一个 views.py 文件,并输入代码:

from django.http import HttpResponse 
def hello(request):
	return HttpResponse("Hello world ! ")

     接着,绑定 URL 与视图函数。打开 urls.py 文件,删除原来代码,将以下代码复制粘贴到 urls.py 文件中:

from django.urls import path
from . import views
urlpatterns = [ path("", views.hello),]

     完成后,刷新浏览器或者重新启动服务器,就可以看到“Hello world !”了,如下图所示:

     2.创建主页面

任务描述

本任务中基于Django框架创建前端模块—“水稻疾病在线问诊平台”的主页面。

知识点:Django、html

重    点:Django、html

难    点:Django、html

内    容:1. 开发准备:创建Django项目、APP、数据库;创建目录,添加静态文件;创建html代码文件

              2. 构建主页面

                  主页面效果如下图所示:

              3. 配置网站

1. 准备工作

     (1)创建Django项目

     进入Ai_Farmer_Doctor目录下,使用 django-admin 来创建Inquiries项目,在终端执行以下命令:

django-admin startproject Inquiries

     命令执行结束后,会在在Ai_Farmer_Doctor/目录下创建好Inquiries/目录,结构如下图所示:

(2)创建APP

     Django 规定,如果要使用模型,必须要创建一个 app。使用以下命令在Inquiries/目录下分别创建名称为"account"和“app”的目录:

django-admin startapp account
django-admin startapp app

     输入命令如下图所示:

     命令执行完毕后,在Inquiries/目录下多了两个子目录:account/、app/,并且里面已经有默认的文件和目录了,创建两个目录结构如下图所示:

     每个文件的作用如下:

  • migrations/:在这个文件夹里,Django会存储一些文件以跟踪在modelspy文件中创建的变更,用来保持数据库和models.py的同步。
  • admin.py:这个文件为一个django内置的应用程序Django Admin的配置文件。
  • apps.py:这是应用程序本身的配置文件。
  • models.py:这里是定义Web应用程序数据实例的地方。models会由Django自动转换为数据库表。
  • tests.py:这个文件用来写当前应用程序的单元测试。
  • views.py:这是处理Web应用程序请求(request)/响应(resopnse)周期的文件。

     (3)创建数据库

     进入Inquiries/目录下,在终端执行以下命令创建数据库:

python manage.py migrate

     命令执行过程如下图所示:

     命令执行完毕后,在Inquiries/目录下生成了db.sqlite3文件,如下图所示。这是一个默认的数据库。SQLite 是Python 默认安装的数据库,在Django 中也可以默认使用。

     (4)创建目录

     在Inquiries目录下,新建static和templates目录,分别用来存放静态文件和模板;

     在static目录中新建css、img、js和icons目录;

     在templates目录下,创建account、app、registration目录。

     创建完成后,如下图所示:

     (5)添加静态文件

     从“资料”中下载“静态文件.zip”压缩包,上传到实验环境中,解压,并将“静态文件/css/”目录下的所有文件复制到Inquiries/static/css/目录下;将“静态文件/js/”目录下的所有文件复制到Inquiries/static/js/目录下;将“静态文件/img/”目录下的所有图片复制到Inquiries/static/img/目录下;将“静态文件/icons/”目录下的所有图片复制到Inquiries/static/icons/目录下。

     (6)创建html文件

     在templates目录下,创建“index.html”文件,创建好的文件如下图所示:

     删除index.html文件中所有生成的代码。

2. 构建主页面

     在“index.html”文件中编写html代码,构建“水稻疾病在线问诊平台“的主页面。

  加载静态文件。

{% load static %}

     定义判断IE版本的语句,用于解决浏览器的兼容性问题。其他浏览器会将其作为注释而忽略这些语句,因为他们是IE专门提供的一种语法,如此就能根据不同的IE版本加载对应的CSS或者JS文件了。 

  • <!--[if IE 7]> 仅IE7可识别 <![endif]-->
  • <!--[if lt IE 7]> IE7以及IE7以下版本可识别 <![endif]-->
  • <!--[if IE 8]> 仅IE8可识别 <![endif]-->
  • <!--[if gt IE 8]> IE8以及IE8以上版本可识别 <![endif]-->
<!--[if lt IE 7]>
<html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>
<html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>
<html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!-->
<html class="no-js"> <!--<![endif]-->

     定义头部标签元素。

     < head>< /head>定义文档的头部,是所有头部元素的容器。< head> 中的元素可以引用脚本、指示浏览器在哪里找到样式表、提供元信息等等。可以添加在头部区域的元素标签说明如下:

  • <meta>:meta标签描述了一些基本的元数据,元数据不显示在页面上,但会被浏览器解析,meta元素通常用于指定网页的描述,关键词,文件的最后修改时间,作者,和其他元数据;
  • <title>:定义了文档的标题;
  • <link>:定义了文档与外部资源之间的关系,<link> 标签通常用于链接到样式表;
  • <script>:用于加载脚本文件。
<head>
    <meta charset="utf-8">
    <meta name="robots" content="all,follow">
    <meta name="googlebot" content="index,follow,snippet,archive">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Home</title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <meta name="keywords" content="">

    <link href="{% static 'css/font-awesome.css' %}" rel="stylesheet">
    <link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">

    <!-- owl carousel css -->
    <link href="{% static 'css/owl.carousel.css' %}" rel="stylesheet">
    <link href="{% static 'css/owl.theme.css' %}" rel="stylesheet">
    <!-- Theme stylesheet -->
    <link href="{% static 'css/style.default.css' %}" rel="stylesheet" id="theme-stylesheet">
    <!-- Custom stylesheet - for your changes -->
    <link href="{% static 'css/custom.css' %}" rel="stylesheet">
    <!-- CSS Animations -->
    <link href="{% static 'css/animate.css' %}" rel="stylesheet">
    <!-- Favicon -->
    <link rel="shortcut icon" href="{% static 'img/favicon.png' %}">

    <!-- Mordernizr -->
    <script src="{% static 'js/modernizr-2.6.2.min.js' %}"></script>

    <!-- Responsivity for older IE -->
    <!--[if lt IE 9]>
    <script src="{% static 'js/respond.min.js' %}"></script>
    <![endif]-->
    <meta property="og:title" content="Landing Page | Landing Page Bootstrap Theme by Bootstrapious.com!">
    <meta property="og:site_name" content="Landing Page | Landing Page Bootstrap Theme by Bootstrapious.com!">
    <meta property="og:type" content="website">
    <meta property="og:url" content="">

    <meta property="og:image" content="">

    <meta name="twitter:card" content="summary">
    <meta name="twitter:creator" content="@bootstrapious">

</head>

     定义主页面的主体。

     < body>< /body>元素定义文档的主体,包含文档的所有内容,如文本、超链接、图像、表格和列表等。

     使用BootStrap 插件监听滚动条位置。实现滚动条滚动,页面导航自动切换的效果。

<body data-spy="scroll" data-target="#navigation" data-offset="120">

      定义页面名称“水稻疾病在线问诊平台”,效果如下图红框内所示:

<div class="navbar navbar-default navbar-fixed-top" role="navigation" id="navbar">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand scrollTo" href="#intro">水稻疾病在线问诊平台</a>
        </div>

     实现导航栏,包含“首页”、“智能问诊”、“关于我们”、“登录”、“注册”5部分。

     这里使用<a>标签定义超链接,用于从一个页面链接到另一个页面。<a> 元素最重要的属性是 href 属性,它指定链接的目标。

     实现效果如下图红框内所示:

        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav navbar-right">
                <li class="active"><a href="#intro">首页</a>
                </li>
                <li><a href="#section2">智能问诊</a>
                </li>
                <li><a href="#section3">关于我们</a>
                </li>
                <li><a href="{% url 'login' %}">登录</a>
                </li>
                <li><a href="{% url 'register' %}" style="!important">注册</a>
                </li>
            </ul>
        </div>

     显示登录后的用户名。

     登录账号之后,会在导航栏下方显示“欢迎您:xx(xx表示用户名)”的信息,实现效果如下图红框内所示:

        <div class="fr"
             style="width: 150px;
             float: right;
             text-align: center;
             letter-spacing: 0.1em;
             font-size: 12px;
             font-weight: bold;">
                {% if user.is_authenticated %}
                <div class="login_btn fl">
                    欢迎您:<em style="color: blue;">{{ user.username }}</em>
                </div>
                {% endif %}
            </div>
    </div>
</div>

     显示背景图片。这里用到了static/css/目录下style.default.css文件中的“#intro”模块,该模块中定义了背景图片的地址以及显示样式。因此,若想要替换背景图片,需修改如下图红框内所示的图像路径:

     在主页面显示的背景效果如下图所示:

<div id="all">
    <div id="intro" class="clearfix">
    </div>

     定义“智能问诊介绍“模块,点击导航栏中的“智能问诊”会跳转到该模块,该模块的效果如下图所示:

     点击”开始问诊“应跳转到”智能问诊“页面,因此,需使用<a>标签定义超链接,这里设置”智能问诊“页面的url为'search',因此后续在实现”智能问诊“页面时应与此处相对应。

    <div class="section  text-gray" id="section2">
        <div class="container">
            <div class="col-md-12">
                <h2 class="title" data-animate="fadeInDown">诊断水稻疾病</h2>
                <div class="row">
                    <div class="col-md-8 col-md-offset-2">
                        <p class="text-large text-thin" data-animate="fadeInUp">水稻作为中国主要的粮食作物之一,每年因病虫害造成的损失多达几百万吨……</p>
                        <p class="text-large text-thin" data-animate="fadeInUp">
                            智能机器人“稻稻”帮助您科学地诊断水稻疾病</p>
                        <a href="{% url 'search' %}" style="font-size: 20px;">开始问诊</a>
                    </div>
                </div>
            </div>
        </div>
    </div>

     为了让主页内容更饱满,在“智能问诊介绍“模块的下方又定义显示”农医对话机器人“、”科学问诊 引领农业发展“等字样,效果如下图所示:

    <div class="section text-gray" id="section3" data-animate="fadeInUp">
        <div class="container">
            <div class="col-md-12">
                <div class="mb20">
                    <h2 class="title" data-animate="fadeInUp">农医对话机器人</h2>
                    <p class="lead text-center" data-animate="fadeInUp">科学问诊 引领农业发展</p>
                </div>
            </div>
        </div>
    </div>

     定义页面底部,显示”关于我们“、”联系方式“、”地址“等信息,效果如下图所示:

    <div class="section" id="footer">
        <div class="container">
            <div style="text-align: left">
                <img src="{% static 'img/people.png' %}">
                <span style="color:white;">关于我们:农医对话机器人</span>
            </div>
            <div style="text-align: left">
                <img src="{% static 'img/tele.png' %}">
                <span style="color:white;">联系方式:xxxxxxxxxx</span>
            </div>
            <div style="text-align: left">
                <img src="{% static 'img/location.png' %}">
                <span style="color:white;">地址:xxxxxxxxxx</span>
            </div>
        </div>
    </div>
</div>

     引入js文件,以控制网页的行为,对页面事件做出响应。

<!-- js base -->
<script src="{% static 'js/jquery-1.11.0.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
<!-- waypoints for scroll spy -->
<script src="{% static 'js/waypoints.min.js' %}"></script>
<!-- owl carousel -->
<script src="{% static 'js/owl.carousel.min.js' %}"></script>
<!-- jQuery scroll to -->
<script src="{% static 'js/jquery.scrollTo.min.js' %}"></script>
<!-- main js file -->
<script src="{% static 'js/front.js' %}"></script>
</body>
</html>
3. 配置网站

     (1)修改settings.py文件

     打开Inquiries/Inquiries/目录下的settings.py文件。

     设置允许任何ip访问。找到ALLOWED_HOSTS变量,进行如下修改:

ALLOWED_HOSTS = ['*']

     修改完成后如下图所示:

     为了使创建的应用生效,找到INSTALLED_APPS变量,将应用程序account、app添加到INSTALLED_APPS列表,如下图:     

     为了使模板生效,找到TEMPLATES,在‘DIRS’中添加如下代码,并将APP_DIRS修改为”False“。

     为了使数据库文件生效,找到DATABASES,修改数据库文件的路径如下:

     为了使静态文件生效,找到STATIC_URL,在下面添加如下代码:

     修改为中文,如下图所示:

     添加url,在setting.py文件的最后添加以下代码:

LOGIN_REDIRECT_URL = 'index'

    代码添加后如下图所示:

     (2)配置视图

     在Inquiries/Inquiries/目录下新建一个 views.py 文件,配置视图。

     在views.py文件中添加以下代码:

     注:render是django提供的方法和规则,当需要返回一个html文件时,就要用render方法来渲染。

from django.shortcuts import render
def home(request): 
    context={}
    return render(request,'index.html',context)

     (3)配置主页路由

     绑定 URL 与视图函数。打开 Inquiries/Inquiries/目录下的urls.py 文件,删除原来代码,将以下代码复制粘贴到 urls.py 文件中:

from django.contrib import admin
from django.urls import path,include
from . import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('',views.home,name="home"),
    path('account/', include("account.urls")),   # 添加下一级路由
    path('app/', include("app.urls")),  # 添加下一级路由
]

3.用户注册和用户登录

任务描述

本任务中实现前端模块—“水稻疾病在线问诊平台”的注册和登录功能。

知识点:Django、HTML、CSS

重    点:Django、HTML、CSS

难    点:Django、HTML、CSS

内    容:1. 开发准备:创建CSS文件和HTML文件

              2. 编写基础文件

              3. 定义用户注册页面,效果如下图所示:

              4. 定义用户注册完成页面,效果如下图所示:

              5. 定义用户登陆页面,效果如下图所示:

              6. 实现用户注册和登录功能

                (1)创建表单

                (2)定义视图函数

                (3)配置路由

1. 准备工作

     (1)创建CSS文件

     在static/css/目录下创建base.css文件。

     (2)创建html文件

     在templates/目录下创建base.html文件;

     在templates/account/目录下创建register.html、register_done.html文件;

     在templates/registration/目录下创建login.html文件。

     删除所有html文件中自动生成的代码。

2. 编写基础文件

     在base.css文件文件中编写代码,以定义表单的位置、输入框的宽度以及错误列表的样式:

/* 设置表单的居中和宽度 */
body > .grid {
  height: 80%;
}
.column {
  max-width: 450px;
}
/* 设置输入框的宽度和错误列表的样式 */
.errorlist {
    list-style: none;
    color: red;
}
#id_question {
    width: 600px;
}

     在base.html文件中编写代码,以实现模板文件。后续实现的注册、登录等html页面可以继承自base.html文件,避免代码出现溶于的情况。

     加载静态文件。

{% load static %}

     定义头部标签元素。

     在<head>标签中定义文档的头部。使用<meta>标签定义基本的元数据,使用<title>标签定义标题;使用<link>标签添加css文件的链接。

<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>{% block title %}{% endblock %}</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css">
  <link rel="stylesheet" href="{% static 'css/base.css' %}">
  {% block css %}{% endblock %}
</head>

     定义页面的主体。

     使用{% block classinfo %}……{% endblock %}标签写个空盒子,以后需要扩展内容就在这个盒子里面添加相应的内容。为了更好的可读性,可以给你的 {% endblock %} 标签一个 名字 ,如 {% block content %}……{% endblock %};

     使用<script>标签引入”jquery.min.js“、”semantic.min.js“等外部脚本文件。

<body>

    {% block content %}{% endblock %}
  <div class="container">
    {% block lslide %}{% endblock %}
  </div>
  <div class="container rslide">
    {% block rslide %}{% endblock %}
  </div>
  <div class="container footer">
    {% block footer %}{% endblock %}
  </div>
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.js"></script>
  {% block js %}{% endblock %}
</body>
</html>
3. 定义用户注册页面

     在register.html文件中编写代码,以定义用户注册页面。

     申明继承”base.html“模板。

{% extends 'base.html' %}

     添加页面标题。

{% block title %}
注册
{% endblock %}

     在{% block content %}……{% endblock %}盒子中添加代码,实现账号注册页面,初始注册页面如下图所示:

       当存在用户名中包含非法字符、两次密码不一致等错误时,点击“注册”按钮后,会显示“!您的输入有误,请重新输入”的提示:

     代码如下:

{% block content %}
<div class="ui middle aligned center aligned grid">
    <div class="column">
        {% if user_form.errors %}
        <h2 class="ui teal image header">
            <i class="exclamation icon"></i>
            <div class="content">
                您的输入有误,请重新输入
            </div>
        </h2>
        {% else %}
        <h2 class="ui teal image header">
            <i class="user icon"></i>
            <div class="content" style="color: #6aae7a;">
                注册您的账号
            </div>
        </h2>
        {% endif %}

        <form action="." method="post" id="register_form" class="ui large form">
            <div class="ui raised segment">
                {% for field in user_form %}
                <div class="field">
                    <div class="ui left icon input">
                        {% if forloop.counter  %}
                        <i class="user icon"></i>
                        {% else %}
                        <i class="lock icon"></i>
                        {% endif %}
                        {{ field }}
                    </div>
                </div>
                <div class="form_error field">{{ field.errors }}</div>
                {% endfor %}
                {% csrf_token %}
                <div class="ui fluid large teal submit button" style="background-color: #6aae7a;"
                     onclick="document.getElementById('register_form').submit()">注册</div>
            </div>
        </form>
    </div>
</div>
<script>
    document.getElementById("id_username").setAttribute("placeholder", "请输入用户名")
    document.getElementById("id_password").setAttribute("placeholder", "请输入密码")
    document.getElementById("id_password2").setAttribute("placeholder", "请再次输入密码")
</script>

{% endblock %}
{% block lslide %}{% endblock %}
{% block rslide %}{% endblock %}
{% block footer %}{% endblock %}
4. 定义用户注册完成页面

     在register_done.html文件中编写代码,定义注册完成页面。

     申明继承”base.html“模板。

{% extends 'base.html' %}

     添加页面标题。

{% block title %}
注册成功
{% endblock %}

     在{% block content %}……{% endblock %}盒子中添加代码,实现账号注册完成后的显示页面,效果如下图所示:

     点击”登录“两个字时页面应自动调转到”登录“页面,因此需要使用<a>标签添加超链接。注意

{% block content %}
    <div class="ui middle aligned center aligned grid">
        <div class="column">
            <h2 class="ui teal image header">
                <i class="user icon"></i>
                <div class="content">
                    欢迎您,{{ new_user.username }}!你现在已经成功注册了账号,点此<a href="{% url 'login' %}">登录</a>
                </div>
            </h2>
        </div>
    </div>
{% endblock %}
{% block lslide %}{% endblock %}
{% block rslide %}{% endblock %}
{% block footer %}{% endblock %}
5. 定义用户登陆页面

    在login.html文件中编写代码,以定义用户登陆页面。

     申明继承”base.html“模板。

{% extends 'base.html' %}

     添加页面标题。

{% block title %}
登录
{% endblock %}

     在{% block content %}……{% endblock %}盒子中添加代码,实现账号注册完成后的显示页面,效果如下图所示:

     当账号不存在或者密码输入错误时,会输出” 您的账号不匹配,请重新输入“的提示,如下图所示:

{% block content %}
<div class="ui middle aligned center aligned grid">
  <div class="column">
      {% if form.errors %}
      <h2 class="ui teal image header">
          <i class="exclamation icon"></i>
          <div class="content">
              您的账号不匹配,请重新输入
          </div>
        </h2>
      {% else %}
      <h2 class="ui teal image header">
          <i class="user icon"></i>
          <div class="content" style="color: #6aae7a;">
            登录您的账号
          </div>
        </h2>
      {% endif %}

    <form method="post" class="ui large form" id="login_form" action="{% url 'login' %}">
      <div class="ui raised segment">
        {% for field in form %}
        <div class="field">
          <div class="ui left icon input">
          {% if forloop.counter %}
          <i class="user icon"></i>
          {% else %}
          <i class="lock icon"></i>
          {% endif %}
          {{ field }}
          </div>
        </div>
        <div class="_error field">{{ field.errors }}</div>
        {% endfor %}
        {% csrf_token %}
        <input type="hidden" name="next" value="{{ next }}">
        <div class="ui fluid large teal submit button" style="background-color: #6aae7a;"
             onclick="document.getElementById('login_form').submit()">登录</div>
      </div>
    </form>

    <div class="ui message">
      没有账号?请在此处<a href="{% url 'register' %}"><b>注册</b></a>
    </div>
  </div>
</div>
<script>
    document.getElementById("id_username").setAttribute("placeholder", "请输入用户名")
    document.getElementById("id_password").setAttribute("placeholder", "请输入密码")
</script>
{% endblock %}
6. 实现用户注册和登录功能

     (1)创建表单

     在Inquiries/account/目录下创建forms.py,并添加以下代码,用于在用户注册时创建表单来提交用户名和密码:

from django import forms
from django.contrib.auth.models import User

# 创建表单,用于提交用户名和密码
class UserRegistrationForm(forms.ModelForm):
    password = forms.CharField(label='密码', widget=forms.PasswordInput) # 注册时第一次输入的密码
    password2 = forms.CharField(label='再次输入密码', widget=forms.PasswordInput) # 确认密码(第二次)
    class Meta:
        model = User
        fields = ('username',)

    # 检查两次输入的密码是否一致
    def clean_password2(self):
        cd = self.cleaned_data
        if cd['password'] != cd['password2']: # 如果两次输入的密码不一致,则抛出错误信息
            raise forms.ValidationError("两次输入的密码不一致")
        return cd['password2'] # 保存第二次(确认)的密码

     当注册账号时,先后两次输入的密码不一致,会出现如下图所示的提示:

     (2)定义视图函数

     在Inquiries/account/目录下的views.py文件中编写代码,定义视图函数,以接收注册和登录账号时的请求,并进行相应的处理。具体解释请见代码中的注释。

from django.shortcuts import render
from .forms import UserRegistrationForm
from django.contrib.auth.decorators import login_required

# 检查用户是否成功登录,登录则重定向到主页面
@login_required
def index(request):
    return render(request, 'app/index.html')

# 用户注册
def register(request):
    if request.method == "POST": # 如果是‘POST’请求
        user_form = UserRegistrationForm(request.POST) # 实例化表单对象
        if user_form.is_valid(): # 验证表单数据是否合法
            new_user = user_form.save(commit=False) # commit=False告诉Django先不提交到数据库
            new_user.set_password(user_form.cleaned_data['password']) # 使用set_password方法将用户的密码进行加密后再进行保存操作
            new_user.save()
            return render(request, 'account/register_done.html', {'new_user': new_user}) # 当用户注册成功,返回register_done页面
    else:
        user_form = UserRegistrationForm()
    return render(request, 'account/register.html', {'user_form': user_form}) # 初始访问的时候返回register页面

     (3)配置路由

     在Inquiries/account/目录下创建urls.py文件,并添加以下代码,以实现路由的配置,简单来说,就是对于某段url该调用哪段代码。

from django.urls import path
from . import views
from django.contrib.auth import views as auth_views

urlpatterns = [
    path('login/',auth_views.LoginView.as_view(),name='login'),
    path('register/', views.register, name='register'),
]

     在Inquiries/Inquiries/settings.py文件中LOGIN_REDIRECT_URL变量的下方添加以下代码:

LOGIN_URL = 'login'

     添加位置如下图所示:

4.智能问诊

任务描述

本任务中实现前端模块—“水稻疾病在线问诊平台”的智能问诊功能。

知识点:Django、HTML、CSS、JS

重    点:Django、HTML、CSS、JS

难    点:Django、HTML、CSS、JS

内    容:1. 开发准备:创建CSS文件和HTML文件

              2. 定义智能问诊页面,效果如下图所示:

              3. 实现智能问诊

                 (1)创建视图

                 (2)配置路由

              4. 启动前端模块  

                 (1)启动neo4j服务

                 (2)启动Redis对话管理服务

                 (3)挂起两句话相关性判断模型

                 (4)挂起对话生成服务

                 (5)启动前端模块,进入“水稻疾病”在线问诊平台如下图所示:

                        智能问诊的对话过程如下图所示:

1. 开发准备

     (1)创建HTML文件

     在templates/app/目录下新建一个html文件,命名为"search.html",创建好的文件如下图所示:

     删除search.html文件中所有生成的代码。

  (2)创建CSS文件

     在static/css/目录下新建一个css文件,命名为“search.css”。

2. 定义智能问诊页面

     在本任务中需要实现智能问诊功能,智能问诊页面效果如下图所示:

     (1)设置“智能问诊”页面的基本样式

     在本模块中,设置html页面的基本样式,在search.css文件中添加本模块的代码。

     由于不同浏览器对html标签的默认外边距和内边距的值不同,为了在主流浏览器中统一标准让布局显示相同,需要将html所有标签的padding,margin都设置为0;

     设置盒模型为border-box, 即盒子大小(宽高)包括padding和border。

  html,
  body {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }

     去除a标签的默认样式:

  • ”text-decoration: none“:去除自带下划线;
  • ”outline: none“:去除轮廓线;
  • ”color: #000”:设置颜色为黑色。
  a {
    text-decoration: none;
    outline: none;
    color: #000;
  }

     设置页面的宽和高均为100%:

  .search {
    width: 100%;
    height: 100%;
  }

     设置头部样式,flex布局,上下居中。

  .header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    box-sizing: border-box;
    padding: 0px 40px;
    width: 100%;
    height: 80px;
    background: #ffffff;
    border-bottom: 1px solid #cccccc;
  }

     设置header中文本的样式。

  .header-left {
    display: flex;
    align-items: center;
    color: #519461;
    font-weight: 500;
    font-size: 22px;
  }

     设置“结束对话”的字体样式。

  .close {
    font-weight: 600;
    cursor: pointer;
  }

     设置header中的头像的样式。

  .avatar {
   margin-right: 16px;
   width: 60px;
   height: 60px;
   border-radius: 50%;
   background: lightgreen;
 }

     设置聊天内容区域的样式。

  .container {
    box-sizing: border-box;
    padding: 30px;
    width: 100%;
    /* height: 300px; */
    min-height: calc(100vh - 280px);
    max-height: calc(100vh - 280px);
    overflow-y: auto;
    background-color: #f7f7f7;
  }

     设置消息体最外层盒子的样式。

  .message {
    margin-bottom: 10px;
    display: flex;
  }

     设置靠右的消息体的样式。

  .message-right {
    width: 100%;
    display: flex;
    flex-direction: row-reverse;
  }

     设置消息体中的头像的样式。

  .avatar-m {
   margin: 0px 10px;
   width: 40px;
   height: 40px;
   border-radius: 50%;
   background-color: aquamarine;
 }

     设置对话消息显示区域文字的样式。

  .text-area {
   max-width: 400px;
   padding: 6px;
   border-radius: 8px;
   border: 1px solid #cccccc;
   background: #ffffff;
 }

     设置时间的样式。

  .date {
    font-size: 12px;
    color: #999;
  }

     设置消息输入框的样式。

  .send-area {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    width: 100%;
    height: 200px;
    background: #ffffff;
  }

     设置发送按钮的样式。

  .add {
   width: 100px;
   height: 30px;
   margin-right: 20px;
   border: none;
   cursor: pointer;
 }

     设置输入文本的样式。

  .input-area {
   padding: 20px;
   box-sizing: border-box;
   width: 100%;
   height: 160px;
   resize: none;
   outline: none;
   border: none;
   font-size: 16px;
   line-height: 1.5em;
 }

     (2)定义智能问诊html页面

     在search.html文件中编写本模块的代码

     加载静态文件。

{% load static %}

     定义头部标签元素。在<head>标签中定义页面的头部。使用<meta>标签定义基本的元数据,使用<title>标签定义标题,使用<link>标签来链接样式文件”search.css“。

<html>
<head>
    <title>Flat Search Box Responsive Widget Template | Home :: w3layouts</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta name="keywords" content="Flat Search Box Responsive, Login form web template, Sign up Web Templates, Flat Web Templates,
Login signup Responsive web template, Smartphone Compatible web template, free webdesigns for Nokia, Samsung, LG, SonyErricsson, Motorola web design" />
    <link rel="stylesheet" href="{% static 'css/search.css' %}">
</head>

     定义页面主体。< body>< /body>元素定义页面的主体,包含文档的所有内容。”智能问诊“页面效果如下图所示:

     其中,点击”结束对话“会返回主页面。

<body>
<div class="search">
    <div class="header">
      <div class="header-left">
        <img src="{% static 'img/rice.png' %}" class="avatar" />
        稻稻正在和您对话
      </div>
       <a href="{% url 'index'%}"><div class="close">结束对话</div></a>
    </div>

    <div class="container" id="container">
    </div>

    <div class="send-area">
      <textarea id="input" class="input-area" placeholder="请输入内容。。。"></textarea>

      <button class="add" id="add">发送</button>
    </div>
  </div>
<script src="{% static 'js/jquery.min.js' %}"></script>
</body>

     实现智能问诊功能。

     在文本框中输入语句后,点击”发送“按钮,会调用”对话生成“服务接口,获取到返回值后会将结果在页面中显示。

     首先,在</body>下面添加<script></script>标签对,后续将在<script></script>中间添加代码。

<script> 
......
</script>

     获取<textarea>元素,监听<textarea>元素的input事件,当输入框内容发生变化时将输入框中的内容储存在一个变量中。

  let inputValue = ''
  let input = document.getElementById('input')
  input.addEventListener('input', function () {
    inputValue = input.value
  })

     发送网络请求。

     获取发送按钮的dom,并监听此dom元素的click事件。当点击发送按钮时判断 isSend 和 inputValue 的值是否都为true。当它们的值都为true时,则执行以下操作:

  • 实例化一个XMLHttpRequest的实例,并设置超时时间为3s;
  • 生成一个FormData实例,向这个FormData实例中添加id_for_user和talk_user属性;
  • 开启一个post的网络请求,调用”对话生成“服务接口;
  • 监听XMLHttpRequest实例的load事件(如果网络请求成功),在回调函数中调用addMessage()方法,并将isSend设置为true。addMessage()方法的作用是在聊天内容显示区域中显示文字;
  • 监听XMLHttpRequest实例的error事件(网络请求失败),在回调函数中调用addMessage()方法,并将isSend设置为true;
  • 监听XMLHttpRequest实例的timeout事件(网络请求超时),在回调函数中调用addMessage()方法,并将isSend设置为true;
  • 发送网络请求;
  • 调用addMessage()方法,将所发内容添加到聊天区域中;
  • 清空输入框和inputValue中的内容,isSend设置为false。
  let isSend = true
  let add = document.getElementById('add')
  add.addEventListener('click', function () {
    console.log('12312', inputValue)
    if (isSend && inputValue) {
      let xhr = new XMLHttpRequest()
      xhr.timeout = 3000
      let formData = new FormData()
      formData.append('id_for_user', 1)
      formData.append('talk_user', inputValue)
      xhr.open('POST', 'http://127.0.0.1:8080/farmer/receive', true)
      xhr.addEventListener("load", function () {
        console.log(this)
        isSend = true
        addMessage(this.responseText, parseTime(new Date().getTime(), '{y}-{m}-{d} {h}:{i}:{s}'), 'left')
      });
      xhr.addEventListener("error", function () {
        addMessage('请求失败!', parseTime(new Date().getTime(), '{y}-{m}-{d} {h}:{i}:{s}'), 'left')
        isSend = true
      });
      xhr.addEventListener("timeout", function () {
        addMessage('请求超时!', parseTime(new Date().getTime(), '{y}-{m}-{d} {h}:{i}:{s}'), 'left')
        isSend = true
      });      
      xhr.send(formData);
      addMessage(inputValue, parseTime(new Date().getTime(), '{y}-{m}-{d} {h}:{i}:{s}'), 'right')
      input.value = ''
      inputValue = ''
      isSend = false
    }
  })      

     定义addMessage()方法,用于聊天内容显示区域中显示文字和头像。该方法接收以下三个参数:

  • message:消息内容, 
  • time:消息发送或接收时间
  • type:可接收值为left或right,分别代表在聊天区域中靠左和靠右

一个消息体由以下元素构成:

  function addMessage (message, time, type) {
    let el = document.createElement('div')
    el.setAttribute('class', `message message-${type}`)
    let avatarEl = document.createElement('img')
    avatarEl.setAttribute('class', 'avatar-m')
    if (type === 'left') {
      avatarEl.setAttribute('src', '{% static 'img/rice.png' %}')
    } else {
      avatarEl.setAttribute('src', '{% static 'img/people1.png' %}')
    }
    let areaEl = document.createElement('div')
    areaEl.setAttribute('class', 'text-area')
    let dateEl = document.createElement('div')
    dateEl.setAttribute('class', 'date')
    dateEl.innerText = time
    let textEl = document.createElement('div') //创建元素
    textEl.setAttribute('class', 'text') //添加属性
    textEl.innerText = message
    areaEl.appendChild(dateEl) //为元素添加子元素
    areaEl.appendChild(textEl)
    el.appendChild(avatarEl)
    el.appendChild(areaEl)
    const containerEl = document.getElementById('container')
    containerEl.appendChild(el)
    containerEl.scrollTop = 10000000000
    input.value = ''
  }

     定义parseTime()方法,用于处理对话过程中每句消息的发送和响应时间。

  function parseTime (time, cFormat) {
  if (arguments.length === 0 || !time) {
    return null
  }
  
  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
  let date
  if (typeof time === 'object') {
    date = time
  } else {
    if ((typeof time === 'string')) {
      if ((/^[0-9]+$/.test(time))) {
        // support "1548221490638"
        time = parseInt(time)
      } else {
        // support safari
        // https://stackoverflow.com/questions/4310953/invalid-date-in-safari
        time = time.replace(new RegExp(/-/gm), '/')
      }
    }

    if ((typeof time === 'number') && (time.toString().length === 10)) {
      time = time * 1000
    }
    date = new Date(time)
  }
  const formatObj = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay()
  }
  const timeStr = format.replace(/{([ymdhisa])+}/g, (result, key) => {
    const value = formatObj[key]
    // Note: getDay() returns 0 on Sunday
    if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
    return value.toString().padStart(2, '0')
  })
  return timeStr
}

     最后,不要忘记补充完成<html></html>标签对。

</html>
3. 实现智能问诊

     (1)创建视图

     在app/目录下的views.py文件中编写本模块的代码,定义视图函数,以接收智能问诊页面的请求,并进行相应的处理。

from django.contrib.auth.decorators import login_required
from django.shortcuts import render
@login_required
def index(request):
    return render(request, 'index.html')
@login_required
def search(request):
    return render(request, 'app/search.html')

     (2)配置路由

     在app/目录下创建urls.py文件,并添加以下代码:

from django.urls import path
from . import views
urlpatterns = [
   path('', views.index, name='index'),
   path('search/', views.search, name="search"),
]
4. 启动前端模块

     按照以下步骤,启动所有的服务:   

  (1)启动neo4j服务

     在终端中,进入/opt/neo4j-community-4.4.12/bin/目录下:

cd /opt/neo4j-community-4.4.12/bin/

     输入以下命令启动Neo4j:

./neo4j start

     启动过程如下图所示:

     (2)启动Redis对话管理服务

     重新打开一个终端,在终端输入以下命令启动Redis:

redis-server

    启动过程如下图所示:

     (3)挂起两句话相关性判断模型

     重新打开一个终端,在终端中,先切换到~/Desktop/Ai_Farmer_Doctor/networking/目录下,再运行下面的命令:

gunicorn -w 1 -b 0.0.0.0:8084 deploy_model:app

     注意:挂起对话生成服务的端口必须是8084,否则,需要修改principal.py文件中的subjectmodel_url参数。

     命令执行结果如下图所示:


      (4)挂起对话生成服务

     重新打开一个终端,在终端中,先切换到~/Desktop/Ai_Farmer_Doctor/networking/目录下,再执行以下命令:

gunicorn -w 1 -b 0.0.0.0:8080 principal:app

     注意:挂起对话生成服务的端口必须是8080,否则,需要修改search.html文件中的发送网络请求部分的访问端口号。eg.如果这里挂起的端口号为8085,则需要将search.html中发送网络请求部分的访问端口号修改为8085。

     启动过程如下图所示:

     (5)启动前端模块

      重新打开一个终端,在终端中,先切换到~/Desktop/Ai_Farmer_Doctor/Inquiries/目录下,再执行以下命令:

python manage.py runserver 0.0.0.0:8000

     说明:此处的端口号可以更改,但要注意不要与上述服务的端口冲突。

     启动过程如下图所示:

     接着, 在浏览器中访问http://localhost:8000/,进入”水稻疾病在线问诊平台“如下图所示:

     点击导航栏中的”注册“,进入账号注册页面:

     填写用户名和密码,并点击”注册“按钮

     账号注册完成后,点击”登录“,来登陆账号:

     账号登录之后,会跳转到首页,并且在导航栏的下方会出现”欢迎您:xx“的信息,“xx”表示用户名:

     点击导航栏中的”智能问诊“,跳转到如下图所示的模块:

     点击”开始问诊“即可进入”智能问诊“页面,如下图所示:

     在消息输入框中输入语句,点击”发送“按钮,即可开始进行对话,如依次输入以下消息:

稻稻,你好
我家的水稻叶尖灰白色干枯,可能是什么病呀?
而且孕穗后干尖更严重
……

     对话结果如下图所示:

     同时,可以在启动对话生成服务的终端中,有以下处理过程:

     此外,若需要在本地浏览器中访问,则需要将search.html文件中请求对话生成服务的IP地址更改为公网IP。

     首先,获取实验蓝图的公网IP,在如下图所示的位置:

     将调用对话生成服务的IP地址改为公网IP。

     更改完成后,重新挂起对话生成服务,并重新启动前端模块。

     重新启动完成后,在本地浏览器中访问http://124.70.109.223:8000/,进入”水稻疾病在线问诊平台“如下图所示:

     其余操作与在实验环境中的操作一致。

5.功能测试

任务描述

在本任务中,对水稻疾病在线问诊平台进行功能测试,并完成《测试报告》,若不满足使用要求,则开发人员需要进行迭代优化。

知识点:功能测试

重    点:功能测试

难    点:功能测试

内    容:1. 测试注册功能

              2. 测试登录功能

              3. 测试智能问诊功能

              4. 本地测试

按照(4.智能问诊) 任务中“4.启动前端模块”的步骤启动各个服务后,按照以下几部分测试每个模块的功能:

1.  测试注册功能

     (1)测试主页面导航栏中的“注册”是否能跳转到注册页面

     点击主页面导航栏中的“注册”,如下图所示:

     能跳转到注册页面,如下图所示:

     通过测试。

     (2)测试特殊字符

     用户注册时,用户名中不应出现特殊字符。参考:使用用户名“~~”进行注册,提示如下图所示:

     出现如上图所示的提示,且无法进行注册,则通过测试。

     (3)两次输入密码不一致

     两次输入密码不一致的情况,点击“注册”按钮后会出现如下图所示的提示:

     出现如上图所示的提示,则通过测试。

     (4)测试同一用户名重复注册

     使用已注册过的用户名,再次注册,点击“注册”按钮后,会出现如下图所示的提示:

     出现如上图所示的提示,则通过测试。

     (5)测试注册完成页面的“登录”是否能跳转到“登录”页面

     用户注册完成后,进入注册完成页面,如下图所示:

     点击“登录”两个字,跳转到登录页面如下图所示:

     能够跳转,则通过测试。

2. 测试登录功能

     (1)测试主页面导航栏中的“登录”是否能跳转到登录页面

     点击主页面导航栏中的“登录”,如下图所示:

     能跳转到登录页面,如下图所示:

     然后,点击上图中的“注册”两个字,能跳转到注册页面,通过测试。

     (2)测试密码不正确的情况

     已注册过的账号,输入错误的密码,出现如下图所示的提示:

     密码错误,无法登录,则通过测试。

     (3)测试使用“智能问诊”功能是否必须要先登录

     回到主页面,点击导航栏中的“智能问诊”,如下图所示:

     点击下图所示的“开始问诊”:

     没有登录账号,则会进入登录账号页面:

     没有登录账号,使用“智能问诊”功能时,能够跳转到“登录”页面,则通过测试。

3. 测试智能问诊功能

     登录账号后,开始测试“智能问诊”功能。

     (1)测试百度智能对话平台是否能正常兜底

     在输入框中输入“你好,稻稻”或者其他自然语句,看对话机器人是否能正常聊天。第一次输入,可能会出现“请求超时”的情况,再重新输入一次,便可以正常对话,如下图所示:

     能够正常对话,则百度智能对话平台能正常兜底,测试通过。

     (2)测试图数据库是否能查询成功

     描述“水稻病症状”,稻稻回答“稻稻判断您的水稻可能以下农业病:XXX”,如下图所示:

     能够查询到对应的水稻疾病,则表示能够正常查询图数据库,通过测试。

     (3)测试能否进行对话管理和句子相关性判断

     分次输入两句描述同一种“水稻病症状”的语句,输出结果如下图所示:

     若两次查询结果相同,稻稻会回答“稻稻觉得还是稻稻之前为您判断的水稻病,并不是新的水稻病”,说明已进行了会话管理。

     且挂起“对话生成”服务的终端,会输出判断两句话是否相关的结果,“1”表示两句话相关。

     测试通过,同学们可通过其他的测试用例进行测试。

     (4)测试点击“结束对话”是否能跳转到主页面

     点击智能问诊页面的“结束对话”,如下图所示:

     能够返回主页面,则通过测试,如下图所示:

     4. 本地测试

     (1)测试是否能在本地正常访问

     将search.html文件中请求对话生成服务的IP地址更改为公网IP后,在本地访问。

     (2)测试注册、登录、智能问诊功能

     按照上述步骤1、2、3测试注册、登录、智能问诊功能。

     测试完成后,完成《测试报告》,若不满足使用要求,则开发人员需要进行迭代优化。

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值