2023-07-13 13:06:09 +09:00
6 changed files with 3301 additions and 1760 deletions

2
backend/.gitignore vendored
View File

@@ -124,3 +124,5 @@ cython_debug/
# VS Code settings # VS Code settings
.vscode/ .vscode/
*.lock

View File

@@ -2,21 +2,150 @@ from fastapi import APIRouter, UploadFile,HTTPException,File
from io import BytesIO from io import BytesIO
import typing as t import typing as t
import pandas as pd import pandas as pd
import json
import httpx
import app.core.config as c
kinton_router = r = APIRouter() kinton_router = r = APIRouter()
@r.post("/upload",) @r.post("/upload",)
async def upload(file:UploadFile = File(...)): async def upload(files:t.List[UploadFile] = File(...)):
dataframes = [] dataframes = []
if file.filename.endswith('.xlsx'): for file in files:
try: if file.filename.endswith('.xlsx'):
content = await file.read() try:
df = pd.read_excel(BytesIO(content)) content = await file.read()
dataframes.append(df) df = pd.read_excel(BytesIO(content))
except Exception as e: print(df)
raise HTTPException(status_code=400, detail=f"Error occurred while parsing file {file.filename}: {str(e)}") dataframes.append(df)
else: except Exception as e:
raise HTTPException(status_code=400, detail=f"File {file.filename} is not an Excel file") raise HTTPException(status_code=400, detail=f"Error occurred while parsing file {file.filename}: {str(e)}")
return {"file": file.filename} 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()

View File

@@ -4,4 +4,12 @@ PROJECT_NAME = "KintoneAppBuilder"
SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL") 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"]

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +1,20 @@
<template> <template>
<div class="q-pa-md"> <div class="q-pa-md">
<q-uploader <q-uploader
:on-finish="uploadFinished"
style="max-width: 400px" style="max-width: 400px"
:url="uploadUrl" :url="uploadUrl"
:label="title" :label="title"
accept=".csv,.xlsx" accept=".csv,.xlsx"
v-on:rejected="onRejected" v-on:rejected="onRejected"
v-on:finish="onUploadFinish" v-on:finish="uploadFinished"
field-name="file" field-name="file"
></q-uploader> ></q-uploader>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useQuasar } from 'quasar'; import { createUploaderComponent, useQuasar } from 'quasar';
const $q=useQuasar(); const $q=useQuasar();
@@ -36,27 +38,25 @@
$q.notify({ $q.notify({
type: 'negative', type: 'negative',
message: `CSVおよびExcelファイルを選択してください。` message: `CSVおよびExcelファイルを選択してください。`
})
});
} }
function onUploadFinish(){ function uploadFinished(){
$q.notify({ $q.notify({
message:"ファイルアップロードしました!", type: 'info',
caption:"通知", caption:"通知",
type:"positive" message: 'ファイルの読込が完了しました。'
}) })
} }
interface Props { interface Props {
title: string; title: string;
uploadUrl:string; uploadUrl:string;
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
title:"設計書から導入する(csv or excel)", title:"設計書から導入する(csv or excel)",
uploadUrl:process.env.KAB_BACKEND_URL uploadUrl:process.env.KAB_BACKEND_URL
}); });
</script> </script>
<style lang="scss"> <style lang="scss">

File diff suppressed because it is too large Load Diff