-
Notifications
You must be signed in to change notification settings - Fork 14.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Calendar Heatmap day offset #28931
Comments
Hey there, @GillesTo! 👋 I'm here to help you with any software bugs, questions, or contributions while we wait for a human maintainer. Let's squash those bugs together! To resolve the day offset issue in the calendar heatmap visualization in Apache Superset version 3.1.2, you need to ensure that the timestamp normalization is correctly handled. The issue might be due to the way timestamps are processed and normalized. In the class CalHeatmapViz(BaseViz):
"""Calendar heatmap."""
viz_type = "cal_heatmap"
verbose_name = _("Calendar Heatmap")
credits = "<a href=https://github.com/wa0x6e/cal-heatmap>cal-heatmap</a>"
is_timeseries = True
@deprecated(deprecated_in="3.0")
def get_data(self, df: pd.DataFrame) -> VizData: # pylint: disable=too-many-locals
if df.empty:
return None
form_data = self.form_data
data = {}
records = df.to_dict("records")
for metric in self.metric_labels:
values = {}
for query_obj in records:
v = query_obj[DTTM_ALIAS]
if hasattr(v, "value"):
v = v.value
# Ensure the timestamp is correctly normalized
v = pd.to_datetime(v, unit='s').normalize()
values[str(v.timestamp())] = query_obj.get(metric)
data[metric] = values
try:
start, end = get_since_until(
relative_start=relative_start,
relative_end=relative_end,
time_range=form_data.get("time_range"),
since=form_data.get("since"),
until=form_data.get("until"),
)
except ValueError as ex:
raise QueryObjectValidationError(str(ex)) from ex
if not start or not end:
raise QueryObjectValidationError(
"Please provide both time bounds (Since and Until)"
)
domain = form_data.get("domain_granularity")
diff_delta = rdelta.relativedelta(end, start)
diff_secs = (end - start).total_seconds()
if domain == "year":
range_ = end.year - start.year + 1
elif domain == "month":
range_ = diff_delta.years * 12 + diff_delta.months + 1
elif domain == "week":
range_ = diff_delta.years * 53 + diff_delta.weeks + 1
elif domain == "day":
range_ = diff_secs // (24 * 60 * 60) + 1 # type: ignore
else:
range_ = diff_secs // (60 * 60) + 1 # type: ignore
return {
"data": data,
"start": start,
"domain": domain,
"subdomain": form_data.get("subdomain_granularity"),
"range": range_,
} In addition to the above, you should also check the function Calendar(element, props) {
const {
data,
height,
cellPadding = 3,
cellRadius = 0,
cellSize = 10,
domainGranularity,
linearColorScheme,
showLegend,
showMetricName,
showValues,
steps,
subdomainGranularity,
timeFormatter,
valueFormatter,
verboseMap,
theme,
} = props;
const container = d3Select(element)
.classed('superset-legacy-chart-calendar', true)
.style('height', height);
container.selectAll('*').remove();
const div = container.append('div');
const subDomainTextFormat = showValues
? (date, value) => valueFormatter(value)
: null;
const metricsData = data.data;
const METRIC_TEXT = t('Metric');
Object.keys(metricsData).forEach(metric => {
const calContainer = div.append('div');
if (showMetricName) {
calContainer.text(`${METRIC_TEXT}: ${verboseMap[metric] || metric}`);
}
const timestamps = metricsData[metric];
const extents = d3Extent(Object.keys(timestamps), key => timestamps[key]);
const step = (extents[1] - extents[0]) / (steps - 1);
const colorScale = getSequentialSchemeRegistry()
.get(linearColorScheme)
.createLinearScale(extents);
const legend = d3Range(steps).map(i => extents[0] + step * i);
const legendColors = legend.map(x => colorScale(x));
const cal = new CalHeatMap();
cal.init({
start: data.start,
data: timestamps,
itemSelector: calContainer.node(),
legendVerticalPosition: 'top',
cellSize,
cellPadding,
cellRadius,
legendCellSize: cellSize,
legendCellPadding: 2,
legendCellRadius: cellRadius,
tooltip: true,
domain: domainGranularity,
subDomain: subdomainGranularity,
range: data.range,
browsing: true,
legend,
legendColors: {
colorScale,
min: legendColors[0],
max: legendColors[legendColors.length - 1],
empty: theme.colors.grayscale.light5,
},
displayLegend: showLegend,
itemName: '',
valueFormatter,
timeFormatter,
subDomainTextFormat,
});
});
}
Calendar.displayName = 'Calendar';
Calendar.propTypes = propTypes;
export default Calendar; This function initializes the calendar heatmap and ensures that the timestamps are correctly processed and displayed. Make sure this function is correctly implemented in your |
Facing the same issue in superset 4.0 |
Bug description
the problem has already been discussed in #24989
but the problem is remaining :
I'm using the 3.1.2 version of superset installed in the container from here : https://superset.apache.org/docs/quickstart
in this case I count the number of lines "created_at" for each day. If you have a look at the picture, I'm supposed to count 6 for the 2024-05-31
![image](https://private-user-images.githubusercontent.com/171673107/336389664-cab2ee15-807c-4480-bb3d-ac4b99741b2c.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzg5Njk0NzAsIm5iZiI6MTczODk2OTE3MCwicGF0aCI6Ii8xNzE2NzMxMDcvMzM2Mzg5NjY0LWNhYjJlZTE1LTgwN2MtNDQ4MC1iYjNkLWFjNGI5OTc0MWIyYy5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwMjA3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDIwN1QyMjU5MzBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1iMWU5N2UxMmQxZGI5YWJmYzdjODIxNjZkZDBlYTEyOTQ5MGYxMmJkNmQxZjcwZTYyZjFlZDZkNjA1OWI1ZWEzJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.wthL-RZdsPdmPZV7nm_ORkUv9b5HRuhP8jj6auyBROk)
but if you have a look at the result, it is nicely counting 6, but 2024-05-31 changed into 2024-05-30
![image](https://private-user-images.githubusercontent.com/171673107/336390071-d4c1bc22-f068-448d-9c5f-25e10d5aaa42.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzg5Njk0NzAsIm5iZiI6MTczODk2OTE3MCwicGF0aCI6Ii8xNzE2NzMxMDcvMzM2MzkwMDcxLWQ0YzFiYzIyLWYwNjgtNDQ4ZC05YzVmLTI1ZTEwZDVhYWE0Mi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwMjA3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDIwN1QyMjU5MzBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1mYTY1ZTQxNzczNDg5NGEwOWY1YjI1M2M2NjgwNzVjZjZhNjIzOTRjOTdkZGIxZDNlZGE5MDQ0YjZjZGJhMTZkJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.ufvzsMC2EdfrKQCGKlCfBy0HEVnML1rC8haZ7n5Hh98)
How to reproduce the bug
data are :
collected from a python program, and stored in a dataframe with this command : df['created_at'] = pd.to_datetime(df['created_at'], errors='coerce', utc=True)
then the dataframe is sent to a posgresql database using this function : df.to_sql(table_name, engine, index=False, if_exists='replace')
in superset :
![image](https://private-user-images.githubusercontent.com/171673107/336397886-d5b65819-bd99-4bfa-afb0-3bf2103a2a24.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzg5Njk0NzAsIm5iZiI6MTczODk2OTE3MCwicGF0aCI6Ii8xNzE2NzMxMDcvMzM2Mzk3ODg2LWQ1YjY1ODE5LWJkOTktNGJmYS1hZmIwLTNiZjIxMDNhMmEyNC5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwMjA3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDIwN1QyMjU5MzBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1hZTlkYmZhYzJkY2U2ZjAzZDJkNzBhZDRmNmFiNjEzOWMzOWFhNGU4NDY3NGM0MmI2MTBmMTU3NjFiZDM2MjY2JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.pd6nrItKoLqafyMJHaagyET7A7OLYoo3hIzdpxwUZio)
![image](https://private-user-images.githubusercontent.com/171673107/336397944-e39a0dbe-bf5d-45e0-aa52-5a931a589422.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzg5Njk0NzAsIm5iZiI6MTczODk2OTE3MCwicGF0aCI6Ii8xNzE2NzMxMDcvMzM2Mzk3OTQ0LWUzOWEwZGJlLWJmNWQtNDVlMC1hYTUyLTVhOTMxYTU4OTQyMi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwMjA3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDIwN1QyMjU5MzBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1hOTlkNjYzY2ZmZmJkY2YzZTg3MzVmNDZkMWRmMWU0YmFhNzQ4MTcyNTA4ZTBiODRiYjJhNzdlYTNkYWI4NjVjJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.nxTKrb8WO686-bXADvLKowo6t0qax3Bxt0P41CXqDok)
![image](https://private-user-images.githubusercontent.com/171673107/336400555-ec0445e6-05fb-4459-97a1-687c518579e2.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzg5Njk0NzAsIm5iZiI6MTczODk2OTE3MCwicGF0aCI6Ii8xNzE2NzMxMDcvMzM2NDAwNTU1LWVjMDQ0NWU2LTA1ZmItNDQ1OS05N2ExLTY4N2M1MTg1NzllMi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjUwMjA3JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI1MDIwN1QyMjU5MzBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT01ZTUzNjdmZjU5MTFmYjM2M2NmMzI0MzhmOTM2OTE3NDI2MDgyYjgzZDU5ODU4YmQ0OWU1OWM5YmM3MDE1NzI0JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.eA7Zhz1DJtJHUxON9TKn0IHOsvxhs8ipXAANO2RHI1s)
Screenshots/recordings
No response
Superset version
3.1.3
Python version
3.9
Node version
16
Browser
Firefox
Additional context
No response
Checklist
The text was updated successfully, but these errors were encountered: