Skip to content

Commit

Permalink
DATASHADES-325 / update chart view creation
Browse files Browse the repository at this point in the history
  • Loading branch information
mutantsan committed May 13, 2024
1 parent e5ad706 commit 3cbd10c
Show file tree
Hide file tree
Showing 20 changed files with 4,310 additions and 141 deletions.
5 changes: 1 addition & 4 deletions ckanext/charts/assets/css/charts.css
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
.charts-view .dataset-resource-form {
position: relative;
padding: 0;
}
.row.wrapper.charts-view .dataset-resource-form{position:relative;padding:0}
7 changes: 7 additions & 0 deletions ckanext/charts/assets/js/vendor/chart.min.js

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions ckanext/charts/assets/js/vendor/chartjs.min.js

Large diffs are not rendered by default.

20 changes: 0 additions & 20 deletions ckanext/charts/assets/js/vendor/charts.min.js

This file was deleted.

65 changes: 57 additions & 8 deletions ckanext/charts/assets/js/vendor/plotly.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion ckanext/charts/assets/webassets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ chartjs:
filter: rjsmin
output: ckanext-charts/%(version)s-chartjs.js
contents:
- js/vendor/charts.min.js
- js/vendor/chartjs.min.js

- js/charts-chartjs.js
- js/charts-render-chartjs.js
Expand Down
17 changes: 16 additions & 1 deletion ckanext/charts/chart_builders/plotly.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,17 @@ def __init__(self, df: pd.DataFrame, settings: dict[str, Any]) -> None:
self.settings = self.drop_view_fields(self.drop_empty_values(self.settings))

def drop_view_fields(self, settings: dict[str, Any]) -> dict[str, Any]:
view_fields = ["title", "notes", "engine", "type"]
view_fields = (
"title",
"description",
"engine",
"type",
"id",
"notes",
"package_id",
"resource_id",
"view_type"
)

return {k: v for k, v in settings.items() if k not in view_fields}

Expand Down Expand Up @@ -143,6 +153,11 @@ def get_validation_schema(self) -> dict[str, Any]:
def get_form_tabs(self) -> list[str]:
return ["General", "Structure", "Data", "Styles"]

def get_fields_by_tab(self, tab: str) -> list[dict[str, Any]]:
fields = self.get_expanded_form_fields()

return [field for field in fields if field["group"] == tab]

def column_field(self, choices: list[dict[str, str]]) -> dict[str, Any]:
return {
"field_name": "column",
Expand Down
21 changes: 16 additions & 5 deletions ckanext/charts/logic/validators.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ckan.plugins.toolkit as tk

from ckanext.charts.logic.schema import default_schema
from ckanext.charts import utils
from ckanext.charts.chart_builders.plotly import PlotlyBarForm


def float_validator(value):
Expand All @@ -12,10 +13,20 @@ def float_validator(value):

def validate_chart_extras(key, data, errors, context):
settings = data.get(("__extras",), {})
resource_id = data.get(("resource_id",)) or settings["resource_id"]

if "engine" not in settings:
settings, _ = tk.navl_validate(settings, default_schema(), {})
data[("__extras",)] = settings
# use plotly bar as default settings
if "engine" not in settings or "type" not in settings:
builder = PlotlyBarForm
else:
builder = utils.get_chart_form_builder(settings["engine"], settings["type"])

for k, v in data[("__extras",)].items():
settings, _ = tk.navl_validate(
settings, builder(resource_id).get_validation_schema(), {}
)

for k, v in settings.items():
data[(k,)] = v

for k, v in settings.pop("__extras", {}).items():
data[(k,)] = v
33 changes: 19 additions & 14 deletions ckanext/charts/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,28 +77,33 @@ def setup_template_variables(
"""

settings, _ = tk.navl_validate(
data_dict["resource_view"],
settings_schema(),
{},
data_dict["resource_view"], settings_schema(), {}
)

# settings = utils.settings_from_dict(data["__extras"])

data = {
"settings": settings,
"column_options": utils.get_column_options(data_dict["resource"]["id"]),
"resource_id": data_dict["resource"]["id"],
}

try:
form_builder = utils.get_chart_form_builder(
settings["engine"],
settings["type"],
)
except exception.ChartTypeNotImplementedError:
pass
# view create or edit
if "resource_view" in context:
try:
form_builder = utils.get_chart_form_builder(
settings["engine"], settings["type"]
)
except exception.ChartTypeNotImplementedError:
pass
else:
data.update({"form_builder": form_builder})
# view show
else:
data.update({"form_builder": form_builder})
data.update(
{
"chart": utils.build_chart_for_resource(
settings, data_dict["resource"]["id"]
)
}
)

return data

Expand Down
6 changes: 3 additions & 3 deletions ckanext/charts/templates/charts/charts_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
{% asset "charts/chartjs" %}
{% asset "charts/charts-css" %}

{% set builder = form_builder(resource_id) %}

{{ settings }}
<div class="charts-view--form">
{% snippet "charts/snippets/charts_form_fields.html", resource_id=resource_id, data=settings.__extras, form_fields=builder.get_expanded_form_fields(), errors=errors %}
{% snippet "charts/snippets/charts_form_fields.html", resource_id=resource_id, data=settings,
builder=form_builder(resource_id), errors=errors %}
</div>

<div class="charts-view--preview" id="charts-view--preview" style="min-height: 450px;"></div>
28 changes: 10 additions & 18 deletions ckanext/charts/templates/charts/snippets/charts_form_fields.html
Original file line number Diff line number Diff line change
@@ -1,33 +1,25 @@
<div class="charts-view--form">
{#}
<ul class="nav nav-pills mb-3" id="pills-tab" role="tablist">
{% for tab in builder.get_form_tabs() %}
<li class="nav-item" role="presentation">
<button class="nav-link {{ 'active' if loop.index == 1 }}" id="pills-{{ tab }}-tab" data-bs-toggle="pill"
data-bs-target="#pills-{{ tab }}" type="button" role="tab" aria-controls="pills-{{ tab }}"
aria-selected="true">{{ tab }}</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link {{ 'active' if loop.index == 1 }}" id="pills-{{ tab }}-tab" data-bs-toggle="pill"
data-bs-target="#pills-{{ tab }}" type="button" role="tab" aria-controls="pills-{{ tab }}"
aria-selected="true">{{ tab }}</button>
</li>
{% endfor %}
</ul>
{#}

{#}
<button class="btn btn-default" id="chart-clear" type="button" style="position: absolute; right: 0; top: 0;">{{ _("Clear") }}</button>
{#}

<div class="tab-content" id="pills-tabContent" style="display: block;"
hx-get="{{ h.url_for('charts_view.update_chart', resource_id=resource_id) }}" hx-trigger="load, submit, change"
hx-include="closest form" hx-target="#charts-view--preview">
{% snippet 'charts/snippets/render_fields.html', fields=form_fields, data=data, errors=errors %}

{#}
<div class="tab-pane fade show active" id="pills-general" role="tabpanel" aria-labelledby="pills-general-tab">
{{ form.input('title', id='field-title', label=_('Title'), placeholder=_('eg. My View'), value=data.title,
error=errors.title, classes=['control-full', 'control-large'], is_required=true) }}
{{ form.markdown('description', id='field-description', label=_('Description'), placeholder=_('eg.
Information about my view'), value=data.description, error=errors.description) }}
</div>
{#}
{% for tab in builder.get_form_tabs() %}
<div class="tab-pane fade show {{ 'active' if loop.index == 1 }}" id="pills-{{ tab }}" role="tabpanel" aria-labelledby="pills-{{ tab }}-tab">
{% snippet 'charts/snippets/render_fields.html', fields=builder.get_fields_by_tab(tab), data=data, errors=errors %}
</div>
{% endfor %}
</div>

<input type="hidden" name="resource_id" value="{{ resource_id }}">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="form-group control-full control-large">
<label for="vol">{{ _("Opacity")}}:</label>
<label for="vol">{{ _("Opacity")}}</label>
<div class="controls ">
<input type="range" id="opacity" name="opacity" min="{{ field.min or 0}}" max="{{ field.max or 1}}" step="{{ field.step or 0.1}}" value="{{ data[field.field_name] }}">
</div>
Expand Down
10 changes: 10 additions & 0 deletions ckanext/charts/theme/charts.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import "variables";
@import "mixins";

.row.wrapper.charts-view {

.dataset-resource-form {
position: relative;
padding: 0;
}
}
Empty file.
9 changes: 9 additions & 0 deletions ckanext/charts/theme/variables.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
$black: black;
$white: white;

$screen-xs-min: 480px;
$screen-sm-min: 768px;
$screen-md-min: 992px;
$screen-lg-min: 1200px;

$border: 1px solid var(--ap-border-color);
1 change: 1 addition & 0 deletions ckanext/charts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import ckan.plugins.toolkit as tk

import ckanext.charts.exception as exception
from ckanext.charts.chart_builders import ChartJSBuilder, PlotlyBuilder
from ckanext.charts.fetchers import DatastoreDataFetcher

Expand Down
61 changes: 24 additions & 37 deletions ckanext/charts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,19 @@

@charts.route("/api/utils/charts/<resource_id>/update-chart")
def update_chart(resource_id: str):
"""TODO: update_chart and update_form are very similar, consider refactoring"""

data = parse_params(tk.request.args)

if "engine" not in data or "type" not in data:
return tk.render("charts/snippets/unknown_chart.html")

try:
form_builder = utils.get_chart_form_builder(
data["engine"],
data["type"],
)
builder = _get_form_builder(data)
except exception.ChartTypeNotImplementedError:
return tk.render("charts/snippets/unknown_chart.html")

settings, _ = tk.navl_validate(
data,
form_builder(resource_id).get_validation_schema(),
{},
)
data, _ = tk.navl_validate(data, builder.get_validation_schema(), {})

try:
return tk.render_snippet(
f"charts/snippets/{settings['engine']}_chart.html",
{"chart": utils.build_chart_for_resource(settings, resource_id)},
f"charts/snippets/{data['engine']}_chart.html",
{"chart": utils.build_chart_for_resource(data, resource_id)},
)
except exception.ChartTypeNotImplementedError:
return tk.render("charts/snippets/unknown_chart.html")
Expand All @@ -47,34 +35,33 @@ def update_chart(resource_id: str):
@charts.route("/api/utils/charts/update-form")
def update_form():
data = parse_params(tk.request.args)
resource_id = tk.get_or_bust(data, "resource_id")

if "engine" not in data or "type" not in data:
return tk.render("charts/snippets/unknown_chart.html")

try:
form_builder = utils.get_chart_form_builder(
data["engine"],
data["type"],
)
builder = _get_form_builder(data)
except exception.ChartTypeNotImplementedError:
return tk.render("charts/snippets/unknown_chart.html")

builder = form_builder(resource_id)
data, errors = tk.navl_validate(data, builder.get_validation_schema(), {})

try:
return tk.render_snippet(
"charts/snippets/charts_form_fields.html",
{
"form_fields": builder.get_expanded_form_fields(),
"resource_id": resource_id,
"data": data,
"errors": errors,
},
)
except exception.ChartTypeNotImplementedError:
return tk.render("charts/snippets/unknown_form.html")
return tk.render_snippet(
"charts/snippets/charts_form_fields.html",
{
"builder": builder,
"resource_id": data["resource_id"],
"data": data,
"errors": errors,
},
)


def _get_form_builder(data: dict):
"""Get form builder for the given engine and chart type"""
if "engine" not in data or "type" not in data:
raise exception.ChartTypeNotImplementedError

builder = utils.get_chart_form_builder(data["engine"], data["type"])

return builder(data["resource_id"])


if plugin_loaded("admin_panel"):
Expand Down
30 changes: 30 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const { resolve } = require("path");
const { src, watch, dest } = require("gulp");
const if_ = require("gulp-if");
const sass = require('gulp-sass')(require('sass'));
const sourcemaps = require("gulp-sourcemaps");
const with_sourcemaps = () => !!process.env.DEBUG

const themeDir = resolve("ckanext/charts/theme");
const assetsDir = resolve("ckanext/charts/assets");

const build = () => {
return src(resolve(themeDir, "charts.scss"))
.pipe(if_(with_sourcemaps(), sourcemaps.init()))
.pipe(sass({ outputStyle: !!process.env.DEBUG ? 'expanded' : 'compressed' }).on('error', sass.logError))
.pipe(if_(with_sourcemaps(), sourcemaps.write()))
.pipe(dest(resolve(assetsDir, "css")))
}


const watchSource = () => {
watch(
themeDir + "/**/*.scss",
{ ignoreInitial: false },
build
)
}


exports.build = build;
exports.watch = watchSource;
Loading

0 comments on commit 3cbd10c

Please sign in to comment.