Skip to content

Commit

Permalink
Prepare file and URL inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
MarekSuchanek committed Oct 31, 2023
1 parent eb3e6d1 commit 4dad69e
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 4 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ classifiers = [
requires-python = '>=3.10, <4'
dependencies = [
'fastapi',
'httpx',
'Jinja2',
'rdflib',
]
Expand Down
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
annotated-types==0.6.0
anyio==3.7.1
certifi==2023.7.22
fastapi==0.104.1
h11==0.14.0
httpcore==0.18.0
httpx==0.25.0
idna==3.4
isodate==0.6.1
Jinja2==3.1.2
Expand Down
55 changes: 52 additions & 3 deletions src/smp_importer/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import fastapi.responses
import fastapi.staticfiles
import fastapi.templating
import httpx
import pydantic

from .logic import prepare_import_mapping


ROOT_DIR = pathlib.Path(__file__).parent
Expand All @@ -23,6 +27,17 @@
templates = fastapi.templating.Jinja2Templates(directory=TEMPLATES_DIR)


class BodyURL(pydantic.BaseModel):
url: str


class BodyFile(pydantic.BaseModel):
name: str
contents: str
type: str
bytesize: int


@app.get('/', response_class=fastapi.responses.HTMLResponse)
async def get_index(request: fastapi.Request):
return templates.TemplateResponse(
Expand All @@ -35,10 +50,44 @@ async def get_index(request: fastapi.Request):
)


@app.get('/api/import', response_class=fastapi.responses.JSONResponse)
async def list_fips(request: fastapi.Request):
@app.post('/api/import-file', response_class=fastapi.responses.JSONResponse)
async def api_import_from_file(body_file: BodyFile):
try:
result = prepare_import_mapping(
contents=body_file.contents,
content_type=body_file.type,
)
return fastapi.responses.JSONResponse(content={
'sourcce': 'file',
'name': body_file.name,
'actions': result['actions'],
})
except Exception as e:
LOG.error(f'Error appeared: {str(e)}', exc_info=e)
raise fastapi.HTTPException(status_code=500)


@app.post('/api/import-url', response_class=fastapi.responses.JSONResponse)
async def api_import_from_url(body_url: BodyURL):
try:
return fastapi.responses.JSONResponse(content={'text': 'Hello'})
result = await fetch_from_url(body_url.url)
return fastapi.responses.JSONResponse(content={
'source': 'url',
'url': body_url.url,
'actions': result['actions'],
})
except Exception as e:
LOG.error(f'Error appeared: {str(e)}', exc_info=e)
raise fastapi.HTTPException(status_code=500)


# ----- logic

async def fetch_from_url(url: str) -> dict:
async with httpx.AsyncClient() as client:
r = await client.get(url)
r.raise_for_status()
return prepare_import_mapping(
contents=r.content.decode(encoding=r.charset_encoding or 'utf-8'),
content_type=r.headers.get('content-type'),
)
131 changes: 131 additions & 0 deletions src/smp_importer/static/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
const importer = new DSWImporter()

importer
.init({
useWizardStyles: true,
windowSize: {
width: 300,
height: 500,
},
})
.then(() => {
jQuery('#file-input').on('input', function (e) {
const files = e.target.files
console.log(files)
const file = files[0]

const reader = new FileReader()
reader.addEventListener('load', (event) => {
let data = ''
try {
data = event.target.result
} catch (error) {
alert('Failed to load file')
}

jQuery.ajax({
type: 'POST',
url: `/api/import-file`,
data: JSON.stringify({
'contents': data,
'type': file.type,
'name': file.name,
'bytesize': file.size,
}),
contentType: "application/json; charset=utf-8",
traditional: true,
success: function (result) {
doImport(result.actions)
},
error: function (result) {
console.log(result)
alert('failed')
}
})
})
reader.readAsText(file)
})

jQuery('#btn-load').on('click', function () {
const url = jQuery('#url-input').val()
console.log(url)

if (!isValidUrl(url)) {
alert('Invalid URL!')
jQuery('#url-input').val('')
} else {
jQuery.ajax({
type: 'POST',
url: `/api/import-url`,
data: JSON.stringify({'url': url}),
contentType: "application/json; charset=utf-8",
traditional: true,
success: function (result) {
doImport(result.actions)
},
error: function (result) {
console.log(result)
alert('failed')
}
})
}
})
})
.catch(error => {
console.error(error)
})


function isValidUrl(urlString) {
const urlPattern = new RegExp('^(https?:\\/\\/)?'+ // validate protocol
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // validate domain name
'((\\d{1,3}\\.){3}\\d{1,3}))'+ // validate OR ip (v4) address
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // validate port and path
'(\\?[;&a-z\\d%_.~+=-]*)?'+ // validate query string
'(\\#[-a-z\\d_]*)?$','i') // validate fragment locator
return !!urlPattern.test(urlString)
}

function doImport(actions) {
const replacements = new Map()
const debug = jQuery('#debug-checkbox').is(':checked')
console.log(actions)

const replacePath = (path) => {
let newPath = [];
path.forEach((pathItem) => {
if (replacements.has(pathItem)) {
newPath.push(replacements.get(pathItem))
} else {
newPath.push(pathItem)
}
})
return newPath
}

actions.forEach((item) => {
if (item.type === 'debug') {
console.log('DEBUG:', item.message)
} else if (item.type === 'setReply') {
const path = replacePath(item.path)
console.log(`SET REPLY: ${path} = "${item.value}"`)
importer.setReply(path, item.value)
} else if (item.type === 'setIntegrationReply') {
const path = replacePath(item.path)
console.log(`SET INTEGRATION REPLY: ${path} = "${item.value}" [${item.itemId}]`)
importer.setIntegrationReply(path, item.value, item.itemId)
} else if (item.type === 'addItem') {
const path = replacePath(item.path)
const itemUuid = importer.addItem(path)
console.log(`ADD ITEM: ${path} = ${itemUuid} [${item.var}]`)
replacements.set(item.var, itemUuid)
}
})

if (debug) {
alert('Stop (debug enabled)')
}

importer.send()
}

28 changes: 27 additions & 1 deletion src/smp_importer/templates/index.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
window.postMessage({
type: 'ready',
origin: '',
styleUrl: 'https://s3.ds-wizard.org/dsw-server-app/17032b53-f4da-47bb-972c-ea82176422eb/public/customization.6595603168296135982.css',
styleUrl: 'https://s3.ds-wizard.org/dsw-server-app/8bfcffb9-d276-4777-92a5-3ca25f7cd2bc/public/customization.2794146750364175066.css',
knowledgeModel: '{}',
})
}
Expand All @@ -37,6 +37,32 @@
<body>
<div class="container">
<h1>SMP Importer</h1>

<div id="input-file">
<h2>From local file</h2>

<div class="mb-3">
<label for="file-input" class="form-label">Select a file with maSMP (RDF/JSON-LD):</label>
<input type="file" id="file-input" class="form-control" />
<p class="hint"></p>
</div>
</div>

<div id="input-url">
<h2>From URL</h2>

<div class="mb-3">
<label for="url-input" class="form-label">Enter a URL with maSMP:</label>
<input type="url" id="url-input" class="form-control" />
<p class="hint"></p>
<button id="btn-load" class="btn btn-primary" type="button">Load maSMP</button>
</div>
</div>

<hr>

<pre id="result-debug"></pre>

</div>
</body>
</html>

0 comments on commit 4dad69e

Please sign in to comment.