Skip to content

Commit b7f7051

Browse files
committed
Lots of fixes
I think this gets us almost all the way there. in insert_row, setting the idx is not implemented yet Also, ruff is complaining about: F405: 'ResultSet' may be undefined ... but I didn't know what to replace that with.
1 parent a9928e5 commit b7f7051

File tree

1 file changed

+38
-273
lines changed

1 file changed

+38
-273
lines changed

pysimplesql/pysimplesql.py

Lines changed: 38 additions & 273 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,10 +1009,10 @@ def requery(
10091009
self.rows = rows
10101010

10111011
if len(self.rows.index):
1012-
# if "sort_order" not in self.rows.attrs:
1013-
# # Store the sort order as a dictionary in the attrs of the DataFrame
1014-
# sort_order = self.rows[self.pk_column].to_list()
1015-
# self.rows.attrs["sort_order"] = {self.pk_column: sort_order}
1012+
# # if "sort_order" not in self.rows.attrs:
1013+
# # Store the sort order as a dictionary in the attrs of the DataFrame
1014+
# sort_order = self.rows[self.pk_column].to_list()
1015+
# self.rows.attrs["sort_order"] = {self.pk_column: sort_order}
10161016

10171017
# now we can restore the sort order
10181018
self.load_sort_settings(sort_settings)
@@ -1376,11 +1376,11 @@ def set_by_pk(
13761376
omit_elements = []
13771377
# don't update self/dependents if we are going to below anyway
13781378
self.prompt_save(update_elements=False)
1379-
1380-
self.current_index = self.rows.index[self.rows[self.pk_column] == pk].tolist()[
1381-
0
1382-
]
13831379

1380+
# find current index of pk in resorted rows (not in-place)
1381+
self.current_index = (
1382+
self.rows.sort_index().index[self.rows[self.pk_column] == pk].tolist()[0]
1383+
)
13841384
if update_elements:
13851385
self.frm.update_elements(self.table, omit_elements=omit_elements)
13861386
if requery_dependents:
@@ -1549,7 +1549,8 @@ def insert_record(
15491549
# Update the pk to match the expected pk the driver would generate on insert.
15501550
new_values[self.pk_column] = self.driver.next_pk(self.table, self.pk_column)
15511551

1552-
# Insert the new values using ResultSet.insert_row(), marking the new row as virtual
1552+
# Insert the new values using ResultSet.insert_row(),
1553+
# marking the new row as virtual
15531554
self.insert_row(new_values)
15541555

15551556
# and move to the new record
@@ -1704,8 +1705,8 @@ def save_record(
17041705
# resultset if possible. The expected pk may have changed from autoincrement
17051706
# and/or concurrent access.
17061707
pk = (
1707-
result.lastrowid
1708-
if result.lastrowid is not None
1708+
result.attrs["lastrowid"]
1709+
if result.attrs["lastrowid"] is not None
17091710
else self.get_current_pk()
17101711
)
17111712
current_row[self.pk_column] = pk
@@ -1722,7 +1723,7 @@ def save_record(
17221723

17231724
# Lets refresh our data
17241725
if self.row_is_virtual():
1725-
# Requery so that the new row honors the order clause
1726+
# Requery so that the new row honors the order clause
17261727
self.requery(select_first=False, update_elements=False)
17271728
if update_elements:
17281729
# Then move to the record
@@ -1745,6 +1746,9 @@ def save_record(
17451746
# do not commit or rollback
17461747
self.driver.commit()
17471748

1749+
# Sort so the saved row honors the current order.
1750+
self.sort(self.table)
1751+
17481752
if update_elements:
17491753
self.frm.update_elements(self.key)
17501754
logger.debug("Record Saved!")
@@ -2187,7 +2191,6 @@ def purge_virtual(self) -> None:
21872191
"""
21882192
# remove the rows where virtual is True in place, along with the corresponding
21892193
# virtual attribute
2190-
# remove the rows where virtual is True in place, along with the corresponding virtual attribute
21912194
idx_to_remove = [idx for idx, v in self.rows.attrs["virtual"].items() if v]
21922195
self.rows.drop(index=idx_to_remove, inplace=True)
21932196
for idx in idx_to_remove:
@@ -2214,13 +2217,14 @@ def sort_by_column(self, column: str, table: str, reverse=False) -> None:
22142217
rels = Relationship.get_relationships(table)
22152218
for rel in rels:
22162219
if column == rel.fk_column:
2217-
rows = rel.frm[
2218-
rel.parent_table
2219-
] # change the rows used for sort criteria
2220-
target_col = rel.pk_column # change our target column to look in
2221-
target_val = rel.frm[
2222-
rel.parent_table
2223-
].description_column # and return the value in this column
2220+
# change the rows used for sort criteria
2221+
rows = rel.frm[rel.parent_table]
2222+
2223+
# change our target column to look in
2224+
target_col = rel.pk_column
2225+
2226+
# and return the value in this column
2227+
target_val = rel.frm[rel.parent_table].description_column
22242228
break
22252229

22262230
print("rels", rels, "\n")
@@ -2351,17 +2355,19 @@ def insert_row(self, row: dict, idx: int = None) -> None:
23512355
row_series = pd.Series(row)
23522356
attrs = self.rows.attrs.copy()
23532357
self.rows = pd.concat([self.rows, row_series.to_frame().T], ignore_index=True)
2354-
self.rows.attrs = attrs.copy()
2355-
# not quite working, but closer
2356-
# just need to update attrs with new index
2357-
return
2358+
self.rows.attrs = attrs
2359+
2360+
# I don't have the idx parameter working yet
23582361
if idx is None:
23592362
idx = len(self.rows.index)
2360-
idx_label = self.rows.index.max() + 1 if len(self.rows.index) > 0 else 0
2361-
self.rows.loc[idx_label] = row_series
2362-
self.rows.attrs["sort_order"][self.pk_column].append(row[self.pk_column])
2363-
self.rows_attrs["virtual"].loc[idx_label] = True
2364-
self.rows.sort_index()
2363+
idx_label = self.rows.index.max() if len(self.rows.index) > 0 else 0
2364+
# self.rows.loc[idx_label] = row_series
2365+
# self.rows.attrs["sort_order"][self.pk_column].append(row[self.pk_column])
2366+
self.rows.attrs["virtual"].loc[idx_label] = True
2367+
2368+
2369+
# self.rows.sort_index() (this wasn't doing anything,
2370+
# since it defaults to inplace=False)
23652371

23662372

23672373
class Form:
@@ -3310,7 +3316,7 @@ def update_elements(
33103316
elif ":table_insert" in m["event"]:
33113317
parent = Relationship.get_parent(data_key)
33123318
if parent is not None:
3313-
disable = (
3319+
disable = bool(
33143320
len(self[parent].rows.index) == 0
33153321
or self._edit_protect
33163322
or Relationship.parent_virtual(data_key, self)
@@ -5289,18 +5295,10 @@ def __init__(
52895295
self.table_heading: TableHeadings = table_heading
52905296

52915297
def __call__(self, column):
5292-
# store the pk:
5293-
pk = self.frm[self.data_key].get_current_pk()
52945298
if len(self.frm[self.data_key].rows.index):
5299+
# sort_cycle takes care of storing pk and calling set_by_pk()
52955300
sort_order = self.frm[self.data_key].sort_cycle(column, self.data_key)
5296-
# We only need to update the selectors not all elements,
5297-
# so first set by the primary key, then update_selectors()
5298-
self.frm[self.data_key].set_by_pk(
5299-
pk,
5300-
update_elements=False,
5301-
requery_dependents=False,
5302-
skip_prompt_save=True,
5303-
)
5301+
53045302
self.frm.update_selectors(self.data_key)
53055303
self.table_heading.update_headings(self.element, column, sort_order)
53065304

@@ -6076,239 +6074,6 @@ def set(
60766074
return df
60776075

60786076

6079-
class ResultSet2(pd.DataFrame):
6080-
"""
6081-
The ResultSet class is a generic result class so that working with the resultset of
6082-
the different supported databases behave in a consistent manner. A `ResultSet` is a
6083-
Pandas dataframe with some extra functionality to make working with abstracted
6084-
database drivers easier.
6085-
6086-
ResultSets can be thought up as rows of information and are build from a Pandas
6087-
DataFrame. Iterating through a ResultSet
6088-
is very simple:
6089-
ResultSet = driver.execute('SELECT * FROM Journal;')
6090-
for index, row in rows.iteritems():
6091-
print(row['title'])
6092-
6093-
Note: The lastrowid is set by the caller, but by pysimplesql convention, the
6094-
lastrowid should only be set after and INSERT statement is executed.
6095-
"""
6096-
6097-
SORT_NONE = 0
6098-
SORT_ASC = 1
6099-
SORT_DESC = 2
6100-
6101-
def __init__(
6102-
self,
6103-
rows: List[Dict[str, Any]] = [],
6104-
lastrowid: int = None,
6105-
exception: str = None,
6106-
column_info: ColumnInfo = None,
6107-
) -> None:
6108-
"""
6109-
Create a new ResultSet instance.
6110-
6111-
:param rows: a list of dicts representing a row of data, with each key being a
6112-
column name
6113-
:param lastrowid: The primary key of an inserted item.
6114-
:param exception: If an exception was encountered during the query, it will be
6115-
passed along here
6116-
:column_info: a `ColumnInfo` object can be supplied so that column information
6117-
can be accessed
6118-
"""
6119-
super().__init__(rows) # initialize the DataFrame with the row values
6120-
self.lastrowid = lastrowid
6121-
self.exception = exception
6122-
self.column_info = column_info
6123-
self.sort_column = None
6124-
self.sort_reverse = False
6125-
self.attrs["original_index"] = self.index.copy() # Store the original index
6126-
self.attrs["virtual"] = pd.Series(
6127-
[False] * len(self), index=self.index, dtype=bool
6128-
) # Store virtual flags for each row
6129-
6130-
def __str__(self):
6131-
return str(self.to_dict(orient="records"))
6132-
6133-
def fetchone(self) -> pd.Series:
6134-
"""
6135-
Fetch the first record in the ResultSet.
6136-
6137-
:returns: A `pd.Series` object representing the row
6138-
"""
6139-
return self.iloc[0] if len(self) else pd.Series(dtype=object)
6140-
6141-
def fetchall(self) -> ResultSet:
6142-
"""
6143-
ResultSets don't actually support a fetchall(), since the rows are already
6144-
returned. This is more of a comfort method that does nothing, for those that are
6145-
used to calling fetchall().
6146-
6147-
:returns: The same ResultSet that called fetchall()
6148-
"""
6149-
return self
6150-
6151-
def insert_row(self, row: dict, idx: int = None) -> None:
6152-
"""
6153-
Insert a new virtual row into the `ResultSet`. Virtual rows are ones that exist
6154-
in memory, but not in the database. When a save action is performed, virtual
6155-
rows will be added into the database.
6156-
6157-
:param row: A dict representation of a row of data
6158-
:param idx: The index where the row should be inserted (default to last index)
6159-
:returns: None
6160-
"""
6161-
row_series = pd.Series(row)
6162-
if idx is None:
6163-
idx = len(self.index)
6164-
idx_label = self.index.max() + 1 if len(self) > 0 else 0
6165-
self.loc[idx_label] = row_series
6166-
self.attrs["original_index"] = self.attrs["original_index"].insert(
6167-
idx, idx_label
6168-
)
6169-
self.attrs["virtual"].loc[idx_label] = True
6170-
self.sort_index()
6171-
6172-
def purge_virtual(self) -> None:
6173-
"""
6174-
Purge virtual rows from the DataFrame.
6175-
6176-
:returns: None
6177-
"""
6178-
virtual_rows = self.attrs["virtual"][self.attrs["virtual"]].index
6179-
self.drop(virtual_rows, inplace=True)
6180-
self.attrs["original_index"] = self.attrs["original_index"].drop(virtual_rows)
6181-
self.attrs["virtual"] = self.attrs["virtual"].drop(virtual_rows)
6182-
6183-
def sort_by_column(self, column: str, table: str, reverse=False) -> None:
6184-
"""
6185-
Sort the `ResultSet` by column. Using the mapped relationships of the database,
6186-
foreign keys will automatically sort based on the parent table's description
6187-
column, rather than the foreign key number.
6188-
6189-
:param column: The name of the column to sort the `ResultSet` by
6190-
:param table: The name of the table the column belongs to
6191-
:param reverse: Reverse the sort; False = ASC, True = DESC
6192-
:returns: None
6193-
"""
6194-
# Target sorting by this ResultSet
6195-
rows = self # search criteria is based on rows
6196-
target_col = column # Looking in rows for this column
6197-
target_val = column # to be equal to the same column in self.rows
6198-
6199-
# We don't want to sort by foreign keys directly - we want to sort by the
6200-
# description column of the foreign table that the foreign key references
6201-
rels = Relationship.get_relationships(table)
6202-
for rel in rels:
6203-
if column == rel.fk_column:
6204-
rows = rel.frm[
6205-
rel.parent_table
6206-
] # change the rows used for sort criteria
6207-
target_col = rel.pk_column # change our target column to look in
6208-
target_val = rel.frm[
6209-
rel.parent_table
6210-
].description_column # and return the value in this column
6211-
break
6212-
6213-
def get_sort_key(row):
6214-
try:
6215-
return next(
6216-
r[target_val]
6217-
for _, r in rows.iterrows()
6218-
if r[target_col] == row[column]
6219-
)
6220-
except StopIteration:
6221-
return None
6222-
6223-
try:
6224-
self.sort_values(
6225-
by=self.index, key=get_sort_key, ascending=not reverse, inplace=True
6226-
)
6227-
except KeyError:
6228-
logger.debug(f"ResultSet could not sort by column {column}. KeyError.")
6229-
6230-
def sort_by_index(self, index: int, table: str, reverse=False):
6231-
"""
6232-
Sort the `ResultSet` by column index Using the mapped relationships of the
6233-
database, foreign keys will automatically sort based on the parent table's
6234-
description column, rather than the foreign key number.
6235-
6236-
:param index: The index of the column to sort the `ResultSet` by
6237-
:param table: The name of the table the column belongs to
6238-
:param reverse: Reverse the sort; False = ASC, True = DESC
6239-
:returns: None
6240-
"""
6241-
column = self.columns[index]
6242-
self.sort_by_column(column, table, reverse)
6243-
6244-
def store_sort_settings(self) -> list:
6245-
"""
6246-
Store the current sort settingg. Sort settings are just the sort column and
6247-
reverse setting. Sort order can be restored with
6248-
`ResultSet.load_sort_settings()`.
6249-
6250-
:returns: A list containing the sort_column and the sort_reverse
6251-
"""
6252-
return [self.sort_column, self.sort_reverse]
6253-
6254-
def load_sort_settings(self, sort_settings: list) -> None:
6255-
"""
6256-
Load a previously stored sort setting. Sort settings are just the sort columm
6257-
and reverse setting.
6258-
6259-
:param sort_settings: A list as returned by `ResultSet.store_sort_settings()`
6260-
"""
6261-
self.sort_column = sort_settings[0]
6262-
self.sort_reverse = sort_settings[1]
6263-
6264-
def sort_reset(self) -> None:
6265-
"""
6266-
Reset the sort order to the original when this ResultSet was created. Each
6267-
ResultRow has the original order stored.
6268-
6269-
:returns: None
6270-
"""
6271-
self.sort_index(inplace=True)
6272-
6273-
def sort(self, table: str) -> None:
6274-
"""
6275-
Sort according to the internal sort_column and sort_reverse variables. This is a
6276-
good way to re-sort without changing the sort_cycle.
6277-
6278-
:param table: The table associated with this ResultSet. Passed along to
6279-
`ResultSet.sort_by_column()`
6280-
:returns: None
6281-
"""
6282-
if self.sort_column is None:
6283-
self.sort_reset()
6284-
else:
6285-
self.sort_by_column(self.sort_column, table, self.sort_reverse)
6286-
6287-
def sort_cycle(self, column: str, table: str) -> int:
6288-
"""
6289-
Cycle between original sort order of the ResultSet, ASC by column, and DESC by
6290-
column with each call.
6291-
6292-
:param column: The column name to cycle the sort on
6293-
:param table: The table that the column belongs to
6294-
:returns: A ResultSet sort constant; ResultSet.SORT_NONE, ResultSet.SORT_ASC, or
6295-
ResultSet.SORT_DESC
6296-
"""
6297-
if column != self.sort_column:
6298-
self.sort_column = column
6299-
self.sort_reverse = False
6300-
self.sort(table)
6301-
return ResultSet.SORT_ASC
6302-
if not self.sort_reverse:
6303-
self.sort_reverse = True
6304-
self.sort(table)
6305-
return ResultSet.SORT_DESC
6306-
self.sort_reverse = False
6307-
self.sort_column = None
6308-
self.sort(table)
6309-
return ResultSet.SORT_NONE
6310-
6311-
63126077
class ReservedKeywordError(Exception):
63136078
pass
63146079

0 commit comments

Comments
 (0)