Merge branch 'master' of https://dev.azure.com/alicorn-dev/KintoneAppBuilder/_git/KintoneAppBuilder
This commit is contained in:
@@ -2,21 +2,150 @@ from fastapi import APIRouter, UploadFile,HTTPException,File
|
||||
from io import BytesIO
|
||||
import typing as t
|
||||
import pandas as pd
|
||||
|
||||
import json
|
||||
import httpx
|
||||
import app.core.config as c
|
||||
|
||||
kinton_router = r = APIRouter()
|
||||
|
||||
|
||||
@r.post("/upload",)
|
||||
async def upload(file:UploadFile = File(...)):
|
||||
async def upload(files:t.List[UploadFile] = File(...)):
|
||||
dataframes = []
|
||||
if file.filename.endswith('.xlsx'):
|
||||
try:
|
||||
content = await file.read()
|
||||
df = pd.read_excel(BytesIO(content))
|
||||
dataframes.append(df)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Error occurred while parsing file {file.filename}: {str(e)}")
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail=f"File {file.filename} is not an Excel file")
|
||||
return {"file": file.filename}
|
||||
for file in files:
|
||||
if file.filename.endswith('.xlsx'):
|
||||
try:
|
||||
content = await file.read()
|
||||
df = pd.read_excel(BytesIO(content))
|
||||
print(df)
|
||||
dataframes.append(df)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Error occurred while parsing file {file.filename}: {str(e)}")
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail=f"File {file.filename} is not an Excel file")
|
||||
|
||||
return {"files": [file.filename for file in files]}
|
||||
|
||||
@r.get("/allapps",)
|
||||
async def allapps():
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/apps.json"
|
||||
r = httpx.get(url,headers=headers)
|
||||
return r.json()
|
||||
|
||||
|
||||
@r.post("/createapp",)
|
||||
async def createapp(name:str):
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
data = {"name":name}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app.json"
|
||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||
result = r.json()
|
||||
if result.get("app") != None:
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/deploy.json"
|
||||
data = {"apps":[result],"revert": False}
|
||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||
return r.json
|
||||
|
||||
property=["label","code","type","required","defaultValue","options"]
|
||||
|
||||
@r.post("/createappfromexcel")
|
||||
async def createappfromexcel(files:t.List[UploadFile] = File(...)):
|
||||
for file in files:
|
||||
if file.filename.endswith('.xlsx'):
|
||||
try:
|
||||
content = await file.read()
|
||||
#アプリ名
|
||||
df = pd.read_excel(BytesIO(content))
|
||||
# print(df)
|
||||
appname = df.iloc[0,2]
|
||||
col=[]
|
||||
for row in range(5,len(df)):
|
||||
if not df.iloc[row,3] in c.KINTONE_FIELD_TYPE:
|
||||
continue
|
||||
p=[]
|
||||
for column in range(1,7):
|
||||
if(not pd.isna(df.iloc[row,column])):
|
||||
if(property[column-1]=="options"):
|
||||
o=[]
|
||||
for v in df.iloc[row,column].split(','):
|
||||
o.append(f"\"{v.split('|')[0]}\":{{\"label\":\"{v.split('|')[0]}\",\"index\":\"{v.split('|')[1]}\"}}")
|
||||
p.append(f"\"{property[column-1]}\":{{{','.join(o)}}}")
|
||||
elif(property[column-1]=="required"):
|
||||
p.append(f"\"{property[column-1]}\":{df.iloc[row,column]}")
|
||||
else:
|
||||
p.append(f"\"{property[column-1]}\":\"{df.iloc[row,column]}\"")
|
||||
col.append(f"\"{df.iloc[row,2]}\":{{{','.join(p)}}}")
|
||||
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
data = {"name":appname}
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app.json"
|
||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||
result1 = r.json()
|
||||
if result1.get("app") != None:
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/form/fields.json"
|
||||
form = f"{{\"app\":{result1['app']},\"properties\":{{{','.join(col)}}}}}".replace("False","false").replace("True","true")
|
||||
print(form)
|
||||
data = json.loads(form)
|
||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||
result2 = r.json()
|
||||
if result2.get("revision") != None:
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/deploy.json"
|
||||
data = {"apps":[{"app":result1["app"],"revision":result2["revision"]}],"revert": False}
|
||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Error occurred while parsing file {file.filename}: {str(e)}")
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail=f"File {file.filename} is not an Excel file")
|
||||
|
||||
return {"app":result1["app"],"revision":result2["revision"]}
|
||||
|
||||
|
||||
@r.post("/updateappfromexcel",)
|
||||
async def updateappfromexcel(app:str,revision:str,files:t.List[UploadFile] = File(...)):
|
||||
for file in files:
|
||||
if file.filename.endswith('.xlsx'):
|
||||
try:
|
||||
content = await file.read()
|
||||
#アプリ名
|
||||
df = pd.read_excel(BytesIO(content))
|
||||
# print(df)
|
||||
appname = df.iloc[0,2]
|
||||
col=[]
|
||||
for row in range(5,len(df)):
|
||||
if not df.iloc[row,3] in c.KINTONE_FIELD_TYPE:
|
||||
continue
|
||||
p=[]
|
||||
for column in range(1,7):
|
||||
if(not pd.isna(df.iloc[row,column])):
|
||||
if(property[column-1]=="options"):
|
||||
o=[]
|
||||
for v in df.iloc[row,column].split(','):
|
||||
o.append(f"\"{v.split('|')[0]}\":{{\"label\":\"{v.split('|')[0]}\",\"index\":\"{v.split('|')[1]}\"}}")
|
||||
p.append(f"\"{property[column-1]}\":{{{','.join(o)}}}")
|
||||
elif(property[column-1]=="required"):
|
||||
p.append(f"\"{property[column-1]}\":{df.iloc[row,column]}")
|
||||
else:
|
||||
p.append(f"\"{property[column-1]}\":\"{df.iloc[row,column]}\"")
|
||||
col.append(f"\"{df.iloc[row,2]}\":{{{','.join(p)}}}")
|
||||
|
||||
headers={c.API_V1_AUTH_KEY:c.API_V1_AUTH_VALUE,"Content-Type": "application/json"}
|
||||
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/form/fields.json"
|
||||
form = f"{{\"app\":{app},\"revision\": {revision},\"properties\":{{{','.join(col)}}}}}".replace("False","false").replace("True","true")
|
||||
print(form)
|
||||
data = json.loads(form)
|
||||
r = httpx.put(url,headers=headers,data=json.dumps(data))
|
||||
result = r.json()
|
||||
if result.get("revision") != None:
|
||||
url = f"{c.BASE_URL}{c.API_V1_STR}/preview/app/deploy.json"
|
||||
data = {"apps":[{"app":app,"revision":result["revision"]}],"revert": False}
|
||||
r = httpx.post(url,headers=headers,data=json.dumps(data))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Error occurred while parsing file {file.filename}: {str(e)}")
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail=f"File {file.filename} is not an Excel file")
|
||||
|
||||
return r.json()
|
||||
|
||||
|
||||
|
||||
@@ -4,4 +4,12 @@ PROJECT_NAME = "KintoneAppBuilder"
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL")
|
||||
|
||||
API_V1_STR = "/api/v1"
|
||||
BASE_URL = "https://mfu07rkgnb7c.cybozu.com"
|
||||
|
||||
API_V1_STR = "/k/v1"
|
||||
|
||||
API_V1_AUTH_KEY = "X-Cybozu-Authorization"
|
||||
|
||||
API_V1_AUTH_VALUE = "TVhaOm1heHoxMjA1"
|
||||
|
||||
KINTONE_FIELD_TYPE=["GROUP","GROUP_SELECT","CHECK_BOX","SUBTABLE","RICH_TEXT","RICH_TEXT","LINK","REFERENCE_TABLE","CALC","TIME","NUMBER","ORGANIZATION_SELECT","FILE","DATETIME","DATE","MULTI_SELECT","SINGLE_LINE_TEXT","MULTI_LINE_TEXT"]
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
KAB_BACKEND_URL="http://127.0.0.1:8000/api/v1/upload"
|
||||
KAB_BACKEND_URL="http://127.0.0.1:8000/api/v1/"
|
||||
|
||||
|
||||
@@ -109,7 +109,9 @@ module.exports = configure(function (/* ctx */) {
|
||||
// directives: [],
|
||||
|
||||
// Quasar plugins
|
||||
plugins: ['Notify']
|
||||
plugins: [
|
||||
'Notify'
|
||||
]
|
||||
},
|
||||
|
||||
// animations: 'all', // --- includes all animations
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="q-pa-md">
|
||||
<q-uploader
|
||||
v-on:finish="uploadFinished"
|
||||
:on-finish="uploadFinished"
|
||||
style="max-width: 400px"
|
||||
:url="uploadUrl"
|
||||
:label="title"
|
||||
accept=".csv,.xlsx"
|
||||
v-on:rejected="onRejected"
|
||||
:on-rejected="onRejected"
|
||||
field-name="file"
|
||||
></q-uploader>
|
||||
</div>
|
||||
@@ -17,20 +17,11 @@
|
||||
|
||||
|
||||
const $q=useQuasar();
|
||||
// const allowTypes=['.xlsx','.csv'];
|
||||
|
||||
// function checkFileType(files : File[] ):File[]{
|
||||
// return files.filter((file)=>{
|
||||
// let filename = file.name.toLowerCase();
|
||||
// for(let ext of allowTypes){
|
||||
// if(filename.endsWith(ext)){
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// });
|
||||
// }
|
||||
|
||||
/**
|
||||
* ファイルアップロードを拒否する時の処理
|
||||
* @param rejectedEntries
|
||||
*/
|
||||
function onRejected (rejectedEntries:any) {
|
||||
// Notify plugin needs to be installed
|
||||
// https://quasar.dev/quasar-plugins/notify#Installation
|
||||
@@ -38,17 +29,37 @@
|
||||
type: 'negative',
|
||||
message: `CSVおよびExcelファイルを選択してください。`
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
function uploadFinished(){
|
||||
/**
|
||||
* ファイルアップロード成功時の処理
|
||||
*/
|
||||
function onUploadFinished({xhr}:{xhr:XMLHttpRequest}){
|
||||
let msg="ファイルのアップロードが完了しました。";
|
||||
if(xhr && xhr.response){
|
||||
msg=`${msg} (${xhr.responseText})`;
|
||||
}
|
||||
$q.notify({
|
||||
type: 'info',
|
||||
message: 'ファイルの読込が完了しました。'
|
||||
})
|
||||
type: 'positive',
|
||||
caption:"通知",
|
||||
message: msg
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param info ファイルアップロード失敗時の処理
|
||||
*/
|
||||
function onFailed({files,xhr}:{files: readonly any[],xhr:any}){
|
||||
let msg ="ファイルアップロードが失敗しました。";
|
||||
if(xhr && xhr.status){
|
||||
msg=`${msg} (${xhr.status }:${xhr.statusText})`
|
||||
}
|
||||
$q.notify({
|
||||
type:"negative",
|
||||
message:msg
|
||||
});
|
||||
}
|
||||
interface Props {
|
||||
title: string;
|
||||
uploadUrl:string;
|
||||
|
||||
27
frontend/src/components/Rule.json
Normal file
27
frontend/src/components/Rule.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name":"保存ボタン押す際、入力値チェック",
|
||||
"condtion":{
|
||||
"logic":{
|
||||
"logicType":"And",
|
||||
"condtions":[
|
||||
{
|
||||
"compareType":"event",
|
||||
"compareItem":"Record-Save"
|
||||
},
|
||||
{
|
||||
"compareType":"field",
|
||||
"compareItem":"テキスト",
|
||||
"op":"!==",
|
||||
"compareValue":"{value}"
|
||||
},
|
||||
{
|
||||
"compareType":"field",
|
||||
"compareItem":"テキスト",
|
||||
"op":"!==",
|
||||
"compareValue":"isZengaku('{value}')"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
9
frontend/src/components/Rules.ts
Normal file
9
frontend/src/components/Rules.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface Rule{
|
||||
id:number;
|
||||
name:string;
|
||||
condtion:CondtionTree
|
||||
}
|
||||
|
||||
export interface CondtionTree{
|
||||
|
||||
}
|
||||
17
frontend/src/pages/RuleEditor.vue
Normal file
17
frontend/src/pages/RuleEditor.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<q-page>
|
||||
<div class="q-pa-md">
|
||||
<div class="q-gutter-sm row items-start">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Todo, Meta } from 'components/models';
|
||||
import DocUploader from 'components/DocUpload.vue';
|
||||
// import ExampleComponent from 'components/ExampleComponent.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
</script>
|
||||
Reference in New Issue
Block a user