Skip to content

Commit

Permalink
Support ascii graph in Jupyter.
Browse files Browse the repository at this point in the history
  • Loading branch information
EvgSkv committed Jul 18, 2023
1 parent 9b49a2e commit 186b9c9
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 18 deletions.
41 changes: 36 additions & 5 deletions colab_logica.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@

"""Library for using Logica in CoLab."""

import getpass
import json

from .common import color
from .common import concertina_lib

from .compiler import functors
from .compiler import rule_translate
from .compiler import universe

from .type_inference.research import infer

import IPython

from IPython.core.magic import register_cell_magic
Expand Down Expand Up @@ -70,6 +75,8 @@

PREAMBLE = None

DISPLAY_MODE = 'colab' # or colab-text


def SetPreamble(preamble):
global PREAMBLE
Expand All @@ -83,6 +90,21 @@ def SetDbConnection(connection):
global DB_CONNECTION
DB_CONNECTION = connection

def ConnectToPostgres(mode='interactive'):
import psycopg2
if mode == 'interactive':
print('Please enter PostgreSQL connection config in JSON format.')
print('Example:')
print('{"host": "myhost", "database": "megadb", '
'"user": "myuser", "password": "42"}')
connection_str = getpass.getpass()
elif mode == 'environment':
connection_str = os.environ.get('LOGICA_PSQL_CONNECTION')
else:
assert False, 'Unknown mode:' + mode
connection_json = json.loads(connection_str)
SetDbConnection(psycopg2.connect(**connection_json))

def EnsureAuthenticatedUser():
global USER_AUTHENTICATED
global PROJECT
Expand Down Expand Up @@ -142,12 +164,17 @@ def RunSQL(sql, engine, connection=None, is_final=False):
client = bigquery.Client(project=PROJECT)
return client.query(sql).to_dataframe()
elif engine == 'psql':
# Sorry, this is not looking good.
from sqlalchemy import text
if is_final:
return pandas.read_sql(text(sql), connection)
cursor = connection.cursor()
cursor.execute(sql)
rows = cursor.fetchall()
df = pandas.DataFrame(
rows, columns=[d[0] for d in cursor.description])
return df
else:
return connection.execute(text(sql))
cursor = connection.cursor()
cursor.execute(sql)
connection.commit()
elif engine == 'sqlite':
try:
if is_final:
Expand Down Expand Up @@ -215,6 +242,9 @@ def Logica(line, cell, run_query):
except rule_translate.RuleCompileException as e:
e.ShowMessage()
return
except infer.TypeErrorCaughtException as e:
e.ShowMessage()
return

engine = program.annotations.Engine()

Expand Down Expand Up @@ -273,7 +303,8 @@ def Logica(line, cell, run_query):
'for now.')

result_map = concertina_lib.ExecuteLogicaProgram(
executions, sql_runner=sql_runner, sql_engine=engine)
executions, sql_runner=sql_runner, sql_engine=engine,
display_mode=DISPLAY_MODE)

for idx, predicate in enumerate(predicates):
t = result_map[predicate]
Expand Down
35 changes: 32 additions & 3 deletions common/concertina_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@

try:
import graphviz
except:
print('Could not import graphviz tools in Concertina.')

try:
from IPython.display import HTML
from IPython.display import display
from IPython.display import update_display
except:
print('Could not import CoLab tools in Concertina.')
print('Could not import IPython in Concertina.')

if '.' not in __package__:
from common import graph_art
Expand Down Expand Up @@ -75,7 +80,7 @@ def __init__(self, config, engine, display_mode='colab'):
self.all_actions = {a["name"] for a in self.config}
self.complete_actions = set()
self.running_actions = set()
assert display_mode in ('colab', 'terminal'), (
assert display_mode in ('colab', 'terminal', 'colab-text'), (
'Unrecognized display mode: %s' % display_mode)
self.display_mode = display_mode
self.display_id = self.GetDisplayId()
Expand Down Expand Up @@ -137,7 +142,14 @@ def AsNodesAndEdges(self):
"""Nodes and edges to display in terminal."""
def ColoredNode(node):
if node in self.running_actions:
return '\033[1m\033[93m' + node + '\033[0m'
if self.display_mode == 'terminal':
return '\033[1m\033[93m' + node + '\033[0m'
elif self.display_mode == 'colab-text':
return (
'<b>' + node + '</b>'
)
else:
assert False, self.display_mode
else:
return node
nodes = []
Expand All @@ -150,11 +162,24 @@ def ColoredNode(node):
edges.append([prerequisite_node, a_node])
return nodes, edges

def StateAsSimpleHTML(self):
style = ';'.join([
'border: 1px solid rgba(0, 0, 0, 0.3)',
'width: fit-content;',
'padding: 20px',
'border-radius: 5px',
'box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2)'])
return HTML('<div style="%s"><pre>%s</pre></div>' % (
style, self.AsTextPicture(updating=False)))

def Display(self):
if self.display_mode == 'colab':
display(self.AsGraphViz(), display_id=self.display_id)
elif self.display_mode == 'terminal':
print(self.AsTextPicture(updating=False))
elif self.display_mode == 'colab-text':
display(self.StateAsSimpleHTML(),
display_id=self.display_id)
else:
assert 'Unexpected mode:', self.display_mode

Expand All @@ -163,6 +188,10 @@ def UpdateDisplay(self):
update_display(self.AsGraphViz(), display_id=self.display_id)
elif self.display_mode == 'terminal':
print(self.AsTextPicture(updating=True))
elif self.display_mode == 'colab-text':
update_display(
self.StateAsSimpleHTML(),
display_id=self.display_id)
else:
assert 'Unexpected mode:', self.display_mode

Expand Down
2 changes: 2 additions & 0 deletions compiler/dialect_libraries/psql_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@
"ARRAY_AGG({value} order by {arg})",
{arg: a.arg, value: a.value});
RecordAsJson(r) = SqlExpr(
"ROW_TO_JSON({r})", {r:});
"""
138 changes: 138 additions & 0 deletions examples/more/Simple_Postgres.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"id": "7e89b446",
"metadata": {},
"source": [
"# Example of running Postgres with text Pipeline overview"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "58f2b0db",
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"from logica import colab_logica\n",
"colab_logica.ConnectToPostgres('interactive')\n",
"colab_logica.DISPLAY_MODE = 'colab-text'"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "82c02eb6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Query is stored at \u001b[1mGreeting_sql\u001b[0m variable.\n"
]
},
{
"data": {
"text/html": [
"<div style=\"border: 1px solid rgba(0, 0, 0, 0.3);width: fit-content;;padding: 20px;border-radius: 5px;box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2)\"><pre> ▚ Greeting</pre></div>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Running predicate: Greeting (0 seconds)\n",
"The following table is stored at \u001b[1mGreeting\u001b[0m variable.\n"
]
},
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>col0</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>Hello world!</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" col0\n",
"0 Hello world!"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" \n"
]
}
],
"source": [
"%%logica Greeting\n",
"\n",
"@Engine(\"psql\");\n",
"\n",
"Greeting(\"Hello world!\");"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
15 changes: 8 additions & 7 deletions logica.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ def main(argv):
print('Not enough arguments. Run \'logica help\' for help.',
file=sys.stderr)
return 1
predicates = argv[3]

if argv[1] == '-':
filename = '/dev/stdin'
Expand All @@ -158,6 +159,13 @@ def main(argv):
if not os.path.exists(filename):
print('File not found: %s' % filename, file=sys.stderr)
return 1

if command == 'run_in_terminal':
from tools import run_in_terminal
artistic_table = run_in_terminal.Run(filename, predicates)
print(artistic_table)
return

program_text = open(filename).read()

try:
Expand Down Expand Up @@ -187,8 +195,6 @@ def main(argv):
type_error_checker.CheckForError()
return 0

predicates = argv[3]

user_flags = ReadUserFlags(parsed_rules, argv[4:])

predicates_list = predicates.split(',')
Expand Down Expand Up @@ -265,11 +271,6 @@ def main(argv):
assert False, 'Unknown engine: %s' % engine
print(o.decode())

if command == 'run_in_terminal':
from tools import run_in_terminal
artistic_table = run_in_terminal.Run(filename, predicate)
print(artistic_table)


def run_main():
"""Run main function with system arguments."""
Expand Down
Loading

0 comments on commit 186b9c9

Please sign in to comment.