This commit is contained in:
Charles Chen 2022-10-23 22:41:00 +08:00
commit 655985f0da
98 changed files with 9259 additions and 0 deletions

View File

@ -0,0 +1,13 @@
### 该问题是怎么引起的?
### 重现步骤
### 报错信息

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

4
.idea/misc.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (model)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/se2021-app-model-merge.iml" filepath="$PROJECT_DIR$/.idea/se2021-app-model-merge.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.6 (model)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

0
Merge/.keep Normal file
View File

592
Merge/Merge.py Normal file
View File

@ -0,0 +1,592 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time : 2021/6/2 13:56 下午
# @Author : Tang JiaxinChen wenjieWang Jiajie
# 对一个app的多个模型进行合并
# 1、合并state_list
# 2、合并transition
# 3、根据得到的state_list确定当前app所包含的states
import sys
sys.path.append('../Model')
from BuildModel import Model
from Transition import Transition
from Event import Event
import pandas as pd
import xml.etree.ElementTree as ET
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from networkx.drawing.nx_pydot import to_pydot
import networkx as nx
import random
class Merge:
def __init__(self): # 初始构造函数
self.app_id = None
self.app_apk_name = None
self.app_package_name = None
self.app_version = None
self.model_list = [] # 待合并的model列表
self.res_state_list = [] # 合并后的state_list
self.res_transitions = [] # 合并后的transitions
self.res_states = {} # 合并后的states
def get_model_id(self):
"""读取模型列表中的模型以获取app_id"""
try:
return self.model_list[0].app_id
except:
return -1
def add_model(self, model): # 向model_list中添加一个模型
if len(self.model_list) == 0:
self.app_id = model.get_id()
self.app_apk_name = model.get_apk_name()
self.app_package_name = model.get_package_name()
self.app_version = model.get_version()
elif model.get_id() != self.app_id:
return
self.model_list.append(model)
def get_res_model(self): # 返回一个合并后的模型
res_model = Model()
res_model.set_id(self.get_model_id())
print(res_model.app_id)
res_model.set_apk_name(self.app_apk_name)
res_model.set_package_name(self.app_package_name)
res_model.set_version(self.app_version)
res_model.state_list = self.res_state_list
res_model.transitions = self.res_transitions
res_model.states = self.res_states
return res_model
def search_state(self, state_list, state): # 在state_list中查找某个state返回其下标
for i in range(len(state_list)):
if self.cmp_xml(state_list[i].layout, state.layout) > 0.9:
return i
return -1
def add_states(self, model):
# 将当前model中的所有state加入res_state_list中返回旧state_id与新state_id的对应关系字典
res_dict = {}
for state in model.state_list:
# 在res_state_list中查找state是否已经存在
new_id = self.search_state(self.res_state_list, state)
if new_id != -1: # 如果查找到则更新state_id
res_dict[state.get_state_id()] = new_id
state.set_state_id(new_id)
else: # 如果没有查找到则设置state_id并将state加入res_state_list
new_id = len(self.res_state_list)
res_dict[state.get_state_id()] = new_id
state.set_state_id(new_id)
self.res_state_list.append(state)
return res_dict
def merge_models(self):
# 建立一个用于存储合并后的transition信息的dataframe
res_df = pd.DataFrame(columns=('source_id', 'target_id', 'action', 'identifier'))
for model in self.model_list: # 依次遍历每个model
model.generate_trans_df() # 为model建立trans_df用于合并
t_dict = self.add_states(model) # 将model的state加入到res_state_list中
# 根据获得的对应结果更新transitions中的state_id
for transition in model.transitions:
transition.set_source_id(t_dict[transition.get_source_id()])
transition.set_target_id(t_dict[transition.get_target_id()])
# 根据获得的对应结果更新trans_df中的state_id并将更新后的row加入res_df
for index, row in model.trans_df.iterrows():
row['source_id'] = t_dict[row['source_id']]
row['target_id'] = t_dict[row['target_id']]
res_df = res_df.append(row, ignore_index=True)
res_df = res_df.drop_duplicates() # 对合并完成后的dataframe进行去重
self.res_transitions = []
transition_id = 0
# 根据去重后的dataframe生成合并后的transitions
for index, row in res_df.iterrows():
transition = Transition()
transition.set_transition_id(transition_id)
transition.set_source_id(row['source_id'])
transition.set_target_id(row['target_id'])
event = Event(row['action'], row['identifier'], '')
transition.set_event(event)
transition_id += 1
self.res_transitions.append(transition)
# 根据res_state_list生成states
for state in self.res_state_list:
self.res_states[state.state_id] = state
def cmp_xml(self, layout1, layout2): # 计算两个xml字符串的相似度
# tree1 = ET.parse(file1)
# tree2 = ET.parse(file2) # 根据xml文件建立element tree
# root1 = tree1.getroot()
# root2 = tree2.getroot() # 获取两棵树的根节点
root1 = ET.fromstring(layout1)
root2 = ET.fromstring(layout2)
return self.cmp_xml_node(root1, root2)
def cmp_xml_node(self, node1, node2): # 计算以两个节点为根的子树的相似度
if node1.tag != node2.tag: # 若两个节点的名称不同
return 0 # 两个节点的相似度为0
if len(node1) == 0 and len(node2) == 0: # 若两个节点均为叶子节点
return self.cmp_xml_leaf(node1, node2)
if len(node1) == 0 or len(node2) == 0: # 若node1或node2是叶子节点而另一个不是
return 0
child_dict1 = self.genenate_child_dict(node1) # 生成node1对应的子节点字典
child_dict2 = self.genenate_child_dict(node2) # 生成node2对应的子节点字典
sim_value = 0 # 两节点的相似度评分
for tag1, child_list1 in child_dict1.items(): # 依次遍历node1的子节点字典
child_list2 = child_dict2.get(tag1) # 在node2的子节点字典中查找由同名子节点构成的列表
if child_list2 is None: # 若查找不到
continue
sim_value += self.cmp_xml_same_tag(child_list1, child_list2) # 计算两个同名的子节点列表的相似度
return sim_value / max(len(child_dict1), len(child_dict2))
def cmp_xml_same_tag(self, list1, list2): # 计算两个同名子节点列表的相似度
sim_value = 0 # 两列表的相似度评分
sim_values = np.zeros((len(list1), len(list2))) # 创建评分矩阵
for i in range(len(list1)):
for j in range(len(list2)):
sim_values[i, j] = self.cmp_xml_node(list1[i], list2[j]) # 计算两两之间的相似度
while sim_values.shape[0] != 0 and sim_values.shape[1] != 0:
max_index = np.argmax(sim_values) # 从所有相似度中找最大值所在的位置
max_row = max_index // sim_values.shape[1]
max_col = max_index % sim_values.shape[1] # 计算最大值所在的行和列
max_sim_value = sim_values[max_row, max_col]
if max_sim_value == 0:
break
sim_value += max_sim_value
sim_values = np.delete(sim_values, max_row, axis=0)
sim_values = np.delete(sim_values, max_col, axis=1) # 删除最大值所在的行和列
return sim_value / max(len(list1), len(list2))
def genenate_child_dict(self, node): # 产生某个节点的子节点字典
children = {}
for child in node:
if child.tag not in children:
children[child.tag] = [child]
else:
children[child.tag].append(child)
return children
def cmp_xml_node_key(self, key, value1, value2): # 根据属性名称,计算两个属性值的相似度
if key == 'index':
return 1
return value1 == value2
def cmp_xml_leaf(self, node1, node2): # 计算两个同名叶子节点的相似度
attrib1 = node1.attrib
attrib2 = node2.attrib
sim_value = 0
for key1, value1 in attrib1.items(): # 依次遍历node1的属性列表
value2 = attrib2.get(key1) # 在node2中查找相应属性的属性值
if value2 is None: # 如果node2中不存在该名字的属性
continue
sim_value += self.cmp_xml_node_key(key1, value1, value2) # 计算两个属性值的相似度
return sim_value / max(len(attrib1), len(attrib2))
def hierarchy_pos(self, G, root=None, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5):
'''
G: the graph (must be a tree)
root: the root node of current branch
- if the tree is directed and this is not given,
the root will be found and used
- if the tree is directed and this is given, then
the positions will be just for the descendants of this node.
- if the tree is undirected and not given,
then a random choice will be used.
width: horizontal space allocated for this branch - avoids overlap with other branches
vert_gap: gap between levels of hierarchy
vert_loc: vertical location of root
xcenter: horizontal location of root
'''
if not nx.is_tree(G):
raise TypeError('cannot use hierarchy_pos on a graph that is not a tree')
if root is None:
if isinstance(G, nx.DiGraph):
root = next(iter(nx.topological_sort(G))) # allows back compatibility with nx version 1.11
else:
root = random.choice(list(G.nodes))
def _hierarchy_pos(G, root, width=1., vert_gap=0.2, vert_loc=0, xcenter=0.5, pos=None, parent=None):
'''
see hierarchy_pos docstring for most arguments
pos: a dict saying where all nodes go if they have been assigned
parent: parent of this branch. - only affects it if non-directed
'''
if pos is None:
pos = {root: (xcenter, vert_loc)}
else:
pos[root] = (xcenter, vert_loc)
children = list(G.neighbors(root))
if not isinstance(G, nx.DiGraph) and parent is not None:
children.remove(parent)
if len(children) != 0:
dx = width / len(children)
nextx = xcenter - width / 2 - dx / 2
for child in children:
nextx += dx
pos = _hierarchy_pos(G, child, width=dx, vert_gap=vert_gap,
vert_loc=vert_loc - vert_gap, xcenter=nextx,
pos=pos, parent=root)
return pos
return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
def model1_transitions_visualization(self, model): # type1对应的model1可视化
source_id_list = []
target_id_list = []
test_list = [0]
data_list = []
j = 0
for transition in model.transitions:
source_id_list.append(transition.source_id)
target_id_list.append(transition.target_id)
visualization_tuple = sorted(zip(source_id_list, target_id_list))
#print(visualization_tuple) # visualization_tuple索引0存储souece_id,索引1存储target_id
plt.figure(figsize=(48, 36))
for i in range(len(visualization_tuple)): # 依次遍历visualization_tuple的元组
if visualization_tuple[i][0] == test_list[j]: # 如果visualization_tuple中的source_id已存在于test_list
data_list.append(visualization_tuple[i]) # 将元组添加至data_list
else: #使用现有data_list中的元组画图
G = nx.Graph()
G.add_edges_from(data_list)
k = test_list[j] #确定source_id作为根节点
try:
plt.subplot(9, 9, 0 + len(test_list)) #创建子图
nx.draw(G, with_labels=True, node_size=300)
G.remove_node(k)
except:
pass
j += 1
test_list.append(visualization_tuple[i][0])
data_list.clear() #画图后清空data_list
data_list.append(visualization_tuple[i]) #添加新元组
plt.subplots_adjust(bottom=0.1, right=0.8, top=0.9)
plt.savefig('model1.png')
def model2_transitions_visualization(self, model): # type1对应的model2可视化
source_id_list = []
target_id_list = []
test_list = [0]
data_list = []
j = 0
for transition in model.transitions:
source_id_list.append(transition.source_id)
target_id_list.append(transition.target_id)
visualization_tuple = sorted(zip(source_id_list, target_id_list))
#print(visualization_tuple)
plt.figure(figsize=(16, 12))
for i in range(len(visualization_tuple)):
if visualization_tuple[i][0] == test_list[j]:
data_list.append(visualization_tuple[i])
else:
G = nx.Graph()
G.add_edges_from(data_list)
k = test_list[j]
try:
plt.subplot(330 + len(test_list))
nx.draw(G, with_labels=True, node_size=300)
G.remove_node(k)
except:
pass
j += 1
test_list.append(visualization_tuple[i][0])
data_list.clear()
data_list.append(visualization_tuple[i])
plt.subplots_adjust(bottom=0.1, right=0.8, top=0.9)
plt.savefig('model2.png')
def model1_transitions_visualization2(self, model): # type2对应的model1可视化
source_id_list = []
target_id_list = []
test_list = [0]
data_list = []
dot_list = []
j = 0
for transition in model.transitions:
source_id_list.append(transition.source_id)
target_id_list.append(transition.target_id)
visualization_tuple = sorted(zip(source_id_list, target_id_list))
#print(visualization_tuple)
plt.figure(figsize=(48, 36))
for i in range(len(visualization_tuple)):
if visualization_tuple[i][0] == test_list[j]:
data_list.append(visualization_tuple[i])
else:
G = nx.Graph()
k = test_list[j]
plt.subplot(9, 9, 0 + len(test_list))
plt.axis('off')
try:
G.add_edges_from(data_list)
pos = self.hierarchy_pos(G, k)
nx.draw(G, pos=pos, with_labels=True, node_size=300)
except: #source_id与target_id相同时使用dot画图
G.add_edges_from(data_list)
dot_list.append(j + 1)
P = to_pydot(G)
P.write_jpeg('pydot.png')
plt.imshow(Image.open('pydot.png'))
G.remove_node(k)
j += 1
test_list.append(visualization_tuple[i][0])
data_list.clear()
data_list.append(visualization_tuple[i])
plt.subplots_adjust(bottom=0.1, right=0.8, top=0.9)
plt.savefig('model1_2.png')
def model2_transitions_visualization2(self, model): # type2对应的model2可视化
source_id_list = []
target_id_list = []
test_list = [0]
data_list = []
dot_list = []
j = 0
for transition in model.transitions:
source_id_list.append(transition.source_id)
target_id_list.append(transition.target_id)
visualization_tuple = sorted(zip(source_id_list, target_id_list))
#print(visualization_tuple)
plt.figure(figsize=(16, 12))
for i in range(len(visualization_tuple)):
if visualization_tuple[i][0] == test_list[j]:
data_list.append(visualization_tuple[i])
else:
G = nx.Graph()
plt.subplot(330 + len(test_list))
plt.axis('off')
k = test_list[j]
try:
G.add_edges_from(data_list)
pos = self.hierarchy_pos(G, k)
nx.draw(G, pos=pos, with_labels=True, node_size=300)
except:
G.add_edges_from(data_list)
dot_list.append(j + 1)
P = to_pydot(G)
P.write_jpeg('pydot.png')
plt.imshow(Image.open('pydot.png'))
G.remove_node(k)
j += 1
test_list.append(visualization_tuple[i][0])
data_list.clear()
data_list.append(visualization_tuple[i])
plt.subplots_adjust(bottom=0.1, right=0.8, top=0.9)
plt.savefig('model2_2.png')
def model1_transitions_visualization3(self, model): # type3对应的model1可视化
source_id_list = []
target_id_list = []
test_list = [0]
data_list = []
j = 0
for transition in model.transitions:
source_id_list.append(transition.source_id)
target_id_list.append(transition.target_id)
visualization_tuple = sorted(zip(source_id_list, target_id_list))
#print(visualization_tuple)
plt.figure(figsize=(48, 36))
for i in range(len(visualization_tuple)):
if visualization_tuple[i][0] == test_list[j]:
data_list.append(visualization_tuple[i])
else:
G = nx.Graph()
k = test_list[j]
plt.subplot(9, 9, 0 + len(test_list))
plt.axis('off')
G.add_edges_from(data_list)
P = to_pydot(G)
P.write_jpeg('pydot.png')
plt.imshow(Image.open('pydot.png'))
G.remove_node(k)
j += 1
test_list.append(visualization_tuple[i][0])
data_list.clear()
data_list.append(visualization_tuple[i])
plt.subplots_adjust(bottom=0.1, right=0.8, top=0.9)
plt.savefig('model1_3.png')
def model2_transitions_visualization3(self, model): # type3对应的model2可视化
source_id_list = []
target_id_list = []
test_list = [0]
data_list = []
j = 0
for transition in model.transitions:
source_id_list.append(transition.source_id)
target_id_list.append(transition.target_id)
visualization_tuple = sorted(zip(source_id_list, target_id_list))
plt.figure(figsize=(16, 12))
for i in range(len(visualization_tuple)):
if visualization_tuple[i][0] == test_list[j]:
data_list.append(visualization_tuple[i])
else:
G = nx.Graph()
plt.subplot(330 + len(test_list))
plt.axis('off')
k = test_list[j]
G.add_edges_from(data_list)
P = to_pydot(G)
P.write_jpeg('pydot.png')
plt.imshow(Image.open('pydot.png'))
G.remove_node(k)
j += 1
test_list.append(visualization_tuple[i][0])
data_list.clear()
data_list.append(visualization_tuple[i])
plt.subplots_adjust(bottom=0.1, right=0.8, top=0.9)
plt.savefig('model2_3.png')
def res_transitions_visualization(self, model): # type1对应的res可视化
source_id_list = []
target_id_list = []
test_list = [0]
data_list = []
j = 0
for transition in model.transitions:
source_id_list.append(transition.source_id)
target_id_list.append(transition.target_id)
visualization_tuple = sorted(zip(source_id_list, target_id_list))
#print(visualization_tuple)
plt.figure(figsize=(48, 36))
for i in range(len(visualization_tuple)):
if visualization_tuple[i][0] == test_list[j]:
data_list.append(visualization_tuple[i])
else:
G = nx.Graph()
G.add_edges_from(data_list)
k = test_list[j]
plt.subplot(9, 7, 0 + len(test_list))
nx.draw(G, with_labels=True, node_size=300)
G.remove_node(k)
j += 1
test_list.append(visualization_tuple[i][0])
data_list.clear()
data_list.append(visualization_tuple[i])
plt.subplots_adjust(bottom=0.1, right=0.8, top=0.9)
plt.savefig('merge.png')
def res_transitions_visualization2(self, model): # type2对应的res可视化
source_id_list = []
target_id_list = []
test_list = [0]
data_list = []
dot_list = []
j = 0
for transition in model.transitions:
source_id_list.append(transition.source_id)
target_id_list.append(transition.target_id)
visualization_tuple = sorted(zip(source_id_list, target_id_list))
#print(visualization_tuple)
plt.figure(figsize=(48, 36))
for i in range(len(visualization_tuple)):
if visualization_tuple[i][0] == test_list[j]:
data_list.append(visualization_tuple[i])
else:
G = nx.Graph()
plt.subplot(9, 7, 0 + len(test_list))
plt.axis('off')
k = test_list[j]
try:
G.add_edges_from(data_list)
pos = self.hierarchy_pos(G, k)
nx.draw(G, pos=pos, with_labels=True, node_size=300)
except:
G.add_edges_from(data_list)
dot_list.append(j + 1)
P = to_pydot(G)
P.write_jpeg('pydot.png')
plt.imshow(Image.open('pydot.png'))
G.remove_node(k)
j += 1
test_list.append(visualization_tuple[i][0])
data_list.clear()
data_list.append(visualization_tuple[i])
plt.subplots_adjust(bottom=0.1, right=0.8, top=0.9)
plt.savefig('merge_2.png')
def res_transitions_visualization3(self, model): # type3对应的res可视化
source_id_list = []
target_id_list = []
test_list = [0]
data_list = []
j = 0
for transition in model.transitions:
source_id_list.append(transition.source_id)
target_id_list.append(transition.target_id)
visualization_tuple = sorted(zip(source_id_list, target_id_list))
#print(visualization_tuple)
plt.figure(figsize=(48, 36))
for i in range(len(visualization_tuple)):
if visualization_tuple[i][0] == test_list[j]:
data_list.append(visualization_tuple[i])
else:
G = nx.Graph()
plt.subplot(9, 7, 0 + len(test_list))
plt.axis('off')
k = test_list[j]
G.add_edges_from(data_list)
P = to_pydot(G)
P.write_jpeg('pydot.png')
plt.imshow(Image.open('pydot.png'))
G.remove_node(k)
j += 1
test_list.append(visualization_tuple[i][0])
data_list.clear()
data_list.append(visualization_tuple[i])
plt.subplots_adjust(bottom=0.1, right=0.8, top=0.9)
plt.savefig('merge_3.png')
def get_model_graph(self, type):
plt.figure(figsize=(48, 36))
if type == 1:
plt.subplot(2, 2, 1)
plt.title('model1')
plt.imshow(Image.open('model1.png'))
plt.axis('off')
plt.subplot(2, 2, 2)
plt.title('model2')
plt.imshow(Image.open('model2.png'))
plt.axis('off')
plt.subplot(2, 2, 3)
plt.title('merge')
plt.imshow(Image.open('merge.png'))
plt.axis('off')
elif type == 2:
plt.subplot(2, 2, 1)
plt.title('model1')
plt.imshow(Image.open('model1_2.png'))
plt.axis('off')
plt.subplot(2, 2, 2)
plt.title('model2')
plt.imshow(Image.open('model2_2.png'))
plt.axis('off')
plt.subplot(2, 2, 3)
plt.title('merge')
plt.imshow(Image.open('merge_2.png'))
plt.axis('off')
elif type == 1:
plt.subplot(2, 2, 1)
plt.title('model1')
plt.imshow(Image.open('model1_3.png'))
plt.axis('off')
plt.subplot(2, 2, 2)
plt.title('model2')
plt.imshow(Image.open('model2_3.png'))
plt.axis('off')
plt.subplot(2, 2, 3)
plt.title('merge')
plt.imshow(Image.open('merge_3.png'))
plt.axis('off')
plt.savefig('total.png')
plt.show()

0
Merge/README.md Normal file
View File

88
Model/App.py Normal file
View File

@ -0,0 +1,88 @@
'''
存储数据库中每个app信息的存储单元
与数据库表项的对应
appId->app_id
apkName->apk_name
packageName->package_name
version->version
states->相应app中所有的state数据<state_id,state_info>键值对存入hashMap中
scenarios->相应app中的场景集合
author zhouxinyu
'''
from AppRunningPath import AppRunningPath
class App:
def __init__(self, id=-1, apk=None, pck=None, v=None):
self.app_id = id
self.apk_name = apk
self.package_name = pck
self.apk_version = v
self.states = {}
self.app_running_path = AppRunningPath()
self.scenarios = []
def set_id(self, id):
self.app_id = id
def set_apk_name(self, apk):
self.apk_name = apk
def set_package_name(self, pck):
self.package_name = pck
def set_version(self, v):
self.apk_version = v
def set_states(self, s):
if self.states == None:
self.states = {}
def set_app_running_path(self, arp):
self.app_running_path = arp
def set_scenarios(self, sc):
if self.scenarios == None:
self.scenarios = []
self.scenarios.extend(sc)
def get_id(self):
return self.app_id
def get_apk_name(self):
return self.apk_name
def get_package_name(self):
return self.package_name
def get_version(self):
return self.apk_version
def get_states(self):
return self.states
def get_app_running_path(self):
return self.app_running_path
def get_scenarios(self):
return self.scenarios
def __str__(self):
st = ''
for key in self.states:
st += str(self.states[key])
st += '\n'
sc = ''
for scene in self.scenarios:
sc += str(scene)
sc += '\n'
return "[app_id:" + str(self.app_id) + " apk_name:" + str(self.apk_name) + " package_name:" + str(
self.package_name) + " version:" + self.apk_version + " states:" + st + " appRunningPath:" + str(
self.app_running_path) + " scenarios:" + sc + "]"

28
Model/AppDatabase.py Normal file
View File

@ -0,0 +1,28 @@
'''
按每个app为存储单元把所有app按<app_id,app>键值对存入hashMap
author zhouxinyu
'''
class AppDatabase:
def __init__(self):
self.app_list = {}
def set_app_list(self, apps):
if self.app_list == None:
self.app_list = {}
self.app_list.update(apps)
def get_app_list(self):
return self.app_list
def __str__(self):
re = ''
for key in self.app_list:
re += str(self.app_list[key])
re += '\n'
return re

31
Model/AppRunningPath.py Normal file
View File

@ -0,0 +1,31 @@
'''
相应app中的所有Transition以<transition_id,transition_info>键值对存入hashMap
author zhouxinyu
'''
class AppRunningPath:
def __init__(self, ts={}):
self.transitions = {}
self.transitions.update(ts)
def get_trans(self):
return self.transitions
def get_state_related_transition(self, state_id):
result = {}
for key in self.transitions:
if self.transitions[key].get_source_id() == state_id:
result[key] = self.transitions[key]
return result
def __str__(self):
re = ''
for key in self.transitions:
re += str(self.transitions[key])
re += '\n'
return re

332
Model/BuildModel.py Normal file
View File

@ -0,0 +1,332 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time : 2021/6/2 14:20 下午
# @Author : Wang ZhixingWang GuoxinTang JiaxinChen Wenjie
# 1、创建Model
# 2、保存Model到output文件夹中。
# 3、保存Model到database中
import sys
sys.path.append('..')
from App import App
from Transition import Transition
import os
from database.api import DBAction
from State import State
from util.read_file import *
from Scenario import Scenario
from Event import Event
import pandas as pd
import json
import shutil
class Model(App):
def __init__(self):
super(Model, self).__init__(id=-1, apk=None, pck=None, v=None)
self.transitions = []
self.state_list = []
self.trans_df = None
'''
App类
app基础信息
app_idapp的id
apk_nameapp的安装包的名称
package_nameapp的包名
apk_versionapp的安装包的版本号
app状态信息
states(dict)整个app所有的state_id到state的索引表
元素类型State
scenariosapp的场景列表
Model类
transitions(list)模型的跳转信息表
元素类型Transition
state_list(list)模型涉及到的app的状态列表
元素类型State
用于合并的数据结构
trans_df(DataFrame)存储source_idtarget_idtrigger_action和trigger_identifier组合的表格
app_running_path
'''
def Build_Model_From_Project(self, project_path):
# 从文件中读取Model
apk_name, package_name, version, author_id = read_app_info(project_path) # 从文件中读取app信息
self.apk_name = apk_name
self.apk_version = version
self.package_name = package_name
states = read_state_info(project_path) # 读取模型涉及到的app状态列表
for (state_id, activity_name, binary_data, layout) in states:
state = State(self.app_id)
state.set_state_id(int(state_id))
state.set_activity_name(activity_name)
state.set_picture(binary_data)
state.set_layout(layout)
# file_path = f"./Data/2021Se/screens/{state_id}.uix"
# with open(file_path, 'w') as f: # 把layout写成uix文件
# f.write(layout)
# new_state.set_layout(file_path)
self.state_list.append(state) # 存入state_list
transitions = read_transitions(project_path) # 读取模型的app跳转信息集合
transition_id = 0
for transition_info in transitions:
if len(transition_info) == 3:
source_state, target_state, trigger_action = int(transition_info[0]), int(transition_info[1]), \
transition_info[2]
transition = Transition(self.app_id)
transition.set_source_id(self.state_list[source_state].state_id)
transition.set_target_id(self.state_list[target_state].state_id)
transition.set_transition_id(transition_id)
event = Event(trigger_action, '', '')
transition.set_event(event)
self.transitions.append(transition) # 转换为Transition类型并存入transitions
else: # len(transition_info) == 4
source_state, target_state, trigger_action, trigger_identifier = int(transition_info[0]), int(
transition_info[1]), transition_info[2], transition_info[3]
transition = Transition(self.app_id)
transition.set_source_id(self.state_list[source_state].state_id)
transition.set_target_id(self.state_list[target_state].state_id)
transition.set_transition_id(transition_id)
event = Event(trigger_action, trigger_identifier, '')
transition.set_event(event)
self.transitions.append(transition) # 转换为Transition类型并存入transitions
transition_id += 1
scenarios = read_scenarios_info(project_path)
for scenario in scenarios:
scenario_name = scenario[0]
desc = scenario[1]
path = scenario[2]
new_scenarios = Scenario(self.app_id)
new_scenarios.set_name(scenario_name)
new_scenarios.set_des(desc)
new_scenarios.set_path(path)
self.scenarios.append(new_scenarios)
def Build_Model_From_Database(self, package_name):
"""修改/增添了从数据库中读取Model的API"""
self.package_name = package_name
db = DBAction()
db.change_db("apprepo")
app_id = db.get_app_id(self.package_name)
if app_id == -1:
return
self.app_id = app_id # 将数据库的app_id传给本Model
# apk_name, package_name, version, author_id = db.get_app_info(package_name) # 从数据库中读取app信息
app_info_list = db.get_app_info(package_name) # 从数据库中读取app信息
self.apk_name = app_info_list[0][0]
self.apk_version = app_info_list[0][2]
self.package_name = app_info_list[0][1]
author_id = app_info_list[0][3]
state_data = db.get_state_list_by_app_id(app_id) # 读取模型涉及到的app状态列表
for data in state_data:
state_id, activity_name, screen_shot, layout = data[0], data[1], data[2], data[3]
new_state = State(self.app_id)
new_state.set_state_id(int(state_id))
new_state.set_activity_name(activity_name)
new_state.set_picture(screen_shot)
new_state.init_pic('./Data/2021Se/screens')
new_state.set_layout(os.path.join('./Data/2021Se', 'screens', str(state_id) + '.uix'))
# new_state.set_layout(os.path.join('..Data', 'screens', str(state_id) + '.uix')) # 不确定!
self.state_list.append(new_state) # 转换为State类型并存入state_list
transition_data = db.get_transition_by_app_id(app_id) # 读取模型的app跳转信息集合
transition_id = 0
for transition_info in transition_data:
if len(transition_info) == 3:
source_state, target_state, trigger_action = int(transition_info[0]), int(transition_info[1]), \
transition_info[2]
transition = Transition(self.app_id)
transition.set_source_id(self.state_list[source_state].state_id)
transition.set_target_id(self.state_list[target_state].state_id)
transition.set_transition_id(transition_id)
event = Event(trigger_action, '', '')
transition.set_event(event)
self.transitions.append(transition) # 转换为Transition类型并存入transitions
else: # len(transition_info) == 4
source_state, target_state, trigger_action, trigger_identifier = int(transition_info[0]), int(
transition_info[1]), transition_info[2], transition_info[3]
transition = Transition(self.app_id)
transition.set_source_id(self.state_list[source_state].state_id)
transition.set_target_id(self.state_list[target_state].state_id)
transition.set_transition_id(transition_id)
event = Event(trigger_action, trigger_identifier, '')
transition.set_event(event)
self.transitions.append(transition) # 转换为Transition类型并存入transitions
transition_id += 1
scenarios_data = db.get_scenarios_by_app_id(app_id)
for scenario in scenarios_data:
scenario_name = scenario[0]
desc = scenario[1]
path = scenario[2]
new_scenarios = Scenario(self.app_id)
new_scenarios.set_name(scenario_name)
new_scenarios.set_des(desc)
new_scenarios.set_path(path)
self.scenarios.append(new_scenarios)
def Save_Model_To_Database(self):
"""
将内存中的Model保存到数据库
"""
print(self.app_id)
db = DBAction()
# app_id = db.get_app_id(self.package_name)
db.change_db("Mergeresult") #用于存储Model结果的数据库
db.insert_row_app_info(self.app_id,self.apk_name,self.package_name,self.version)
for state in self.state_list:
db.insert_row_state(self.app_id, state.state_id, state.activity_name, state.picture, state.layout)
for transition in self.transitions:
# db.insert_row_transition(self.app_id, transition.source_state, transition.target_state, transition.event.trigger_action, transition.event.trigger_identifier, transition.event.condition)
#SOURCE_STATE / TARGET_STATE UNKNOWN
db.insert_row_transition(self.app_id, -1, -1,
transition.event.trigger_action, transition.event.trigger_identifier,
transition.event.conditions)
for scenario in self.scenarios:
db.insert_row_scenarios(self.app_id, scenario.scenario_name, scenario.description, scenario.path)
def Save_Model_To_Local(self, out_dir):
# 将内存中的Model保存到文件夹
"""
保存模型到文件
:param out_dir: 目标文件夹
"""
if not os.path.exists(out_dir):
os.mkdir(out_dir)
out_path = out_dir + 'screens/' # out_path: res/screens
if not os.path.exists(out_path):
os.mkdir(out_path)
for state in self.state_list:
layout_file = os.path.join(out_path, str(state.state_id) + '.uix') # layout_file: res/screens/0.uix
with open(layout_file, 'w') as f:
f.write(state.layout)
f.close()
state.init_pic(out_path)
with open(out_dir + 'app_info.lst', 'w') as f:
f.write(self.apk_name + '\n')
f.write(self.package_name + '\n')
f.write(self.apk_version + '\n')
f.close()
with open(out_dir + 'transitions.lst', 'w') as f:
for transition in self.transitions:
f.write(transition.get_line() + '\n')
f.close()
with open(out_dir + 'scenarios.lst', 'w') as f:
for scenario in self.scenarios:
f.write(str(scenario) + '\n')
f.close()
with open(out_dir + 'window_info.lst', 'w') as f:
for state in self.state_list:
f.write(str(state.state_id) + ' ' + state.get_activity_name() + '\n')
f.close()
def Print_Model(self):
# 打印出Model的内容
print("============== %s ===============" % self.package_name)
print('-------- state_info --------')
for state in self.state_list:
print(state.state_id, state.activity_name)
print('------ transitions_info ------')
for transition in self.transitions:
print(transition.source_id, transition.target_id, transition.event.trigger_action,
transition.event.trigger_identifier)
print('------ scenarios_info ------')
for scenario in self.scenarios:
print(scenario.scenario_name, scenario.path)
def generate_json(self):
# 将内存中的跳转数据转换为一个用dict表示的跳转表
data = {}
for transition in self.transitions:
source_id = transition.get_source_id()
target_id = transition.get_target_id()
if source_id < target_id:
if source_id not in data:
data[source_id] = [target_id]
elif target_id not in data[source_id]:
data[source_id].append(target_id)
return data
def generate_trans_df(self): # 生成包含跳转及对应事件的dataframe
self.trans_df = pd.DataFrame(columns=('source_id', 'target_id', 'action', 'identifier'))
for transition in self.transitions:
t_dict = {'source_id': transition.get_source_id(),
'target_id': transition.get_target_id(),
'action': transition.get_event().get_trigger_action(),
'identifier': transition.get_event().get_trigger_identifier()}
self.trans_df = self.trans_df.append(t_dict, ignore_index=True)
def generate_ifml_json(self):
gap = 40
elements = []
data = []
self.genarate_element(0, data, elements)
def create_element(self, id, type, x, y, width, heigth):
element = {"id": id,
"type": type,
"attributes": {
"name": id,
"default": False,
"landmark": False,
"xor": False
},
"metadata": {
"graphics": {
"position": {
"x": x,
"y": y
},
"size": {
"width": width,
"height": heigth
}
}
}
}
return element
def generate_element(self, root, data, elements):
if root not in data:
element = {"id": root,
"type": "ifml.ViewContainer",
"attributes": {
"name": root,
"default": False,
"landmark": False,
"xor": False
},
"metadata": {
"graphics": {
"position": {
"x": 40,
"y": 100
},
"size": {
"width": 180,
"height": 100
}
}
}
}
return {"id": root}
children = []
def tree_to_dict(self, root, data):
if root not in data:
return {"id": root}
children = []
for state_id in data[root]:
child = self.tree_to_dict(state_id, data)
children.append(child)
return {"id": root, "children": children}

37
Model/Event.py Normal file
View File

@ -0,0 +1,37 @@
'''
将transition中的Event单独成类
与数据库transition表的对应关系
triggerAction->trigger_action
triggerIdentifier->trigger_identifier
conditions->conditions
@author zhouxinyu
'''
class Event:
def __init__(self, ta, ti, con):
self.trigger_action = ta
self.trigger_identifier = ti
self.conditions = con
def get_trigger_action(self):
return self.trigger_action
def set_trigger_action(self, ta):
self.trigger_action = ta
def get_trigger_identifier(self):
return self.trigger_identifier
def set_trigger_identifier(self, ti):
self.trigger_identifier = ti
def get_conditions(self):
return self.conditions
def set_conditions(self, con):
self.conditions = con

58
Model/Scenario.py Normal file
View File

@ -0,0 +1,58 @@
'''
场景类与数据库scenarios表项对应关系
appId->app_id
scenarioName->scenario_name
description->description
path->path
author zhouxinyu
'''
import os
# Todo 将以下代码Scenario1改写到Scenario中
#我们模型合并用的是Scenario1代码我们修改他。整合到标准呢的Scenario中。
'''
class Scenario1(object):
def __init__(self, scenario_name, description, path):
self.scenario_name = scenario_name
self.description = description
self.path = path.split('-')
self.state_list = set(self.path)
def get_line(self):
return '[%s] [%s] [%s]' % (self.scenario_name, self.description, '-'.join(self.path))
'''
class Scenario:
def __init__(self, id=-1):
self.app_id = id
self.scenario_name = None
self.description = None
self.path = None
def set_name(self, n):
self.scenario_name = n
def set_des(self, d):
self.description = d
def set_path(self, p):
self.path = p
def get_name(self):
return self.scenario_name
def get_des(self):
return self.description
def get_path(self):
return self.path
def __str__(self):
return "[app_id:" + str(self.app_id) + " scenario_name:" + str(self.scenario_name) + " description:" + str(
self.description) + " path:" + str(self.path) + "]"

80
Model/State.py Normal file
View File

@ -0,0 +1,80 @@
'''
状态类与数据库state表对应关系
appId->app_id
stateId->state_id
activityName->activity_name
picture->screen_shot
layout->layout
由于picture存储为blob 二进制流通过base64进行编码因此需要用decoder进行解码decoder即为此解码对象
author zhouxinyu
'''
import base64
import os
from os import path
class State:
def __init__(self, id=-1):
self.app_id = id
self.state_id = -1
self.activity_name = None
self.picture = None
self.layout = None
def set_state_id(self, id):
self.state_id = id
def set_activity_name(self, ac):
self.activity_name = ac
def set_picture(self, ifstream):
self.picture = ifstream
def set_layout(self, l):
self.layout = l
def get_state_id(self):
return self.state_id
def get_activity_name(self):
return self.activity_name
def get_picture(self):
return self.picture
def get_layout(self):
return self.layout
# initPic()将数据转换为图片文件并保存
def init_pic(self, out_path=None):
b = base64.b64decode(self.picture)
try:
if out_path is None:
'''pic_file = open('..\\..\\out\\' + str(self.state_id) + '.png','wb')
pic_file.write(b)'''
# 其他位置调用该模块时,../../out/寻址有问题
# 修改为使用os.path寻址的方法
curpath = path.dirname(__file__)
parent_path = os.path.dirname(curpath)
# papa_path = os.path.dirname(parent_path)
# papa_path = os.path.dirname(papa_path)
# papa_path = os.path.dirname(papa_path)
# out_path = papa_path + "\\out\\"
out_path = parent_path + "/out/"
if not os.path.exists(out_path):
os.makedirs(out_path)
pic_file = open(out_path + str(self.state_id) + '.png', 'wb')
pic_file.write(b)
finally:
pic_file.close()
def __str__(self):
return "[app_id:" + str(self.app_id) + " state_id:" + str(self.state_id) + " activity_name:" + str(
self.activity_name) + " layout:" + str(self.layout) + "]"

71
Model/Transition.py Normal file
View File

@ -0,0 +1,71 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/5/12 0:50 下午
# @Author : Chen Wenjie
# 1. 新增get_line()类方法用于bulid_model.py中save_model()函数调用。
# 2. 修改__str__ 与transition类最新数据成员适配。
'''
迁移类与数据库transition表的对应关系
appId->app_id
transitionId->transition_id
sourceId->source_id
targetId->target_id
triggerAction->trigger_action
triggerIdentifier->trigger_identifier
condition->conditions
author zhouxinyu
'''
from Model.Event import Event
class Transition:
def __init__(self, id=-1):
self.transition_id = -1
self.app_id = id
self.source_id = -1
self.target_id = -1
self.event = None
def set_transition_id(self, id):
self.transition_id = id
def set_source_id(self, id):
self.source_id = id
def set_target_id(self, id):
self.target_id = id
def set_event(self, eve):
self.event = eve
def get_transition_id(self):
return self.transition_id
def get_source_id(self):
return self.source_id
def get_target_id(self):
return self.target_id
def get_event(self):
return self.event
def get_line(self):
if self.event.trigger_identifier != "":
return str(self.get_source_id()) + ' ' + str(self.get_target_id()) + ' ' + self.event.trigger_action + ' ' + self.event.trigger_identifier
else:
return str(self.get_source_id()) + ' ' + str(self.get_target_id()) + ' ' + self.event.trigger_action
def __str__(self):
return "[transition_id:" + str(self.transition_id) + " app_id:" + str(self.app_id) + " source->target:" + str(
self.source_id) + "->" + str(self.target_id) + " trigger_action:" + str(
self.event.get_trigger_action()) + " trigger_identifier:" + str(
self.event.get_trigger_identifier()) + " conditions:" + str(self.event.get_conditions()) + "]"

237
Model/Visualization.py Normal file
View File

@ -0,0 +1,237 @@
# -*- encoding:utf-8 -*-
#!/usr/bin/env python
# @Time : 2021/7/11 0:50 上午
# @Author : Chen Wenjie
# 1. 以页面为节点,页面之间的联系方式为边,定义图类
# 2. 基于界面关系图进行可视化并生成circle与force两种不同类型的关系图
from collections import defaultdict
import json
import os
from pyecharts import options as opts
from pyecharts.charts import Graph, Page
import pyecharts.charts
import numpy as np
import pandas as pd
import random
# --------------- nested Vertex class ------------------
class Vertex:
"""创建节点类"""
def __init__(self, x, times):
"""初始化节点,x为节点的value, times为节点出现的频次"""
vertex = {}
self.element = x
self.times = times
try:
vertex["name"] = x
if times < 5:
vertex["symbolSize"] = 15
vertex["category"] = 2
color = "#FF0000"
elif times <= 15:
vertex["symbolSize"] = 35
vertex["category"] = 1
color = "#3CB371"
else:
vertex["symbolSize"] = 50
vertex["category"] = 0
color = "#9400D3"
except:
vertex["symbolSize"] = 10
vertex["category"] = 3
color = "#00BFFF"
try:
vertex["value"] = "连接页面数:" + str(times)
except:
vertex["value"] = "连接页面数0"
vertex['draggable'] = "True"
# vertex["itemStyle"] = {"normal":{"color":color}}
self.vertex = vertex
def element(self):
"""返回节点的元素值"""
return self.element
def vertex(self):
"""返回节点"""
return self.vertex
def __hash__(self):
"""允许节点作为映射的键"""
return hash(id(self))
class Edge:
"""创建边类"""
def __init__(self, u, v, x):
"""初始化边"""
self.origin = u
self.destination = v
self.element = x
def endpoints(self):
"""以元组的方式返回边的两个端点(u,v)"""
return (self.origin, self.destination)
def opposite(self, v):
"""假设顶点v是边的一个端点返回另一个端点"""
return self.destination if v is self.origin else self.origin
def element(self):
return self.element
def __hash__(self):
"""实现边的映射"""
return hash(self.origin, self.destination)
class Graph:
"""图类"""
def __init__(self, vertex_count=0, directed=False):
"""首先创建个空图默认为undirected"""
self.vertices = [] # 这里存储了图的节点
self.vertex_count = vertex_count # 这里存储了图的节点数
self.adjacency_matrix = defaultdict(list) # 这里存储了图的边(字典)
self.edges = [] # 这里存储了图的边(列表,节点的连接关系)
self.links = [] # 这里存储了图的边(列表,用于绘制关系图)
def read_edge(self, res):
"""从res模型中读取所有的页面连接信息"""
# 三元组的形式从模型中读取节点跳转信息
edges = []
for i in range(len(res.transitions)):
edges.append(
tuple((res.transitions[i].get_source_id(),
res.transitions[i].get_target_id(),
res.transitions[i].get_event().get_trigger_action())
))
# 将读取的边附给图类的edges
self.edges = edges
def insert_edge(self):
"""用加边的方式添加图的节点"""
for i in range(len(self.edges)):
u = self.edges[i][0]
v = self.edges[i][1]
self.adjacency_matrix[u].append(v)
def insert_vertex(self):
"""插入节点"""
# 获取最后一个页面的编号
max = 0
for i in range(len(self.edges)):
if self.edges[i][0] > max:
max = self.edges[i][0]
if self.edges[i][1] > max:
max = self.edges[i][1]
# 测算每个节点所连通的节点数
aDict = {}
for i in range(len(self.edges)):
aDict[self.edges[i][0]] = aDict.get(self.edges[i][0], 0) + 1
print("max:"+str(max))
# 生成系列节点
for i in range(1, max + 1):
try:
v = Vertex(i, aDict[i])
except:
v = Vertex(i, 0)
self.vertices.append(v.vertex)
self.vertex_count += 1
def add_links(self):
"""插入节点连接,用于生成关系图"""
for i in range(len(self.edges)):
self.links.append(
{
"source": self.edges[i][0],
"target": self.edges[i][1],
"value": self.edges[i][2]
}
)
def generate_graph(self, res):
"""总调函数,传入合并后的模型,生成图"""
self.read_edge(res)
self.insert_vertex()
self.insert_edge()
self.add_links()
def visualization(self):
"""关系图的可视化"""
categories = [
{
"name": "重要节点"
},
{
"name": "次重要节点"
},
{
"name": "一般节点"
},
{
"name": "孤立节点"
}
]
# circular layout
graph = (
pyecharts.charts.Graph(init_opts=opts.InitOpts(width="1000px", height="800px"))
.add("",
self.vertices,
self.links,
categories=categories,
repulsion=50,
linestyle_opts=opts.LineStyleOpts(curve=0.2),
is_rotate_label=True,
layout="circular",
label_opts=opts.LabelOpts(position="right"),
edge_label=opts.LabelOpts(
is_show=False, position="middle", formatter="{b} 的数据 {c}"
)
)
.set_global_opts(
title_opts=opts.TitleOpts(title="ARP路径关系图"),
legend_opts=opts.LegendOpts(orient="vertical", pos_left="2%", pos_top="20%"
)
)
)
graph.render("circle_layout_graph.html")
# force layout
graph = (
pyecharts.charts.Graph(init_opts=opts.InitOpts(width="960px", height="800px"))
.add("",
self.vertices,
self.links,
categories=categories,
repulsion=50,
edge_length=100,
gravity=0.05,
# linestyle_opts=opts.LineStyleOpts(curve=0.2),
is_rotate_label=True,
layout="force",
label_opts=opts.LabelOpts(position="right"),
edge_label=opts.LabelOpts(
is_show=False, position="middle", formatter="{b} 的数据 {c}"
)
)
.set_global_opts(
title_opts=opts.TitleOpts(title="ARP路径关系图"),
legend_opts=opts.LegendOpts(orient="vertical", pos_left="2%", pos_top="20%"
)
)
)
graph.render("force_layout_graph.html")

36
README.en.md Normal file
View File

@ -0,0 +1,36 @@
# SE2021-APP-Model-Merge
#### Description
对同一个App不同的ARP模型进行合并支持对人工标注的泛化或传播在此基础上可以进行基于GNN的自动标注
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

49
README.md Normal file
View File

@ -0,0 +1,49 @@
# 模型合并
# 注意:在修改实验代码时,一定要先将网站上的版本下载下来,再在下载的版本的基础上修改!
## 一、介绍
对同一个App不同的ARP模型进行合并支持对人工标注的泛化或传播
在此基础上可以进行基于GNN的自动标注
## 二、软件架构
### 2.1、数据库架构
mysql
### 2.2、软件体系
- python
- python3
- flask
- uwsgi
- react
- ubuntu
## 三、安装教程
## 四、使用要求
## 五、参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
## 六、当前进度
更新时间7.14
已完成:
- 从文件中读取模型数据
- 从数据库中读取模型数据
- 模型合并操作
- 服务器搭建
- 将代码部署在服务器上
- 界面和服务器
- 前后端交互
- 后端与数据库连接
待完成:
- 使用react重构前端代码

1548
circle_layout_graph.html Normal file

File diff suppressed because it is too large Load Diff

0
config.yaml Normal file
View File

File diff suppressed because one or more lines are too long

0
database/README.md Normal file
View File

4
database/__init__.py Normal file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2020/11/4 6:38 下午
# @Author : Wang Guoxin

284
database/api.py Normal file
View File

@ -0,0 +1,284 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/6/3 12:40 下午
# @Author : Wang GuoxinChen Wenjie, Tang Jiaxin
# 新增读取并执行sql文件的类方法exe_sql()便于后续直接通过python配置mysql数据库
import crypt
import re
import mysql.connector
from pymysql.converters import escape_string
'''
对数据库进行增删改查
'''
class DBAction(object):
#Todo 建议将数据库配置信息配置在yaml文件中。
def __init__(self):
self.db = mysql.connector.connect(
host="localhost",
user="root",
passwd="wjj",
database="apprepo"
)
print("successfully connected!")
def close(self):
self.db.close()
def change_db(self,db_name):
"""更换数据库"""
cursor = self.db.cursor()
sql = "use {0}".format(db_name)
cursor.execute(sql)
cursor.close()
self.db.commit()
def exe_sql(self,path):
"""传入本地sql文件path并执行"""
cursor = self.db.cursor()
try:
with open(path, encoding='utf-8', mode='r') as f:
# 读取整个sql文件以分号切割。[:-1]删除最后一个元素,也就是空字符串
sql_list = f.read().split(';')[:-1]
for x in sql_list:
# 判断包含空行的
if '\n' in x:
# 替换空行为1个空格
x = x.replace('\n', ' ')
# 判断多个空格时
if ' ' in x:
# 替换为空
x = x.replace(' ', '')
# sql语句添加分号结尾
sql_item = x + ';'
# print(sql_item)
cursor.execute(sql_item)
print("执行成功sql: %s" % sql_item)
except Exception as e:
print(e)
print('执行失败sql: %s' % sql_item)
finally:
# 关闭mysql连接
cursor.close()
self.db.commit()
def insert_row_app_info(self,app_id, apk_name, package_name, version):
cursor = self.db.cursor()
sql = "INSERT INTO app_info (app_id, apk_name, package_name, version) VALUES (%s, %s, %s, %s)"
cursor.execute(sql, (app_id,apk_name, package_name, version))
cursor.close()
self.db.commit()
def insert_row_state(self, app_id, state_id, activity_name, screen_shot, layout):
cursor = self.db.cursor()
sql = "INSERT INTO state (app_id, state_id, activity_name, screen_shot, layout) VALUES (%s, %s, %s, %s, %s)"
cursor.execute(sql, (app_id, state_id, activity_name, screen_shot, layout))
cursor.close()
self.db.commit()
def insert_row_transition(self, app_id, source_state, target_state, trigger_action, trigger_identifier, condition):
cursor = self.db.cursor()
sql = "INSERT INTO transition (app_id, source_state, target_state, trigger_action, trigger_identifier, conditions) VALUES (%s, %s, %s, %s, %s, %s)"
cursor.execute(sql, (app_id, source_state, target_state, trigger_action, trigger_identifier, condition))
cursor.close()
self.db.commit()
def insert_row_scenarios(self, app_id, scenario_name, description, path):
cursor = self.db.cursor()
sql = "INSERT INTO scenarios (app_id, scenario_name, description, path) VALUES (%s, %s, %s, %s)"
cursor.execute(sql, (app_id, scenario_name, description, path))
cursor.close()
self.db.commit()
def get_all_app_package_name(self):
cursor = self.db.cursor()
query = "SELECT package_name FROM app_info"
cursor.execute(query)
rows = list(cursor)
package_name_list = []
for row in rows:
package_name_list.append(row[0])
return package_name_list
def get_app_id(self, package_name):
cursor = self.db.cursor()
query = "SELECT app_id FROM app_info WHERE package_name = %s"
cursor.execute(query, (package_name,))
rows = list(cursor)
found_app_id = -1
if len(rows) == 1:
(found_app_id,) = rows[0]
elif len(rows) > 1:
print('ERROR: Multiple records.')
else:
print('ERROR: No matched record.')
cursor.close()
return found_app_id
def get_app_info(self, package_name):
"""根据安装包名读取app_info表返回info表的所有APP信息进行初始化"""
cursor = self.db.cursor()
query = "SELECT apk_name, package_name, version, author FROM app_info WHERE package_name = %s"
cursor.execute(query, (package_name,))
#获取所有记录表
app_info_lst = cursor.fetchall()
# for row in results:
# apk_name = row[0]
# package_name = row[1]
# version = row[2]
# author_id = row[3]
# app_info_lst = [apk_name,package_name,version,author_id]
cursor.close()
return app_info_lst
def get_state_by_id(self, state_id):
cursor = self.db.cursor()
query = "SELECT screen_shot, layout FROM state WHERE state_id = %s"
cursor.execute(query, (state_id,))
data = cursor.fetchall()
return data[0][0], data[0][1]
def get_state_list_by_app_id(self, app_id):
cursor = self.db.cursor()
query = "SELECT state_id, activity_name, screen_shot, layout FROM state WHERE app_id = %s"
cursor.execute(query, (app_id,))
data = cursor.fetchall()
return data
def get_transition_by_app_id(self, app_id):
cursor = self.db.cursor()
query = "SELECT source_state, target_state, trigger_action, trigger_identifier, conditions FROM transition WHERE app_id = %s"
cursor.execute(query, (app_id,))
data = cursor.fetchall()
return data
def get_scenarios_by_app_id(self, app_id):
cursor = self.db.cursor()
query = "SELECT scenario_name, description, path FROM scenarios WHERE app_id = %s"
cursor.execute(query, (app_id,))
data = cursor.fetchall()
return data
def create_user(self,uid,uname,upassword):
"""存储用户信息"""
self.db.change_db('users')
cursor = self.db.cursor()
sql = "INSERT INTO user_info (uid, name, password) VALUES (%s, %s, %s)"
cursor.execute(sql, (uid, uname, upassword))
cursor.close()
self.db.commit()
def create_email(self,uid,email):
"""存入邮箱"""
self.db.change_db('users')
cursor = self.db.cursor()
query = "INSERT INTO user_email (uid, email) VALUES (%s, %s)"
cursor.execute(query, (uid, email,))
cursor.close()
self.db.commit()
def register_check(self,uname):
"""用户注册时检查数据库中是否已存在该用户名,若是则返回失败"""
self.db.change_db('users')
cursor = self.db.cursor()
query = "SELECT * FROM user_info WHERE name = %s"
cursor.execute(query, (uname,))
result = cursor.fetchall()
if result:
return False #查询到存在同名name说明注册冲突返回False
else:
return True #查询到未有同名name说明可以注册返回True
def check_email_uid(self,uid):
"""使用uid验证邮箱表中是否已存用户邮箱"""
self.db.change_db('users')
cursor = self.db.cursor()
query = "SELECT * FROM user_email WHERE uid = %s"
cursor.execute(query, (uid,))
result = cursor.fetchall()[0]
return result
def check_email_email(self, email):
"""使用email验证邮箱表中是否已存用户邮箱"""
self.db.change_db('users')
cursor = self.db.cursor()
query = "SELECT * FROM user_email WHERE email = %s"
cursor.execute(query, (email,))
result = cursor.fetchall()[0]
return result
def get_uid(self):
"""获取用户表中的最大uid"""
self.db.change_db('users')
cursor = self.db.cursor()
query = "SELECT MAX(uid) FROM user_info"
cursor.execute(query,)
maxuid = cursor.fetchall()[0]
if maxuid:
return maxuid + 1
else:
return 1
def get_name_uid(self,uname):
"""获取用户名对应的uid"""
self.db.change_db('users')
cursor = self.db.cursor()
query = "SELECT uid FROM user_info WHERE name = %s"
cursor.execute(query, (uname,))
uid = cursor.fetchall()[0]
return uid
def login_check(self,uname,upassword):
"""根据用户表单提交的用户名与用户密码,进行登录核验"""
self.db.change_db('users')
cursor = self.db.cursor()
query = "SELECT password FROM user_info WHERE name = %s"
cursor.execute(query, (uname,))
password = cursor.fetchall()[0]
if upassword == password:
#("welcome {0} !".format(uname))
return True
else:
#print("password wrong!")
return False
def upload_data(self,uid,app_name,time,file_path):
"""向文件数据库中添加记录字段为用户ID、文件路径"""
self.db.change_db('users')
cursor = self.db.cursor()
query = "INSERT INTO data_info(uid,app_name,time,file_path) values(%s,%s,%s,%s)"
cursor.execute(query, (uid, app_name, time, file_path,))
cursor.close()
def delete_data(self,uid,file_path):
"""从文件数据库中删除记录依据为用户ID和文件路径"""
self.db.change_db('users')
cursor = self.db.cursor()
query = "DELETE FROM data_info where uid= %s AND file_path= %s"
cursor.execute(query, (uid, file_path,))
cursor.close()
def select_data(self,uid):
"""从文件数据库中查找记录依据为用户ID"""
self.db.change_db('users')
cursor = self.db.cursor()
query = "SELECT * FROM data_info where uid= %s"
cursor.execute(query, (uid,))
result = cursor.fetchall() # 它的返回值是多个元组,即返回多个行记录
cursor.close()
di = {}
for uid,app_name,time,file_path in result:
di[file_path] = [time, app_name]
return di
'''
测试
if __name__ == "__main__":
db = DBAction()
print(db.get_app_id("com.forrestguice.suntimeswidget"))
'''

69
database/init.py Normal file
View File

@ -0,0 +1,69 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/5/24 0:50 下午
# @Author : Chen Wenjie
'''
用于初始化构建本地/云数据库
新增了对用户数据库内含用户信息表数据存储表的初始化与交互
'''
import mysql.connector
from api import DBAction
import api
def sourceData(source_sql_file_path):
"""从sql文件中读取数据并存入本地数据库"""
db_name = input("select a database to exe data insert:") #输入要使用的数据库名
db = DBAction()
db.change_db(db_name)
db.exe_sql(source_sql_file_path)
def initDatabase(init_sql_file_path):
"""初始化database"""
db = DBAction()
db.exe_sql(init_sql_file_path)
if __name__ == "__main__":
# 1.初始化原始数据库
# print("__init_database__")
# init_sql_path = "./sql/init_apprepo.sql"
# initDatabase(init_sql_path)
# 2.初始化模型合并结果数据库
# print("__init_database__")
# init_sql_path = "./sql/init_merge.sql"
# initDatabase(init_sql_path)
# 3.读取本地数据
# print("__source_data__")
# source_sql_path = r'./sql/File_Manager_Pro_0.5_2021-02-09.sql'
# sourceData(source_sql_path)
# 4.初始化用户数据库
print("_init_database_users")
initDatabase('./sql/init_users.sql')
# pass
# 5.尝试插入用户登录数据
uname = "wjj"
upassword = "123"
create_user(uname, upassword)
# 6.尝试核对用户登录信息
uname = "wjj"
upassword = "1234"
login_check(uname, upassword)
# 7.尝试插入用户模型数据
uid = 2
file_path = "E:\\Desktop\\test"
app_name = 'QQ'
time = '2021/5/31'
upload_data(uid, app_name, time, file_path)
# 8.尝试读取用户模型数据
uid = 2
result = select_data(uid)

158
database/prep.py Normal file
View File

@ -0,0 +1,158 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2020/11/4 6:56 下午
# @Author : Wang Guoxin
import os
from database.api import DBAction
import base64
import xml.etree.ElementTree
def convertToBinaryData(filepath):
"""
将图片转化为二进制格式
:param filepath: 图片地址
:return: 二进制数据
"""
with open(filepath, 'rb') as file:
binary_data = file.read()
encodestring = base64.b64encode(binary_data)
return encodestring
def encode_layout(filepath):
tree = xml.etree.ElementTree.parse(filepath)
root = tree.getroot()
return xml.etree.ElementTree.tostring(root).decode()
def read_q_testing_result(project_dir):
"""
读取Q-testing探索App生成的结果
:param project_dir: 项目文件夹地址
:return: apk_name, package_name, version, states, jump_pairs
"""
file = open(os.path.join(project_dir, 'app_info.lst'), 'r')
apk_name = file.readline().strip()
package_name = file.readline().strip()
version = file.readline().strip()
states = []
file = open(os.path.join(project_dir, 'window_info.lst'), 'r')
for l in file.readlines():
line = l.split()
state_id, activity_name = line[0], line[1]
screen_shot_path = os.path.join(project_dir, 'temp-screen-shot', state_id+'.png')
layout_file_path = os.path.join(project_dir, 'temp-gui-hierarchy', state_id+'.xml')
if not os.path.exists(screen_shot_path):
print(screen_shot_path + ' not exists')
exit(0)
if not os.path.exists(layout_file_path):
print(layout_file_path + ' not exists')
exit(0)
binary_data = convertToBinaryData(screen_shot_path)
layout = encode_layout(layout_file_path)
states.append((line[0], line[1], binary_data, layout))
jump_pairs = []
file = open(os.path.join(project_dir, 'jump_pairs.lst'), 'r')
for l in file.readlines():
line = l.split(maxsplit=2)
if len(line) == 3:
action_str = line[2].strip()
at_pos = action_str.find("@")
# TODO 是否要记录不发生跳转的事件
if line[0] != line[1]:
if at_pos == -1:
jump_pairs.append([line[0], line[1], action_str])
else:
action_str = action_str[at_pos + 1:]
if action_str.startswith('click') or action_str.startswith('clickLong') or action_str.startswith('edit'):
spilt_index = action_str.find('(')
action_type = action_str[:spilt_index]
action_identifier = action_str[spilt_index+1:-1]
#print([line[0], line[1], action_type, action_identifier])
jump_pairs.append([line[0], line[1], action_type, action_identifier])
else:
jump_pairs.append([line[0], line[1], action_str])
#print([line[0], line[1], action_str])
else:
print("WARNING: Skipped invalid record in " + os.path.join(project_dir, 'jump_pairs.lst'))
print(line)
return apk_name, package_name, version, states, jump_pairs
def read_scenario_info(project_dir):
"""
读取功能场景及其路径
:param project_dir: 项目文件夹地址
:return: scenario_name, path
"""
scenario_name_list = []
path_list = []
scenarios = os.listdir(os.path.join(project_dir, 'feature'))
for scenario in scenarios:
if scenario == '.DS_Store':
continue
scenario_name = scenario.split(' ')[0]
path_str = scenario.split(' ')[1]
scenario_name_list.append(scenario_name)
path_list.append(path_str)
return scenario_name_list, path_list
def process_project(project_path):
"""
处理单个项目文件夹
:param project_path: 项目文件夹路径
:return:
"""
apk_name, package_name, version, states, jump_pairs = read_q_testing_result(project_path)
db = DBAction()
package_name_list = db.get_all_app_package_name()
if package_name in package_name_list:
print("Skipped exist apk " + package_name)
return
# 插入 app_info
db.insert_row_app_info(apk_name, package_name, version)
app_id = db.get_app_id(package_name)
if app_id != -1:
# 插入 state
for (state_id, activity_name, binary_data, layout) in states:
db.insert_row_state(app_id, state_id, activity_name, binary_data, layout)
# 插入 transition
for jump_pair in jump_pairs:
if len(jump_pair) == 3:
source_state, target_state, trigger_action= jump_pair[0], jump_pair[1], jump_pair[2]
db.insert_row_transition(app_id, source_state, target_state, trigger_action, '', '')
elif len(jump_pair) == 4:
source_state, target_state, trigger_action, trigger_identifier = jump_pair[0], jump_pair[1], jump_pair[2], jump_pair[3]
db.insert_row_transition(app_id, source_state, target_state, trigger_action, trigger_identifier, '')
else:
print("WARNING: error jump_pair")
# 插入功能场景及其路径
# scenario_name_list, path_list = read_scenario_info(project_path)
# for index in range(len(scenario_name_list)):
# print(app_id, scenario_name_list[index], '', path_list[index])
# db.insert_row_scenarios(app_id, scenario_name_list[index], '', path_list[index])
print('## Project processed: ' + project_path)
def process_whole(path):
"""
处理包含项目文件夹的整体文件夹
:param path: 项目文件夹所在文件夹路径
:return:
"""
for f in os.listdir(path):
project_path = os.path.join(path, f)
if os.path.isdir(project_path):
process_project(project_path)
if __name__ == "__main__":
process_whole('/Users/wangguoxin/Projects/AppRepository/ProjectCollections/Q-testing-out/success')
#process_project('/Users/wangguoxin/Projects/AppRepository/ProjectCollections/Q-testing-out/debug_output1/org.asdtm.goodweather_13')

0
database/sql/.keep Normal file
View File

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,39 @@
CREATE DATABASE IF NOT EXISTS APPREPO;
CREATE DATABASE IF NOT EXISTS APPREPO;
CREATE TABLE IF NOT EXISTS APPREPO.app_info(
app_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
apk_name VARCHAR(128) NOT NULL,
package_name VARCHAR(128) NOT NULL,
version VARCHAR(32) NOT NULL,
author VARCHAR(32)
);
CREATE TABLE IF NOT EXISTS APPREPO.state(
app_id INT UNSIGNED NOT NULL,
state_id INT UNSIGNED NOT NULL PRIMARY KEY,
activity_name VARCHAR(128) NOT NULL,
screen_shot mediumblob NOT NULL,
layout LONGTEXT NOT NULL ,
FOREIGN KEY (app_id) REFERENCES app_info(uid)
);
CREATE TABLE IF NOT EXISTS APPREPO.transition(
transition_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
app_id INT UNSIGNED NOT NULL,
source_state INT UNSIGNED NOT NULL,
target_state INT UNSIGNED NOT NULL,
trigger_action TEXT NOT NULL,
trigger_identifier TEXT,
conditions TEXT ,
FOREIGN KEY (app_id) REFERENCES app_info(uid)
);
CREATE TABLE IF NOT EXISTS APPREPO.scenarios(
app_id INT UNSIGNED NOT NULL,
scenario_name VARCHAR(128) NOT NULL,
description VARCHAR(128) NOT NULL,
path VARCHAR(128) NOT NULL ,
FOREIGN KEY (app_id) REFERENCES app_info(uid)
);

View File

@ -0,0 +1,58 @@
CREATE DATABASE IF NOT EXISTS MergeResult;
CREATE TABLE IF NOT EXISTS MergeResult.app_info(
app_id INT NOT NULL PRIMARY KEY,
apk_name VARCHAR(128) NOT NULL,
package_name VARCHAR(128) NOT NULL,
version VARCHAR(32) ,
author VARCHAR(32)
);
CREATE TABLE IF NOT EXISTS MergeResult.state(
state_id INT NOT NULL PRIMARY KEY,
app_id INT NOT NULL,
activity_name VARCHAR(128) ,
screen_shot mediumblob ,
layout LONGTEXT ,
FOREIGN KEY (app_id) REFERENCES app_info(uid)
);
CREATE TABLE IF NOT EXISTS MergeResult.transition(
transition_id BigInt NOT NULL AUTO_INCREMENT PRIMARY KEY,
app_id INT NOT NULL,
source_state INT ,
target_state INT ,
trigger_action TEXT ,
trigger_identifier TEXT,
conditions TEXT ,
FOREIGN KEY (app_id) REFERENCES app_info(uid)
);
CREATE TABLE IF NOT EXISTS MergeResult.scenarios(
scenario_name VARCHAR(128) ,
app_id INT NOT NULL,
description VARCHAR(128) ,
path VARCHAR(128),
FOREIGN KEY (app_id) REFERENCES app_info(uid)
);
-- @author: Chen wenjie
-- @time: 2021-6-2 00:34
-- 配置用于存储merge后的数据的数据库
--
--
-- def get_res_model(self): # 返回一个合并后的模型
-- res_model = Model()
-- res_model.set_id(self.app_id)
-- res_model.set_apk_name(self.app_apk_name)
-- res_model.set_package_name(self.app_package_name)
-- res_model.set_version(self.app_version)
-- res_model.state_list = self.res_state_list
-- res_model.transitions = self.res_transitions
-- res_model.states = self.res_states
-- return res_model

View File

@ -0,0 +1,25 @@
CREATE DATABASE IF NOT EXISTS USERS;
CREATE TABLE IF NOT EXISTS USERS.user_info(
uid VARCHAR(128) PRIMARY KEY,
name VARCHAR(128) NOT NULL,
password VARCHAR(128) NOT NULL
);
CREATE TABLE IF NOT EXISTS USERS.user_email(
uid VARCHAR(128) PRIMARY KEY,
email VARCHAR(128)
);
CREATE TABLE IF NOT EXISTS USERS.data_info(
uid VARCHAR(128),
app_name VARCHAR(128),
time VARCHAR(128),
file_path VARCHAR(128)
);
-- @author: Chen Wenjie, Wang Jiajie
-- @time: 2021-7-12 10:19
-- 配置用于存储用户信息与模型数据的数据库

51
dict.md Normal file
View File

@ -0,0 +1,51 @@
### 模型合并组数据字典
#### 一、数据项
#####1app_id
描述model所属app的id
类型char
#####2apk_name
描述app安装包的名称
类型char
#####3package_name
描述app的package名称
类型char
#####4version
描述app的版本号
类型char
#####5window_info
描述:当前聚焦的应用窗口信息
类型char
#####6state_activity
描述:当前进行的页面的状态信息
类型char
#####7页面标识
描述对页面进行标识和描述的xml格式文件
类型:.uix
#####8页面截屏
描述png格式的页面截屏
类型:.png
#####9页面编号
描述该arp模型中页面的标号
类型int
#### 二、数据结构
数据结构反映数据之间的组合关系
#####1jump_pairs
描述:描绘了两两界面的跳转逻辑
组成:来源界面编号 目标界面编号 跳转方式
#####2scenarios
描述:描述了应用程序某个使用场景的使用方式和界面跳转逻辑
组成:操作名(列表) 操作说明(对操作进行的解释组成的列表) 界面跳转表(界面编号组成的列表)

1548
force_layout_graph.html Normal file

File diff suppressed because it is too large Load Diff

34
main.py Normal file
View File

@ -0,0 +1,34 @@
import sys
sys.path.append('./Model')
sys.path.append('./Merge')
from BuildModel import Model
from Merge import Merge
# 读两个模型
model1 = Model()
model2 = Model()
model1.Build_Model_From_Project('./Data/2020FallSE/181860039_孔鹏翔_AnyMemo')
model2.Build_Model_From_Project('./Data/2020Fall软件方法学/MG20330042_毛心怡_MG20330037_刘疏观_MG1633116_李达_AnyMemo')
print('model1.state_list:', len(model1.state_list))
print('model2.state_list:', len(model2.state_list))
print('model1.transitions:', len(model1.transitions))
print('model2.transitions:', len(model2.transitions))
# 以APP名称定义一个Merge类加入这两个模型
AnyMemo = Merge()
AnyMemo.add_model(model1)
AnyMemo.add_model(model2)
# 进行模型合并操作,返回合并好的模型
AnyMemo.merge_models()
res = AnyMemo.get_res_model()
print('res.state_list:', len(res.state_list))
print('res.states:', len(res.states))
print('res.transitions:', len(res.transitions))
# 模型可视化:关系图
g = Graph()
g.generate_graph(res)
print(g.vertices)
g.visualization()

0
output/.keep Normal file
View File

13
requirements.txt Normal file
View File

@ -0,0 +1,13 @@
certifi==2020.12.5
chardet==4.0.0
mkl-fft==1.3.0
mkl-random==1.1.1
mkl-service==2.3.0
mysql==0.0.2
mysql-connector==2.2.9
mysqlclient==2.0.3
numpy==1.19.2
opencv-python==4.5.1.48
Pillow==8.1.2
PyMySQL==1.0.2
six==1.15.0

32
sql/init.sql Normal file
View File

@ -0,0 +1,32 @@
CREATE DATABASE IF NOT EXISTS AppRepo;
CREATE TABLE IF NOT EXISTS AppRepo.app_info(
app_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
apk_name VARCHAR(128) NOT NULL,
package_name VARCHAR(128) NOT NULL,
version VARCHAR(32)
);
CREATE TABLE IF NOT EXISTS AppRepo.state(
app_id INT UNSIGNED NOT NULL,
state_id INT UNSIGNED NOT NULL,
activity_name VARCHAR(128) NOT NULL
);
CREATE TABLE IF NOT EXISTS AppRepo.transition(
transition_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
app_id INT UNSIGNED NOT NULL,
source_state INT UNSIGNED NOT NULL,
target_state INT UNSIGNED NOT NULL,
trigger_action TEXT NOT NULL,
trigger_identifier TEXT,
conditions TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS AppRepo.scenarios(
app_id INT UNSIGNED NOT NULL,
scenario_name VARCHAR(128) NOT NULL,
description VARCHAR(128) NOT NULL,
path VARCHAR(128) NOT NULL
);

105
util/read_file.py Normal file
View File

@ -0,0 +1,105 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/1/7 4:08 下午
# @Author : Wang Guoxin
"""
从作业里读取模型信息且作业中的各种文件应该是格式化过的清理过的数据
"""
import os
import chardet
import xml.etree.ElementTree
import base64
import re
def get_file_encoding(path):
f = open(path, 'rb')
data = f.read()
return chardet.detect(data).get('encoding')
def image2base64(filepath):
"""
将图片转化为二进制格式
:param filepath: 图片地址
:return: 二进制数据的base64编码
"""
with open(filepath, 'rb') as file:
binary_data = file.read()
encodestring = base64.b64encode(binary_data)
return encodestring
def encode_layout(filepath):
tree = xml.etree.ElementTree.parse(filepath)
root = tree.getroot()
return xml.etree.ElementTree.tostring(root).decode()
def read_app_info(project_path):
author_id = None
with open(os.path.join(project_path, 'app_info.lst'), 'r', encoding='utf-8') as f:
lines = f.readlines()
app_name = lines[0].strip()
package_name = lines[1].strip()
version = lines[2].strip()
return app_name, package_name, version, author_id
def read_state_info(project_path):
states = []
state_activity_file = os.path.join(project_path, 'window_info.lst')
file = open(state_activity_file, 'r', encoding=get_file_encoding(state_activity_file))
for l in file.readlines():
l = l.strip()
line = l.split()
state_id, activity_name = line[0], line[1]
screen_shot_path = os.path.join(project_path, 'screens', state_id + '.png')
layout_file_path = os.path.join(project_path, 'screens', state_id + '.uix')
if not os.path.exists(screen_shot_path):
print(screen_shot_path + ' not exists')
exit(0)
# 对于动态界面可能无法获取布局文件的情况设layout为空字符
if not os.path.exists(layout_file_path):
print(layout_file_path + ' not exists')
layout = ''
else:
layout = encode_layout(layout_file_path)
binary_data = image2base64(screen_shot_path)
states.append((state_id, activity_name, binary_data, layout))
return states
def read_transitions(project_path):
transitions = []
file = open(os.path.join(project_path, 'jump_pairs.lst'), 'r')
for line in file.readlines():
line = line.strip()
fields = re.split(r" (?![^(]*\))", line, maxsplit=3)
if len(fields) == 3:
transitions.append([fields[0], fields[1], fields[2]])
elif len(fields) == 4:
transitions.append([fields[0], fields[1], fields[2], fields[3]])
else:
print("WARNING: Skipped invalid record:" + line)
print(len(fields))
return transitions
def read_scenarios_info(project_path):
scenarios_file = os.path.join(project_path, 'scenarios.lst')
file = open(scenarios_file, 'r', encoding=get_file_encoding(scenarios_file))
scenarios = []
for line in file.readlines():
if line.startswith('[功能名称]'):
continue
line = line.strip()
fields = re.findall(r'[[](.*?)[]]', line)
print(fields)
scenarios.append([fields[0], fields[1], fields[2]])
return scenarios

0
服务器/.keep Normal file
View File

439
服务器/app.py Normal file
View File

@ -0,0 +1,439 @@
'''
@Time : 2021/7/11 20:30 下午
@Author : Tang Jiaxin, Wang Jiajie
服务器的各种功能
'''
# coding: utf-8
from flask import Flask, request, jsonify, send_from_directory
from flask_cors import *
import json
import os
import io
import zipfile
import shutil
from werkzeug.utils import secure_filename
import sys
import time
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import string
import smtplib
import socks
import socket
from email.mime.text import MIMEText
from email.utils import formataddr
sys.path.append('./Merge')
sys.path.append('./Model')
sys.path.append('./database')
from BuildModel import Model
from Merge import Merge
from Visualization import Graph
from database.api import DBAction
app = Flask(__name__)
db = DBAction()
CORS(app, supports_credentials=True, resources=r"/*")
@app.route('/', methods = ['GET'])
def welcome():
return 'welcome!'
def unfold(file_name, path):
if not zipfile.is_zipfile(file_name):
return False
zip_file = zipfile.ZipFile(file_name)
zip_list = zip_file.namelist()
for f in zip_list:
zip_file.extract(f, path)
zip_file.close()
return True
def fold(dirpath, resFilePath):
print(dirpath, resFilePath)
zip_file = zipfile.ZipFile(resFilePath, 'w', zipfile.ZIP_DEFLATED)
for path, dirnames, filenames in os.walk(dirpath):
fpath = path.replace(dirpath, '')
for filename in filenames:
zip_file.write(os.path.join(path, filename), os.path.join(fpath, filename))
zip_file.close()
@app.route('/file_upload', methods = ['POST'], strict_slashes = False)
def receive_file():
basedir = os.path.dirname(__file__)
usr_name = 'usr' + request.form['cookie'] #usr_name: usr0
file_dir = os.path.join(basedir, 'files', usr_name) #file_dir: ./files/usr0
if not os.path.exists(os.path.join(basedir, 'files')):
os.makedirs(os.path.join(basedir, 'files'))
if not os.path.exists(file_dir):
os.makedirs(file_dir)
app = request.form['app']
datetime = time.asctime(time.localtime(time.time()))
print(app)
print(datetime)
f = request.files['input_file']
if not f:
return jsonify({'state': 1001, 'msg': '无法获取上传的文件'})
fname = secure_filename(f.filename) #fname: file_name.zip
res_file_name = fname.split('.')[0] #res_file_name: file_name
print(fname, res_file_name)
file_list = os.listdir(file_dir) #file_list: all file_name in ./files/usr0
for file_name in file_list: #file_name: file_name
if file_name == res_file_name:
return jsonify({'state': 1003, 'msg': '该名称的文件已经存在'})
file_path = os.path.join(file_dir, fname) #file_path: ./files/usr0/file_name.zip
f.save(file_path)
res = unfold(file_path, file_dir) #unfold ./files/usr0/file_name.zip to ./files/usr0
print(usr_name, app, datetime, res_file_name)
db.upload_data(usr_name, app, datetime, res_file_name)
os.remove(file_path)
if not res:
return jsonify({'state': 1002, 'msg': '无法解压上传的文件'})
return jsonify({'state': 200, 'msg': '上传成功'})
@app.route('/file_download', methods = ['GET', 'POST'])
def send_download_file():
basedir = os.path.dirname(__file__)
if request.method == 'GET':
return 'ok'
else:
file_name = request.form['fileName'] #file_name: files/usr0/file_name/ or temp/tempfile0/
print(file_name)
file_name_list = file_name.split('/')
kind = file_name_list[0] #kind :files or temp
fname = ''
usr_name = ''
if kind == 'files':
usr_name = file_name_list[1] #usr_name: usr0
fname = file_name_list[2] #fname: file_name
else:
fname = file_name_list[1] #fname: tempfile0
fold_name = fname + '.zip' #fold_name: file_name.zip or tempfile0.zip
fold_path = os.path.join(os.path.join(kind, usr_name), fold_name) #fold_path: files/usr0/file_name.zip or temp/tempfile0.zip
if not os.path.exists(fold_path):
fold(file_name, fold_path)
return send_from_directory(os.path.join(basedir, kind, usr_name), fold_name, as_attachment=True) #send file_name.zip in ./files/usr0 or tempfile0.zip in ./temp
def get_file_lines(file_name):
f = open(file_name, 'r')
count = -1
for index, line in enumerate(f):
count += 1
f.close()
return count
@app.route('/file_display', methods = ['POST'])
def send_display_file():
basedir = os.path.dirname(__file__)
data = json.loads(request.get_data(as_text=True))
file_name = data['file'] #file_name: files/usr0/file_name
print(file_name)
file_path = os.path.join(basedir, file_name) #file_path: ./files/usr0/file_name
if not os.path.exists(file_path):
return jsonify({'state': 1004, 'msg': '无法打开希望获取的文件'})
transitions_path = os.path.join(file_path, 'transitions.lst') #transitions_path: ./files/usr0/file_name/transitions.lst
s = 'transition数' + str(get_file_lines(transitions_path)) + '<br>'
state_path = os.path.join(file_path, 'window_info.lst')
s += ('state数' + str(get_file_lines(state_path)) + '<br>')
return jsonify({'state': 200, 'data': s})
@app.route('/file_graph', methods = ['POST'])
def send_graph_file():
basedir = os.path.dirname(__file__)
data = json.loads(request.get_data(as_text=True))
file_name = data['file'] #file_name: files/usr0/file_name
print(file_name)
file_path = os.path.join(basedir, file_name) #file_path: ./files/usr0/file_name
if not os.path.exists(file_path):
return jsonify({'state': 1004, 'msg': '无法打开希望获取的文件'})
model = Model()
model.Build_Model_From_Project(file_path)
graph = Graph()
graph.generate_graph(model)
graph.visualization()
f = open('force_layout_graph.html', 'r')
s = f.read()
return jsonify({'state': 200, 'data': s})
@app.route('/file_list', methods = ['POST'])
def send_file_list():
basedir = os.path.dirname(__file__)
data = json.loads(request.get_data(as_text=True))
usr_name = 'usr' + data['cookie'] #usr_name: usr0
file_dir = os.path.join(basedir, 'files', usr_name) #file_dir: ./files/usr0
file_list = os.listdir(file_dir) #file_list: all files in ./files/usr0
di = db.select_data(usr_name)
li = {}
for file_name in file_list:
print(file_name) #file_name: file_name or file_name.zip
if not os.path.isdir(os.path.join(file_dir, file_name)): #ignore ./files/usr0/file_name.zip
continue
time, app = di[file_name]
file_path = 'files/' + usr_name + '/' + file_name + '/' #file_path: files/usr0/file_name/
t_di = {'file': file_path, 'time': time}
if not app in li:
li[app] = [t_di]
else:
li[app].append(t_di)
return jsonify({'state': 200, 'data': li})
def merge_models(files, resPath):
merge = Merge()
for f in files:
model = Model()
model.Build_Model_From_Project(f)
merge.add_model(model)
merge.merge_models()
res = merge.get_res_model()
print('resPath:' + resPath)
res.Save_Model_To_Local(resPath)
@app.route('/file_merge', methods = ['POST'])
def file_merge():
basedir = os.path.dirname(__file__)
file_dir = os.path.join(basedir, 'temp') #file_dir: ./temp
file_cnt = 0
if not os.path.exists(file_dir):
os.makedirs(file_dir)
else:
file_cnt = len(os.listdir(file_dir)) #file_cnt: number of files in ./temp
data = json.loads(request.get_data(as_text=True))
app = data['app']
files = data['files']
print(app, files)
file_name = 'tempfile' + str(file_cnt) + '/' #file_name: tempfile0/
file_path = os.path.join(file_dir, file_name) #file_path: ./temp/tempfile0/
merge_models(files, file_path)
return jsonify({'state': 200, 'file': 'temp/' + file_name}) #send temp/tempfile0/
@app.route('/merge_process', methods = ['POST'])
def merge_process():
f = open('process.txt', 'r')
data = f.readlines()
f.close()
return jsonify({'state': 200, 'process': data[-1]})
@app.route('/save_result', methods = ['POST'])
def save_result():
basedir = os.path.dirname(__file__)
data = json.loads(request.get_data(as_text=True))
usr_name = 'usr' + data['cookie'] #usr_name: usr0
temp_name = data['tempname'] #temp_name: temp/tempfile0/
file_name = data['filename'] #file_name: file_name
app = data['app']
file_dir = os.path.join(basedir, 'files', usr_name) #file_dir: ./files/usr0
print(temp_name, file_name)
file_list = os.listdir(file_dir) #file_list: all file_name in ./files/usr0
for fname in file_list: #fname: file_name
if file_name == fname:
return jsonify({'state': 1003, 'msg': '该名称的文件已经存在'})
file_path = os.path.join(file_dir, file_name) #file_path: ./files/usr0/file_name
shutil.copytree(temp_name, file_path)
shutil.rmtree('temp')
os.mkdir('temp')
temp_time = time.asctime( time.localtime(time.time()) )
db.upload_data(usr_name, app, temp_time, file_name)
return jsonify({'state': 200, 'msg': '保存成功'})
@app.route('/abort_result', methods = ['POST'])
def abort_result():
data = json.loads(request.get_data(as_text=True))
temp_name = data['tempname'] #temp_name: temp/tempfile0/
shutil.rmtree('temp')
os.mkdir('temp')
return jsonify({'state': 200, 'msg': '删除成功'})
@app.route('/file_remove', methods = ['POST'])
def remove_file():
data = json.loads(request.get_data(as_text=True))
file_list = data['files'] #file_list: list of files/usr0/file_name
for file_name in file_list: #file_name: files/usr0/file_name
print(file_name)
file_name_list = file_name.split('/')
db.delete_data(file_name_list[1], file_name_list[2])
shutil.rmtree(file_name)
return jsonify({'state': 200, 'msg': '删除成功'})
@app.route('/register', methods=['POST'])
def register():
data = json.loads(request.get_data(as_text=True))
username = data['user_name']
password = data['password']
print(username, password)
uid = db.get_uid()
#判断用户名是否已经存在若数据库中不存在则返回uid否则返回“注册失败用户名已存在”
if db.register_check(username):
db.create_user(uid, username, password)
basedir = os.path.dirname(__file__)
usr_name = 'usr' + str(uid) #usr_name: usr0
file_dir = os.path.join(basedir, 'files', usr_name) #file_dir: ./files/usr0
if not os.path.exists(os.path.join(basedir, 'files')):
os.makedirs(os.path.join(basedir, 'files'))
if not os.path.exists(file_dir):
os.makedirs(file_dir)
return jsonify({'state': 200, 'uid': uid})
else:
return jsonify({'state': 1005, 'msg': '注册失败,用户名已存在'})
@app.route('/login', methods = ['POST'])
def login():
data = json.loads(request.get_data(as_text=True))
user_name = data['user_name']
password = data['password']
uid = db.get_name_uid(user_name)
if uid:
mail_addr = db.check_email_uid(uid)
if db.login_check(user_name, password):
#print(username, password)
return jsonify({'state': 200, 'uid': uid, 'mail_addr': mail_addr})
else:
return jsonify({'state': 1006, 'msg': '密码错误'})
else:
return jsonify({'state': 1007, 'msg': '用户不存在'})
@app.route('/change_password', methods = ['POST'])
def change_password():
data = json.loads(request.get_data(as_text=True))
uid = data['cookie']
old_password = data['old_password']
new_password = data['new_password']
if db.check_password(uid, old_password):
db.change_password(uid,new_password)
return jsonify({'state': 200, 'msg': '密码修改成功'})
else:
return jsonify({'state': 1006, 'msg': '密码错误'})
@app.route('/mail_login', methods = ['POST'])
def mail_login():
data = json.loads(request.get_data(as_text=True))
code = data['check_code']
if 'user_name' in data:
user_name = data['user_name']
uid = db.get_name_uid(user_name)
mail_addr = db.check_email_uid(uid)
if not mail_addr:
return jsonify({'state': 1100, 'msg': '用户未绑定邮箱'})
else:
mail_addr = data['mail_addr']
uid = db.check_email_email(mail_addr)
if not uid:
return jsonify({'state': 1101, 'msg': '邮箱对应的用户不存在'})
user_name = db.get_user_name(uid)
print(mail_addr, code)
ret = send_mail(mail_addr, code)
if ret:
return jsonify({'state': 200, 'uid': uid, 'user_name': user_name, 'mail_addr': mail_addr})
else:
return jsonify({'state': 1102, 'msg': '发送邮件时出错'})
@app.route('/mail_bind', methods = ['POST'])
def mail_bind():
data = json.loads(request.get_data(as_text=True))
uid = data['cookie']
mail_addr = data['mail_addr']
index = db.check_email_email(mail_addr)
if index:
return jsonify({'state':1105,'msg':'邮箱已被其他用户绑定'})
else:
if 'check_code' in data:
code = data['check_code']
print(mail_addr, code)
ret = send_mail(mail_addr, code)
if ret:
return jsonify({'state': 200, 'msg': '邮件发送成功'})
else:
return jsonify({'state': 1102, 'msg': '发送邮件时出错'})
else:
db.create_email(uid, mail_addr)
return jsonify({'state': 200, 'msg': '邮箱绑定成功'})
@app.route('/check_code', methods = ['POST'])
def check_code():
data = json.loads(request.get_data(as_text=True))
s = data['str']
code = create_check_code(200, 50, s)
return code
def send_mail(receiver, code):
sender = '405903102@qq.com' # 发件人邮箱账号
password = 'xozekekxpckqbjfb' # 发件人邮箱授权码
try:
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, '114.212.83.198', 1080)
socks.wrapmodule(smtplib)
msg = MIMEText('验证码为:' + code + ',如非本人操作,请忽略此邮件', 'plain', 'utf-8') #填写邮件内容
msg['From'] = formataddr(["distant east coast", sender]) # 设置发件人邮箱账号
msg['To'] = formataddr(["", receiver]) # 设置收件人邮箱账号
msg['Subject'] = "验证码" # 设置邮件主题
server = smtplib.SMTP_SSL("smtp.qq.com", 465) # 使用发件人邮箱中的SMTP服务器
server.login(sender, password) # 登录发件人邮箱
server.sendmail(sender, [receiver, ], msg.as_string()) # 发送邮件
server.quit() # 关闭连接
except Exception:
return False
return True
def create_check_code(w, h, s):
'''
根据给定的字符串生成一个验证码
'''
# 随机背景色创建图片
bgcolor = (random.randrange(160,255), random.randrange(50,160), 255)
img = Image.new(
mode = "RGB",
size = (w, h),
color = bgcolor)
draw = ImageDraw.Draw(img, mode = "RGB")
# 绘制干扰线
line_number = random.randint(3, 5)
for i in range(line_number):
begin = (random.randint(0, w), random.randint(0, h))
end = (random.randint(0, w), random.randint(0, h))
linecolor = (
random.randint(150, 255),
random.randint(150, 255),
random.randint(150, 255))
draw.line([begin, end], fill=linecolor)
# 绘制字符
for i in range(4):
char = s[i]
textcolor = (
random.randint(0, 150),
random.randint(0, 150),
random.randint(0, 150))
fontsize = random.randint(2 * (h // 3), h)
font = ImageFont.truetype("Times.ttc", fontsize)
draw.text((i * (w // 4) + random.randint(0, (w // 8)), random.randint(0, h - fontsize)), char, textcolor, font)
# 加边缘滤镜
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
# 保存图片
buf = io.BytesIO()
img.save(buf, "png")
return buf.getvalue()
if __name__ == '__main__':
app.run(host='0.0.0.0', port='8001')

0
界面/.keep Normal file
View File

BIN
界面/Times.ttc Normal file

Binary file not shown.

231
界面/about.html Normal file
View File

@ -0,0 +1,231 @@
<!-- /*
* @Author: Chen Wenjie
* @Date: 2021-06-18 11:06:33
* @Last Modified by: Chen Wenjie Fan Runpu
* @Last Modified time: 2021-06-18 11:06:33
*/ -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>关于我们~</title>
<!-- 引入Bootstrap核心样式文件必须 -->
<link rel="stylesheet" href="lib/bootstrap/css/bootstrap.css" />
<!-- 引入自己的样式或其他文件 -->
<link rel="stylesheet" href="css/index.css" />
<link rel="stylesheet" type="text/css" href="css/swiper.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css">
<link rel="stylesheet" href="css/about.css">
</head>
<body>
<!--头部-->
<!-- <header id="lk_header"> -->
<!--下部分-->
<!-- <nav
class="navbar navbar-default navbar-static-top navbar-lk"
style="background-color: rgb(86, 61, 124)"
>
<div class="box">
<div class="navbar-header">
<button
type="button"
class="navbar-toggle collapsed"
data-toggle="collapse"
data-target="#lk_nav"
aria-expanded="false"
>
<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" href="#">
<img src="imgs/success.png" alt="logo" width="70" />
</a>
</div>
<div class="collapse navbar-collapse" id="lk_nav">
<ul class="nav navbar-nav">
<li ><a href="./fonts/index.html">首页 </a></li>
<li><a href="./others.html">Ta的纸条</a></li>
<li><a href="./mybook.html">我的日记</a></li>
<li class="active">
<a href="about.html">关于我们</a>
</li>
<li>
<a href="./friend.html"
>友情链接</a
>
</li>
<!-- <ul class="nav navbar-nav navbar-right hidden-sm hidden-xs">
<li><a href="#">个人中心</a></li>
</ul> -->
</div>
</div>
</nav>
</header> -->
<!--/头部-->
<!-- team部分 -->
<div>
<div class="block1">
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide" >
<div class="imgBx">
<img src = "./pictures/1.jpeg"/>
</div>
<div class="details">
<h3>19 计科 樊润璞<br><span><u>会议操盘手/前端工程师</u></span><p>"相信你的队友,做好自己的事"</p></h3>
</div>
</div>
<div class="swiper-slide" >
<div class="imgBx">
<img src = "./pictures/2.jpeg">
</div>
<div class="details">
<h3>19 信管 陈文杰<br><span><u>系统分析师/数据库工程师</u></span><p>"如果第一次你没有成功那么称之为1.0版,继续加油。"</p></h3>
</div>
</div>
<div class="swiper-slide" >
<div class="imgBx">
<img src = "./pictures/3.jpeg">
</div>
<div class="details">
<h3>18 计科 唐家昕<br><span><u>后端工程师/前端架构师</u></span><p>"所有的程序员都剧作家,而所有计算机都是糟糕的演员。"</p></h3>
</div>
</div>
<div class="swiper-slide" >
<div class="imgBx">
<img src = "./pictures/4.jpeg">
</div>
<div class="details">
<h3>18 信管 王嘉杰<br><span><u>数据库工程师/后端工程师</u></span><p>"程序员的烦恼是,你永远无法知道一个程序员在做什么,直到为时已晚。"</p></h3>
</div>
</div>
<div class="swiper-slide" >
<div class="imgBx">
<img src = "./pictures/5.jpeg">
</div>
<div class="details">
<h3>19 AI 张运筹<br><span><u>规范制定者/前后端交互</u></span><p>"工作拉下得越早,赶上去所需要的时间越多。"</p></h3>
</div>
</div>
<div class="swiper-slide" >
<div class="imgBx">
<img src = "./pictures/6.jpeg">
</div>
<div class="details">
<h3>亲爱的老师&助教<br><span><u>坚实后盾/DDL推进器</u></span><p>"这是我们这个行业的一件咄咄怪事:我们不仅不从错误中学习,我们也不从成功中学习。"</p></h3>
</div>
</div>
</div>
<div class="swiper-pagination"></div>
</div>
<div class="block" style="margin-top: 0;">
<div class="box3">
<p>CONTACT US</p>
<input type="button" class="button" id="return" value="返回">
</div>
<script>
document.getElementById("return").onclick=function() {
window.location.assign("select.html");
}
</script>
</div>
<div class="block2" style="margin-top: 0em;">
<div class="icons">
<a href="#">
<div class="layer">
<span></span>
<span></span>
<span></span>
<span></span>
<span class="icon iconfont">&#xe703</span>
</div>
</a>
<a href="#">
<div class="layer">
<span></span>
<span></span>
<span></span>
<span></span>
<span class="icon iconfont">&#xe6c9</span>
</div>
<span class="text">Wechet</span>
</a>
<a href="#">
<div class="layer">
<span></span>
<span></span>
<span></span>
<span></span>
<span class="fab fa-facebook-f"></span>
</div>
<span class="text">Facebook</span>
</a>
<a href="#">
<div class="layer">
<span></span>
<span></span>
<span></span>
<span></span>
<span class="icon iconfont">&#xe639;</span>
</div>
<span class="text">Email</span>
</a>
<a href="#">
<div class="layer">
<span></span>
<span></span>
<span></span>
<span></span>
<span class="icon iconfont">&#xe63b;</span>
</div>
<span class="text">Phone</span>
</a>
</div>
</div>
</div>
<!-- /team 结束-->
<script src="lib/jquery/jquery.js"></script>
<!-- 引入所有的Bootstrap的JS插件 -->
<script src="lib/bootstrap/js/bootstrap.js"></script>
<!-- 你自己的脚本文件 -->
<script src="js/index.js"></script>
<script type="text/javascript" src="./js/swiper.min.js"></script>
<script>
var swiper = new Swiper('.swiper-container', {
effect: 'coverflow',
grabCursor: true,
centeredSlides: true,
slidesPerView: 'auto',
coverflowEffect: {
rotate: 50,
stretch: 0,
depth: 100,
modifier: 1,
slideShadows: true,
},
pagination: {
el: '.swiper-pagination',
},
});
</script>
</body>
</html>

View File

@ -0,0 +1,76 @@
<!DOCTYPE html>
<!--
@Time : 2021/7/11 14:44 下午
@Author : Tang Jiaxin
密码修改界面,修改密码
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>修改密码</title>
<script src="https://ajax.microsoft.com/ajax/jquery/jquery-3.5.1.js"></script>
<script src="connect_login.js"></script>
</head>
<style type="text/css">
html, body {
height: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
p {
color: white;
font-size: 20px;
}
form {
position: absolute;
left: 45%;
top: 20%;
height: 80%;
width: 55%;
}
h1 {
color: white;
text-align: center;
}
.button {
position: absolute;
top: 70%;
width: 8%;
height: 6%;
font: 16px Arial, sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 50px;
cursor: pointer;
}
#confirm {
left: 40%;
}
#return {
left: 52%;
}
</style>
<body>
<h1>修改密码</h1>
<form id="password_change_form" method="POST">
<div>
<p>请输入旧密码</p>
<input type="password" name="old_password" id="old_password">
<p>请输入新密码</p>
<input type="password" name="new_password1" id="new_password1">
<p>请再次输入新密码</p>
<input type="password" name="new_password2" id="new_password2">
</div>
</form>
<input type="button" class="button" id="confirm"
value="确定" onclick="connect_change_password()">
<input type="button" class="button" id="return" value="返回">
</body>
<script>
document.getElementById("return").onclick=function() {
window.history.back();
}
</script>
</html>

200
界面/connect_download.js Normal file
View File

@ -0,0 +1,200 @@
/*
@Time : 2021/7/13 15:11 下午
@Author : Tang Jiaxin
文件下载与展示功能
*/
//以提交表单的形式从服务器下载文件
function download_file(file)
{
var url = "http://114.212.189.166:8001/file_download";
var requestForm = document.createElement("form");
requestForm.action = url;
requestForm.method = "post";
var input = document.createElement("input");
input.type = "hidden";
input.name = "fileName";
input.value = file;
requestForm.appendChild(input);
$(document.body).append(requestForm);
requestForm.submit();
requestForm.remove();
}
//从服务器下载一个文件
function connect_download_file()
{
var url = document.URL;
var index1 = url.indexOf("display_file=");
var index2 = url.indexOf("display_app=");
var file = "";
if(index2 == -1)
file = url.substring(index1 + 13);
else
file = url.substring(index1 + 13, index2 - 1);
$.ajax(
{
url: "http://114.212.189.166:8001/file_download",
type: "get",
success: function()
{
download_file(file);
},
error: function()
{
alert("服务器连接失败");
}
}
)
}
//从服务器获取一个文件的内容
function connect_display_file()
{
var url = document.URL;
var index1 = url.indexOf("display_file=");
var index2 = url.indexOf("display_app=");
var file = "";
if(index2 == -1)
file = url.substring(index1 + 13);
else
file = url.substring(index1 + 13, index2 - 1);
$.ajax(
{
url: "http://114.212.189.166:8001/file_display",
type: "post",
dataType: "json",
data: JSON.stringify({"file": file}),
contentType:'application/json; charset=utf-8',
success: function(message)
{
state = message["state"];
if(state == 200)
{
data = message["data"];
$("p#response").html(data);
}
else
$("p#response").text("结果获取失败:" + message["msg"]);
},
error: function()
{
$("p#response").text("服务器连接失败");
}
}
)
}
//从服务器获取可视化界面
function connect_graph_display()
{
var url = document.URL;
var index = url.indexOf("display_file=");
var file = url.substring(index + 13);
$.ajax(
{
url: "http://114.212.189.166:8001/file_graph",
type: "post",
dataType: "json",
data: JSON.stringify({"file": file}),
contentType:'application/json; charset=utf-8',
success: function(message)
{
state = message["state"];
if(state == 200)
{
data = message["data"];
document.write(data);
}
else
alert("可视化结果获取失败:" + message["msg"]);
},
error: function()
{
$("p#response").text("服务器连接失败");
}
}
)
}
//跳转到可视化页面
function graph_display()
{
var url = document.URL;
var index1 = url.indexOf("display_file=");
var index2 = url.indexOf("display_app=");
var file = "";
if(index2 == -1)
file = url.substring(index1 + 13);
else
file = url.substring(index1 + 13, index2 - 1);
window.location.assign("graph.html?display_file=" + file);
}
//展示一个文件列表
function display_file_list(file_list, show_input)
{
var table = document.getElementById("table");
table.innerHTML = "<th>编号</th><th>所属APP</th><th>文件名称</th><th>提交时间</th>";
var index = 1;
for(var app in file_list)
{
var files = file_list[app];
for(let i = 0; i < files.length; i++)
{
var file = files[i]["file"];
var time = files[i]["time"];
var arr = file.split('/');
var filename = arr[arr.length - 2];
table.innerHTML += ("<tr>"
+ "<td>" + index + "</td>"
+ "<td>" + app + "</td>"
+ "<td><a href='javascript:void(0);' onclick='display_file("
+ "\"" + file + "\"" + ")'>"
+ filename + "</a></td>"
+ "<td>" + time + "</td>"
+ "<td><input type='checkbox' class='checkbox' value='"
+ app + " " + file + "'></td>"
+ "</tr>");
index++;
}
}
if(!show_input)
{
var items = document.getElementsByClassName("checkbox");
for(let i = 0; i < items.length; i++)
items[i].style.display = "none";
}
}
//从服务器获取已经上传的文件的列表
function connect_display_file_list(show_input)
{
$.ajax(
{
url: "http://114.212.189.166:8001/file_list",
type: "post",
dataType: "json",
data: JSON.stringify({"cookie": window.localStorage["cookie"]}),
contentType:'application/json; charset=utf-8',
success: function(message)
{
state = message["state"];
if(state == 200)
display_file_list(message["data"], show_input);
else
$("p#response").text("文件读取失败");
},
error: function()
{
$("p#response").text("服务器连接失败");
}
}
)
}
//从模型合并界面跳转到文件展示界面,并展示一个文件的内容
function display_file(file)
{
window.location.assign("display.html?display_file="+ file);
}

401
界面/connect_login.js Normal file
View File

@ -0,0 +1,401 @@
/*
@Time : 2021/7/12 20:13 下午
@Author : Tang Jiaxin
登录注册功能向服务器提交登录注册等请求
*/
function connect_register()
{
var register_info = document.getElementById("register_form");
var user_name = register_info["user_name"].value;
var password = register_info["password1"].value;
var password2 = register_info["password2"].value;
if(!user_name)
{
alert("请输入用户名!");
return;
}
if(!password || !password2)
{
alert("请输入密码!");
return;
}
if(password != password2)
{
alert("两次输入的密码不一致!");
return;
}
var user_name_reg = /^\w{1,20}$/;
if(!user_name_reg.test(user_name))
{
alert("输入的用户名不合理!");
return;
}
var password_reg = /^\w{6,20}$/;
if(!password_reg.test(password))
{
alert("输入的密码长度太短或太长!");
return;
}
var data = {"user_name": user_name, "password": password};
$.ajax(
{
url: "http://114.212.189.166:8001/register",
type: "post",
dataType: "json",
data: JSON.stringify(data),
contentType:'application/json; charset=utf-8',
success: function(message)
{
var state = message["state"];
if(state == 200)
{
alert("注册成功!");
var uid = message["uid"];
window.localStorage["cookie"] = uid.toString();
window.localStorage["user_name"] = user_name;
window.location.assign("select.html");
}
else
alert("注册失败:" + message["msg"]);
},
error: function()
{
alert("服务器连接失败");
}
}
)
}
function create_check_code()
{
var alphabet = [];
for(let c = 0; c <= 9; c++)
alphabet.push(String.fromCharCode(48 + c));
for(let c = 0; c <= 25; c++)
{
alphabet.push(String.fromCharCode(65 + c));
alphabet.push(String.fromCharCode(97 + c));
}
function getRndInteger(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
var str = "";
for(let i = 0; i < 4; i++)
str += alphabet[getRndInteger(0, 62)];
window.sessionStorage["check_code"] = str;
return str;
}
function connect_check_code()
{
var request = new XMLHttpRequest();
request.open("post", "http://114.212.189.166:8001/check_code", true);
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
request.responseType = "blob";
request.onload = function()
{
if(this.status == 200)
{
var data = this.response;
var img = document.createElement("img");
img.onload = function(e)
{
window.URL.revokeObjectURL(img.src);
};
img.src = window.URL.createObjectURL(data);
$("#check_code_img").html(img);
}
}
var check_code_data = create_check_code();
request.send(JSON.stringify({"str": check_code_data}));
}
function connect_login()
{
var login_info = document.getElementById("login_form");
var user_name = login_info["user_name"].value;
var password = login_info["password"].value;
var check_code = login_info["check_code"].value;
if(!user_name)
{
alert("请输入用户名!");
return;
}
if(!password)
{
alert("请输入密码!");
return;
}
if(!check_code)
{
alert("请输入验证码!");
return;
}
if(check_code != window.sessionStorage["check_code"])
{
alert("验证码错误!");
return;
}
var data = {"user_name": user_name, "password": password};
$.ajax(
{
url: "http://114.212.189.166:8001/login",
type: "post",
dataType: "json",
data: JSON.stringify(data),
contentType:'application/json; charset=utf-8',
success: function(message)
{
var state = message["state"];
var uid = message["uid"];
var mail_addr = message["mail_addr"];
if(state == 200)
{
window.localStorage["cookie"] = uid.toString();
window.localStorage["user_name"] = user_name;
if(mail_addr)
window.localStorage["mail_addr"] = mail_addr;
window.location.assign("select.html");
}
else
alert("登录失败:" + message["msg"]);
},
error: function()
{
alert("服务器连接失败");
}
}
)
}
function connect_mail_login()
{
var login_info = document.getElementById("mail_login_form");
var user_mail = login_info["user_mail"].value;
if(!user_mail)
{
alert("请输入用户名或邮箱!");
return;
}
var check_code = create_check_code();
var data = {"check_code": check_code};
var index = user_mail.indexOf("@");
if(index == -1)
data["user_name"] = user_mail;
else
data["mail_addr"] = user_mail;
$.ajax(
{
url: "http://114.212.189.166:8001/mail_login",
type: "post",
dataType: "json",
data: JSON.stringify(data),
contentType:'application/json; charset=utf-8',
success: function(message)
{
var state = message["state"];
var uid = message["uid"];
var mail_addr = message["mail_addr"];
var user_name = message["user_name"];
if(state == 200)
{
alert("邮件发送成功,请填写收到的验证码");
window.sessionStorage["check_code"] = check_code;
window.sessionStorage["cookie"] = uid.toString();
window.sessionStorage["mail_addr"] = mail_addr;
window.sessionStorage["user_name"] = user_name;
}
else
alert("邮件发送失败:" + message["msg"]);
},
error: function()
{
alert("服务器连接失败");
}
}
)
}
function mail_login()
{
var login_info = document.getElementById("mail_login_form");
var check_code = login_info["check_code"].value;
if(!check_code)
{
alert("请输入验证码!");
return;
}
if(check_code != window.sessionStorage["check_code"])
{
alert("验证码错误!");
return;
}
window.localStorage["cookie"] = window.sessionStorage["cookie"];
window.localStorage["mail_addr"] = window.sessionStorage["mail_addr"];
window.localStorage["user_name"] = window.sessionStorage["user_name"];
window.location.assign("select.html");
}
function connect_change_password()
{
var password_change_info = document.getElementById("password_change_form");
var old_password = password_change_info["old_password"].value;
var new_password = password_change_info["new_password1"].value;
var new_password2 = password_change_info["new_password2"].value;
if(!old_password)
{
alert("请输入旧密码!");
return;
}
if(!new_password)
{
alert("请输入新密码!");
return;
}
if(!new_password2)
{
alert("请再次输入新密码!");
return;
}
if(new_password != new_password2)
{
alert("两次输入的新密码不一致!");
return;
}
if(old_password == new_password)
{
alert("新密码不能与旧密码相同!");
return;
}
var password_reg = /^\w{6,20}$/;
if(!password_reg.test(new_password))
{
alert("新密码长度太短或太长!");
return;
}
$.ajax(
{
url: "http://114.212.189.166:8001/change_password",
type: "post",
dataType: "json",
data: JSON.stringify({'old_password': old_password,
'new_password': new_password,
'cookie': window.localStorage["cookie"]}),
contentType:'application/json; charset=utf-8',
success: function(message)
{
var state = message["state"];
if(state == 200)
alert("密码修改成功");
else
alert("密码修改失败:" + message["msg"]);
},
error: function()
{
alert("服务器连接失败");
}
}
)
}
function connect_mail_bind()
{
var mail_bind_info = document.getElementById("mail_bind_form");
var mail_addr = mail_bind_info["mail_addr"].value;
if(!mail_addr)
{
alert("请输入邮箱!");
return;
}
var check_code = create_check_code();
$.ajax(
{
url: "http://114.212.189.166:8001/mail_bind",
type: "post",
dataType: "json",
data: JSON.stringify({"mail_addr": mail_addr,
"cookie": window.localStorage["cookie"], "check_code": check_code}),
contentType:'application/json; charset=utf-8',
success: function(message)
{
var state = message["state"];
if(state == 200)
{
alert("邮件发送成功,请填写收到的验证码");
window.sessionStorage["check_code"] = check_code;
}
else
alert("邮件发送失败:" + message["msg"]);
},
error: function()
{
alert("服务器连接失败");
}
}
)
}
function connect_mail_update()
{
var mail_bind_info = document.getElementById("mail_bind_form");
var mail_addr = mail_bind_info["mail_addr"].value;
var check_code = mail_bind_info["check_code"].value;
if(!check_code)
{
alert("请输入验证码!");
return;
}
if(check_code != window.sessionStorage["check_code"])
{
alert("验证码错误!");
return;
}
if(!mail_addr)
{
alert("请输入邮箱!");
return;
}
$.ajax(
{
url: "http://114.212.189.166:8001/mail_bind",
type: "post",
dataType: "json",
data: JSON.stringify({"mail_addr": mail_addr,
"cookie": window.localStorage["cookie"]}),
contentType:'application/json; charset=utf-8',
success: function(message)
{
var state = message["state"];
if(state == 200)
{
alert("邮箱绑定成功");
window.localStorage["mail_addr"] = mail_addr;
}
else
alert("邮件发送失败:" + message["msg"]);
},
error: function()
{
alert("服务器连接失败");
}
}
)
}
function get_user_info()
{
$("p#user_name").text("用户名:" + localStorage["user_name"]);
if(window.localStorage.getItem("mail_addr"))
$("p#mail_addr").text("邮箱:" + localStorage["mail_addr"]);
else
$("p#mail_addr").text("邮箱:未绑定");
}
function exit_account()
{
window.localStorage.removeItem("cookie");
window.localStorage.removeItem("user_name");
if(window.localStorage.getItem("mail_addr"))
window.localStorage.removeItem("mail_addr");
window.location.assign("entrance.html");
}

215
界面/connect_merge.js Normal file
View File

@ -0,0 +1,215 @@
/*
@Time : 2021/7/13 14:49 下午
@Author : Tang Jiaxin
向服务器发送文件合并和保存合并好的文件的请求
*/
//向服务器发送文件合并请求
function connect_merge()
{
var items = document.getElementsByClassName("checkbox");
var files = {"files": []};
for(let i = 0; i < items.length; i++)
{
if(!items[i].checked)
continue;
var t = items[i].value.split(" ");
var app = t[0];
var file = t[1];
if(!files["app"])
files["app"] = app;
else if(app != files["app"])
{
alert("选择的模型不属于同一APP");
return;
}
files["files"].push(file);
}
if(files["files"].length < 2)
{
alert("请至少选择两个待合并的模型");
return;
}
var timer = self.setInterval("connect_merge_process()", 1000);
$.ajax(
{
url: "http://114.212.189.166:8001/file_merge",
type: "post",
dataType: "json",
data: JSON.stringify(files),
contentType:'application/json; charset=utf-8',
success: function(message)
{
var state = message["state"];
var tempname = message["file"];
if(state == 200)
{
window.clearInterval(timer);
display_result(tempname, app);
}
else
{
window.clearInterval(timer);
alert("合并失败:" + message["msg"]);
}
},
error: function()
{
window.clearInterval(timer);
alert("服务器连接失败");
}
}
)
var button1 = document.getElementById("button1");
button1.style.visibility = "hidden";
var button2 = document.getElementById("button2");
button2.style.visibility = "hidden";
var merge_info = document.getElementById("merge");
merge_info.style.visibility = "visible";
}
function connect_merge_process()
{
$.ajax(
{
url: "http://114.212.189.166:8001/merge_process",
type: "post",
success: function(message)
{
var state = message["state"];
if(state == 200)
$("p#merge").text(message["process"]);
else
alert("合并失败:" + message["msg"]);
},
error: function()
{
alert("服务器连接失败");
}
}
)
}
//向服务器发送保存合并好的文件的请求
function connect_save_result(tempname, filename, app)
{
$.ajax(
{
url: "http://114.212.189.166:8001/save_result",
type: "post",
dataType: "json",
data: JSON.stringify({'tempname': tempname,
'filename': filename, 'app': app,
'cookie': window.localStorage["cookie"]}),
contentType:'application/json; charset=utf-8',
success: function(message)
{
var state = message["state"];
if(state == 200)
merge_file();
else
alert("保存失败:" + message["msg"]);
},
error: function()
{
alert("服务器连接失败");
}
}
)
}
//向服务器发送删除合并好的文件的请求
function connect_delete_result(tempname)
{
$.ajax(
{
url: "http://114.212.189.166:8001/abort_result",
type: "post",
dataType: "json",
data: JSON.stringify({'tempname': tempname}),
contentType:'application/json; charset=utf-8',
success: function(message)
{
var state = message["state"];
if(state == 200)
merge_file();
else
alert("删除失败:" + message["msg"]);
},
error: function()
{
alert("服务器连接失败");
}
}
)
}
//从模型合并界面跳转到结果展示界面,并展示合并结果
function display_result(tempname, app)
{
window.location.assign("result.html?display_file="+ tempname
+ "&display_app=" + app);
}
//从结果展示界面跳转到模型合并界面
function merge_file()
{
window.location.assign("merge.html");
}
//将合并完成后的文件保存至服务器
function save_result()
{
var button1 = document.getElementById("button1");
button1.style.visibility = "hidden";
var button2 = document.getElementById("button2");
button2.style.visibility = "hidden";
var button3 = document.getElementById("button3");
button3.style.visibility = "hidden";
var button4 = document.getElementById("button4");
button4.style.visibility = "hidden";
var form = document.getElementById("form");
form.style.visibility = "visible";
}
//放弃合并结果
function abort_result()
{
var url = document.URL;
var index1 = url.indexOf("display_file=");
var index2 = url.indexOf("display_app=");
var tempname = url.substring(index1 + 13, index2 - 1);
connect_delete_result(tempname);
}
//取消保存
function cancel_save()
{
var button1 = document.getElementById("button1");
button1.style.visibility = "visible";
var button2 = document.getElementById("button2");
button2.style.visibility = "visible";
var button3 = document.getElementById("button3");
button3.style.visibility = "visible";
var button4 = document.getElementById("button4");
button4.style.visibility = "visible";
var form = document.getElementById("form");
form.style.visibility = "hidden";
}
//确认保存
function save()
{
filename = document.getElementById("file_name");
if(!filename.value)
{
alert("请输入文件名称");
return;
}
var url = document.URL;
var index1 = url.indexOf("display_file=");
var index2 = url.indexOf("display_app=");
var tempname = url.substring(index1 + 13, index2 - 1);
var app = url.substring(index2 + 12);
connect_save_result(tempname, filename.value, app);
}

78
界面/connect_remove.js Normal file
View File

@ -0,0 +1,78 @@
/*
@Time : 2021/6/1 20:04 下午
@Author : Tang Jiaxin
文件删除功能
*/
//向服务器发送文件删除请求
function connect_remove_file()
{
var items = document.getElementsByClassName("checkbox");
var files = {"files": []};
for(let i = 0; i < items.length; i++)
{
if(!items[i].checked)
continue;
var t = items[i].value.split(" ");
var file = t[1];
files["files"].push(file);
}
if(files["files"].length == 0)
{
alert("请选择待删除的文件");
return;
}
$.ajax(
{
url: "http://114.212.189.166:8001/file_remove",
type: "post",
dataType: "json",
data: JSON.stringify(files),
contentType:'application/json; charset=utf-8',
success: function(message)
{
var state = message["state"];
if(state == 200)
connect_display_file_list(true);
else
alert("删除失败:" + message["msg"]);
},
error: function()
{
alert("服务器连接失败");
}
}
)
}
//删除文件
function remove_file()
{
var items = document.getElementsByClassName("checkbox");
for(let i = 0; i < items.length; i++)
items[i].style.display = "inline";
var button2 = document.getElementById("button2");
button2.style.visibility = "visible";
var button4 = document.getElementById("button4");
button4.style.visibility = "visible";
var button1 = document.getElementById("button1");
button1.style.visibility = "hidden";
var button3 = document.getElementById("button3");
button3.style.visibility = "hidden";
}
//取消删除文件
function cancel_remove()
{
var items = document.getElementsByClassName("checkbox");
for(let i = 0; i < items.length; i++)
items[i].style.display = "none";
var button2 = document.getElementById("button2");
button2.style.visibility = "hidden";
var button4 = document.getElementById("button4");
button4.style.visibility = "hidden";
var button1 = document.getElementById("button1");
button1.style.visibility = "visible";
var button3 = document.getElementById("button3");
button3.style.visibility = "visible";
}

80
界面/connect_upload.js Normal file
View File

@ -0,0 +1,80 @@
/*
@Time : 2021/7/14 9:05 上午
@Author : Tang Jiaxin
文件上传功能向服务器上传文件
*/
function connect_upload()
{
var file = document.getElementById("file_form");
var file_name = file["input_file"].value;
if(!file_name)
{
$("p#response").text("请选择文件!");
return;
}
var index1 = file_name.lastIndexOf("_");
var index2 = file_name.indexOf(".zip");
var app_name = file_name.substring(index1 + 1, index2);
var formData = new FormData(file);
formData.append("app", app_name);
formData.append("cookie", window.localStorage["cookie"]);
$.ajax(
{
url: "http://114.212.189.166:8001/file_upload",
type: "post",
data: formData,
dataType: "json",
processData: false,
contentType: false,
xhr: function()
{
myXhr = $.ajaxSettings.xhr();
if(myXhr.upload)
{
myXhr.upload.addEventListener("progress",
progressHandlingFunction, false);
}
return myXhr;
},
beforeSend: function()
{
ot = new Date().getTime();
oloaded = 0;
},
success: function(message)
{
state = message["state"];
if(state == 200)
$("p#response").text("文件上传成功");
else
$("p#response").text("文件上传失败:" + message["msg"]);
var return_button = document.getElementById("return");
return_button.style.visibility = "visible";
},
error: function()
{
$("p#response").text("服务器连接失败");
var return_button = document.getElementById("return");
return_button.style.visibility = "visible";
}
}
)
function progressHandlingFunction(evt)
{
var nt = new Date().getTime();
var pertime = (nt - ot) / 1000;
ot = new Date().getTime();
var perload = evt.loaded - oloaded;
oloaded = evt.loaded;
var speed = perload / pertime;
speed = speed.toFixed(1);
var resttime = ((evt.total - evt.loaded) / speed).toFixed(1);
var percent = evt.loaded / evt.total * 100;
$("p#response").text("当前速度:" + (speed / 1024).toFixed(2)
+ "KB/s剩余时间" + resttime + "s当前进度"
+ percent.toFixed(2) + "%");
}
var return_button = document.getElementById("return");
return_button.style.visibility = "hidden";
}

0
界面/css/.keep Normal file
View File

360
界面/css/about.css Normal file
View File

@ -0,0 +1,360 @@
html,body
{
background-color: #000;
font-family: 'Poppins', sans-serif;
/* background: #202626; */
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: 14px;
color: #000;
margin: 0;
padding: 0;
height: 100vh;
position: relative;
background-image: url(../pictures/bg.jpg);
background-size: cover;
overflow: hidden;
background-repeat: no-repeat;
}
.block0{
margin: 0;
padding: 0;
height: 3vh;
position: relative;
/* background-image: url(bg.jpg); */
/* background-size: cover;
background-repeat: no-repeat; */
}
.block0 .box3{
margin-top: 3vh;
position: absolute;
left: 20%;
top: 50%;
transform: translate(-50%,-50%);
}
.box3 h1{
font-size: 3em;
color: rgba(255, 255, 255, 1);
background-image: url(../imgs/water.png);
text-transform: uppercase;
letter-spacing: 8px;
/* 注意添加私有前缀 */
-webkit-background-clip: text;
font-weight: 800;
animation: wave 10s linear infinite;
}
.abstract
{
padding: 0;
position: absolute;
transform: translate(-50%,-50%);
justify-content: center;
text-align: center;
height: 1vh;
}
.abstract h1
{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
.swiper-container
{
position: relative;
width: 100%;
padding-top: 50px;
padding-bottom: 50px;
}
.swiper-slide
{
background-position: center;
background-size: cover;
width: 300px;
height: 420px;
background: #fff;
}
.swiper-slide .imgBx
{
width: 100%;
height: 300px;
overflow: hidden;
}
.swiper-slide .imgBx img
{
width: 100%;
height: 100%;
/* padding-bottom: 100%; */
overflow:hidden;
background-position: center center;
background-repeat: no-repeat;
-webkit-background-size:cover;
-moz-background-size:cover;
background-size:cover;
}
.swiper-slide .details
{
box-sizing: border-box;
height:120px;
font-size: 10px;
padding: 20px;
background: white;
}
.swiper-slide .details h3
{
margin: 0 ;
padding: 0;
font-size: 20px;
text-align: center;
line-height: 20px;
}
.swiper-slide .details h3 span
{
font-size: 16px;
color: burlywood;
}
.swiper-slide .details p
{
font-size: 14px;
color:gray;
opacity: 0.6;
letter-spacing: 0.5px;
}
.block{
margin: 0;
padding: 0;
height: 10vh;
position: relative;
/* background-image: url(bg.jpg); */
/* background-size: cover;
background-repeat: no-repeat; */
}
.box3{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
.box3 p{
font-size:2em;
color: rgba(255, 255, 255, 0.3);
background-image: url("../imgs/water.png");
background-repeat: round;
background-position: bottom;
text-transform: uppercase;
letter-spacing: 8px;
text-align: center;
/* 注意添加私有前缀 */
-webkit-background-clip: text;
font-weight: 500;
animation: wave 15s linear infinite;
}
@keyframes wave{
from{
background-position: 0 0;
}
to{
background-position: 1000px 0;
}
}
body .block2
{
/* position: relative;
width: 1080px;
height: 100px; */
background-color:rgba(0, 0, 0, 0.5);
position: relative;
width: 100%;
height: 150px;
/* padding-bottom: 0;
margin-bottom: 0; */
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
.icons{
display: inline-flex;
position: relative;
margin: auto;
}
.icons a{
margin: 0 25px;
text-decoration: none;
color: #fff;
display: block;
position: relative;
}
.icons a .layer{
width: 55px;
height: 55px;
transition: transform 0.3s;
}
.icons a:hover .layer{
transform: rotate(-35deg) skew(20deg);
}
.icons a .layer span{
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
border: 1px solid #fff;
border-radius: 5px;
transition: all .3s;
}
.icons a .layer span.fab{
font-size: 20px;
line-height: 55px;
text-align: center;
}
.icons a:hover .layer span:nth-child(1){
opacity: 0.2;
}
.icons a:hover .layer span:nth-child(2){
opacity: 0.4;
transform: translate(5px, -5px);
}
.icons a:hover .layer span:nth-child(3){
opacity: 0.6;
transform: translate(10px, -10px);
}
.icons a:hover .layer span:nth-child(4){
opacity: 0.8;
transform: translate(15px, -15px);
}
.icons a:hover .layer span:nth-child(5){
opacity: 1;
transform: translate(20px, -20px);
}
.icons a:nth-child(1) .layer span,
.icons a:nth-child(1) .text{
color: #4267B2;
border-color: #4267B2;
}
.icons a:nth-child(2) .layer span,
.icons a:nth-child(2) .text{
color: #1DA1F2;
border-color: #1DA1F2;
}
.icons a:nth-child(3) .layer span,
.icons a:nth-child(3) .text{
color: #E1306C;
border-color: #E1306C;
}
.icons a:nth-child(4) .layer span,
.icons a:nth-child(4) .text{
color: #2867B2;
border-color: #2867B2;
}
.icons a:nth-child(5) .layer span,
.icons a:nth-child(5) .text{
color: #ff0000;
border-color: #ff0000;
}
.icons a:nth-child(1) .layer span{
box-shadow: -1px 1px 3px #4267B2;
}
.icons a:nth-child(2) .layer span{
box-shadow: -1px 1px 3px #1DA1F2;
}
.icons a:nth-child(3) .layer span{
box-shadow: -1px 1px 3px #E1306C;
}
.icons a:nth-child(4) .layer span{
box-shadow: -1px 1px 3px #4267B2;
}
.icons a:nth-child(5) .layer span{
box-shadow: -1px 1px 3px #ff0000;
}
.icons a .text{
position: absolute;
left: 50%;
bottom: -5px;
opacity: 0;
transform: translateX(-50%);
transition: bottom .3s ease, opacity .3s ease;
}
.icons a:hover .text{
bottom: -35px;
opacity: 1;
}
@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1593756665368'); /* IE9 */
src: url('iconfont.eot?t=1593756665368#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAWcAAsAAAAACtQAAAVQAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDMgqIaIcVATYCJAMUCwwABCAFhG0HUxssCREVpAGS/UywY7oMa60OvwwW6dZkz9Omzft/l5LdCLIQMxoRJaIVJzURaB3uHKmawZkIAXBsrjUr6dK3aVx969qRA6bgvyVAgN7BsdTPBMv7SpfqWFsF4GjAA4pI/NAV8AY0son4FvFMM5+T8DgQAAkxyADVN3bsCTUEiMcRABo/ZtQQqD0BCAlMgtqtirhUAL0CEWq2it0H8LLx8/QN8kgNMIgc4qldRzYMh/nz0i84t/qt6K8hYKwuBoC9CuAAMgAIAE2NTIwHtxoZ4JA+vyfADECCGgzs89LPyz+/8wX3+7+WAbkk6R8eIICBwAFSQXQEItZsFybOqBgcZznxgRHnHSIFAr7gTiwvWrxEAqABaCJANyBSTFBSg4OBSyPQhukkata0atIUlTLOvtu6x7HPtlcaYt9udTk8Nrdk8eqj3kIeFXz9eZywxdsQcuuNRHGrr3G0/WRSz7aHvoQ33mjzenu5pefPe1rDndKQXpbtxyWfzwJT1j8vsdidZVVpnmSBfqrkdkpkdirO22V7L6bRWkXo4LFQT2iXvYlsRqPFbZTcdkWK7d4dtt4bfcQr9rSc8BbvCk3R6zHKHhsNqn1Ba2yWbLNNynbTbV/rXZ/Yk3fUl7/u0Fcr77bDYEjt5VqzWlGccGFHp9VwwFvq2hFaLXk8BlK6DbLL0MvgUgmyo9c5T2PfDVVvR6V17KN2tr393VeN1sMpO84oTl/R8edG97nkvUfJeVmxb81fF9nm6+GVPXutCT+yeZ5bCrYrx5+DtWgM3ObrOXi3PW3ojNW7TcE790ZwW+iIPXcc99JHuVBeG1IfI4mH6K7JIWH+TGCfeRGzswqat3TuHNEcoa7bYg3i5dqwXdP7ZYZ3ZkVtIhv0+ryJbCi9OkLX5XDQQJo/ndqi58BtbP305qYk0yz9IJo/jrpQ1/lj4Yq+tGgs6yrndnyx+LtAf1NfGcj9cSPZhhOYGJdDmGWq5YfPTjnhxKcHw9YG5Ci1sxKJiQwYZtWCtKKGNNrZ0tB32cD/9ZWDu/cYUhnnH8Aftr+FW7O0xJjID6XIXbocXnkYI/JK/MsXmjvuOZeG8NldJ/Ihr4+r69uvfmxqEp+kzB5+fs11HDt8QhM3YvEYS2lf6rMTrL1xw/+dO3/wVY+aTlTuMqP/icG4fISOLbcNN9SOyiBWGZyCsh4trSH1IcqpV5ce7amPvPiWsLT/naXIr4PX3ek4XXjrYqS+/qhKlB17w2vYyN6OIWww672Kjfbceq9DFlH42qdXgxYGXT1VXJP1Fq+rz1mZQ0X5W1nv7taKgN/zSvLIUA7L4j3zSvM98S1RUKhWV6CrVUgQMRsA4H/MD/Mcg32QP+Xdjz7J7/JWAOAX2Rdn9mHYOvXvnwaFVPymli/2be+Yvv0L8/6DAooZTk8nMhvPY2PGdcLXLAZF4b1QMbwni9gKNXBOmE7ZeWdvRof3q3EZMjMBchkcYGiHaOBQw4QUsBkgQkYBqKBGNUhIR9PVMvSYCg4SAgCkYRkAQYNDwBCMS8ChwQOkgH0PRBjxHaigIQ4SLKS/pQzCpTHV1SEJyIGeMDG9CzkcKnP6C2VciXObDdYPnZ/K0Dedv/3EHV0VZ/gkhxAECKc3eMDTcF01GKdnZKFRIZixbUXUmBqmt6xydUgCckBPMDG9C23rUAW+/4UyrsQlLPrR/KHz085Br9FlQD4de6ZFuzLXJzkIWBNAsFP1Bh4Yw1VqaGCiD5qRhYYqUDOjFjcTWR1N865tHRsfoiXFQYgx4iSQSCoU13+cjmnP+UR2FUlpbe2t0euwOHU8QuIyywA=') format('woff2'),
url('iconfont.woff?t=1593756665368') format('woff'),
url('iconfont.ttf?t=1593756665368') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('iconfont.svg?t=1593756665368#iconfont') format('svg'); /* iOS 4.1- */
}
.block2 .icons .iconfont {
font-family: "iconfont" !important;
font-size: 30px;
line-height: 55px;
text-align: center;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-z-weixin:before {
content: "\e6c9";
}
.icon-dianhua:before {
content: "\e63b";
}
.icon-qq-square:before {
content: "\e703";
}
.icon-youxiang:before {
content: "\e639";
}
.button {
position: absolute;
top: 70%;
width: 100%;
height: 50%;
font: 16px Arial, sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 50px;
cursor: pointer;
}

161
界面/css/index.css Normal file
View File

@ -0,0 +1,161 @@
/***********************通用的样式***********************/
body{
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #000;
margin: 0;
box-sizing: border-box;
display: block;
-webkit-text-size-adjust:100%;
}
.btn-register {
color: #FFFFFF;
background-color: #0AB4F7;
border-color: #F4F7F7;
}
.btn-register:hover,
.btn-register:focus,
.btn-register:active,
.btn-register.active,
.open .dropdown-toggle.btn-register {
color: #FFFFFF;
background-color: #086DF2;
border-color: #F4F7F7;
}
.btn-register:active,
.btn-register.active,
.open .dropdown-toggle.btn-register {
background-image: none;
}
.btn-register.disabled,
.btn-register[disabled],
fieldset[disabled] .btn-register,
.btn-register.disabled:hover,
.btn-register[disabled]:hover,
fieldset[disabled] .btn-register:hover,
.btn-register.disabled:focus,
.btn-register[disabled]:focus,
fieldset[disabled] .btn-register:focus,
.btn-register.disabled:active,
.btn-register[disabled]:active,
fieldset[disabled] .btn-register:active,
.btn-register.disabled.active,
.btn-register[disabled].active,
fieldset[disabled] .btn-register.active {
background-color: #0AB4F7;
border-color: #F4F7F7;
}
.btn-register .badge {
color: #0AB4F7;
background-color: #FFFFFF;
}
@font-face {
font-family: lk;
src: url('../fonts/lk.eot') format('embedded-opentype'),
url('../fonts/lk.svg') format('svg'),
url('../fonts/lk.ttf') format('truetype'),
url('../fonts/lk.woff') format('woff');
}
[class^="icon-"],
[class*=" icon-"]{
font-family: lk;
font-style: normal;
}
#lk_about, #lk_hot, #lk_link{
padding: 20px 0;
}
#lk_about .title, #lk_hot .title, #lk_link .title{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-bottom: 15px;
}
/***********************通用的样式***********************/
/***********************头部的样式***********************/
#lk_header{
}
.icon-phone::before{
content: '\e958';
font-size: 13px;
}
.icon-tel::before{
content: '\e942';
font-size: 13px;
}
#lk_header .navbar-lk{
/* background-color: #FFF; */
margin-bottom: 0;
}
#lk_header .navbar-lk .navbar-brand{
height: 70px;
padding: 10px 15px;
}
#lk_header .navbar-lk .navbar-nav a{
height: 70px;
line-height: 40px;
color:white;
font-size: 20px;
font-weight: bold;
font-family: "Yahei";
}
#lk_header .navbar-lk .navbar-nav li.active a,
#lk_header .navbar-lk .navbar-nav li a:hover{
background-color: transparent;
border-bottom: 2px solid #0AB4F7;
}
#lk_header .navbar-lk .navbar-toggle {
margin-top: 18px;
}
#lk_nav a{
color: white;
}
/***********************头部的样式***********************/
/***********************焦点图的样式***********************/
#lk_carousel{
}
#lk_carousel .item{
background: no-repeat center center;
-webkit-background-size: cover;
background-si
}
@media screen and ( min-width: 800px){
#lk_carousel .item{
height: 850px;
}
}
/***********************焦点图的样式***********************/

12
界面/css/swiper.min.css vendored Normal file

File diff suppressed because one or more lines are too long

82
界面/display.html Normal file
View File

@ -0,0 +1,82 @@
<!DOCTYPE html>
<!--
@Time : 2021/7/13 12:55 下午
@Author : Tang Jiaxin
文件展示界面展示一个ARP模型文件
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>文件展示</title>
<script src="https://ajax.microsoft.com/ajax/jquery/jquery-3.5.1.js"></script>
<script src="connect_download.js"></script>
</head>
<style type="text/css">
html, body {
height: 100%;
width: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
div {
position: absolute;
left: 5%;
top: 10%;
height: 75%;
width: 90%;
background-color: rgba(0, 0, 0, 0.4);
overflow: auto;
}
h1 {
text-align: center;
color: chartreuse;
font-family: 楷体;
}
button {
position: absolute;
height: 6%;
top: 90%;
font:16px Arial,sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius:50px;
cursor: pointer;
}
#button1 {
left: 35%;
width: 8%;
}
#button2 {
left: 46%;
width: 10%;
}
#button3 {
left: 60%;
width: 6%;
}
p {
position: absolute;
left: 10%;
top: 10%;
font-size: 20px;
color: white;
font-family: 楷体;
}
</style>
<body onload="connect_display_file()">
<h1>文件展示</h1>
<div>
<p id="response"></p><br>
</div>
<button id="button1" onclick="connect_download_file()">下载文件</button>
<button id="button2" onclick="graph_display()">可视化展示</button>
<button id="button3">返回</button>
</body>
<script>
document.getElementById("button3").onclick=function() {
window.history.back();
}
</script>
</html>

53
界面/entrance.html Normal file
View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<!--
@Time : 2021/5/10 17:44 下午
@Author : Tang Jiaxin
初始界面显示APP的标题和入口
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>APP模型合并</title>
</head>
<style type="text/css">
html, body {
height: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
div {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.app_title {
color: white;
font-size: 50px;
}
.enter_img {
width: 150px;
height: 150px;
}
.enter_img:hover {
cursor: pointer;
}
</style>
<body>
<div>
<p class="app_title">
APP模型合并
</p>
<img src="pictures/star.png" alt="进入系统" class="enter_img" id="entrance">
</div>
</body>
<script>
document.getElementById("entrance").onclick=function() {
window.location.assign("login.html");
}
</script>
</html>

104
界面/files.html Normal file
View File

@ -0,0 +1,104 @@
<!DOCTYPE html>
<!--
@Time : 2021/6/1 20:40 下午
@Author : Tang Jiaxin
文件查看界面,查看当前已经上传至服务器的文件
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>文件查看</title>
<script src="https://ajax.microsoft.com/ajax/jquery/jquery-3.5.1.js"></script>
<script src="connect_download.js"></script>
<script src="connect_remove.js"></script>
</head>
<style type="text/css">
html, body {
height: 100%;
width: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
div {
position: absolute;
left: 5%;
top: 10%;
height: 75%;
width: 90%;
background-color: rgba(0, 0, 0, 0.4);
overflow: auto;
}
h1 {
text-align: center;
color: chartreuse;
font-family: 楷体;
}
button {
position: absolute;
height: 6%;
top: 90%;
font:16px Arial, sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius:50px;
cursor: pointer;
}
#button1, #button2 {
left: 40%;
width: 8%;
}
#button3, #button4 {
left: 55%;
width: 6%;
}
#button2, #button4 {
visibility: hidden;
}
p {
position: absolute;
left: 10%;
top: 20%;
color: white;
font-size: 20px;
font-family: 楷体;
}
table {
position: absolute;
left: 10%;
top: 10%;
width: 80%;
}
th, td {
color: white;
}
th {
font-size: 25px;
font-family: 楷体;
}
td {
text-align: center;
}
a {
color: white;
}
</style>
<body onload="connect_display_file_list(false)">
<h1>当前服务器上的文件列表</h1>
<div>
<table id="table">
</table>
<p id="response"></p>
</div>
<button id="button1" onclick="remove_file()">删除文件</button>
<button id="button2" onclick="connect_remove_file()">确认删除</button>
<button id="button3">返回</button>
<button id="button4" onclick="cancel_remove()">取消</button>
</body>
<script>
document.getElementById("button3").onclick=function() {
window.history.back();
}
</script>
</html>

17
界面/graph.html Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<!--
@Time : 2021/7/13 12:54 下午
@Author : Tang Jiaxin
可视化界面展示可视化的arp模型
-->
<html>
<head>
<meta charset="UTF-8">
<title>可视化展示</title>
<script src="https://ajax.microsoft.com/ajax/jquery/jquery-3.5.1.js"></script>
<script src="connect_download.js"></script>
</head>
<body onload="connect_graph_display()">
</body>
</html>

0
界面/js/.keep Normal file
View File

84
界面/js/index.js Normal file
View File

@ -0,0 +1,84 @@
function contact() {
var r = confirm("想要获取我们的联系方式吗?");
if (r == true) {
window.alert("E-mai: 191820019@smail.nju.edu.cn \n author by Chen Wenjie");
}
}
$(function () {
$(window).on("resize", function () {
// 1.1 获取窗口的宽度
let clientW = $(window).width();
// console.log(clientW);
// 1.2 设置临界值
let isShowBigImage = clientW >= 800;
// 1.3 获取所有的item
let $allItems = $("#lk_carousel .item");
// console.log($allItems);
// 1.4 遍历
$allItems.each(function (index, item) {
// 1.4.1 取出图片的路径
let src = isShowBigImage
? $(item).data("lg-img")
: $(item).data("sm-img");
let imgUrl = 'url("' + src + '")';
// 1.4.2 设置背景
$(item).css({
backgroundImage: imgUrl,
});
// 1.4.3 设置img标签
if (!isShowBigImage) {
let $img = "<img src='" + src + "'>";
$(item).empty().append($img);
} else {
$(item).empty();
}
});
});
$(window).trigger("resize");
// 2. 工具提示
$('[data-toggle="tooltip"]').tooltip();
// 3. 动态处理宽度
$(window).on("resize", function () {
let $ul = $("#lk_product .nav");
let $allLis = $("[role='presentation']", $ul);
// console.log($allLis);
// 3.1 遍历
let totalW = 0;
$allLis.each(function (index, item) {
totalW += $(item).width();
});
// console.log(totalW);
let parentW = $ul.parent().width();
// 3.2 设置宽度
if (totalW > parentW) {
$ul.css({
width: totalW + "px",
});
} else {
$ul.removeAttr("style");
}
});
// 4. 导航处理
let allLis = $("#lk_nav li");
$(allLis[2]).on("click", function () {
$("html,body").animate({ scrollTop: $("#lk_hot").offset().top }, 1000);
});
$(window).trigger("resize");
});

13
界面/js/swiper.min.js vendored Normal file

File diff suppressed because one or more lines are too long

107
界面/login.html Normal file
View File

@ -0,0 +1,107 @@
<!DOCTYPE html>
<!--
@Time : 2021/7/10 9:44 上午
@Author : Tang Jiaxin
登录界面,用户登录
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
<script src="https://ajax.microsoft.com/ajax/jquery/jquery-3.5.1.js"></script>
<script src="connect_login.js"></script>
</head>
<style type="text/css">
html, body {
height: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
p {
color: white;
font-size: 20px;
}
form {
position: absolute;
left: 30%;
top: 20%;
height: 80%;
width: 55%;
}
h1 {
color: white;
text-align: center;
}
.button {
position: absolute;
top: 70%;
width: 8%;
height: 6%;
font: 16px Arial, sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 50px;
cursor: pointer;
}
#confirm {
left: 30%;
}
#return {
left: 40%;
}
#mail {
left: 50%;
}
#register {
left: 60%;
}
#user_password {
margin-left: 27%;
}
#check_code_img {
position: absolute;
left: 40%;
top: 40%;
}
#check_code {
position: absolute;
left: 10%;
top: 35%;
}
</style>
<body onload="connect_check_code()">
<h1>用户登录</h1>
<form id="login_form" method="POST">
<div id="user_password">
<p>请输入用户名</p>
<input type="text" name="user_name" id="user_name">
<p>请输入密码</p>
<input type="password" name="password" id="password">
</div>
<br><br>
<div id="check_code">
<p>请输入验证码</p>
<input type="text" name="check_code" id="check_code_input">
</div>
<div id="check_code_img" onclick="connect_check_code()"></div>
</form>
<input type="button" class="button" id="confirm"
value="确定" onclick="connect_login()">
<input type="button" class="button" id="return" value="返回">
<input type="button" class="button" id="mail" value="邮箱验证登录">
<input type="button" class="button" id="register" value="用户注册">
</body>
<script>
document.getElementById("return").onclick=function() {
window.location.assign("entrance.html");
}
document.getElementById("mail").onclick=function() {
window.location.assign("maillogin.html");
}
document.getElementById("register").onclick=function() {
window.location.assign("register.html");
}
</script>
</html>

80
界面/mailbind.html Normal file
View File

@ -0,0 +1,80 @@
<!DOCTYPE html>
<!--
@Time : 2021/7/11 15:44 下午
@Author : Tang Jiaxin
邮箱绑定界面,为账户绑定邮箱
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>绑定邮箱</title>
<script src="https://ajax.microsoft.com/ajax/jquery/jquery-3.5.1.js"></script>
<script src="connect_login.js"></script>
</head>
<style type="text/css">
html, body {
height: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
p {
color: white;
font-size: 20px;
}
form {
position: absolute;
left: 45%;
top: 20%;
height: 80%;
width: 55%;
}
h1 {
color: white;
text-align: center;
}
.button {
position: absolute;
top: 70%;
width: 8%;
height: 6%;
font: 16px Arial, sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 50px;
cursor: pointer;
}
#send {
left: 35%;
}
#confirm {
left: 46%;
}
#return {
left: 57%;
}
</style>
<body>
<h1>邮箱绑定</h1>
<form id="mail_bind_form" method="POST">
<div>
<p>请输入邮箱</p>
<input type="text" name="mail_addr" id="mail_addr">
<br><br><br><br>
<p>请输入验证码</p>
<input type="text" name="check_code" id="check_code">
</div>
</form>
<input type="button" class="button" id="send"
value="发送邮件" onclick="connect_mail_bind()">
<input type="button" class="button" id="confirm"
value="确定" onclick="connect_mail_update()">
<input type="button" class="button" id="return" value="返回">
</body>
<script>
document.getElementById("return").onclick=function() {
window.history.back();
}
</script>
</html>

90
界面/maillogin.html Normal file
View File

@ -0,0 +1,90 @@
<!DOCTYPE html>
<!--
@Time : 2021/7/10 15:44 下午
@Author : Tang Jiaxin
邮箱登录界面,通过邮箱认证登录
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>邮箱登录</title>
<script src="https://ajax.microsoft.com/ajax/jquery/jquery-3.5.1.js"></script>
<script src="connect_login.js"></script>
</head>
<style type="text/css">
html, body {
height: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
p {
color: white;
font-size: 20px;
}
form {
position: absolute;
left: 30%;
top: 20%;
height: 80%;
width: 55%;
}
h1 {
color: white;
text-align: center;
}
.button {
position: absolute;
top: 70%;
width: 8%;
height: 6%;
font: 16px Arial, sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 50px;
cursor: pointer;
}
#send {
left: 30%;
}
#confirm {
left: 40%;
}
#return {
left: 50%;
}
#password {
left: 60%;
}
div {
margin-left: 27%;
}
</style>
<body>
<h1>邮件登录</h1>
<form id="mail_login_form" method="POST">
<div>
<p>请输入用户名或邮箱</p>
<input type="text" name="user_name" id="user_name">
<br><br><br><br>
<p>请输入验证码</p>
<input type="text" name="check_code" id="check_code">
</div>
</form>
<input type="button" class="button" id="send"
value="发送邮件" onclick="connect_mail_login()">
<input type="button" class="button" id="confirm"
value="确定" onclick="mail_login()">
<input type="button" class="button" id="return" value="返回">
<input type="button" class="button" id="password" value="用户名密码登录">
</body>
<script>
document.getElementById("return").onclick=function() {
window.location.assign("entrance.html");
}
document.getElementById("password").onclick=function() {
window.location.assign("login.html");
}
</script>
</html>

108
界面/merge.html Normal file
View File

@ -0,0 +1,108 @@
<!DOCTYPE html>
<!--
@Time : 2021/6/2 23:40 下午
@Author : Tang Jiaxin
模型合并界面,选择已经上传至服务器的文件进行合并
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>模型合并</title>
<script src="https://ajax.microsoft.com/ajax/jquery/jquery-3.5.1.js"></script>
<script src="connect_download.js"></script>
<script src="connect_merge.js"></script>
</head>
<style type="text/css">
html, body {
height: 100%;
width: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
div {
position: absolute;
left: 5%;
top: 10%;
height: 75%;
width: 90%;
background-color: rgba(0, 0, 0, 0.4);
overflow: auto;
}
h1 {
text-align: center;
color: chartreuse;
font-family: 楷体;
}
button {
position: absolute;
height: 6%;
top: 90%;
font:16px Arial, sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius:50px;
cursor: pointer;
}
#button1 {
left: 40%;
width: 8%;
}
#button2 {
left: 55%;
width: 6%;
}
p {
color: white;
font-size: 20px;
font-family: 楷体;
}
#response {
position: absolute;
left: 10%;
top: 20%;
}
#merge {
position: absolute;
left: 47%;
top: 88%;
visibility: hidden;
}
table {
position: absolute;
left: 10%;
top: 10%;
width: 80%;
}
th, td {
color: white;
}
th {
font-size: 25px;
font-family: 楷体;
}
td {
text-align: center;
}
a {
color: white;
}
</style>
<body onload="connect_display_file_list(true)">
<h1>请选择需要合并的文件</h1>
<div>
<table id="table">
</table>
<p id="response"></p>
</div>
<button id="button1" onclick="connect_merge()">开始合并</button>
<button id="button2">返回</button>
<p id="merge">正在合并中...</p>
</body>
<script>
document.getElementById("button2").onclick=function() {
window.location.assign("select.html");
}
</script>
</html>

0
界面/pictures/.keep Normal file
View File

BIN
界面/pictures/1.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
界面/pictures/2.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
界面/pictures/3.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
界面/pictures/4.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
界面/pictures/5.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
界面/pictures/6.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
界面/pictures/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
界面/pictures/doc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
界面/pictures/files.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
界面/pictures/member.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
界面/pictures/merge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
界面/pictures/sign.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
界面/pictures/star.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
界面/pictures/star2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
界面/pictures/upload.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
界面/pictures/user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
界面/pictures/信封.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
界面/pictures/回退.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
界面/pictures/登录.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

74
界面/register.html Normal file
View File

@ -0,0 +1,74 @@
<!DOCTYPE html>
<!--
@Time : 2021/7/9 10:44 上午
@Author : Tang Jiaxin
注册界面,注册一个新用户
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>用户注册</title>
<script src="https://ajax.microsoft.com/ajax/jquery/jquery-3.5.1.js"></script>
<script src="connect_login.js"></script>
</head>
<style type="text/css">
html, body {
height: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
p {
color: white;
font-size: 20px;
}
form {
position: absolute;
left: 45%;
top: 20%;
height: 80%;
width: 55%;
}
h1 {
color: white;
text-align: center;
}
.button {
position: absolute;
top: 70%;
width: 8%;
height: 6%;
font: 16px Arial, sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 50px;
cursor: pointer;
}
#confirm {
left: 40%;
}
#return {
left: 52%;
}
</style>
<body>
<h1>用户注册</h1>
<form id="register_form" method="POST">
<p>请输入用户名</p>
<input type="text" name="user_name" id="user_name">
<p>请输入密码</p>
<input type="password" name="password1" id="password1">
<p>请再次输入密码</p>
<input type="password" name="password2" id="password2">
</form>
<input type="button" class="button" id="confirm"
value="确定" onclick="connect_register()">
<input type="button" class="button" id="return" value="返回">
</body>
<script>
document.getElementById("return").onclick=function() {
window.history.back();
}
</script>
</html>

107
界面/result.html Normal file
View File

@ -0,0 +1,107 @@
<!DOCTYPE html>
<!--
@Time : 2021/7/13 11:44 上午
@Author : Tang Jiaxin
结果展示界面,展示合并结果
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>结果展示</title>
<script src="https://ajax.microsoft.com/ajax/jquery/jquery-3.5.1.js"></script>
<script src="connect_download.js"></script>
<script src="connect_merge.js"></script>
</head>
<style type="text/css">
html, body {
height: 100%;
width: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
div {
position: absolute;
left: 5%;
top: 10%;
height: 75%;
width: 90%;
background-color: rgba(0, 0, 0, 0.4);
overflow: auto;
}
h1 {
text-align: center;
color: chartreuse;
font-family: 楷体;
}
button, #input1, #input2 {
font: 16px Arial,sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius:50px;
cursor: pointer;
}
#input1, #input2 {
width: 10%;
height: 30%;
display: inline;
}
button {
position: absolute;
top: 90%;
height: 6%;
}
#button1 {
left: 40%;
width: 8%;
}
#button2 {
left: 50%;
width: 12%;
}
#button3 {
left: 65%;
width: 8%;
}
#button4 {
left: 30%;
width: 8%;
}
p {
font-size: 20px;
color: white;
font-family: 楷体;
display: inline;
}
#response {
position: absolute;
left: 10%;
top: 10%;
}
form {
position: absolute;
visibility: hidden;
top: 90%;
left: 30%;
width: 60%;
height: 10%;
}
</style>
<body onload="connect_display_file()">
<h1>结果展示</h1>
<div>
<p id="response"></p><br>
</div>
<button id="button4" onclick="graph_display()">可视化展示</button>
<button id="button1" onclick="connect_download_file()">下载结果</button>
<button id="button2" onclick="save_result()">保存到服务器</button>
<button id="button3" onclick="abort_result()">删除结果</button>
<form id="form">
<p>请输入希望保存的文件名:</p>
<input type="text" name="file_name" id="file_name">
<input id="input1" type="button" value="确认" onclick="save()">
<input id="input2" type="button" value="取消" onclick="cancel_save()">
</form>
</body>
</html>

99
界面/select.html Normal file
View File

@ -0,0 +1,99 @@
<!DOCTYPE html>
<!--
@Time : 2021/7/13 16:31 下午
@Author : Tang Jiaxin
功能选择界面,选择需要使用的功能
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>功能选择</title>
</head>
<style type="text/css">
html, body {
height: 100%;
width: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
div {
position: absolute;
left: 10%;
top: 10%;
width: 80%;
height: 80%;
}
.function {
width: 15%;
}
.function:hover {
cursor: pointer;
}
#upload {
position: absolute;
left: 42%;
top: 0px;
}
#doc {
position: absolute;
left: 60%;
top: 28%;
}
#merge {
position: absolute;
left: 24%;
top: 28%;
}
#member {
position: absolute;
left: 31%;
top: 66%;
}
#files {
position: absolute;
left: 53%;
top: 66%;
}
.user {
position: fixed;
left: 90%;
top: 10%;
width: 5%;
}
.user:hover {
cursor: pointer;
}
</style>
<body>
<img src="pictures/user.png" class="user" id="user">
<div>
<img src="pictures/upload.png" class="function" id="upload">
<img src="pictures/doc.png" class="function" id="doc">
<img src="pictures/merge.png" class="function" id="merge">
<img src="pictures/member.png" class="function" id="member">
<img src="pictures/files.png" class="function" id="files">
</div>
</body>
<script>
document.getElementById("upload").onclick=function() {
window.location.assign("upload.html");
}
document.getElementById("doc").onclick=function() {
window.location.assign("documents.html");
}
document.getElementById("merge").onclick=function() {
window.location.assign("merge.html");
}
document.getElementById("member").onclick=function() {
window.location.assign("about.html");
}
document.getElementById("files").onclick=function() {
window.location.assign("files.html");
}
document.getElementById("user").onclick=function() {
window.location.assign("user.html");
}
</script>
</html>

83
界面/upload.html Normal file
View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<!--
@Time : 2021/7/13 12:50 下午
@Author : Tang Jiaxin
文件上传界面,从本地上传文件到服务器
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
<script src="https://ajax.microsoft.com/ajax/jquery/jquery-3.5.1.js"></script>
<script src="connect_upload.js"></script>
</head>
<style type="text/css">
html, body {
height: 100%;
width: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
div {
position: absolute;
left: 0%;
top: 0%;
height: 100%;
width: 40%;
background-color: rgba(0, 0, 0, 0.4);
}
p {
color: white;
font-size: 20px;
font-family: 楷体;
}
form {
position: absolute;
left: 15%;
top: 30%;
height: 30%;
width: 80%;
}
.button {
width: 30%;
height: 15%;
font: 16px Arial, sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 50px;
cursor: pointer;
}
button {
position: absolute;
left: 40%;
top: 80%;
width: 20%;
height: 6%;
font: 16px Arial, sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 50px;
cursor: pointer;
}
</style>
<body>
<div>
<form enctype="multipart/form-data" id="file_form" method="POST">
<p>请选择待上传的文件</p>
<input type="file" name="input_file" id="input_file">
<br><br><br><br>
<input type="button" class="button"
value="提交文件" onclick="connect_upload()">
</form>
<p id="response"></p><br>
<button id="return">返回</button>
</div>
</body>
<script>
document.getElementById("return").onclick=function() {
window.history.back();
}
</script>
</html>

97
界面/user.html Normal file
View File

@ -0,0 +1,97 @@
<!DOCTYPE html>
<!--
@Time : 2021/7/11 23:40 下午
@Author : Tang Jiaxin
用户主页,显示用户的个人信息
-->
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>用户主页</title>
<script src="https://ajax.microsoft.com/ajax/jquery/jquery-3.5.1.js"></script>
<script src="connect_login.js"></script>
</head>
<style type="text/css">
html, body {
height: 100%;
}
body {
background: url(pictures/background.jpg);
background-size: cover;
}
p {
position: absolute;
color: white;
font-size: 20px;
}
div {
position: absolute;
left: 30%;
top: 0%;
height: 100%;
width: 40%;
background-color: rgba(0, 0, 0, 0.4);
}
h1 {
color: white;
text-align: center;
}
button {
position: absolute;
top: 70%;
width: 18%;
height: 6%;
font: 16px Arial, sans-serif bold;
color: white;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 50px;
cursor: pointer;
}
#user_name {
top: 20%;
left: 20%;
}
#mail_addr {
top: 30%;
left: 20%;
}
#user_page {
top: 10%;
left: 40%;
}
#mail_bind {
left: 10%;
}
#exit {
left: 30%;
}
#change {
left: 50%;
}
#return {
left: 70%;
}
</style>
<body onload="get_user_info()">
<div>
<h1 id="user_page">个人主页</h1>
<p id="user_name">用户名:</p>
<p id="mail_addr">邮箱:</p>
<button id="mail_bind">绑定邮箱</button>
<button id="change">修改密码</button>
<button id="exit" onclick="exit_account()">退出账号</button>
<button id="return">返回</button>
</div>
</body>
<script>
document.getElementById("return").onclick=function() {
window.history.back();
}
document.getElementById("mail_bind").onclick=function() {
window.location.assign("mailbind.html");
}
document.getElementById("change").onclick=function() {
window.location.assign("changepassword.html");
}
</script>
</html>