Everything is working I am just about to refactor indicator storage.
This commit is contained in:
parent
73ed1a092a
commit
f2b7621b6d
|
|
@ -481,6 +481,7 @@ class UserIndicatorManagement(UserExchangeManagement):
|
||||||
:param indicators: A DataFrame containing indicator attributes and properties.
|
:param indicators: A DataFrame containing indicator attributes and properties.
|
||||||
"""
|
"""
|
||||||
for _, indicator in indicators.iterrows():
|
for _, indicator in indicators.iterrows():
|
||||||
|
try:
|
||||||
# Convert necessary fields to JSON strings
|
# Convert necessary fields to JSON strings
|
||||||
src_string = json.dumps(indicator['source'])
|
src_string = json.dumps(indicator['source'])
|
||||||
prop_string = json.dumps(indicator['properties'])
|
prop_string = json.dumps(indicator['properties'])
|
||||||
|
|
@ -493,6 +494,9 @@ class UserIndicatorManagement(UserExchangeManagement):
|
||||||
# Insert the row into the database and cache using DataCache
|
# Insert the row into the database and cache using DataCache
|
||||||
self.data.insert_row(cache_name='indicators', columns=columns, values=values)
|
self.data.insert_row(cache_name='indicators', columns=columns, values=values)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error saving indicator {indicator['name']} for creator {indicator['creator']}: {str(e)}")
|
||||||
|
|
||||||
def remove_indicator(self, indicator_name: str, user_name: str) -> None:
|
def remove_indicator(self, indicator_name: str, user_name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Removes a specific indicator from the database and cache.
|
Removes a specific indicator from the database and cache.
|
||||||
|
|
|
||||||
18
src/app.py
18
src/app.py
|
|
@ -166,19 +166,19 @@ def settings():
|
||||||
return jsonify({"success": False, "message": "User not logged in"}), 401
|
return jsonify({"success": False, "message": "User not logged in"}), 401
|
||||||
|
|
||||||
# Get the setting that the application wants to change.
|
# Get the setting that the application wants to change.
|
||||||
# todo: migrate everything to ajax and clean all this up!!!
|
try:
|
||||||
params = request.form
|
# Handle JSON or form submissions
|
||||||
if not (setting := request.form.get('setting')):
|
|
||||||
if request.is_json:
|
if request.is_json:
|
||||||
setting = request.json.get('setting')
|
data = request.json
|
||||||
params = request.json.get('indicator')
|
setting = data.get('setting')
|
||||||
|
params = data.get('indicator', {})
|
||||||
|
else:
|
||||||
|
setting = request.form.get('setting')
|
||||||
|
params = request.form.to_dict()
|
||||||
|
|
||||||
if not setting:
|
if not setting:
|
||||||
return jsonify({"success": False, "message": "No setting provided"}), 400
|
return jsonify({"success": False, "message": "No setting provided"}), 400
|
||||||
else:
|
|
||||||
# Redirect if this is a form submission (non-async request)
|
|
||||||
return redirect('/')
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Change the setting.
|
# Change the setting.
|
||||||
brighter_trades.adjust_setting(user_name=user_name, setting=setting, params=params)
|
brighter_trades.adjust_setting(user_name=user_name, setting=setting, params=params)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,11 @@ import talib
|
||||||
indicators_registry = {}
|
indicators_registry = {}
|
||||||
|
|
||||||
|
|
||||||
|
# Function to generate random hex color
|
||||||
|
def generate_random_color():
|
||||||
|
return f"#{random.randrange(0x1000000):06x}"
|
||||||
|
|
||||||
|
|
||||||
class Indicator:
|
class Indicator:
|
||||||
def __init__(self, name: str, indicator_type: str, properties: dict):
|
def __init__(self, name: str, indicator_type: str, properties: dict):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
@ -88,7 +93,7 @@ class SMA(Indicator):
|
||||||
def __init__(self, name: str, indicator_type: str, properties: dict):
|
def __init__(self, name: str, indicator_type: str, properties: dict):
|
||||||
super().__init__(name, indicator_type, properties)
|
super().__init__(name, indicator_type, properties)
|
||||||
# Default display properties for SMA
|
# Default display properties for SMA
|
||||||
self.properties.setdefault('color', f"#{random.randrange(0x1000000):06x}")
|
self.properties.setdefault('color', generate_random_color())
|
||||||
self.properties.setdefault('thickness', 1) # Default line thickness
|
self.properties.setdefault('thickness', 1) # Default line thickness
|
||||||
self.properties.setdefault('period', 20)
|
self.properties.setdefault('period', 20)
|
||||||
|
|
||||||
|
|
@ -158,6 +163,9 @@ class BolBands(Indicator):
|
||||||
self.properties.setdefault('devup', 2) # Standard deviation for the upper band
|
self.properties.setdefault('devup', 2) # Standard deviation for the upper band
|
||||||
self.properties.setdefault('devdn', 2) # Standard deviation for the lower band
|
self.properties.setdefault('devdn', 2) # Standard deviation for the lower band
|
||||||
self.properties.setdefault('ma', 0) # Moving average type (0 = Simple Moving Average in TA-Lib)
|
self.properties.setdefault('ma', 0) # Moving average type (0 = Simple Moving Average in TA-Lib)
|
||||||
|
self.properties.setdefault('color_1', generate_random_color()) # Upper band
|
||||||
|
self.properties.setdefault('color_2', generate_random_color()) # Middle band
|
||||||
|
self.properties.setdefault('color_3', generate_random_color()) # Lower band
|
||||||
|
|
||||||
def calculate(self, candles: pd.DataFrame, user_name: str, num_results: int = 1) -> pd.DataFrame:
|
def calculate(self, candles: pd.DataFrame, user_name: str, num_results: int = 1) -> pd.DataFrame:
|
||||||
"""
|
"""
|
||||||
|
|
@ -199,6 +207,9 @@ class MACD(Indicator):
|
||||||
self.properties.setdefault('fast_p', 12)
|
self.properties.setdefault('fast_p', 12)
|
||||||
self.properties.setdefault('slow_p', 26)
|
self.properties.setdefault('slow_p', 26)
|
||||||
self.properties.setdefault('signal_p', 9)
|
self.properties.setdefault('signal_p', 9)
|
||||||
|
self.properties.setdefault('color_1', generate_random_color()) # Upper band
|
||||||
|
self.properties.setdefault('color_2', generate_random_color()) # Middle band
|
||||||
|
self.properties.setdefault('color_3', generate_random_color()) # Lower band
|
||||||
|
|
||||||
def calculate(self, candles: pd.DataFrame, user_name: str, num_results: int = 1) -> pd.DataFrame:
|
def calculate(self, candles: pd.DataFrame, user_name: str, num_results: int = 1) -> pd.DataFrame:
|
||||||
"""
|
"""
|
||||||
|
|
@ -346,11 +357,11 @@ class Indicators:
|
||||||
# Create the dictionary
|
# Create the dictionary
|
||||||
result = {}
|
result = {}
|
||||||
for _, row in indicators_df.iterrows():
|
for _, row in indicators_df.iterrows():
|
||||||
|
# Include all properties from the properties dictionary, not just a limited subset.
|
||||||
result[row['name']] = {
|
result[row['name']] = {
|
||||||
'type': row['kind'],
|
'type': row['kind'],
|
||||||
'visible': row['visible'],
|
'visible': row['visible'],
|
||||||
'value': row['properties'].get('value', ''),
|
**row['properties'] # This will include all properties in the dictionary
|
||||||
'color': row['properties'].get('color', '')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
@ -374,22 +385,46 @@ class Indicators:
|
||||||
# Set visibility for the specified indicator names
|
# Set visibility for the specified indicator names
|
||||||
self.indicators.loc[self.indicators['name'].isin(indicator_names), 'visible'] = 1
|
self.indicators.loc[self.indicators['name'].isin(indicator_names), 'visible'] = 1
|
||||||
|
|
||||||
def edit_indicator(self, user_name: str, params: Any):
|
def edit_indicator(self, user_name: str, params: dict):
|
||||||
# if 'submit' in request.form:
|
"""
|
||||||
# # Get the name of the indicator
|
Edits an existing indicator's properties.
|
||||||
# indicator = request.form['submit']
|
|
||||||
# # Drop the name and action from our received data
|
:param user_name: The name of the user.
|
||||||
# attributes = dict(list(request.form.items())[2:])
|
:param params: The updated properties of the indicator.
|
||||||
# # All the numbers are string now so turn them back to (int)
|
"""
|
||||||
# for a in attributes:
|
indicator_name = params.get('name')
|
||||||
# if attributes[a].isdigit():
|
if not indicator_name:
|
||||||
# attributes[a] = int(attributes[a])
|
raise ValueError("Indicator name is required for editing.")
|
||||||
# # if visible is unchecked it doesn't get sent by the form
|
|
||||||
# if 'visible' not in attributes:
|
# Get the indicator from the user's indicator list
|
||||||
# attributes.update({'visible': False})
|
user_id = self.users.get_id(user_name)
|
||||||
# # Set the data in indicators according to <attributes>
|
indicator_row = self.indicators.query('name == @indicator_name and creator == @user_id')
|
||||||
# brighter_trades.indicators.indicator_list[indicator] = attributes
|
|
||||||
pass
|
if indicator_row.empty:
|
||||||
|
raise ValueError(f"Indicator '{indicator_name}' not found for user '{user_name}'.")
|
||||||
|
|
||||||
|
# Update the top-level fields
|
||||||
|
top_level_keys = ['name', 'visible', 'kind'] # Top-level keys, expand this if needed
|
||||||
|
for key, value in params.items():
|
||||||
|
if key in top_level_keys and key in indicator_row.columns:
|
||||||
|
self.indicators.at[indicator_row.index[0], key] = value
|
||||||
|
|
||||||
|
# Update 'source' dictionary fields
|
||||||
|
if 'source' in indicator_row.columns and isinstance(indicator_row['source'].iloc[0], dict):
|
||||||
|
source_dict = indicator_row['source'].iloc[0] # Direct reference, no need for reassignment later
|
||||||
|
for key, value in params.items():
|
||||||
|
if key in source_dict:
|
||||||
|
source_dict[key] = value
|
||||||
|
|
||||||
|
# Update 'properties' dictionary fields
|
||||||
|
if 'properties' in indicator_row.columns and isinstance(indicator_row['properties'].iloc[0], dict):
|
||||||
|
properties_dict = indicator_row['properties'].iloc[0] # No copy, modify directly
|
||||||
|
for key, value in params.items():
|
||||||
|
if key in properties_dict:
|
||||||
|
properties_dict[key] = value
|
||||||
|
|
||||||
|
# Save the updated indicator for the user in the database.
|
||||||
|
self.users.save_indicators(indicator_row)
|
||||||
|
|
||||||
def new_indicator(self, user_name: str, params) -> None:
|
def new_indicator(self, user_name: str, params) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
@ -548,6 +583,7 @@ class Indicators:
|
||||||
:param visible: Whether to display it in the chart view.
|
:param visible: Whether to display it in the chart view.
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
# Todo: Possible refactor to save without storing the indicator instance
|
||||||
|
|
||||||
self.indicators = self.indicators.reset_index(drop=True)
|
self.indicators = self.indicators.reset_index(drop=True)
|
||||||
creator_id = self.users.get_id(creator)
|
creator_id = self.users.get_id(creator)
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,10 @@ class Comms {
|
||||||
const response = await fetch('/settings', {
|
const response = await fetch('/settings', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(indicatorData)
|
body: JSON.stringify({
|
||||||
|
setting: 'edit_indicator',
|
||||||
|
indicator: indicatorData
|
||||||
|
})
|
||||||
});
|
});
|
||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -100,9 +100,13 @@ class Indicator {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDisplay(name, priceValue, value_name) {
|
updateDisplay(name, priceValue, value_name) {
|
||||||
// let rounded_value = (Math.round(priceValue * 100) / 100).toFixed(2);
|
// Update the data in the edit and view indicators panel
|
||||||
// // Update the data in the edit and view indicators panel
|
let element = document.getElementById(this.name + '_' + value_name)
|
||||||
// document.getElementById(this.name + '_' + value_name).value = rounded_value;
|
if (element){
|
||||||
|
element.value = (Math.round(priceValue * 100) / 100).toFixed(2);
|
||||||
|
} else {
|
||||||
|
console.warn(`Element with ID ${this.name}_${value_name} not found.`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setHist(name, data) {
|
setHist(name, data) {
|
||||||
|
|
@ -217,15 +221,15 @@ class RSI extends Indicator {
|
||||||
indicatorMap.set("RSI", RSI);
|
indicatorMap.set("RSI", RSI);
|
||||||
|
|
||||||
class MACD extends Indicator {
|
class MACD extends Indicator {
|
||||||
constructor(name, charts, color_m, color_s, lineWidth = 2) {
|
constructor(name, charts, color_1, color_2, lineWidth = 2) {
|
||||||
super(name);
|
super(name);
|
||||||
if (!charts.hasOwnProperty('chart3')) {
|
if (!charts.hasOwnProperty('chart3')) {
|
||||||
charts.create_MACD_chart();
|
charts.create_MACD_chart();
|
||||||
}
|
}
|
||||||
let chart = charts.chart3;
|
let chart = charts.chart3;
|
||||||
|
|
||||||
this.addLine('line_m', chart, color_m, lineWidth);
|
this.addLine('line_m', chart, color_1, lineWidth);
|
||||||
this.addLine('line_s', chart, color_s, lineWidth);
|
this.addLine('line_s', chart, color_2, lineWidth);
|
||||||
this.addHist(name, chart);
|
this.addHist(name, chart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,7 +258,7 @@ class MACD extends Indicator {
|
||||||
})), 'signal');
|
})), 'signal');
|
||||||
|
|
||||||
// Set the histogram
|
// Set the histogram
|
||||||
this.setHist('hist', filteredData.map(row => ({
|
this.setHist(this.name, filteredData.map(row => ({
|
||||||
time: row.time,
|
time: row.time,
|
||||||
value: row.hist
|
value: row.hist
|
||||||
})));
|
})));
|
||||||
|
|
@ -316,11 +320,11 @@ class Volume extends Indicator {
|
||||||
indicatorMap.set("Volume", Volume);
|
indicatorMap.set("Volume", Volume);
|
||||||
|
|
||||||
class Bolenger extends Indicator {
|
class Bolenger extends Indicator {
|
||||||
constructor(name, chart, color_u, color_m, color_l, lineWidth = 2) {
|
constructor(name, chart, color_1, color_2, color_3, lineWidth = 2) {
|
||||||
super(name);
|
super(name);
|
||||||
this.addLine('line_u', chart, color_u, lineWidth);
|
this.addLine('line_u', chart, color_1, lineWidth);
|
||||||
this.addLine('line_m', chart, color_m, lineWidth);
|
this.addLine('line_m', chart, color_2, lineWidth);
|
||||||
this.addLine('line_l', chart, color_l, lineWidth);
|
this.addLine('line_l', chart, color_3, lineWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getIndicatorConfig() {
|
static getIndicatorConfig() {
|
||||||
|
|
@ -365,7 +369,7 @@ class Indicators {
|
||||||
this.comms = comms;
|
this.comms = comms;
|
||||||
}
|
}
|
||||||
|
|
||||||
create_indicators(indicators, charts, bt_data) {
|
create_indicators(indicators, charts) {
|
||||||
for (let name in indicators) {
|
for (let name in indicators) {
|
||||||
if (!indicators[name].visible) continue;
|
if (!indicators[name].visible) continue;
|
||||||
|
|
||||||
|
|
@ -380,9 +384,9 @@ class Indicators {
|
||||||
if (arg === 'charts') return charts;
|
if (arg === 'charts') return charts;
|
||||||
if (arg === 'chart_1') return charts.chart_1;
|
if (arg === 'chart_1') return charts.chart_1;
|
||||||
if (arg === 'color') return indicators[name].color;
|
if (arg === 'color') return indicators[name].color;
|
||||||
if (arg === 'color_1') return '#FF0000'; //bt_data.indicators[name].color_1 || red;
|
if (arg === 'color_1') return indicators[name].color_1 || 'red';
|
||||||
if (arg === 'color_2') return 'red'; // bt_data.indicators[name].color_2 || white;
|
if (arg === 'color_2') return indicators[name].color_2 || 'white';
|
||||||
if (arg === 'color_3') return 'red'; // bt_data.indicators[name].color_3 || blue;
|
if (arg === 'color_3') return indicators[name].color_3 || 'blue';
|
||||||
});
|
});
|
||||||
|
|
||||||
this.i_objs[name] = new IndicatorConstructor(...preparedArgs);
|
this.i_objs[name] = new IndicatorConstructor(...preparedArgs);
|
||||||
|
|
@ -460,15 +464,20 @@ class Indicators {
|
||||||
const row = event.target.closest('.indicator-row');
|
const row = event.target.closest('.indicator-row');
|
||||||
const inputs = row.querySelectorAll('input, select');
|
const inputs = row.querySelectorAll('input, select');
|
||||||
|
|
||||||
|
// Gather the indicator name from the row
|
||||||
|
const nameDiv = row.querySelector('div:nth-child(2)'); // Second <div> contains the name
|
||||||
|
const indicatorName = nameDiv.innerText.trim(); // Get the indicator name
|
||||||
|
|
||||||
// Gather input data
|
// Gather input data
|
||||||
const formObj = {};
|
const formObj = { name: indicatorName }; // Initialize formObj with the name
|
||||||
inputs.forEach(input => {
|
inputs.forEach(input => {
|
||||||
formObj[input.name] = input.type === 'checkbox' ? input.checked : input.value;
|
formObj[input.name] = input.type === 'checkbox' ? input.checked : input.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Call comms to send data to the server
|
||||||
this.comms.updateIndicator(formObj).then(response => {
|
this.comms.updateIndicator(formObj).then(response => {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
alert('Indicator updated successfully.');
|
window.location.reload(); // This triggers a full page refresh
|
||||||
} else {
|
} else {
|
||||||
alert('Failed to update the indicator.');
|
alert('Failed to update the indicator.');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,155 @@
|
||||||
<div class="content" id="indicator_panel" style="max-height: 70px;">
|
<div class="content" id="indicator_panel" style="max-height: 70px;">
|
||||||
<!-- Edit Indicator Panel -->
|
<!-- Indicator Panel Section -->
|
||||||
|
|
||||||
<div id="edit_indcr_panel" style="display: grid; grid-template-columns: 1fr 1fr;">
|
<div id="edit_indcr_panel" style="display: grid; grid-template-columns: 1fr 1fr;">
|
||||||
|
<!-- Button for adding a new indicator -->
|
||||||
<div class="section" style="grid-column: 1;">
|
<div class="section" style="grid-column: 1;">
|
||||||
<button class="btn" onclick="UI.indicators.open_form()">New Indicator</button>
|
<button class="btn" onclick="UI.indicators.open_form()">New Indicator</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Button for displaying indicator options -->
|
||||||
<div class="section" style="grid-column: 2;">
|
<div class="section" style="grid-column: 2;">
|
||||||
<button class="btn">Indicator Options</button>
|
<button class="btn">Indicator Options</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" style="display: grid; grid-row: 2; grid-column: 1 / span 2; grid-template-columns: 75px 200px 200px auto; overflow-x: scroll;">
|
|
||||||
<h3 style="grid-column: 1 / span 2; text-align: left;">Indicators</h3>
|
|
||||||
<hr style="width: 100%; grid-column: 1 / span 10;">
|
|
||||||
<div style="grid-column: 2;">
|
|
||||||
<h3>Name</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<div style="grid-column: 3;">
|
|
||||||
<h3>Type</h3>
|
<!-- Section for displaying the list of indicators -->
|
||||||
|
<div class="section" style="margin-top: 15px; overflow-x: auto; display: grid; grid-auto-rows: minmax(80px, auto); grid-template-columns: 75px 200px 150px 100px 75px 100px 100px auto; gap: 10px; padding-left: 10px;">
|
||||||
|
<!-- Background color change for this section -->
|
||||||
|
<div style="background-color: #F7E1C1; grid-column: 1 / span 8; padding: 5px 0; display: grid; grid-template-columns: 75px 200px 150px 100px 75px 100px 100px auto; gap: 10px;">
|
||||||
|
<div style="text-align: center;"><h3 class="header-text">Remove or Edit</h3></div>
|
||||||
|
<div style="text-align: center;"><h3 class="header-text">Name</h3></div>
|
||||||
|
<div style="text-align: center;"><h3 class="header-text">Type</h3></div>
|
||||||
|
<div style="text-align: center;"><h3 class="header-text">Value</h3></div>
|
||||||
|
<div style="text-align: center;"><h3 class="header-text">Visible</h3></div>
|
||||||
|
<div style="text-align: center;"><h3 class="header-text">Period</h3></div>
|
||||||
|
<div style="text-align: center;"><h3 class="header-text">Color</h3></div>
|
||||||
|
<div style="text-align: center;"><h3 class="header-text">Properties</h3></div>
|
||||||
|
<hr style="grid-column: 1 / span 8;width: 100%; height: 1px; border: 1px solid #ccc; margin: 5px 0;">
|
||||||
</div>
|
</div>
|
||||||
<div style="grid-column: 4 / span 8;">
|
|
||||||
<h3>Properties</h3>
|
<!-- Loop through each indicator in indicator_list -->
|
||||||
</div>
|
|
||||||
<!-- Edit Indicator Rows without form -->
|
|
||||||
{% for indicator in indicator_list %}
|
{% for indicator in indicator_list %}
|
||||||
<div class="indicator-row" style="display: grid; grid-column: 1 / span 10; grid-template-columns: 75px 200px 200px repeat(7, 1fr);">
|
<div class="indicator-row" style="display: grid; grid-column: 1 / span 8; align-items: center; grid-template-columns: 75px 200px 150px 100px 75px 100px 100px auto; gap: 10px; padding-left: 10px;">
|
||||||
<input type="hidden" name="setting" value="edit_indicator" />
|
|
||||||
<div id="edit_indctr_controls" style="grid-column: 1;">
|
<!-- Buttons to delete or update the indicator -->
|
||||||
|
<div style="text-align: center;">
|
||||||
<button type="button" class="e_btn" onclick="UI.indicators.deleteIndicator('{{indicator}}', event)">✘</button>
|
<button type="button" class="e_btn" onclick="UI.indicators.deleteIndicator('{{indicator}}', event)">✘</button>
|
||||||
<button type="button" class="e_btn" style="color:darkgreen;" onclick="UI.indicators.updateIndicator(event)">✔</button>
|
<button type="button" class="e_btn" style="color:darkgreen;" onclick="UI.indicators.updateIndicator(event)">✔</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="iename" style="grid-column: 2;">{{indicator}}</div>
|
<!-- Display the indicator name -->
|
||||||
|
<div style="text-align: center;">{{indicator}}</div>
|
||||||
|
|
||||||
{% for property in indicator_list[indicator] %}
|
<!-- Fixed property: Type (Dropdown) -->
|
||||||
{% set list1 = property.split('_') %}
|
<div style="text-align: center;">
|
||||||
<div class="ieprop">
|
{% if 'type' in indicator_list[indicator] %}
|
||||||
<label class="ietextbox" for="{{indicator}}_{{property}}">{{property}}</label>
|
<select class="ietextbox" id="{{indicator}}_type" name="type">
|
||||||
{% if property=='type' %}
|
|
||||||
<select class="ietextbox" id="{{indicator}}_{{property}}" name="{{property}}">
|
|
||||||
{% for i_type in indicator_types %}
|
{% for i_type in indicator_types %}
|
||||||
<option value="{{i_type}}" {% if indicator_list[indicator][property] == i_type %} selected="selected"{%endif%}>{{i_type}}</option>
|
<option value="{{i_type}}" {% if indicator_list[indicator]['type'] == i_type %} selected="selected"{% endif %}>{{i_type}}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
{% elif property=='ma' %}
|
|
||||||
<select class="ietextbox" id="{{indicator}}_{{property}}" name="{{property}}">
|
|
||||||
{% for ma_val in ma_vals %}
|
|
||||||
<option value="{{ma_vals[ma_val]}}" {% if indicator_list[indicator][property] == ma_vals[ma_val] %} selected="selected"{%endif%}>{{ma_val}}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
{% elif property=='color' or list1[0]=='color' %}
|
|
||||||
<input class="ietextbox" type="color" id="{{indicator}}_{{property}}" value="{{indicator_list[indicator][property]}}" name="{{property}}">
|
|
||||||
{% elif property=='period' %}
|
|
||||||
<input class="ietextbox" type="number" id="{{indicator}}_{{property}}" value="{{indicator_list[indicator][property]}}" name="{{property}}">
|
|
||||||
{% elif property=='visible' %}
|
|
||||||
<input class="ietextbox" type="checkbox" id="{{indicator}}_{{property}}" value="{{indicator_list[indicator][property]}}" name="{{property}}" {% if indicator in checked %} checked {%endif%}>
|
|
||||||
{% elif property=='value' %}
|
|
||||||
<input class="ie_value" type="number" id="{{indicator}}_{{property}}" value="{{indicator_list[indicator][property]}}" name="{{property}}" readonly>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<input class="ietextbox" type="text" id="{{indicator}}_{{property}}" value="{{indicator_list[indicator][property]}}" name="{{property}}">
|
<span>-</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Fixed property: Value (Readonly Number) -->
|
||||||
|
<div style="text-align: center;">
|
||||||
|
{% if 'value' in indicator_list[indicator] %}
|
||||||
|
<input class="ie_value" type="number" id="{{indicator}}_value" value="{{indicator_list[indicator]['value']}}" name="value" readonly>
|
||||||
|
{% else %}
|
||||||
|
<span>-</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Fixed property: Visible (Checkbox) -->
|
||||||
|
<div style="text-align: center;">
|
||||||
|
{% if 'visible' in indicator_list[indicator] %}
|
||||||
|
<input class="ietextbox" type="checkbox" id="{{indicator}}_visible" value="{{indicator_list[indicator]['visible']}}" name="visible" {% if indicator in checked %} checked {% endif %}>
|
||||||
|
{% else %}
|
||||||
|
<span>-</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Fixed property: Period (Number Input) -->
|
||||||
|
<div style="text-align: center;">
|
||||||
|
{% if 'period' in indicator_list[indicator] %}
|
||||||
|
<input class="ietextbox" type="number" id="{{indicator}}_period" value="{{indicator_list[indicator]['period']}}" name="period">
|
||||||
|
{% else %}
|
||||||
|
<span>-</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Fixed property: Color (Color Picker) -->
|
||||||
|
<div style="text-align: center;">
|
||||||
|
{% if 'color' in indicator_list[indicator] %}
|
||||||
|
<input class="ietextbox" type="color" id="{{indicator}}_color" value="{{indicator_list[indicator]['color']}}" name="color">
|
||||||
|
{% else %}
|
||||||
|
<span>-</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dynamic properties (remaining ones not covered by known properties) -->
|
||||||
|
<div style="grid-column: span 1; text-align: left; display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;">
|
||||||
|
{% for property, value in indicator_list[indicator].items() %}
|
||||||
|
{% if property not in ['type', 'value', 'color', 'period', 'visible'] %}
|
||||||
|
<div style="margin-bottom: 17px;">
|
||||||
|
<label for="{{indicator}}_{{property}}" class="ietextbox-label">{{property}}</label>
|
||||||
|
{% if 'color' in property %}
|
||||||
|
<!-- Color picker for properties with 'color' in the name -->
|
||||||
|
<input class="ietextbox" type="color" id="{{indicator}}_{{property}}" value="{{value}}" name="{{property}}">
|
||||||
|
{% else %}
|
||||||
|
<!-- Regular text input for other properties -->
|
||||||
|
<input class="ietextbox" type="text" id="{{indicator}}_{{property}}" value="{{value}}" name="{{property}}">
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
|
||||||
<!-- End of Rows for each indicator -->
|
|
||||||
</div>
|
</div>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.section .column {
|
||||||
|
padding: 10px;
|
||||||
|
max-width: 200px; /* Set max-width for all columns */
|
||||||
|
}
|
||||||
|
|
||||||
|
.section h3 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section .buttons-column {
|
||||||
|
max-width: 75px; /* Buttons column */
|
||||||
|
}
|
||||||
|
|
||||||
|
.section .visible-column {
|
||||||
|
max-width: 75px; /* Visible checkbox column */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alternate row background colors */
|
||||||
|
.indicator-row:nth-child(even) {
|
||||||
|
background-color: #E0E0E0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indicator-row:nth-child(odd) {
|
||||||
|
background-color: #F7E1C1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bold labels with drop shadow for dynamic properties */
|
||||||
|
.ietextbox-label {
|
||||||
|
font-weight: bold;
|
||||||
|
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drop shadow for header text */
|
||||||
|
.header-text {
|
||||||
|
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue