Skip to content

Commit

Permalink
new item module
Browse files Browse the repository at this point in the history
  • Loading branch information
moxxos committed Dec 5, 2022
1 parent 0ba5b05 commit 4f2c02f
Show file tree
Hide file tree
Showing 8 changed files with 43,021 additions and 57 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [0.1.6.] - 2022-12-04
### New Item module
#

## [0.1.5.] - 2022-10-18
### Display output now works on Windows.
### Added image caching.
Expand Down
70 changes: 56 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# GPPC (Gold Piece Price Checker)

Check OSRS Grand Exchange prices from the command line.
Includes limited module functionality.
Includes module functionality.

# Usage

Installation
### Installation

```bash
$ pip install gppc
Expand All @@ -17,28 +17,70 @@ $ cd gppc
$ pip install .
```

Get the price and recent 24h change of many different Grand Exchange items.
### Get the price and recent 24h change of many different Grand Exchange items.

```bash
$ gppc 'gold bar' coal
$ gppc gold_bar coal
```
![Image](https://raw.githubusercontent.com/moxxos/gppc/main/gppc_example.jpg)

Import as a module
### Import as a module

```bash
$ import gppc
$ gppc.search('coal')
```python
>>> import gppc
>>> gppc.search('coal')
```

## New Feature
### Create item instances to check item recent history.


```python
>>> from gppc import Item
>>> coal = Item('coal')
>>> coal.recent_historical
Price Average Volume
Date
2022-06-09 161 160 46780917
2022-06-10 164 160 13746833
2022-06-11 164 160 44698810
... ... ... ...
2022-12-02 171 163 24461683
2022-12-03 174 163 43027137
2022-12-04 173 163 25469205
[179 rows x 3 columns]
```
### Save multiple item histories for future use.
```python
>>> coal.save_historical()
SAVED ITEM: Coal, id: 453
179 NEW DATES CREATED
179 RECORDS UPDATED

>>> bond.save_historical()
SAVED ITEM: Old school bond, id: 13190
179 RECORDS UPDATED
```
### Check item full history at a later date if past history is saved.
```python
>>> coal.full_historical
Price Average Volume
Date
2022-06-09 161 160 46780917
2022-06-10 164 160 13746833
2022-06-11 164 160 44698810
... ... ... ...
2023-12-02 171 163 24461683
2023-12-03 174 163 43027137
2023-12-04 173 163 25469205
[544 rows x 3 columns]
```

# Updates

#### TODO
- [ ] Add option for a full version showing all item data
- [ ] Add option to disable images
- [ ] Add compact version with no images and smaller vertical display
- [x] Add some caching for images and other static item data
- [ ] Add tests and documentation
## [CHANGELOG](https://github.com/moxxos/gppc/blob/main/CHANGELOG.md)

## [CHANGELOG](https://github.com/moxxos/gppc/blob/main/CHANGELOG.md)
#### TODO
- [ ] Finish command line display
- [ ] Add tests and documentation
5 changes: 1 addition & 4 deletions src/gppc/__description__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@
__title__ = "gppc"
__author__ = "moxxos"
__copyright__ = "Copyright (C) 2022 moxxos"
__short_description__ = """
Check OSRS Grand Exchange prices from the command line.
Includes limited module functionality.
"""
__short_description__ = "Check OSRS Grand Exchange prices from the command line. Includes module functionality to check item full price history."
2 changes: 1 addition & 1 deletion src/gppc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
GPPC (Gold Piece Price Checker)
-------------------------------
Check OSRS Grand Exchange prices from the command line.
Includes limited module functionality.
Includes module functionality to check full item price history.
Copyright (C) 2022 moxxos
"""
Expand Down
52 changes: 31 additions & 21 deletions src/gppc/_db.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Implements the caching functionality.
Implements caching functionality.
Copyright (C) 2022 moxxos
"""
Expand All @@ -14,7 +14,6 @@


_DATABASE_NAME = 'gppc.sql'
_USE_MEMORY = True
_MAINTABLE_NAME = 'items'
_ITEM_ID = 'id'
_ITEM_NAME = 'item_name'
Expand All @@ -37,11 +36,13 @@ def __init__(self) -> None:

self.__past_dates = [desc[0] for desc in self.__db_cur.execute(
f"SELECT * FROM {_MAINTABLE_NAME}").description][3:]
self.__today = date.today()
self.__past_dates = [self.__format_date(
past_date) for past_date in self.__past_dates]

def __init_db(self) -> None:
res = self.__db_cur.execute(
f"SELECT name FROM sqlite_master WHERE name='{_MAINTABLE_NAME}'")
res = self.__db_cur.execute(f"""SELECT name
FROM sqlite_master
WHERE name='{_MAINTABLE_NAME}'""")
if res.fetchone() is not None:
return
self.__db_cur.execute(f"""CREATE TABLE
Expand All @@ -53,14 +54,17 @@ def close_db(self) -> None:
"""Close the db."""
self.__db_conn.close()

@property
def past_dates(self):
return self.__past_dates

def add_date_column(self, input_date: date):
self.__db_cur.execute(
f"""ALTER TABLE {_MAINTABLE_NAME}
ADD COLUMN {self.__format_date(input_date)}""")
self.__db_cur.execute(f"""ALTER TABLE {_MAINTABLE_NAME}
ADD COLUMN {self.__format_date(input_date)}""")
self.__db_conn.commit()

def does_date_exist(self, input_date: date):
return self.__format_date(input_date) in self.__past_dates
return input_date in self.__past_dates

def check_item_date(self, item_id: str, input_date: date) -> bool:
"""Check if an item has historical data stored on the input date."""
Expand All @@ -70,7 +74,8 @@ def check_item_date(self, item_id: str, input_date: date) -> bool:
return True
return False

def store_item_date(self, item_id, input_date: date, price: str, average: str, volume: str):
def store_item_date(self, item_id, input_date: date,
price: str, average: str, volume: str):
"""
Store an items historical data on the given date.
Assumes the item's basic data and the date exists.
Expand All @@ -88,8 +93,9 @@ def get_item_date(self, item_id: str, input_date: date | str):
"""Get item data on the input date. Assumes the date exists."""
if (isinstance(input_date, date)):
input_date = self.__format_date(input_date)
res = self.__db_cur.execute(f"""SELECT {self.__format_date(input_date)}
FROM {_MAINTABLE_NAME} WHERE {_ITEM_ID}='{item_id}'""")
res = self.__db_cur.execute(f"""SELECT {input_date}
FROM {_MAINTABLE_NAME}
WHERE {_ITEM_ID}='{item_id}'""")
if (item_date := res.fetchone()) is not None:
return item_date[0]
return item_date
Expand All @@ -102,21 +108,25 @@ def get_item_past(self, item_id: str):
"""
past_date_data = []
for past_date in self.__past_dates:
past_date_data.append(
(past_date, *self.__format_date_data(
date_data_str=self.get_item_date(item_id, past_date))))
if not self.check_item_date(item_id, past_date):
print('WARNING MISSING DATE DATA: ' + str(past_date))
else:
past_date_data.append(
(past_date, *self.__format_date_data(
date_data_str=self.get_item_date(item_id, past_date))))
return past_date_data

def is_item_stored(self, item_id: str) -> bool:
"""Check if an item is stored."""
res = self.__db_cur.execute(
f"SELECT {_ITEM_ID} FROM {_MAINTABLE_NAME} WHERE {_ITEM_ID}='{item_id}'")
res = self.__db_cur.execute(f"""SELECT {_ITEM_ID}
FROM {_MAINTABLE_NAME}
WHERE {_ITEM_ID}='{item_id}'""")
return res.fetchone() is not None

def store_item(self, item_id: str, item_name: str, item_smimg: str) -> None:
"""Store an item."""
self.__db_cur.execute(f"""INSERT INTO
{_MAINTABLE_NAME} ({_ITEM_ID}, {_ITEM_NAME}, {_SM_IMG})
self.__db_cur.execute(f"""INSERT INTO
{_MAINTABLE_NAME} ({_ITEM_ID}, {_ITEM_NAME}, {_SM_IMG})
VALUES(?, ?, ?)""", (item_id, item_name, item_smimg))
self.__db_conn.commit()

Expand All @@ -128,13 +138,13 @@ def retrieve_item(self, item_id: str) -> tuple[str, str]:
WHERE {_ITEM_ID}='{item_id}'""")
return res.fetchone()

@staticmethod
@ staticmethod
def __format_date(input_date: date | str) -> str | date:
if isinstance(input_date, date):
return '_' + str(input_date).replace('-', '_')
return date(*list(map(int, input_date.split('_')[1:])))

@staticmethod
@ staticmethod
def __format_date_data(price=None, average=None, volume=None, date_data_str=None):
if (price is None and date_data_str is not None):
return date_data_str.split('|')
Expand Down
1 change: 0 additions & 1 deletion src/gppc/_gppc.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
# Standard Library
import argparse
from html.parser import HTMLParser
from datetime import date

# External Packages
import requests
Expand Down
74 changes: 58 additions & 16 deletions src/gppc/_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
from html.parser import HTMLParser

import requests
import pandas

from gppc._gppc import _search_item_data
from gppc._db import DbManager
from gppc._display import _get_item_pic


_WIKI_API = ''
_BACKUP_API = ''
_VAR_PRICE = 'average180'
_VAR_TRADE = 'trade180'
_DATA_START = '.push([new Date(\''
Expand All @@ -24,7 +26,7 @@
_DATA_START_LEN = len(_DATA_START)
_DATA_END_LEN = len(_DATA_END)

ItemHistoryData = list[str, str, str, str]
ItemHistoryData = tuple[str, str, str, str]


class Item():
Expand All @@ -41,17 +43,7 @@ def __init__(self, item: str):
# (item_name, item_id, item_price, item_change, item_url, item_pic_url)
self.__item_data = search_results[item_index]

# Store item basic data if it does not already exist
db_man = DbManager()

if db_man.is_item_stored(self.__item_data[1]):
_, self.__item_pic = db_man.retrieve_item(self.__item_data[1])
else:
self.__item_pic = _get_item_pic(self.__item_data[5])
db_man.store_item(self.__item_data[1],
self.__item_data[0], self.__item_pic)

db_man.close_db()
self.__init_item()

self.__item_parser = self._ItemPageParser()
self.__item_parser.feed(item_page.text)
Expand All @@ -65,9 +57,23 @@ def __init__(self, item: str):
else:
raise Exception('Item not found')

def __init_item(self):
# Store item basic data if it does not already exist
db_man = DbManager()

if db_man.is_item_stored(self.__item_data[1]):
_, self.__item_pic = db_man.retrieve_item(self.__item_data[1])
else:
self.__item_pic = _get_item_pic(self.__item_data[5])
db_man.store_item(self.__item_data[1],
self.__item_data[0], self.__item_pic)

db_man.close_db()

@property
def recent_historical(self):
return tuple(self.__recent_historical)
"""Returns the most recent 6-month item history."""
return Item.__format_history(self.__recent_historical)

@property
def gp_change_stats(self):
Expand All @@ -79,22 +85,49 @@ def pc_change_stats(self):

@property
def full_historical(self):
"""
Stores recent historical item date in the cache. Then retrieves
all item history data.
"""

self.__init_item() # for debugging purposes
self.save_historical()
db_man = DbManager()
full_historical = tuple(db_man.get_item_past(self.__item_data[1]))

db_man.close_db()

def save_historical(self):
return Item.__format_history(full_historical)

def save_historical(self) -> int:
"""
Stores all current and historical item data in the app cache. If the item
Stores recent historical item data in the cache. If the item
already exists it will read the existing data and add any new historical data.
"""

update_count = 0
create_count = 0
db_man = DbManager()

for item_date_data in self.__recent_historical:
# Date, Price, Average, Volume
if not db_man.does_date_exist(item_date_data[0]):
db_man.add_date_column(item_date_data[0])
create_count += 1
if not db_man.check_item_date(self.__item_data[1], item_date_data[0]):
db_man.store_item_date(self.__item_data[1], *item_date_data)
update_count += 1

db_man.close_db()
if (create_count or update_count):
print(
f"SAVED ITEM: {self.__item_data[0]}, id: {self.__item_data[1]}")
if (create_count):
print(f"{create_count} NEW DATES CREATED")
if (update_count):
print(f"{update_count} RECORDS UPDATED")

return update_count

@ staticmethod
def __process_raw_history(raw_data: str,
Expand Down Expand Up @@ -123,6 +156,15 @@ def __process_raw_history(raw_data: str,
trade))
return Item.__process_raw_history(raw_data[loc:], output_array)

@staticmethod
def __format_history(data_historical: list[ItemHistoryData]):
data = list(map(lambda date_data: date_data[1:], data_historical))
dates = list(map(lambda date_data: date_data[0], data_historical))
return pandas.DataFrame(data, index=dates,
columns=['Price',
'Average',
'Volume']).rename_axis('Date')

class _ItemPageParser(HTMLParser):
def __init__(self) -> None:
super().__init__()
Expand Down
Loading

0 comments on commit 4f2c02f

Please sign in to comment.