Skip to content

Commit e0777fb

Browse files
added AI changes
1 parent 2667b1c commit e0777fb

File tree

10 files changed

+602
-14
lines changed

10 files changed

+602
-14
lines changed

ckanext/scheming/assets/js/scheming-ai-suggestions.js

Lines changed: 327 additions & 0 deletions
Large diffs are not rendered by default.

ckanext/scheming/assets/resource.config

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ main = base/main
55
main =
66
js/scheming-multiple-text.js
77
js/scheming-repeating-subfields.js
8-
js/scheming-suggestions.js
8+
js/scheming-suggestions.js
9+
js/scheming-ai-suggestions.js

ckanext/scheming/assets/styles/scheming.css

Lines changed: 159 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ a.btn.btn-multiple-remove {
4444
.form-label label::after {
4545
content: none !important;
4646
}
47+
/* --- General Suggestion UI Enhancements --- */
4748
.control-group.has-suggestion .control-label {
4849
position: relative; /* For potential loader positioning */
4950
display: flex; /* To align label text and button nicely */
@@ -124,6 +125,7 @@ a.btn.btn-multiple-remove {
124125
}
125126

126127

128+
/* --- Custom Popover Modernization --- */
127129
.custom-suggestion-popover {
128130
max-width: 380px;
129131
width: auto;
@@ -133,15 +135,15 @@ a.btn.btn-multiple-remove {
133135
box-shadow: 0 8px 24px rgba(0,0,0,0.12), 0 4px 10px rgba(0,0,0,0.08);
134136
padding: 18px;
135137
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
136-
line-height: 1.5;
138+
line-height: 1.5; /* Improved line height for readability */
137139
}
138-
.custom-suggestion-popover:before { display: none; }
140+
.custom-suggestion-popover:before { display: none; } /* Removed default arrow */
139141

140142
.suggestion-popover-content strong {
141143
display: block;
142144
margin-bottom: 12px;
143145
color: #212529;
144-
font-size: 17px;
146+
font-size: 17px; /* Slightly larger title */
145147
font-weight: 600;
146148
}
147149

@@ -160,6 +162,7 @@ a.btn.btn-multiple-remove {
160162
overflow-y: auto;
161163
}
162164

165+
/* Formula Toggle & Display */
163166
.formula-toggle { margin: 10px 0 5px 0; }
164167
.formula-toggle-btn {
165168
background: none; border: none; color: #007bff; font-size: 13px;
@@ -333,8 +336,160 @@ input.formula-apply-error, textarea.formula-apply-error, select.formula-apply-er
333336
border-radius: 3px;
334337
}
335338

339+
/* Fix for colon issue (from original CSS) */
340+
.form-label label::after,
341+
.control-label-with-suggestion label:after,
342+
label:after,
343+
.control-label-with-suggestion .form-label::after {
344+
content: none !important;
345+
}
336346

347+
/* Ensure the icon for preformulated field is vertically aligned */
337348
#dataset-edit div[class*="control-group"] > label > span.preformulated-icon > svg {
338-
vertical-align: middle;
349+
vertical-align: middle; /* Better alignment */
339350
margin-left: 5px;
351+
}
352+
353+
354+
355+
/* Add to /ckanext/scheming/assets/styles/scheming.css */
356+
357+
/* AI Suggestion button on fields */
358+
.ai-suggestion-btn {
359+
display: inline-flex;
360+
align-items: center;
361+
justify-content: center;
362+
vertical-align: middle;
363+
margin-left: 8px;
364+
border: none;
365+
background-color: transparent;
366+
color: #4a86e8;
367+
width: 20px;
368+
height: 20px;
369+
padding: 0;
370+
cursor: pointer;
371+
position: relative;
372+
top: -1px;
373+
}
374+
375+
.ai-suggestion-btn:hover, .ai-suggestion-btn:focus {
376+
transform: scale(1.2);
377+
outline: none;
378+
}
379+
380+
.ai-suggestion-btn svg {
381+
height: 30px;
382+
fill: #2f9eff;
383+
stroke: #0040ff;
384+
}
385+
386+
387+
/* AI Suggestion popover */
388+
.ai-suggestion-popover {
389+
max-width: 350px;
390+
position: absolute;
391+
background-color: #fff;
392+
border: none;
393+
border-radius: 12px;
394+
box-shadow: 0 8px 32px rgba(0,0,0,0.12), 0 4px 8px rgba(0,0,0,0.08);
395+
padding: 16px;
396+
z-index: 1000;
397+
}
398+
399+
.ai-suggestion-popover:before {
400+
content: '';
401+
display: block;
402+
position: absolute;
403+
width: 12px;
404+
height: 12px;
405+
background: #fff;
406+
transform: rotate(45deg);
407+
top: -6px;
408+
left: 15px;
409+
box-shadow: -2px -2px 5px rgba(0,0,0,0.05);
410+
}
411+
412+
.ai-suggestion-header {
413+
display: flex;
414+
justify-content: space-between;
415+
align-items: center;
416+
margin-bottom: 10px;
417+
}
418+
419+
.ai-suggestion-header strong {
420+
color: #4a86e8;
421+
font-size: 16px;
422+
}
423+
424+
.ai-suggestion-source {
425+
font-size: 12px;
426+
color: #666;
427+
font-style: italic;
428+
}
429+
430+
/* AI Suggestion context section */
431+
.ai-suggestion-context-section {
432+
margin-top: 15px;
433+
padding-top: 15px;
434+
border-top: 1px solid #eee;
435+
}
436+
437+
.ai-suggestion-context-label {
438+
font-size: 13px;
439+
color: #333;
440+
margin-bottom: 8px;
441+
}
442+
443+
.ai-suggestion-context {
444+
width: 100%;
445+
min-height: 60px;
446+
padding: 8px;
447+
border: 1px solid #ddd;
448+
border-radius: 4px;
449+
margin-bottom: 10px;
450+
font-size: 13px;
451+
resize: vertical;
452+
}
453+
454+
.ai-suggestion-regenerate-btn {
455+
display: flex;
456+
align-items: center;
457+
gap: 6px;
458+
background-color: #4a86e8;
459+
color: white;
460+
border: none;
461+
border-radius: 4px;
462+
padding: 8px 12px;
463+
cursor: pointer;
464+
font-weight: 600;
465+
transition: all 0.2s ease;
466+
font-size: 13px;
467+
}
468+
469+
.ai-suggestion-regenerate-btn:hover {
470+
background-color: #3a76d8;
471+
transform: translateY(-2px);
472+
box-shadow: 0 4px 8px rgba(74, 134, 232, 0.3);
473+
}
474+
475+
.ai-suggestion-regenerate-btn.regenerating {
476+
background-color: #999;
477+
transform: none;
478+
box-shadow: none;
479+
cursor: wait;
480+
}
481+
482+
.ai-suggestion-regenerate-btn svg {
483+
width: 16px;
484+
height: 16px;
485+
stroke: #fff;
486+
}
487+
488+
/* AI Suggestion apply button styling */
489+
.ai-suggestion-apply-btn {
490+
background-color: #4a86e8 !important; /* Override the regular suggestion button color */
491+
}
492+
493+
.ai-suggestion-apply-btn:hover {
494+
background-color: #3a76d8 !important; /* Darker on hover */
340495
}

ckanext/scheming/assets/webassets.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
scheming_css:
2+
filter: cssrewrite
23
output: ckanext-scheming/%(version)s_scheming_css.css
34
contents:
45
- styles/scheming.css
@@ -28,4 +29,13 @@ suggestions:
2829
preload:
2930
- base/main
3031
contents:
31-
- js/scheming-suggestions.js
32+
- js/scheming-suggestions.js
33+
34+
ai-suggestions:
35+
filter: rjsmin
36+
output: ckanext-scheming/%(version)s_scheming-ai-suggestions.js
37+
contents:
38+
- js/scheming-ai-suggestions.js
39+
extra:
40+
preload:
41+
- base/main

ckanext/scheming/helpers.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,3 +537,70 @@ def is_preformulated_field(field):
537537
This helper returns True only if the field has a 'formula' key with a non-empty value
538538
"""
539539
return bool(field.get('formula', False))
540+
541+
542+
543+
544+
@helper
545+
def scheming_has_ai_suggestion_fields(schema):
546+
"""
547+
Check if the schema has any fields that support AI suggestions
548+
549+
Args:
550+
schema: The schema dictionary
551+
552+
Returns:
553+
bool: True if any field supports AI suggestions, False otherwise
554+
"""
555+
if not schema:
556+
return False
557+
558+
if 'dataset_fields' in schema:
559+
for field in schema['dataset_fields']:
560+
if field.get('ai_suggestion', False):
561+
return True
562+
563+
if 'resource_fields' in schema:
564+
for field in schema['resource_fields']:
565+
if field.get('ai_suggestion', False):
566+
return True
567+
568+
return False
569+
570+
@helper
571+
def scheming_field_supports_ai_suggestion(field):
572+
"""
573+
Check if a field supports AI suggestions
574+
575+
Args:
576+
field: The field dictionary from the schema
577+
578+
Returns:
579+
bool: True if the field supports AI suggestions, False otherwise
580+
"""
581+
return field.get('ai_suggestion', False)
582+
583+
@helper
584+
def scheming_get_ai_suggestion_value(field_name, data=None):
585+
"""
586+
Get a dummy AI suggestion value for a field
587+
588+
Args:
589+
field_name: Name of the field
590+
data: Form data dictionary (unused in dummy implementation)
591+
592+
Returns:
593+
str: A dummy AI suggestion value
594+
"""
595+
suggestions = {
596+
'title': "AI-Generated Dataset Title",
597+
'notes': "This dataset contains various metrics and measurements collected from multiple sources. The data is structured in a tabular format with columns for dates, locations, and values.",
598+
'tag_string': "ai, data, analytics, metrics",
599+
'spatial_extent': "POLYGON((-122.4194 37.7749, -122.4194 37.8049, -122.3894 37.8049, -122.3894 37.7749, -122.4194 37.7749))",
600+
'name': "Resource Data File",
601+
'description': "This dataset contains comprehensive non-emergency service request records from New York City's 311 system, spanning from [LIKELY_START_DATE] to [LIKELY_END_DATE]. Each record represents a citizen-reported issue or service request submitted through the 311 call center, mobile app, or web portal. The data includes detailed categorization of request types across multiple domains including noise complaints, housing conditions, street/sidewalk issues, sanitation problems, and transportation concerns. Each record contains temporal data (created date, closed date, resolution time), spatial information (borough, community district, census tract, neighborhood, latitude/longitude coordinates), and administrative details (agency assigned, current status, resolution description)."
602+
}
603+
604+
return suggestions.get(field_name, f"AI-Generated suggestion for {field_name}")
605+
606+

ckanext/scheming/plugins.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import os
44
import inspect
55
import logging
6-
from functools import wraps
6+
from functools import wraps
77

88
import six
99
import yaml

ckanext/scheming/suggestion_test_schema.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,23 @@ dataset_fields:
2020
label: Description
2121
form_snippet: markdown.html
2222
form_placeholder: eg. Some useful notes/blurb about the data
23-
suggestion_formula: Latitudinal range {{ (dpps[dpp.LAT_FIELD].stats.max|float) - (dpps[dpp.LAT_FIELD].stats.min|float) }} {{"the quick brown fox"|truncate_with_ellipsis(5)}}
23+
suggestion_formula: Latitudinal range {{ (dpps[dpp.LAT_FIELD].stats.max|float) - (dpps[dpp.LAT_FIELD].stats.min|float) }}
2424

2525
- field_name: spatial_extent
2626
label: Spatial Extent
2727
form_snippet: markdown.html
28-
suggestion_formula: '{{ spatial_extent_wkt(resource.LONGITUDE.stats.min, resource.LATITUDE.stats.min, resource.LONGITUDE.stats.max, resource.LATITUDE.stats.max) }}'
28+
suggestion_formula: '{{ spatial_extent_wkt() }}'
2929

30-
- field_name: test_field
31-
label: Test Field
30+
- field_name: description
31+
label: Description AI
3232
form_snippet: markdown.html
33-
formula: 'Test field value:{{package.author}}: {{package.author_email}}'
33+
ai_suggestion: true
3434

3535
- field_name: frequency_info
3636
label: Frequency Information
3737
form_snippet: markdown.html
3838
form_placeholder: eg. Daily, Weekly, Monthly, Annually, etc.
39-
formula: '{{ get_frequency_top_values("LATITUDE") }}'
39+
formula: '{{ get_frequency_top_values("Agency") }}'
4040

4141
- field_name: tag_string
4242
label: Tags

ckanext/scheming/templates/scheming/form_snippets/markdown.html

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
11
{% import 'macros/form.html' as form %}
22
{% include 'scheming/snippets/suggestions_asset.html' %}
3+
{% include 'scheming/snippets/ai_suggestions_asset.html' %}
34

4-
{# Check if this is a preformulated field #}
5+
{# Check if this is a preformulated field (i.e., has a 'formula') #}
56
{% set is_preformulated = h.is_preformulated_field(field) %}
7+
{% set supports_ai_suggestion = h.scheming_field_supports_ai_suggestion(field) %}
68

79
{# Create a custom label with suggestion button(s) and icons #}
810
{% set suggestion = h.scheming_field_suggestion(field) %}
911
{% set label_text = h.scheming_language_text(field.label) %}
1012
{% set label_with_extras %}
13+
<div class="control-label-with-suggestion">
1114
<span class="form-label">{{ label_text }}:</span>
1215
{%- if suggestion -%}
1316
{%- snippet 'scheming/snippets/suggestion_button.html', field=field, data=data -%}
1417
{%- endif -%}
18+
{%- if supports_ai_suggestion -%}
19+
<button type="button" class="ai-suggestion-btn" title="AI Suggestion Available" data-module="scheming-ai-suggestions" data-field-name="{{ field.field_name }}" data-suggestion-value="{{ h.scheming_get_ai_suggestion_value(field.field_name) }}" data-suggestion-source="AI Generated">
20+
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-5.0 -10.0 110.0 135.0"> <path d="m49.988 1.7656c-2.6641 0-5.3281 0.14844-7.9727 0.44531-1.875 0.21094-3.5156 1.1562-4.5 2.5938-0.82031 1.1953-1.0938 2.5938-0.78125 3.9609-11.805 3.8203-20.672 13.5-23.727 25.211h-2.3359c-4.9141 0-8.9141 4-8.9141 8.9141v3.875c0 4.9141 4 8.9141 8.9141 8.9141h2.75c2.4297 5.5859 7.4844 9.7812 13.594 11.023-0.41406 0.22656-0.82031 0.46875-1.2188 0.72656h-11.141c-7.1172 0-12.898 5.7891-12.898 12.898v7.0703c0 0.75781 0.60938 1.3672 1.3672 1.3672h2.6797c-0.054688 0.45312-0.085938 0.89844-0.085938 1.3438 0 2.7812 0.97656 5.4922 2.7422 7.6328 0.25781 0.3125 0.64844 0.49219 1.0547 0.49219h80.961c0.40625 0 0.79688-0.17969 1.0547-0.49219 1.7656-2.1406 2.7422-4.8516 2.7422-7.6328 0-0.44531-0.03125-0.89844-0.085938-1.3438h2.6875c0.75781 0 1.3672-0.60938 1.3672-1.3672v-7.0703c-0.023438-7.1016-5.8125-12.891-12.922-12.891h-11.141c-0.39844-0.25781-0.80469-0.5-1.2188-0.72656 6.1094-1.2422 11.164-5.4375 13.594-11.023h2.75c4.9141 0 8.9141-4 8.9141-8.9141v-3.875c0-3.5312-2.0703-6.5781-5.0469-8.0234v-14.211c2.2812-0.60937 3.9688-2.6797 3.9688-5.1484 0-2.9375-2.3906-5.3359-5.3359-5.3359-2.9453 0-5.3359 2.3906-5.3359 5.3359 0 2.4688 1.6875 4.5469 3.9688 5.1484v13.398c-0.375-0.046875-0.75-0.078125-1.1328-0.078125h-2.3359c-3.0547-11.711-11.93-21.391-23.727-25.211 0.30469-1.3672 0.039062-2.7656-0.78125-3.9688-0.98438-1.4375-2.625-2.3828-4.5-2.5938-2.6445-0.29688-5.3086-0.44531-7.9727-0.44531zm-0.011719 2.7344c2.5625 0 5.125 0.14062 7.6641 0.42969 1.0859 0.125 2.0156 0.64062 2.5547 1.4219 0.28125 0.41406 0.57813 1.0781 0.35938 1.9062l-0.83594 3.1875c-0.52344 1.9922-2.7109 3.4375-5.2031 3.4375h-9.0703c-2.4922 0-4.6797-1.4453-5.2031-3.4375l-0.55469-2.1172-0.28125-1.0703c-0.21875-0.82812 0.070312-1.4922 0.35156-1.9062 0.53906-0.78125 1.4688-1.3047 2.5547-1.4219 2.5469-0.28906 5.1016-0.42969 7.6641-0.42969zm-12.578 6.9219 0.1875 0.71875c0.84375 3.2266 4.0781 5.4766 7.8516 5.4766h9.0703c3.7734 0 7-2.25 7.8516-5.4766l0.1875-0.71875c13.516 4.4453 22.836 17.227 22.836 31.562v5.1719c0 8.9297-7.2656 16.203-16.203 16.203h-38.414c-8.9297 0-16.203-7.2656-16.203-16.203v-5.1719c0-14.328 9.3203-27.117 22.836-31.562zm54.406 1.4922c1.4375 0 2.6016 1.1641 2.6016 2.6016s-1.1641 2.6016-2.6016 2.6016-2.6016-1.1719-2.6016-2.6016 1.1641-2.6016 2.6016-2.6016zm-50.727 13.266c-10.25 0-18.586 8.3359-18.586 18.586v2.7031c0 5.5781 4.5391 10.109 10.109 10.109h34.742c5.5781 0 10.109-4.5312 10.109-10.109v-2.7031c0-10.25-8.3359-18.586-18.586-18.586zm0.007813 2.7344h17.789c8.7422 0 15.852 7.1094 15.852 15.852v2.7031c0 4.0703-3.3125 7.375-7.375 7.375h-34.742c-4.0703 0-7.375-3.3047-7.375-7.375v-2.7031c0-8.7422 7.1094-15.852 15.852-15.852zm-30.438 7.8047h1.7422c-0.36719 2.0469-0.5625 4.1406-0.5625 6.2656v5.1719c0 1.6562 0.21875 3.2656 0.61719 4.7969h-1.7969c-3.4062 0-6.1797-2.7734-6.1797-6.1797v-3.875c0-3.4062 2.7734-6.1797 6.1797-6.1797zm76.906 0h1.7422c3.4062 0 6.1797 2.7734 6.1797 6.1797v3.875c0 3.4062-2.7734 6.1797-6.1797 6.1797h-1.7969c0.39844-1.5312 0.61719-3.1406 0.61719-4.7969v-5.1719c0-2.125-0.19531-4.2188-0.5625-6.2656zm-49.684 5.3047c-1.668 0-3.332 0.63281-4.5977 1.8984-0.53125 0.53125-0.53125 1.3984 0 1.9297s1.3984 0.53125 1.9297 0c1.4688-1.4688 3.8672-1.4688 5.3359 0 0.26563 0.26563 0.61719 0.39844 0.96875 0.39844 0.35156 0 0.70313-0.13281 0.96875-0.39844 0.53125-0.53125 0.53125-1.3984 0-1.9297-1.2695-1.2656-2.9375-1.8984-4.6055-1.8984zm24.211 0c-1.668 0-3.332 0.63281-4.5977 1.8984-0.53125 0.53125-0.53125 1.3984 0 1.9297s1.3984 0.53125 1.9297 0c1.4688-1.4688 3.8672-1.4688 5.3359 0 0.26562 0.26563 0.61719 0.39844 0.96875 0.39844s0.70312-0.13281 0.96875-0.39844c0.53125-0.53125 0.53125-1.3984 0-1.9297-1.2695-1.2656-2.9375-1.8984-4.6055-1.8984zm-25.941 25.062h27.68c4.5625 0 8.9375 1.9297 12.008 5.3047 3.0625 3.375 4.5859 7.9062 4.1562 12.445l-0.99219 10.68h-58.031l-0.99219-10.68c-0.42188-4.5391 1.0938-9.0781 4.1641-12.445 3.0703-3.3672 7.4453-5.3047 12.008-5.3047zm-21.508 3.0938h7.8359c-0.125 0.125-0.24219 0.25-0.36719 0.375-3.5859 3.9375-5.3594 9.2344-4.8672 14.539l0.085937 0.95312h-12.859v-5.7031h0.007812c0-5.6094 4.5625-10.164 10.164-10.164zm62.836 0h7.8359c5.6094 0 10.164 4.5625 10.164 10.164v5.7031h-12.852l0.085937-0.95312c0.49219-5.3047-1.2812-10.602-4.8672-14.539-0.11719-0.13281-0.24219-0.25-0.36719-0.375zm-39.812 6.5625c-3.9297 0-7.125 3.1953-7.125 7.125 0 3.9297 3.1953 7.125 7.125 7.125h24.641c3.9297 0 7.125-3.1953 7.125-7.125 0-3.9297-3.1953-7.125-7.125-7.125zm0 2.7422h24.641c2.4219 0 4.3906 1.9688 4.3906 4.3906s-1.9688 4.3906-4.3906 4.3906h-24.641c-2.4219 0-4.3906-1.9688-4.3906-4.3906s1.9688-4.3906 4.3906-4.3906zm-29.102 9.2969h9.0391l0.625 6.7344h-8.0469c-1.125-1.5625-1.7344-3.4531-1.7344-5.3906 0-0.44531 0.046875-0.89062 0.11719-1.3438zm73.805 0h9.0391c0.070312 0.45312 0.11719 0.89844 0.11719 1.3438 0 1.9375-0.60938 3.8281-1.7344 5.3906h-8.0469z"/> </svg>
21+
</button>
22+
{%- endif -%}
1523
{%- if is_preformulated -%}
1624
<span class="preformulated-icon" title="This field is automatically populated">
1725
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path> </svg>
1826
</span>
1927
{%- endif -%}
28+
</div>
2029
{% endset %}
2130

2231
{# Prepare attributes for the form macro #}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{% asset 'ckanext-scheming/ai-suggestions' %}

0 commit comments

Comments
 (0)