|
|
from flask import Flask, render_template, session, redirect, url_for, flash, request, jsonify
|
|
|
import os
|
|
|
from flask_sqlalchemy import SQLAlchemy
|
|
|
from flask_script import Manager, Shell
|
|
|
from forms import Login, SearchBookForm, ChangePasswordForm, EditInfoForm, SearchStudentForm, NewStoreForm, StoreForm, BorrowForm
|
|
|
from flask_login import UserMixin, LoginManager, login_required, login_user, logout_user, current_user
|
|
|
import time, datetime
|
|
|
|
|
|
|
|
|
basedir = os.path.abspath(os.path.dirname(__file__))
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
manager = Manager(app)
|
|
|
|
|
|
app.config['SECRET_KEY'] = 'hard to guess string'
|
|
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
|
|
|
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
|
|
|
db = SQLAlchemy(app)
|
|
|
|
|
|
|
|
|
def make_shell_context():
|
|
|
return dict(app=app, db=db, Admin=Admin, Book=Book)
|
|
|
|
|
|
|
|
|
manager.add_command("shell", Shell(make_context=make_shell_context))
|
|
|
|
|
|
login_manager = LoginManager()
|
|
|
login_manager.init_app(app)
|
|
|
login_manager.session_protection = 'basic'
|
|
|
login_manager.login_view = 'login'
|
|
|
login_manager.login_message = u"请先登录。"
|
|
|
|
|
|
|
|
|
class Admin(UserMixin, db.Model):
|
|
|
__tablename__ = 'admin'
|
|
|
admin_id = db.Column(db.String(6), primary_key=True)
|
|
|
admin_name = db.Column(db.String(32))
|
|
|
password = db.Column(db.String(24))
|
|
|
right = db.Column(db.String(32))
|
|
|
|
|
|
def __init__(self, admin_id, admin_name, password, right):
|
|
|
self.admin_id = admin_id
|
|
|
self.admin_name = admin_name
|
|
|
self.password = password
|
|
|
self.right = right
|
|
|
|
|
|
def get_id(self):
|
|
|
return self.admin_id
|
|
|
|
|
|
def verify_password(self, password):
|
|
|
if password == self.password:
|
|
|
return True
|
|
|
else:
|
|
|
return False
|
|
|
|
|
|
def __repr__(self):
|
|
|
return '<Admin %r>' % self.admin_name
|
|
|
|
|
|
|
|
|
class Book(db.Model):
|
|
|
__tablename__ = 'book'
|
|
|
isbn = db.Column(db.String(13), primary_key=True)
|
|
|
book_name = db.Column(db.String(64))
|
|
|
author = db.Column(db.String(64))
|
|
|
press = db.Column(db.String(32))
|
|
|
class_name = db.Column(db.String(64))
|
|
|
|
|
|
def __repr__(self):
|
|
|
return '<Book %r>' % self.book_name
|
|
|
|
|
|
|
|
|
class Student(db.Model):
|
|
|
__tablename__ = 'student'
|
|
|
card_id = db.Column(db.String(8), primary_key=True)
|
|
|
student_id = db.Column(db.String(9))
|
|
|
student_name = db.Column(db.String(32))
|
|
|
sex = db.Column(db.String(2))
|
|
|
telephone = db.Column(db.String(11), nullable=True)
|
|
|
enroll_date = db.Column(db.String(13))
|
|
|
valid_date = db.Column(db.String(13))
|
|
|
loss = db.Column(db.Boolean, default=False) # 是否挂失
|
|
|
debt = db.Column(db.Boolean, default=False) # 是否欠费
|
|
|
|
|
|
def __repr__(self):
|
|
|
return '<Student %r>' % self.student_name
|
|
|
|
|
|
|
|
|
class Inventory(db.Model):
|
|
|
__tablename__ = 'inventory'
|
|
|
barcode = db.Column(db.String(6), primary_key=True)
|
|
|
isbn = db.Column(db.ForeignKey('book.isbn'))
|
|
|
storage_date = db.Column(db.String(13))
|
|
|
location = db.Column(db.String(32))
|
|
|
withdraw = db.Column(db.Boolean, default=False) # 是否注销
|
|
|
status = db.Column(db.Boolean, default=True) # 是否在馆
|
|
|
admin = db.Column(db.ForeignKey('admin.admin_id')) # 入库操作员
|
|
|
|
|
|
def __repr__(self):
|
|
|
return '<Inventory %r>' % self.barcode
|
|
|
|
|
|
|
|
|
class ReadBook(db.Model):
|
|
|
__tablename__ = 'readbook'
|
|
|
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
|
barcode = db.Column(db.ForeignKey('inventory.barcode'), index=True)
|
|
|
card_id = db.Column(db.ForeignKey('student.card_id'), index=True)
|
|
|
start_date = db.Column(db.String(13))
|
|
|
borrow_admin = db.Column(db.ForeignKey('admin.admin_id')) # 借书操作员
|
|
|
end_date = db.Column(db.String(13), nullable=True)
|
|
|
return_admin = db.Column(db.ForeignKey('admin.admin_id')) # 还书操作员
|
|
|
due_date = db.Column(db.String(13)) # 应还日期
|
|
|
|
|
|
def __repr__(self):
|
|
|
return '<ReadBook %r>' % self.id
|
|
|
|
|
|
|
|
|
@login_manager.user_loader
|
|
|
def load_user(admin_id):
|
|
|
return Admin.query.get(int(admin_id))
|
|
|
|
|
|
|
|
|
@app.route('/', methods=['GET', 'POST'])
|
|
|
def login():
|
|
|
form = Login()
|
|
|
if form.validate_on_submit():
|
|
|
user = Admin.query.filter_by(admin_id=form.account.data, password=form.password.data).first()
|
|
|
if user is None:
|
|
|
flash('账号或密码错误!')
|
|
|
return redirect(url_for('login'))
|
|
|
else:
|
|
|
login_user(user)
|
|
|
session['admin_id'] = user.admin_id
|
|
|
session['name'] = user.admin_name
|
|
|
return redirect(url_for('index'))
|
|
|
return render_template('login.html', form=form)
|
|
|
|
|
|
|
|
|
@app.route('/logout')
|
|
|
@login_required
|
|
|
def logout():
|
|
|
logout_user()
|
|
|
flash('您已经登出!')
|
|
|
return redirect(url_for('login'))
|
|
|
|
|
|
|
|
|
@app.route('/index')
|
|
|
@login_required
|
|
|
def index():
|
|
|
return render_template('index.html', name=session.get('name'))
|
|
|
|
|
|
|
|
|
@app.route('/echarts')
|
|
|
@login_required
|
|
|
def echarts():
|
|
|
days = []
|
|
|
num = []
|
|
|
today_date = datetime.date.today()
|
|
|
today_str = today_date.strftime("%Y-%m-%d")
|
|
|
today_stamp = time.mktime(time.strptime(today_str + ' 00:00:00', '%Y-%m-%d %H:%M:%S'))
|
|
|
ten_ago = int(today_stamp) - 9 * 86400
|
|
|
for i in range(0, 10):
|
|
|
borr = ReadBook.query.filter_by(start_date=str((ten_ago+i*86400)*1000)).count()
|
|
|
retu = ReadBook.query.filter_by(end_date=str((ten_ago+i*86400)*1000)).count()
|
|
|
num.append(borr + retu)
|
|
|
days.append(timeStamp((ten_ago+i*86400)*1000))
|
|
|
data = []
|
|
|
for i in range(0, 10):
|
|
|
item = {'name': days[i], 'num': num[i]}
|
|
|
data.append(item)
|
|
|
return jsonify(data)
|
|
|
|
|
|
|
|
|
@app.route('/user/<id>')
|
|
|
@login_required
|
|
|
def user_info(id):
|
|
|
user = Admin.query.filter_by(admin_id=id).first()
|
|
|
return render_template('user-info.html', user=user, name=session.get('name'))
|
|
|
|
|
|
|
|
|
@app.route('/change_password', methods=['GET', 'POST'])
|
|
|
@login_required
|
|
|
def change_password():
|
|
|
form = ChangePasswordForm()
|
|
|
if form.password2.data != form.password.data:
|
|
|
flash(u'两次密码不一致!')
|
|
|
if form.validate_on_submit():
|
|
|
if current_user.verify_password(form.old_password.data):
|
|
|
current_user.password = form.password.data
|
|
|
db.session.add(current_user)
|
|
|
db.session.commit()
|
|
|
flash(u'已成功修改密码!')
|
|
|
return redirect(url_for('index'))
|
|
|
else:
|
|
|
flash(u'原密码输入错误,修改失败!')
|
|
|
return render_template("change-password.html", form=form)
|
|
|
|
|
|
|
|
|
@app.route('/change_info', methods=['GET', 'POST'])
|
|
|
@login_required
|
|
|
def change_info():
|
|
|
form = EditInfoForm()
|
|
|
if form.validate_on_submit():
|
|
|
current_user.admin_name = form.name.data
|
|
|
db.session.add(current_user)
|
|
|
flash(u'已成功修改个人信息!')
|
|
|
return redirect(url_for('user_info', id=current_user.admin_id))
|
|
|
form.name.data = current_user.admin_name
|
|
|
id = current_user.admin_id
|
|
|
right = current_user.right
|
|
|
return render_template('change-info.html', form=form, id=id, right=right)
|
|
|
|
|
|
|
|
|
@app.route('/search_book', methods=['GET', 'POST'])
|
|
|
@login_required
|
|
|
def search_book(): # 这个函数里不再处理提交按钮,使用Ajax局部刷新
|
|
|
form = SearchBookForm()
|
|
|
return render_template('search-book.html', name=session.get('name'), form=form)
|
|
|
|
|
|
|
|
|
@app.route('/books', methods=['POST'])
|
|
|
def find_book():
|
|
|
|
|
|
def find_name():
|
|
|
return Book.query.filter(Book.book_name.like('%'+request.form.get('content')+'%')).all()
|
|
|
|
|
|
def find_author():
|
|
|
return Book.query.filter(Book.author.contains(request.form.get('content'))).all()
|
|
|
|
|
|
def find_class():
|
|
|
return Book.query.filter(Book.class_name.contains(request.form.get('content'))).all()
|
|
|
|
|
|
def find_isbn():
|
|
|
return Book.query.filter(Book.isbn.contains(request.form.get('content'))).all()
|
|
|
|
|
|
methods = {
|
|
|
'book_name': find_name,
|
|
|
'author': find_author,
|
|
|
'class_name': find_class,
|
|
|
'isbn': find_isbn
|
|
|
}
|
|
|
books = methods[request.form.get('method')]()
|
|
|
data = []
|
|
|
for book in books:
|
|
|
count = Inventory.query.filter_by(isbn=book.isbn).count()
|
|
|
available = Inventory.query.filter_by(isbn=book.isbn, status=True).count()
|
|
|
item = {'isbn': book.isbn, 'book_name': book.book_name, 'press': book.press, 'author': book.author,
|
|
|
'class_name': book.class_name, 'count': count, 'available': available}
|
|
|
data.append(item)
|
|
|
return jsonify(data)
|
|
|
|
|
|
|
|
|
@app.route('/user/book', methods=['GET', 'POST'])
|
|
|
def user_book():
|
|
|
form = SearchBookForm()
|
|
|
return render_template('user-book.html', form=form)
|
|
|
|
|
|
|
|
|
@app.route('/search_student', methods=['GET', 'POST'])
|
|
|
@login_required
|
|
|
def search_student():
|
|
|
form = SearchStudentForm()
|
|
|
return render_template('search-student.html', name=session.get('name'), form=form)
|
|
|
|
|
|
|
|
|
def timeStamp(timeNum):
|
|
|
if timeNum is None:
|
|
|
return timeNum
|
|
|
else:
|
|
|
timeStamp = float(float(timeNum)/1000)
|
|
|
timeArray = time.localtime(timeStamp)
|
|
|
print(time.strftime("%Y-%m-%d", timeArray))
|
|
|
return time.strftime("%Y-%m-%d", timeArray)
|
|
|
|
|
|
|
|
|
@app.route('/student', methods=['POST'])
|
|
|
def find_student():
|
|
|
stu = Student.query.filter_by(card_id=request.form.get('card')).first()
|
|
|
if stu is None:
|
|
|
return jsonify([])
|
|
|
else:
|
|
|
valid_date = timeStamp(stu.valid_date)
|
|
|
return jsonify([{'name': stu.student_name, 'gender': stu.sex, 'valid_date': valid_date, 'debt': stu.debt}])
|
|
|
|
|
|
|
|
|
@app.route('/record', methods=['POST'])
|
|
|
def find_record():
|
|
|
records = db.session.query(ReadBook).join(Inventory).join(Book).filter(ReadBook.card_id == request.form.get('card'))\
|
|
|
.with_entities(ReadBook.barcode, Inventory.isbn, Book.book_name, Book.author, ReadBook.start_date,
|
|
|
ReadBook.end_date, ReadBook.due_date).all() # with_entities啊啊啊啊卡了好久啊
|
|
|
data = []
|
|
|
for record in records:
|
|
|
start_date = timeStamp(record.start_date)
|
|
|
due_date = timeStamp(record.due_date)
|
|
|
end_date = timeStamp(record.end_date)
|
|
|
if end_date is None:
|
|
|
end_date = '未归还'
|
|
|
item = {'barcode': record.barcode, 'book_name': record.book_name, 'author': record.author,
|
|
|
'start_date': start_date, 'due_date': due_date, 'end_date': end_date}
|
|
|
data.append(item)
|
|
|
return jsonify(data)
|
|
|
|
|
|
|
|
|
@app.route('/user/student', methods=['GET', 'POST'])
|
|
|
def user_student():
|
|
|
form = SearchStudentForm()
|
|
|
return render_template('user-student.html', form=form)
|
|
|
|
|
|
|
|
|
@app.route('/storage', methods=['GET', 'POST'])
|
|
|
@login_required
|
|
|
def storage():
|
|
|
form = StoreForm()
|
|
|
if form.validate_on_submit():
|
|
|
book = Book.query.filter_by(isbn=request.form.get('isbn')).first()
|
|
|
exist = Inventory.query.filter_by(barcode=request.form.get('barcode')).first()
|
|
|
if book is None:
|
|
|
flash(u'添加失败,请注意本书信息是否已录入,若未登记,请在‘新书入库’窗口录入信息。')
|
|
|
else:
|
|
|
if len(request.form.get('barcode')) != 6:
|
|
|
flash(u'图书编码长度错误')
|
|
|
else:
|
|
|
if exist is not None:
|
|
|
flash(u'该编号已经存在!')
|
|
|
else:
|
|
|
item = Inventory()
|
|
|
item.barcode = request.form.get('barcode')
|
|
|
item.isbn = request.form.get('isbn')
|
|
|
item.admin = current_user.admin_id
|
|
|
item.location = request.form.get('location')
|
|
|
item.status = True
|
|
|
item.withdraw = False
|
|
|
today_date = datetime.date.today()
|
|
|
today_str = today_date.strftime("%Y-%m-%d")
|
|
|
today_stamp = time.mktime(time.strptime(today_str + ' 00:00:00', '%Y-%m-%d %H:%M:%S'))
|
|
|
item.storage_date = int(today_stamp)*1000
|
|
|
db.session.add(item)
|
|
|
db.session.commit()
|
|
|
flash(u'入库成功!')
|
|
|
return redirect(url_for('storage'))
|
|
|
return render_template('storage.html', name=session.get('name'), form=form)
|
|
|
|
|
|
|
|
|
@app.route('/new_store', methods=['GET', 'POST'])
|
|
|
@login_required
|
|
|
def new_store():
|
|
|
form = NewStoreForm()
|
|
|
if form.validate_on_submit():
|
|
|
if len(request.form.get('isbn')) != 13:
|
|
|
flash(u'ISBN长度错误')
|
|
|
else:
|
|
|
exist = Book.query.filter_by(isbn=request.form.get('isbn')).first()
|
|
|
if exist is not None:
|
|
|
flash(u'该图书信息已经存在,请核对后再录入;或者填写入库表。')
|
|
|
else:
|
|
|
book = Book()
|
|
|
book.isbn = request.form.get('isbn')
|
|
|
book.book_name = request.form.get('book_name')
|
|
|
book.press = request.form.get('press')
|
|
|
book.author = request.form.get('author')
|
|
|
book.class_name = request.form.get('class_name')
|
|
|
db.session.add(book)
|
|
|
db.session.commit()
|
|
|
flash(u'图书信息添加成功!')
|
|
|
return redirect(url_for('new_store'))
|
|
|
return render_template('new-store.html', name=session.get('name'), form=form)
|
|
|
|
|
|
|
|
|
@app.route('/borrow', methods=['GET', 'POST'])
|
|
|
@login_required
|
|
|
def borrow():
|
|
|
form = BorrowForm()
|
|
|
return render_template('borrow.html', name=session.get('name'), form=form)
|
|
|
|
|
|
|
|
|
@app.route('/find_stu_book', methods=['GET', 'POST'])
|
|
|
def find_stu_book():
|
|
|
stu = Student.query.filter_by(card_id=request.form.get('card')).first()
|
|
|
today_date = datetime.date.today()
|
|
|
today_str = today_date.strftime("%Y-%m-%d")
|
|
|
today_stamp = time.mktime(time.strptime(today_str + ' 00:00:00', '%Y-%m-%d %H:%M:%S'))
|
|
|
if stu is None:
|
|
|
return jsonify([{'stu': 0}]) # 没找到
|
|
|
if stu.debt is True:
|
|
|
return jsonify([{'stu': 1}]) # 欠费
|
|
|
if int(stu.valid_date) < int(today_stamp)*1000:
|
|
|
return jsonify([{'stu': 2}]) # 到期
|
|
|
if stu.loss is True:
|
|
|
return jsonify([{'stu': 3}]) # 已经挂失
|
|
|
books = db.session.query(Book).join(Inventory).filter(Book.book_name.contains(request.form.get('book_name')),
|
|
|
Inventory.status == 1).with_entities(Inventory.barcode, Book.isbn, Book.book_name, Book.author, Book.press).\
|
|
|
all()
|
|
|
data = []
|
|
|
for book in books:
|
|
|
item = {'barcode': book.barcode, 'isbn': book.isbn, 'book_name': book.book_name,
|
|
|
'author': book.author, 'press': book.press}
|
|
|
data.append(item)
|
|
|
return jsonify(data)
|
|
|
|
|
|
|
|
|
@app.route('/out', methods=['GET', 'POST'])
|
|
|
@login_required
|
|
|
def out():
|
|
|
today_date = datetime.date.today()
|
|
|
today_str = today_date.strftime("%Y-%m-%d")
|
|
|
today_stamp = time.mktime(time.strptime(today_str + ' 00:00:00', '%Y-%m-%d %H:%M:%S'))
|
|
|
barcode = request.args.get('barcode')
|
|
|
card = request.args.get('card')
|
|
|
book_name = request.args.get('book_name')
|
|
|
readbook = ReadBook()
|
|
|
readbook.barcode = barcode
|
|
|
readbook.card_id = card
|
|
|
readbook.start_date = int(today_stamp)*1000
|
|
|
readbook.due_date = (int(today_stamp)+40*86400)*1000
|
|
|
readbook.borrow_admin = current_user.admin_id
|
|
|
db.session.add(readbook)
|
|
|
db.session.commit()
|
|
|
book = Inventory.query.filter_by(barcode=barcode).first()
|
|
|
book.status = False
|
|
|
db.session.add(book)
|
|
|
db.session.commit()
|
|
|
bks = db.session.query(Book).join(Inventory).filter(Book.book_name.contains(book_name), Inventory.status == 1).\
|
|
|
with_entities(Inventory.barcode, Book.isbn, Book.book_name, Book.author, Book.press).all()
|
|
|
data = []
|
|
|
for bk in bks:
|
|
|
item = {'barcode': bk.barcode, 'isbn': bk.isbn, 'book_name': bk.book_name,
|
|
|
'author': bk.author, 'press': bk.press}
|
|
|
data.append(item)
|
|
|
return jsonify(data)
|
|
|
|
|
|
|
|
|
@app.route('/return', methods=['GET', 'POST'])
|
|
|
@login_required
|
|
|
def return_book():
|
|
|
form = SearchStudentForm()
|
|
|
return render_template('return.html', name=session.get('name'), form=form)
|
|
|
|
|
|
|
|
|
@app.route('/find_not_return_book', methods=['GET', 'POST'])
|
|
|
def find_not_return_book():
|
|
|
stu = Student.query.filter_by(card_id=request.form.get('card')).first()
|
|
|
today_date = datetime.date.today()
|
|
|
today_str = today_date.strftime("%Y-%m-%d")
|
|
|
today_stamp = time.mktime(time.strptime(today_str + ' 00:00:00', '%Y-%m-%d %H:%M:%S'))
|
|
|
if stu is None:
|
|
|
return jsonify([{'stu': 0}]) # 没找到
|
|
|
if stu.debt is True:
|
|
|
return jsonify([{'stu': 1}]) # 欠费
|
|
|
if int(stu.valid_date) < int(today_stamp)*1000:
|
|
|
return jsonify([{'stu': 2}]) # 到期
|
|
|
if stu.loss is True:
|
|
|
return jsonify([{'stu': 3}]) # 已经挂失
|
|
|
books = db.session.query(ReadBook).join(Inventory).join(Book).filter(ReadBook.card_id == request.form.get('card'),
|
|
|
ReadBook.end_date.is_(None)).with_entities(ReadBook.barcode, Book.isbn, Book.book_name, ReadBook.start_date,
|
|
|
ReadBook.due_date).all()
|
|
|
data = []
|
|
|
for book in books:
|
|
|
start_date = timeStamp(book.start_date)
|
|
|
due_date = timeStamp(book.due_date)
|
|
|
item = {'barcode': book.barcode, 'isbn': book.isbn, 'book_name': book.book_name,
|
|
|
'start_date': start_date, 'due_date': due_date}
|
|
|
data.append(item)
|
|
|
return jsonify(data)
|
|
|
|
|
|
|
|
|
@app.route('/in', methods=['GET', 'POST'])
|
|
|
@login_required
|
|
|
def bookin():
|
|
|
barcode = request.args.get('barcode')
|
|
|
card = request.args.get('card')
|
|
|
record = ReadBook.query.filter(ReadBook.barcode == barcode, ReadBook.card_id == card, ReadBook.end_date.is_(None)).\
|
|
|
first()
|
|
|
today_date = datetime.date.today()
|
|
|
today_str = today_date.strftime("%Y-%m-%d")
|
|
|
today_stamp = time.mktime(time.strptime(today_str + ' 00:00:00', '%Y-%m-%d %H:%M:%S'))
|
|
|
record.end_date = int(today_stamp)*1000
|
|
|
record.return_admin = current_user.admin_id
|
|
|
db.session.add(record)
|
|
|
db.session.commit()
|
|
|
book = Inventory.query.filter_by(barcode=barcode).first()
|
|
|
book.status = True
|
|
|
db.session.add(book)
|
|
|
db.session.commit()
|
|
|
bks = db.session.query(ReadBook).join(Inventory).join(Book).filter(ReadBook.card_id == card,
|
|
|
ReadBook.end_date.is_(None)).with_entities(ReadBook.barcode, Book.isbn, Book.book_name, ReadBook.start_date,
|
|
|
ReadBook.due_date).all()
|
|
|
data = []
|
|
|
for bk in bks:
|
|
|
start_date = timeStamp(bk.start_date)
|
|
|
due_date = timeStamp(bk.due_date)
|
|
|
item = {'barcode': bk.barcode, 'isbn': bk.isbn, 'book_name': bk.book_name,
|
|
|
'start_date': start_date, 'due_date': due_date}
|
|
|
data.append(item)
|
|
|
return jsonify(data)
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
manager.run()
|