I'm trying to write a python wrapper which is designed to read multiple records from csv(or mysql) and update values of a predefined range of a sheet, the range is consisted of value cells and formula cells, my purpose is to update value cells only and keep formula cells in the range unchanged.
In order to do this, I first tried setting cell values one by one, using if to skip formula cells, but it was to slow because there were more than 10 thousand cells, then I tried setDataArray which was fast enough but formulas were overrode by values, then I created an array and set values and formulas into the array and used setFormulaArray to put the values and formulas into the range, the function was what I needed, by it took more than one minutes to finish
I know the setFormulaArray will update the formulas but I don't need this to happen, however as there is no option in the API to skip formulas I can only use the same formula to update the original formula in a certain cell.
Is there any solution to improve the performance when using setFormulaArry, or is there any solution to update only value cells and skip formula cells in a range?
bellow is my code
import uno
import time
#to open the libreoffice calc file
local_ctx = uno.getComponentContext()
smgr_local = local_ctx.ServiceManager
resolver = smgr_local.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", local_ctx)
url = "uno:socket,host=localhost,port=2083,tcpNoDalay=1;urp;StarOffice.ComponentContext"
uno_ctx = resolver.resolve(url)
uno_smgr = uno_ctx.ServiceManager
desktop = uno_smgr.createInstanceWithContext("com.sun.star.frame.Desktop", uno_ctx )
PropertyValue = uno.getClass('com.sun.star.beans.PropertyValue')
inProps = PropertyValue( "Hidden" , 0 , True, 0 )
document = desktop.loadComponentFromURL("file:///D:/salse.ods", "_blank", 0, inProps )
# get the sheet and read original data and formula from the sheet
sheets = document.getSheets()
xs = sheets["salse"]
cursor=xs.createCursor()
cursor.gotoStartOfUsedArea(False)
cursor.gotoEndOfUsedArea(True)
cra=cursor.getRangeAddress()
rng=xs.getCellRangeByPosition(cra.StartColumn,cra.StartRow,cra.EndColumn,cra.EndRow)
ft=rng.getFormulaArray()
#some code here to change values in ft....
# bellow took more than one minutes
rng.setFormulaArray(ft)
it is only a test of setFormulaArray performance
update1:
I still can't find the ideal solution,however, my solution is to create an identical sheet for each of the sheets in the workbook and add ,in the original sheets, references to the newly created sheets on the precisely same cells, for example:
example
sheetA is the original sheet
sheetA-1 is newly created one
then modify data cells in sheetA to reference to sheetA-1
then setDataArray to update the entire range in sheetA-1, data will automatically change in corresponding cells in sheetA
Related
I need formulas that do these things for me automatically
Count and display the number of new pieces of data added to the spreadsheet =TODAY()
On a separate sheet, list the number of new pieces of data added to the main sheet each day
Count the average number of pieces of content added to the spreadsheet in a given month
The challenge for me here is that every new data is being inserted on the top row of the spreadsheet just below the header so the start of the range changes every time. Is there a way to permanently include in the range all the newly inserted rows below the header? Here's the link to the spreadsheet
use in cell H1:
={COUNTIF(INDIRECT("D4:D"), TODAY());ARRAYFORMULA(COUNTIFS(
INDIRECT("B4:B"), "content", MONTH(
INDIRECT("D4:D")), MONTH(TODAY()))/COUNTIFS(MONTH(
INDIRECT("D4:D")), MONTH(TODAY())))}
Is it possible to create a line chart using summarized data from dataset?
My scenario is the following:
detail section: each one of the cells are one output field in the dataset
JAN FEB MAR
item1 R X R
item2 X A R
item3 R R R
footer section: here we count the occurrence of each value per month by using count aggregation elements and filter by value
TOTALS:
R 2 1 3
X 1 1 0
A 0 1 0
And what I need to do is to add a chart that shows something like this:
Needed Chart
And what I have is this:
report output
report design
In EXCEL this is really simple, but I cannot figure out how it can be done in BIRT.
I thought something like to create a new data set with 3 output fields for R, X, and A and each row will be one month, so I will have a transposed table and that way it will me much easier for graph it. But I cannot do it using the aggregation fields, and I cannot find out how to it with the output fields from the main dataset.
Any ideas? If you need the source rptdesign file I can provide it to you, but the logic in it could be hard to understand.
Any help is appreciated, and thanks in advance.
Have a great 2020.
First of all, a BIRT rule of thumb: if you need aggregations outside of a table, create them outside of a table. Do not try to access values in a table from the outside. It is possible, it may sometimes be the only solution, but it usually messes up your whole report, it is hard to debug, and even harder to maintain.
Aggregate
As your dataset looks quite simple and you already know how and what to aggregate, your first call should be computed columns in the dataset:
Here you aggregate in the language according to your datasource. If that is SQL, I guess a COUNT and GROUP BY statement will do the job.
Create all the columns you need for your graph here.
BTW: Computed columns are usually the silver bullet in BIRT. I use them for almost any pre-computation or custom field creation.
Visualize
You did not mention the library you are using for graphs, so I will assume you want to use the basic BIRT graphs. The basic charts with the months on the x-axis will do your job. I just want to add here that you have two options for multiple series:
You can either prepare your dataset so that you can feed the graph with a series per type (one line in your chart example) OR maybe easier: use optional y series grouping on your computed columns (as mentioned):
This way your graph will create the separate series for you. I hope this helps!
If you get stuck with the basic birt grpahs in general, you might want to think about finding a JavaScript-based graph library that does exactly what you need and implement that. Remember: you can put almost anything that is based on JavaScript into BIRT.
Final remark: For the sake of your report end users, please use a multi-bar chart. Line charts are not readable for overlapping values.
Thanks Kekzpanda for your help and time helping me in this question I had.
After struggling for a while I finally reach for a solution transposing the "table" of aggregations I had in the table footer, by using javascript arrays and an extra dataset and here are the steps I did in case someone else have the same problem:
For example, you need to transpose a table of 10 columns and 3 records
In report initialize method create an array with [10,3] dimension
// bi dimensional array indexes
var i=0;
var j=0;
// array definition and initialize it using 'for' iteration
var matriz = new Array(10);
for (i=0;i<10;i++){
matriz[i] = new Array(3);
}
// restart the array indexes in case you need to go through it in the future
i=0;
j=0;
Then you need to save the aggregation field value in one of the positions in the array. For that click on the aggregation field and go to the onCreate method and add the following code:
matriz[0][1]=this.getValue();
When finish all the aggregation fields, you will have an array with the transpose table. CHECKED!
Move the data in the array to the new dataset, select the fetch method and add something like this:
if(i >= array.length) return(false); // when you finish going through each item in the array.
row["A"]=matriz[i][0];
row["B"]=matriz[i][1];
row["C"]=matriz[i][2];
i++; // increment the first index by 1 to move to next row in the array
return (true);
Now you have your new data set with the transpose data.
Now work with this data set and graph the data, create the different series in the graph design for each column in the dataset.
Hope this help.
Bye.
Edit: This question relates to the "old" google spreadsheets that were current in 2013.
Is it possible to use named ranges in google spreadsheet charts?
I need to dynamically update the dimension (starting position and size) of a range and be able to reflect it in a chart, i.e. I need the chart source range to follow the position of moved source data range.
Extra information:
I've got two sheets. One has a header followed by 'input' row and data rows. When I type anything into 'input' row my script automatically adds a row just after this one and moves my new input there, therefore creating new row of data.
About chart: If I assign a data range that includes only data rows, new row won't be included because whole 'old' range got shifted. $ sign is ignored when specifying data range. So what I'm doing is to include in the range my 'input' + 1 header row. They have no values so in the chart you cannot see data points in the first two positions. I'm using a combo chart with another range from another sheet that has no header, so it looks like this:
(usually there is some value for the green histogram right at the origin of the chart).
So the line chart is shifted by two positions to the right, but it should start from position 0, so I want the chart to somehow follow the range or to redefine it, so it always stars from the first data row. I hope it makes sense.
Charts use range as argument, have you tried to simply use getRangeByName to define the range ?
Charts don't support named ranges. However, you can set up your chart so that the source data range is an entire column (for example, Sheet1!D:D). Then the chart will show all the values in that column, taking into account even rows that you will add (or delete) after chart creation. If the first rows (for example 4) contain table headers you can specify something like Sheet1!D5:D as your source data range
In searching for this, I found a solution I thought I'd share.
I have a set of data from which I'd like to create a set of charts. My fairly simple workaround was to create a new tab for each chart with a pivot table to pull in my data based on my criteria for each chart. I inserted a row at the top to hold a SUM of my values in the pivot table below, rather than referencing the moving "Total" cell in the pivot table itself.
Note: I hid the title row of the pivot table and unchecked the "Totals" within it to get the SUM to work correctly. Hope this helps someone!
I have a collumn with this contend inside:
=sheet1.F8
=sheet1.F15
=sheet1.F22
Normally in the excel I select the three cells and pull down, excel understand the logic and the next cell above will be "sheet1.F29". But if I do this in Libreoffice it puts "sheet1.F11".
Do you have any idea of how to do this ? (I have several sheets like this logic and I'd like to use Libreoffice instead Excel).
I found a workaround and replying here:
It is setting the reference to “F11” because the first row contains a reference to F8 and the new entry is 3 rows beneath this i.e., the offset is based on the first cell of the selection, thus 8+3=11. A workaround is to use the addresses of the destination and combine this with the INDIRECT() function. For example, create a separate column with “Sheet1.F8”, “Sheet1.F15”, and Sheet1.F22” etc. Highlight and drag this column of values to obtain a column of correct address references. Then adjacent to this use (given “Sheet1.F8” in sheet 2 cell A1) =INDIRECT(A1) in B1. Now drag this down the B column to obtain the required values.
Ok... So. I have an interactive report in APEX that displays data about documents in our system. Our documents are supposed to be reviewed every two years and there is a column in the table that contains the date that the document should be next reviewed by. We want to visually indicate either in the cell that contains the date, or another cell in the row, when the date reaches certain benchmarks (i.e. When the date is within 6 months of the current date, 3 months, 2, 1 etc.)
What I need to hopefully do is to change the color of the text (or background) of a specific cell based on either the value of that cell or the value of another cell in the same row. There would need to be a process or some function that does some computation to determine the span between the sysdate and the date contained in the cell... It does not necessarily need to calculate when the page is loaded but a daily or weekly process or something would be good.
Is it possible to set up processes or triggers that execute daily without human interaction? I also have a need to use the same functionality for sending reminder emails about these upcoming deadlines. (i.e. when a document is 6 months out from needing to be reviewed an email would be sent out, at 3 months, 2, 1 etc.)
Example with hiredate on emp: i'm about to colour the cells that are > 11000. (sysdate-hiredate ranges from about 10k to 12k). You could use a field you calculate in the query, or one you filled in through some procedure, doesn't matter :)
select empno, ename, job, mgr, hiredate, sal, comm, deptno,
trunc((trunc(sysdate)-hiredate)) to_colour_or_not
from emp
You will need 2 dynamic actions to colour rows in reports: an onload action, and an after refresh. If you skip the after refresh, rows won't be coloured after for example pagination, due to partial page refreshing.
Dynamic action one: After refresh on the region:
True action:
$("td[headers='TO_COLOUR_OR_NOT']").each(function(){
alert($(this).text());
if(parseInt($(this).text()) > 11000){
$(this).css({"background-color":"red"});
};
});
Example of using one column as a condition to colour another column. Always be carefull with what you test and what you test it for! For example, the hiredate column is a date, be sure to treat it as such if necessary! Further caution too: if your date format is set as DD-MON-YYYY, then you'd have to do the mapping for month to number (JAN = 1, DEC = 12)! Maybe it is an option to change the date format for this column even...
$("td[headers='HIREDATE']").each(function(){
var i_date = $(this).text();
//date format = MM/DD/YYYY
//be carefull with date formats.
//in my case, i know my date format and know it won't change
//my code is far from a complete parse of possible date values!
var dMonth = i_date.substring(0, 2),
dDay = i_date.substring(3, 5),
dYear = i_date.substring(6);
var d = new Date(dYear, dMonth, dDay, 0, 0, 0, 0);
if(d.getFullYear() <= 1981){
//we are looping over TD elements. I want to colour the
//column ENAME in red when the condition is true for this row.
//so, $(this) = TD element we loop with. parent = TR element,
//then back to the children of the row
$(this).parent().children("td[headers='ENAME']").css({"background-color":"red"});
};
});
The second dynamic action: on load
As true action, use the same code as the true action for the refresh.
With the JS you can do whatever you want: you just need to know which cells you wish to paint. Use the [headers=''] to target the cells you'd like (jquery selectors).
Instead of the css(), you can use addClass for example, if that is more what you'd like.
Do mind: IRs come with built-in mouseover actions. This causes your painted cells not to show in their colour when there is a mouseover action. If you don't want this, you'd need another dynamic action for the mouseover/mouseleave events, and target those cells necessary.
As for scheduled jobs: check out DBMS_JOBS.
In an Interactive Report, you also can define REPORTS ( with HIGHLIGHTS), which will do the trick for you.
Choose button ACTIONS, Then FORMAT (on Pop-up) and the HIGHLIGHT
In HighLight you can give a Name, Type (Row or Cell), Background and Text Colour, and the COLUMN on which you want an OPERATOR and an EXPRESSION.
You Apply the Highlight (Make Several Highlights /other-name when you need more than one colour-highlight depending on your EXPRESSION-s).
Together with the most important Filters then SAVE it as a Report (First Primary).
After having defined the Primary Report you can take this as the basis-report. Make some other Reports (and save them) with other filters, but with the same Highlights as the Primary Report).
Have FUN with it.