Skip to content

Commit

Permalink
updated header; updated person selection; added nominate path to header(
Browse files Browse the repository at this point in the history
  • Loading branch information
tholzheim committed May 28, 2024
1 parent 1a87cb6 commit 9bc1bf7
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 28 deletions.
34 changes: 30 additions & 4 deletions snapquery/person_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from ngwidgets.input_webserver import WebSolution
from nicegui import run, ui
from nicegui.element import Element
from nicegui.elements.button import Button

from snapquery.models.person import Person
from snapquery.pid_lookup import PersonLookup
Expand Down Expand Up @@ -107,6 +108,7 @@ def __init__(
self.search_name = ""
self.suggested_persons = []
self.person_lookup = PersonLookup(nqm=solution.webserver.nqm)
self.selection_btn: Optional[Button] = None
self.person_selection()

@ui.refreshable
Expand All @@ -120,9 +122,7 @@ def person_selection(self):
with splitter.before:
with ui.card() as self.selection_card:
with ui.row():
self.label = ui.label(
"Please identify yourself by entering or looking up a valid PID(Wikidata ID, ORCID, dblp):"
)
self.label = ui.label("Name or Pid:")
with ui.row():
self.name_input = ui.input(
label="name",
Expand All @@ -134,15 +134,41 @@ def person_selection(self):
self.identifier_input = ui.input(
label="PID",
placeholder="Q80",
on_change=self.suggest_persons,
on_change=self.check_pid,
value=person.wikidata_id,
).props("size=20")
# if self.selection_btn is None:
self.selection_btn = ui.button(
text="Continue", on_click=self.btn_selection_callback
)
self.selection_btn.disable()
with splitter.after:
with ui.element("column").classes(" w-full h-full gap-2"):
self.suggestion_list = ui.column().classes(
"rounded-md border-2 p-3"
)

async def btn_selection_callback(self):
person = Person()
pid_value = PIDs().pid4id(self.identifier_input.value)
if pid_value.pid.name == "Wikidata":
person.wikidata_id = self.identifier_input.value
elif pid_value.pid.name == "dblp":
person.dblp_id = self.identifier_input.value
elif pid_value.pid.name == "ORCID":
person.orcid_id = self.identifier_input.value
person.label = self.name_input.value
self.selection_callback(person)

async def check_pid(self):
pid = PIDs().pid4id(self.identifier_input.value)
if pid is not None and pid.is_valid() and self.selection_btn is not None:
print("Is valid PID")
self.selection_btn.enable()
elif self.selection_btn:
self.selection_btn.disable()
ui.update()

async def suggest_persons(self):
"""
based on given input suggest potential persons
Expand Down
12 changes: 9 additions & 3 deletions snapquery/pid.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,30 @@
Created on 2024-05-26
@author: wf
"""

from dataclasses import dataclass
from typing import Optional
import re


@dataclass
class PID:
"""
A persistent identifier source e.g. ORCID, dblpID or wikidata id
"""

name: str
logo: str
formatter_url: str
regex: str


@dataclass
class PIDValue:
"""
Represents a specific instance of a persistent identifier with its value.
"""

pid: PID
value: str

Expand All @@ -33,13 +38,14 @@ def html(self) -> str:
return f'<a href="{self.url}"><img src="{self.pid.logo}" alt="{self.pid.name} logo"> {self.value}</a>'

def is_valid(self) -> bool:
return re.match(self.pid.regex, self.value) is not None
return re.match(self.pid.regex, self.value) is not None


class PIDs:
"""
Available PIDs
"""

def __init__(self):
self.pids = {
"orcid": PID(
Expand All @@ -59,7 +65,7 @@ def __init__(self):
logo="https://www.wikidata.org/static/favicon/wikidata.ico",
formatter_url="https://www.wikidata.org/wiki/{}",
regex=r"^Q[0-9]+$",
)
),
}

def pid4id(self, identifier: str) -> Optional[PIDValue]:
Expand Down
33 changes: 26 additions & 7 deletions snapquery/qimport_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from nicegui import ui

from snapquery.models.person import Person
from snapquery.person_selector import PersonView
from snapquery.qimport import QueryImport
from snapquery.snapquery_core import NamedQuery

Expand All @@ -34,6 +33,7 @@ def __init__(
self.url = ""
self.title = ""
self.description = ""
self.comment = ""
self.query = None
if self.solution:
self.qimport = QueryImport()
Expand All @@ -46,6 +46,7 @@ def setup_ui(self):
"""
with self.solution.container:
with ui.row() as self.input_row:
self.input_row.classes("h-full")
ui.input(
label="namespace", placeholder="e.g. wikidata-examples"
).bind_value(self, "namespace")
Expand All @@ -65,16 +66,22 @@ def setup_ui(self):
ui.button(
icon="publish", text="Publish Query", on_click=self.on_import_button
)
with ui.row() as self.details_row:
with ui.input(label="title").props("size=80").bind_value(self, "title"):
ui.tooltip("Descriptive title of the query")
ui.textarea(label="description").bind_value(self, "description")
self.named_query_link = ui.html()
self.query_row = ui.row().classes("w-full h-full ")
self.query_row = ui.row().classes("w-full h-full flex ")
with self.query_row:
ui.textarea(label="query").bind_value(self, "query").classes(
"w-full h-full border-solid m-5 border-gray-dark border-2 rounded-md"
"w-full h-full resize min-h-80 border-solid m-5 border-gray-dark border-2 rounded-md"
)
with ui.row() as self.details_row:
self.details_row.classes("flex")
ui.textarea(label="description").bind_value(
self, "description"
).classes("w-1/2 border-solid m-5 border-gray-dark border-2 rounded-md")
ui.textarea(label="comment").bind_value(self, "comment").classes(
"w-2/5 border-solid m-5 border-gray-dark border-2 rounded-md"
)
self.named_query_link = ui.html()

def on_import_button(self, _args):
"""
Expand All @@ -84,19 +91,31 @@ def on_import_button(self, _args):
with self.query_row:
ui.notify("input a query first")
return
if self.person:
self.comment = f"[query nominated by {self.person}] {self.comment}"
nq_record = {
"namespace": self.namespace,
"name": self.name,
"title": self.title,
"url": self.url,
"description": self.description,
"sparql": self.query.query,
"comment": self.comment,
"sparql": self.query.query if isinstance(self.query, Query) else self.query,
}
nq = NamedQuery.from_record(nq_record)
self.nqm.add_and_store(nq)
with self.query_row:
ui.notify(f"added named query {self.name}")
self.named_query_link.content = nq.as_link()
self.clear_inputs()

def clear_inputs(self):
self.query = None
self.name = None
self.url = None
self.title = None
self.description = None
self.comment = None

def on_input_button(self, _args):
"""
Expand Down
15 changes: 10 additions & 5 deletions snapquery/snapquery_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
@author: wf
"""

import datetime
import json
import os
Expand Down Expand Up @@ -153,6 +154,7 @@ class NamedQuery:
title: Optional[str] = None
# multiline description
description: Optional[str] = None
comment: Optional[str] = None

def __post_init__(self):
"""
Expand All @@ -174,6 +176,7 @@ def get_samples(cls) -> dict[str, "NamedQuery"]:
url="https://www.wikidata.org/wiki/Wikidata:SPARQL_query_service/queries/examples#Cats",
title="Cats on Wikidata",
description="This query retrieves all items classified under 'house cat' (Q146) on Wikidata.",
comment="modified cats query from wikidata-examples",
sparql="""# snapquery cats example
SELECT ?item ?itemLabel
WHERE {
Expand Down Expand Up @@ -421,6 +424,7 @@ def get_lod_with_stats(self) -> tuple[list[dict], QueryStats]:
Returns:
List[dict]: A list where each dictionary represents a row of results from the SPARQL query.
"""
print(f"Querying {self.endpoint.name} with query {self.named_query.name}")
query_stat = QueryStats(
query_id=self.named_query.query_id, endpoint_name=self.endpoint.name
)
Expand All @@ -429,7 +433,8 @@ def get_lod_with_stats(self) -> tuple[list[dict], QueryStats]:
query_stat.records = len(lod) if lod else -1
query_stat.done()
except Exception as ex:
lod = None
lod = []
print(ex)
query_stat.error(ex)
return (lod, query_stat)

Expand Down Expand Up @@ -558,7 +563,7 @@ def from_samples(
nqm = NamedQueryManager(db_path=db_path, debug=debug)
path_obj = Path(db_path)
if not path_obj.exists() or path_obj.stat().st_size == 0:
for (source_class, pk) in [
for source_class, pk in [
(NamedQuery, "query_id"),
(QueryStats, "stats_id"),
(QueryDetails, "quer_id"),
Expand Down Expand Up @@ -611,7 +616,7 @@ def execute_query(
params_dict: Dict,
endpoint_name: str = "wikidata",
limit: int = None,
with_stats:bool=True,
with_stats: bool = True,
):
"""
execute the given named_query
Expand All @@ -635,8 +640,8 @@ def execute_query(
results, stats = query_bundle.get_lod_with_stats()
self.store_stats([stats])
else:
results=query_bundle.get_lod()
stats=None
results = query_bundle.get_lod()
stats = None
return results, stats

def add_and_store(self, nq: NamedQuery):
Expand Down
10 changes: 8 additions & 2 deletions snapquery/snapquery_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ def setup_ui(self):
self.query_settings_row.classes("w-full")
ui.number(label="limit").bind_value(self, "limit")
ui.number(label="time out").bind_value(self, "timeout")
endpoint_selector = ui.select(list(self.nqm.endpoints.keys()),
endpoint_selector = ui.select(
list(self.nqm.endpoints.keys()),
value=self.solution.endpoint_name,
label="default endpoint"
label="default endpoint",
)
endpoint_selector.bind_value(
app.storage.user,
Expand Down Expand Up @@ -258,6 +259,11 @@ def setup_ui(self):
"""
setup my user interface
"""
with ui.row().classes("w-full"):
with ui.column().classes("w-full"):
ui.label("Available Queries").classes("text-xl")
ui.label("select a query to view and execute").classes("text-slate-400")

with ui.row() as self.search_row:
self.name_search_input = (
ui.input(label="name", on_change=self.on_search_change)
Expand Down
28 changes: 21 additions & 7 deletions snapquery/snapquery_webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,16 @@ def configure_settings(self):
"endpoint_name",
)

def setup_menu(self, detailed: bool = None):
def setup_menu(self, detailed: bool = True):
"""
setup the menu
"""
ui.button(icon="menu", on_click=lambda: self.header.toggle())
self.webserver: SnapQueryWebServer
super().setup_menu(detailed=detailed)
with self.header:
self.header.value = False
self.link_button("Nominate a Query", "/nominate", "post_add", new_tab=False)
if self.webserver.authenticated():
self.link_button("logout", "/logout", "logout", new_tab=False)
if self.webserver.login.authenticated():
Expand Down Expand Up @@ -344,13 +347,24 @@ def selection_callback(person: Person):
target="https://wiki.bitplan.com/index.php/Snapquery#nominate",
)
PersonView(person).classes("ml-auto bg-slate-100 rounded-md")
self.query_import_view = QueryImportView(
self, allow_importing_from_url=False
)
with ui.row().classes("w-full"):
self.query_import_view = QueryImportView(
self, allow_importing_from_url=False, person=person
)

self.person_selector = PersonSelector(
solution=self, selection_callback=selection_callback
)
with ui.column():
ui.label(text="Nominate your Query").classes("text-xl")
ui.link(
text="see the documentation for detailed information on the nomination procedure",
new_tab=True,
target="https://wiki.bitplan.com/index.php/Snapquery#nominate",
)
ui.label(
"Please identify yourself by entering or looking up a valid PID(Wikidata ID, ORCID, dblp)."
)
self.person_selector = PersonSelector(
solution=self, selection_callback=selection_callback
)

await self.setup_content_div(show)

Expand Down

0 comments on commit 9bc1bf7

Please sign in to comment.