Set subject dynamically when creating new tasks, and make some minor improvments to the automations UI

This commit is contained in:
sabaimran
2024-05-01 13:54:59 +05:30
parent d1b2037676
commit c30ba2e551
2 changed files with 62 additions and 32 deletions

View File

@@ -5,12 +5,11 @@
<h2 class="section-title"> <h2 class="section-title">
<img class="card-icon" src="/static/assets/icons/automation.svg?v={{ khoj_version }}" alt="Automate"> <img class="card-icon" src="/static/assets/icons/automation.svg?v={{ khoj_version }}" alt="Automate">
<span class="card-title-text">Automate</span> <span class="card-title-text">Automate</span>
<div class="instructions"> <!-- <div class="instructions">
<a href="https://docs.khoj.dev/features/automations">ⓘ Help</a> <a href="https://docs.khoj.dev/features/automations">ⓘ Help</a>
</div> </div> -->
</h2> </h2>
<div class="section-body"> <div class="section-body">
<h4>Automations</h4>
<button id="create-automation-button" type="button" class="positive-button"> <button id="create-automation-button" type="button" class="positive-button">
<img class="automation-action-icon" src="/static/assets/icons/new.svg" alt="Automations"> <img class="automation-action-icon" src="/static/assets/icons/new.svg" alt="Automations">
<span id="create-automation-button-text">Create</span> <span id="create-automation-button-text">Create</span>
@@ -34,6 +33,7 @@
} }
div#automations { div#automations {
margin-bottom: 12px; margin-bottom: 12px;
grid-template-columns: 1fr;
} }
button.negative-button { button.negative-button {
background-color: gainsboro; background-color: gainsboro;
@@ -44,6 +44,35 @@
.positive-button:hover { .positive-button:hover {
background-color: var(--summer-sun); background-color: var(--summer-sun);
} }
div.automation-buttons {
display: grid;
grid-gap: 8px;
grid-template-columns: 1fr 3fr;
}
button.save-automation-button {
background-color: var(--summer-sun);
}
button.save-automation-button:hover {
background-color: var(--primary-hover);
}
div.new-automation {
background-color: #f9f9f9;
border-radius: 10px;
box-shadow: 0 4px 6px 0 hsla(0, 0%, 0%, 0.2);
padding: 20px;
margin-bottom: 20px;
transition: box-shadow 0.3s ease, transform 0.3s ease;
}
div.new-automation:hover {
box-shadow: 0 10px 15px 0 hsla(0, 0%, 0%, 0.1);
transform: translateY(-5px);
}
</style> </style>
<script> <script>
function deleteAutomation(automationId) { function deleteAutomation(automationId) {
@@ -84,13 +113,12 @@
let automationEl = document.createElement("div"); let automationEl = document.createElement("div");
automationEl.innerHTML = ` automationEl.innerHTML = `
<div class="card automation" id="automation-card-${automationId}"> <div class="card automation" id="automation-card-${automationId}">
<label for="subject">Subject</label>
<input type="text" <input type="text"
id="automation-subject-${automationId}" id="automation-subject-${automationId}"
name="subject" name="subject"
data-original="${automation.subject}" data-original="${automation.subject}"
value="${automation.subject}"> value="${automation.subject}">
<label for="query-to-run">Query to Run</label> <label for="query-to-run">Your automation</label>
<textarea id="automation-queryToRun-${automationId}" <textarea id="automation-queryToRun-${automationId}"
data-original="${automation.query_to_run}" data-original="${automation.query_to_run}"
name="query-to-run">${automation.query_to_run}</textarea> name="query-to-run">${automation.query_to_run}</textarea>
@@ -102,12 +130,14 @@
data-original="${automation.schedule}" data-original="${automation.schedule}"
title="${automationNextRun}" title="${automationNextRun}"
value="${automation.schedule}"> value="${automation.schedule}">
<button type="button" <div class="automation-buttons">
class="save-automation-button positive-button" <button type="button"
id="save-automation-button-${automationId}">Save</button> class="delete-automation-button negative-button"
<button type="button" id="delete-automation-button-${automationId}">Delete</button>
class="delete-automation-button negative-button" <button type="button"
id="delete-automation-button-${automationId}">Delete</button> class="save-automation-button positive-button"
id="save-automation-button-${automationId}">Save</button>
</div>
<div id="automation-success-${automationId}" style="display: none;"></div> <div id="automation-success-${automationId}" style="display: none;"></div>
</div> </div>
`; `;
@@ -155,14 +185,13 @@
} }
async function saveAutomation(automationId, create=false) { async function saveAutomation(automationId, create=false) {
const subject = encodeURIComponent(document.getElementById(`automation-subject-${automationId}`).value);
const queryToRun = encodeURIComponent(document.getElementById(`automation-queryToRun-${automationId}`).value); const queryToRun = encodeURIComponent(document.getElementById(`automation-queryToRun-${automationId}`).value);
const scheduleEl = document.getElementById(`automation-schedule-${automationId}`); const scheduleEl = document.getElementById(`automation-schedule-${automationId}`);
const notificationEl = document.getElementById(`automation-success-${automationId}`); const notificationEl = document.getElementById(`automation-success-${automationId}`);
const saveButtonEl = document.getElementById(`save-automation-button-${automationId}`); const saveButtonEl = document.getElementById(`save-automation-button-${automationId}`);
const actOn = create ? "Create" : "Save"; const actOn = create ? "Create" : "Save";
if (subject === "" || queryToRun == "" || scheduleEl.value == "") { if (queryToRun == "" || scheduleEl.value == "") {
return; return;
} }
@@ -186,10 +215,13 @@
const encodedCrontime = encodeURIComponent(crontime); const encodedCrontime = encodeURIComponent(crontime);
// Construct query string and select method for API call // Construct query string and select method for API call
let query_string = `q=${queryToRun}&subject=${subject}&crontime=${encodedCrontime}&city=${ip_data.city}&region=${ip_data.region}&country=${ip_data.country_name}&timezone=${ip_data.timezone}`; let query_string = `q=${queryToRun}&crontime=${encodedCrontime}&city=${ip_data.city}&region=${ip_data.region}&country=${ip_data.country_name}&timezone=${ip_data.timezone}`;
let method = "POST"; let method = "POST";
if (!create) { if (!create) {
const subject = encodeURIComponent(document.getElementById(`automation-subject-${automationId}`).value);
query_string += `&automation_id=${automationId}`; query_string += `&automation_id=${automationId}`;
query_string += `&subject=${subject}`;
method = "PUT" method = "PUT"
} }
@@ -231,29 +263,27 @@
var automationEl = document.createElement("div"); var automationEl = document.createElement("div");
automationEl.classList.add("card"); automationEl.classList.add("card");
automationEl.classList.add("automation"); automationEl.classList.add("automation");
automationEl.classList.add("new-automation")
const placeholderId = Date.now(); const placeholderId = Date.now();
automationEl.id = "automation-card-" + placeholderId; automationEl.id = "automation-card-" + placeholderId;
automationEl.innerHTML = ` automationEl.innerHTML = `
<label for="subject">Subject</label> <label for="query-to-run">Your new automation</label>
<input type="text"
id="automation-subject-${placeholderId}"
name="subject"
placeholder="My Personal Newsletter">
<label for="query-to-run">Query to Run</label>
<textarea id="automation-queryToRun-${placeholderId}" placeholder="Share a Newsletter including: 1. Weather forecast for this Week. 2. A Book Highlight from my Notes. 3. Recap News from Last Week"></textarea> <textarea id="automation-queryToRun-${placeholderId}" placeholder="Share a Newsletter including: 1. Weather forecast for this Week. 2. A Book Highlight from my Notes. 3. Recap News from Last Week"></textarea>
<label for="schedule">Schedule</label> <label for="schedule">Schedule</label>
<input type="text" <input type="text"
id="automation-schedule-${placeholderId}" id="automation-schedule-${placeholderId}"
name="schedule" name="schedule"
placeholder="9AM every morning"> placeholder="9AM every morning">
<button type="button" <div class="automation-buttons">
class="save-automation-button" <button type="button"
onclick="saveAutomation(${placeholderId}, true)" class="delete-automation-button negative-button"
id="save-automation-button-${placeholderId}">Create</button> onclick="deleteAutomation(${placeholderId}, true)"
<button type="button" id="delete-automation-button-${placeholderId}">Cancel</button>
class="delete-automation-button" <button type="button"
onclick="deleteAutomation(${placeholderId}, true)" class="save-automation-button"
id="delete-automation-button-${placeholderId}">Delete</button> onclick="saveAutomation(${placeholderId}, true)"
id="save-automation-button-${placeholderId}">Create</button>
</div>
<div id="automation-success-${placeholderId}" style="display: none;"></div> <div id="automation-success-${placeholderId}" style="display: none;"></div>
`; `;
document.getElementById("automations").insertBefore(automationEl, document.getElementById("automations").firstChild); document.getElementById("automations").insertBefore(automationEl, document.getElementById("automations").firstChild);

View File

@@ -34,6 +34,7 @@ from khoj.routers.helpers import (
ApiUserRateLimiter, ApiUserRateLimiter,
CommonQueryParams, CommonQueryParams,
ConversationCommandRateLimiter, ConversationCommandRateLimiter,
acreate_title_from_query,
schedule_automation, schedule_automation,
update_telemetry_state, update_telemetry_state,
) )
@@ -425,7 +426,6 @@ def delete_automation(request: Request, automation_id: str) -> Response:
async def post_automation( async def post_automation(
request: Request, request: Request,
q: str, q: str,
subject: str,
crontime: str, crontime: str,
city: Optional[str] = None, city: Optional[str] = None,
region: Optional[str] = None, region: Optional[str] = None,
@@ -435,8 +435,8 @@ async def post_automation(
user: KhojUser = request.user.object user: KhojUser = request.user.object
# Perform validation checks # Perform validation checks
if is_none_or_empty(q) or is_none_or_empty(subject) or is_none_or_empty(crontime): if is_none_or_empty(q) or is_none_or_empty(crontime):
return Response(content="A query, subject and crontime is required", status_code=400) return Response(content="A query and crontime is required", status_code=400)
if not cron_descriptor.get_description(crontime): if not cron_descriptor.get_description(crontime):
return Response(content="Invalid crontime", status_code=400) return Response(content="Invalid crontime", status_code=400)
@@ -452,7 +452,7 @@ async def post_automation(
crontime = " ".join(crontime.split(" ")[:5]) crontime = " ".join(crontime.split(" ")[:5])
# Convert crontime to standard unix crontime # Convert crontime to standard unix crontime
crontime = crontime.replace("?", "*") crontime = crontime.replace("?", "*")
subject = subject.strip() subject = await acreate_title_from_query(q)
# Schedule automation with query_to_run, timezone, subject directly provided by user # Schedule automation with query_to_run, timezone, subject directly provided by user
try: try: