Skip to content

Commit

Permalink
Formats and svg fix
Browse files Browse the repository at this point in the history
  • Loading branch information
susuhahnml committed Oct 12, 2024
1 parent 7cd4f96 commit fae00c6
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 85 deletions.
142 changes: 90 additions & 52 deletions clingraph/clingo_utils.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
"""
Functions used for the clingo integration
"""

import json
import logging
import jsonschema
import base64
from clingo.control import Control
from clingo.script import enable_python
from clingo.symbol import String
from clingo.symbol import String, SymbolType
from jsonschema import validate
from .orm import Factbase
from .exceptions import InvalidSyntaxJSON, InvalidSyntax

enable_python()
log = logging.getLogger('custom')
log = logging.getLogger("custom")


class ClingraphContext:
"""
Provides avaliable python functions to be used in a visualization encoding
passed in the command line via option `--viz-encoding`
"""

def pos(self, x,y,scale=1):
def pos(self, x, y, scale=1):
"""
Position in the form of a tuple
Expand All @@ -31,8 +34,8 @@ def pos(self, x,y,scale=1):
(clingo.Symbol.String) position as a string of form (x,y)!
"""
scale = float(str(scale).strip('"'))
x = float(str(x))*scale
y = float(str(y))*scale
x = float(str(x)) * scale
y = float(str(y)) * scale
return String(f"{x},{y}!")

def concat(self, *args):
Expand All @@ -44,19 +47,27 @@ def concat(self, *args):
Returns:
(clingo.Symbol.String) The string concatenating all symbols
"""
return String(''.join([str(x).strip('"') for x in args]))
return String("".join([str(x).strip('"') for x in args]))

def format(self, s, *args):
"""
Formats the string with the given arguments
Args:
s (clingo.Symbol.String): The string to format, for example "{0} and {1}"
args: All symbols that can be accessed by the position starting in 0
args: All symbols that can be accessed by the position starting in 0.
If there is a single tuple as an argument, then its arguments are considered one by one.
Returns:
(clingo.Symbol.String) The string concatenating all symbols
"""
args_str = [str(v).strip('"') for v in args]
if (
len(args) == 1
and args[0].type == SymbolType.Function
and args[0].name == ""
):
args_str = [str(v).strip('"') for v in args[0].arguments]
else:
args_str = [str(v).strip('"') for v in args]
return String(s.string.format(*args_str))

def stringify(self, s, capitalize=False):
Expand All @@ -69,7 +80,7 @@ def stringify(self, s, capitalize=False):
(clingo.Symbol.String) The string
"""
val = str(s).strip('"')
val = val.replace('_',' ')
val = val.replace("_", " ")
if capitalize:
val = val[0].upper() + val[1:]
return String(val)
Expand All @@ -84,8 +95,7 @@ def cluster(self, s):
(clingo.Symbol.String) The string with the cluster name
"""
val = str(s).strip('"')
return String("cluster_"+val)

return String("cluster_" + val)

def html_escape(self, s):
"""
Expand All @@ -98,11 +108,26 @@ def html_escape(self, s):
"""

return String(
str(s).strip('"')
.replace('&', '&')
.replace('"', '"')
.replace('<', '&lt;')
.replace('>', '&gt;'))
str(s)
.strip('"')
.replace("&", "&amp;")
.replace('"', "&quot;")
.replace("<", "&lt;")
.replace(">", "&gt;")
)

def decodeB64(self, s):
"""
Decodes a base 64 string
Args:
s (clingo.Symbol.String): A string in base 64
Returns:
(clingo.Symbol.String): The string decoded
"""
s = str(s)
decoded = base64.b64decode(s).decode("ascii")
return String(decoded)

def svg_init(self, property_name, property_value):
"""
Expand Down Expand Up @@ -149,7 +174,7 @@ def svg(self, event, element, property_name, property_value):
element = str(element).strip('"')
property_name = str(property_name).strip('"')
property_value = str(property_value).strip('"')
s=String(f"{event}___{element}___{property_name}___{property_value} ")
s = String(f"{event}___{element}___{property_name}___{property_value} ")
return s

def color(self, option, opacity=None):
Expand All @@ -175,7 +200,7 @@ def color(self, option, opacity=None):
"yellow": "#FFAB00",
"danger": "#FF5630",
"red": "#FF5630",
"light": "#F4F5F7"
"light": "#F4F5F7",
}
if option not in colors:
return String("#000000")
Expand All @@ -195,26 +220,25 @@ def clinguin_fontname(self):

return String("Helvetica Neue")



def __getattr__(self, name):
# pylint: disable=import-outside-toplevel

import __main__

return getattr(__main__, name)


clingo_json_schema = {
"type": "object",
"required": ["Call","Result"],
"properties":{
"required": ["Call", "Result"],
"properties": {
"Call": {
"type" : "array",
"type": "array",
},
"Result":{
"Result": {
"type": "string",
}
}
},
},
}


Expand All @@ -235,14 +259,20 @@ def parse_clingo_json(json_str):
try:
j = json.loads(json_str.encode())
validate(instance=j, schema=clingo_json_schema)
if j['Result'] == 'UNSATISFIABLE':
log.warning("Passing an unsatisfiable instance in the JSON. This wont produce any results")
if j["Result"] == "UNSATISFIABLE":
log.warning(
"Passing an unsatisfiable instance in the JSON. This wont produce any results"
)

if len(j["Call"]) > 1:
log.warning("Calls will multiple theads from clingo are not supported by clingraph")
log.warning(
"Calls will multiple theads from clingo are not supported by clingraph"
)

if not "Witnesses" in j["Call"][0]:
log.warning("No Witnesses (stable models) in the JSON output, no output will be produced by clingraph")
log.warning(
"No Witnesses (stable models) in the JSON output, no output will be produced by clingraph"
)
witnesses = []
else:
witnesses = j["Call"][0]["Witnesses"]
Expand All @@ -255,10 +285,12 @@ def parse_clingo_json(json_str):
return models_prgs

except json.JSONDecodeError as e:
raise InvalidSyntax('The json can not be read.',str(e)) from None
raise InvalidSyntax("The json can not be read.", str(e)) from None
except jsonschema.exceptions.ValidationError as e:
raise InvalidSyntaxJSON('The json does not have the expected structure. Make sure you used the -outf=2 option in clingo.',str(e)) from None

raise InvalidSyntaxJSON(
"The json does not have the expected structure. Make sure you used the -outf=2 option in clingo.",
str(e),
) from None


def _get_json(args, stdin):
Expand Down Expand Up @@ -320,14 +352,15 @@ def _get_json(args, stdin):
elem.addEventListener(event, function() {
console.log(event)
local_event = event;
class_name = local_event + "_" + elem.id;
var children = Object.values(document.getElementsByClassName(class_name));
elem_id = elem.id.split(' ').join('');
class_name = local_event + "___" + elem_id;
var children = elements.filter(el => Array.from(el.classList).some(cls => cls.startsWith(class_name)));
children.forEach(c => {
c.classList.forEach(c_elem =>{
c_vals = c_elem.split('___')
if (c_vals.length == 4){
if (c_vals[0]==local_event){
if(c_vals[1]==elem.id){
if(c_vals[1]==elem_id){
property = c_vals[2]
property_val = c_vals[3]
c.style[property]=property_val
Expand All @@ -348,18 +381,20 @@ def _get_json(args, stdin):
</svg>
"""


def add_svg_interaction_to_string(s):
"""
Adds the svg interaction script to string representation of the svg image
Args:
s [str]: the svg string
"""
s = s.replace("#111111","currentcolor")
s = s.replace("#111111", "currentcolor")
s = s[:-8]
s+= SVG_SCRIPT
s += SVG_SCRIPT
return s


def add_svg_interaction(paths):
"""
Adds the svg interaction script to a list of svg files defined in the paths.
Expand All @@ -373,12 +408,13 @@ def add_svg_interaction(paths):
if not path_dic:
continue
for path in path_dic.values():
with open(path, 'r', encoding='UTF-8') as f:
with open(path, "r", encoding="UTF-8") as f:
s = f.read()
s = add_svg_interaction_to_string(s)
with open(path, 'w', encoding='UTF-8') as f:
with open(path, "w", encoding="UTF-8") as f:
f.write(s)


ADD_IDS_PRG = """
#defined edge/2.
#defined edge/1.
Expand All @@ -394,48 +430,50 @@ def add_svg_interaction(paths):
attr(graph,ID,id,ID):-graph(ID,_).
"""


def add_elements_ids(ctl):
"""
Adds a program to the control that will set the ids of the elements to the id attribute
Args:
ctl Clingo.Control: The clingo control object that is used
"""

ctl.add("base",[],ADD_IDS_PRG)
ctl.add("base", [], ADD_IDS_PRG)


def _get_fbs_from_encoding(args,stdin,prgs_from_json):
def _get_fbs_from_encoding(args, stdin, prgs_from_json):
"""
Obtains the factbase by running clingo to compute the stable models
of a visualization encoding
"""
fbs = []

def add_fb_model(m):
fbs.append(Factbase.from_model(m,
prefix=args.prefix,
default_graph=args.default_graph))
fbs.append(
Factbase.from_model(m, prefix=args.prefix, default_graph=args.default_graph)
)

cl_args = ["-n1"]
if args.seed is not None:
cl_args.append(f'--seed={args.seed}')
cl_args.append(f"--seed={args.seed}")
if prgs_from_json is not None:
for prg in prgs_from_json:
ctl = Control(cl_args)
ctl.load(args.viz_encoding.name)
ctl.add("base",[],prg)
if args.format == 'svg':
ctl.add("base", [], prg)
if args.format == "svg":
add_elements_ids(ctl)
ctl.ground([("base", [])],ClingraphContext())
ctl.ground([("base", [])], ClingraphContext())
ctl.solve(on_model=add_fb_model)
else:
ctl = Control(cl_args)
ctl.load(args.viz_encoding.name)
ctl.add("base",[],stdin)
if args.format == 'svg':
ctl.add("base", [], stdin)
if args.format == "svg":
add_elements_ids(ctl)
for f in args.files:
ctl.load(f.name)
ctl.ground([("base", [])],ClingraphContext())
ctl.ground([("base", [])], ClingraphContext())
ctl.solve(on_model=add_fb_model)

return fbs
37 changes: 37 additions & 0 deletions docs/clingraph/examples/color.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
### Graph coloring

**Features used:**
- Clingo integration
- Multi model
- Rendering
- Model selection
- View

```console
clingo examples/color/color.lp --outf=2 | clingraph --out=render --view --dir='out/color' --format=png --select-model=0 -log=info
```

![](https://raw.githubusercontent.com/potassco/clingraph/master/examples/color/default.png)

- `color.lp`
```prolog
node(1..6).
edge(
(1,2); (1,3); (1,4);
(2,4); (2,5); (2,6);
(3,4); (3,5);
(5,6)
).
color(red; green; blue).
{ assign(N, C) : color(C) } = 1 :- node(N).
:- edge((N, M)), assign(N, C), assign(M, C).
#show node/1.
#show edge/1.
#show attr(node, N, style, filled): node(N).
#show attr(node, N, color, C) : assign(N, C).
```
7 changes: 7 additions & 0 deletions docs/clingraph/examples/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```{include} ../../../examples/README.md
```

```{toctree}
color.md
```

Loading

0 comments on commit fae00c6

Please sign in to comment.