Skip to content

Commit

Permalink
Fixed index search issue, fixed but with getting all files, and other…
Browse files Browse the repository at this point in the history
… minor bug fixes
  • Loading branch information
OwenCochell committed May 16, 2022
1 parent 61a857d commit b3d5ad5
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 38 deletions.
4 changes: 2 additions & 2 deletions cursepy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

# Import the necessary components:

from cursepy.wrapper import CurseClient
from cursepy.wrapper import CurseClient, MinecraftWrapper


# Define some metadata here:

__version__ = '1.2.0'
__version__ = '1.3.0'
__author__ = 'Owen Cochell'
24 changes: 10 additions & 14 deletions cursepy/classes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ def low_download(self, url: str, path: str=None) -> bytes:

# Get the data:

data = url_proto.get_data('')
data = url_proto.get_data(url)

# Determine if we should write to a file:

Expand Down Expand Up @@ -602,14 +602,20 @@ class CurseDependency(BaseCurseInstance):

"""
CurseDependency - Represents a dependency of an addon.
A dependency is an addon file that is wanted or required by another addon.
This class represents these dependencies.
We contain useful metadata on each dependency,
such as the addon ID and file ID this addon is apart of.
We also offer methods to retrieve the addon and file.
In some cases, we will not have all the required dependency information
such as the dependency ID and file ID.
This can happen if you use the ForgeSVC handlers and get ALL addon files
instead of requesting a specific one.
If this dependency instance is limited, then 'id' and 'file_id' will be None.
We contain the following parameters:
* id - ID of the dependency
Expand Down Expand Up @@ -650,16 +656,6 @@ def addon(self) -> CurseAddon:

return self.hands.addon(self.addon_id)

def file(self) -> CurseFile:
"""
Gets the CurseFile this dependency is apart of.
:return: CurseFile this dependency is apart of
:rtype: CurseFile
"""

return self.hands.file(self.addon_id, self.file_id)


@dataclass
class CurseAddon(BaseCurseInstance):
Expand Down
50 changes: 47 additions & 3 deletions cursepy/classes/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class SearchParam:
We define the following values:
* filter - Term to search for(i.e, 'Inventory Mods')
* index - Page index of results to view
* index - Addon index to start on
* pageSize - Number of items to display per page
* gameVersion - Game version to search under
* sort - Sorting method to use
Expand All @@ -34,8 +34,8 @@ class SearchParam:
"""

searchFilter: Optional[str] = field(default=None) # Term to search for
index: Optional[int] = field(default=None) # Page of search results to view
pageSize: Optional[int] = field(default=None) # Number of items to display per page
index: Optional[int] = field(default=0) # Index of addon to start on
pageSize: Optional[int] = field(default=20) # Number of items to display per page
gameVersion: Optional[int] = field(default=None) # Game version to use
sort: Optional[int] = field(default=None) # Sort method to use

Expand All @@ -59,6 +59,50 @@ def asdict(self) -> dict:

return asdict(self)

def set_page(self, num: int):
"""
Changes the page we are on.
We change the index to reach the next page,
we use this equation to determine this:
index = num * pageSize
This will set the index to the given page.
For example, if we set the page number to two,
have a page size of five, and an index of three,
then the resulting index after the operation will be 10.
:param page: Page number to set the index to
:type page: int
"""

self.index = num * self.pageSize

def bump_page(self, num: int=1):
"""
Bumps the page up or down.
We add the page change to the current index using this equation:
index += num * pageSize
This will change the page in relation with the current index.
For example, if you bump the page up twice, have a page size of five
and an index of three, the resulting index after the operation will be 13.
You can supply a negative number to bump the page downward.
If the index ends up below zero, then we will set the index to zero.
:param num: Number of pages to bump
:type num: int
"""

self.index += num * self.pageSize

if self.index < 0:

self.index = 0

def url_convert(search: SearchParam, url: str='') -> str:
"""
Expand Down
14 changes: 12 additions & 2 deletions cursepy/handlers/forgesvc.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ def format(self, data: dict) -> Tuple[base.CurseFile, ...]:

# Convert the file:

final.append(SVCFile.low_format(file, id))
final.append(SVCFile.low_format(file, id, limited=True))

# Return the data:

Expand Down Expand Up @@ -527,13 +527,17 @@ def format(self, data: dict) -> base.CurseFile:
return self.low_format(data, id)

@staticmethod
def low_format(data: dict, addon_id: int) -> base.CurseFile:
def low_format(data: dict, addon_id: int, limited=False) -> base.CurseFile:
"""
Low-level format method.
We are static to allow other classes to use us to format data.
The actual 'format()' method will extract the addon ID from the URL
and pass it along to us.
In some cases, we are not given full dependence info,
for example when all files for a given addon is requested.
You can use the 'limited' parameter to prevent a key error.
:param data: Data to be formatted
:type data: dict
Expand All @@ -549,6 +553,12 @@ def low_format(data: dict, addon_id: int) -> base.CurseFile:

for depen in data['dependencies']:

if limited:

final.append(base.CurseDependency(None, depen['addonId'], None, depen['type']))

continue

# Add the dependency ID's:

final.append(base.CurseDependency(depen['id'], depen['addonId'], depen['fileId'], depen['type']))
Expand Down
22 changes: 19 additions & 3 deletions cursepy/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def iter_search(self, game_id: int, category_id: int, search: SearchParam=None)

# No results! Raise 'StopIteration'!

raise StopIteration()
break

# Iterate over the result:

Expand All @@ -179,7 +179,7 @@ def iter_search(self, game_id: int, category_id: int, search: SearchParam=None)

# Bump the index and continue:

search.index =+ 1
search.bump_page()

def addon(self, addon_id: int) -> base.CurseAddon:
"""
Expand Down Expand Up @@ -324,6 +324,7 @@ class MinecraftWrapper(CurseClient):
MODPACKS = 4471
MODS = 6
WORLDS = 17
BUKKIT = 5

def search_resource_packs(self, search: SearchParam=None) -> Tuple[base.CurseAddon, ...]:
"""
Expand Down Expand Up @@ -377,10 +378,25 @@ def search_worlds(self, search: SearchParam=None) -> Tuple[base.CurseAddon, ...]
:param search: SearchParam to use, defaults to None
:type search: SearchParam, optional
:return: Tuple of CurseAddons, optional
:return: Tuple of CurseAddons
:rtype: Tuple[base.CurseAddon, ...]
"""

# Search the worlds:

return self.search(MinecraftWrapper.GAME_ID, MinecraftWrapper.WORLDS, search)

def search_plugins(self, search: SearchParam=None) -> Tuple[base.CurseAddon]:
"""
Searches the Plugins category for addons.
Again, we use the SearchParam for the search operation.
:param search: SearchParam to use, defaults to None
:type search: SearchParam, optional
:return: Tuple of CurseAddons
:rtype: Tuple[base.CurseAddon]
"""

# Search the plugins:

return self.search(MinecraftWrapper.GAME_ID, MinecraftWrapper.BUKKIT, search)
28 changes: 21 additions & 7 deletions docs/source/basic/collection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,23 @@ Check out this example of sorting by popularity:
'index' and 'pageSize' are used since search
results are usually limited to 'pages'
to save some bandwidth.
'pageSize' is the size of each page.
For example, if your page size is five, then you will get five results
with each search operation.

'index' is the page to retrieve,
and 'pageSize' is the size of each page.
'index' is NOT the page number.
Instead, it is the addon to start the search operation at.
For example, if you have index set at three, you will NOT be at page three.
instead, the search operation will start at the fourth addon.

If you wish to traverse pages, you can use the 'set_page()' and 'bump_page()' methods.
The 'set_page()' method sets the index to the given page.
The 'bump_page()' method adds the pages to the index.

For example, lets say you have a search parameter with an index of three,
and a page size of five.
If you set the page to three, then the index will be set to 15.
If you bump the page three times, then the index will be set to 18.

Here is an example of getting the second page of search results:

Expand All @@ -353,9 +367,9 @@ Here is an example of getting the second page of search results:
search = client.get_search()
# Set the page index to 1:
# Set the index to page 2:
search.index = 1
search.set_page(2)
# Get the results:
Expand Down Expand Up @@ -388,9 +402,9 @@ and print each name:
print(addon.name)
'iter_search' only bumps the index after each call,
so you can start at a page by setting the 'index'
value on the SearchParam before passing it along.
'iter_search' calls 'bump_page()' after each call,
so you can start at a certain index
on the SearchParam before passing it along.
The 'iter_search' does not alter any other parameters,
so your search preferences will be saved.

Expand Down
14 changes: 10 additions & 4 deletions docs/source/basic/curse_inst.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ and how to decode it.
Because cursepy is modular, this data can be literally anything!
So having a standardized way to get this data is very important.

CIs also provide all addon info in a convent way.
CIs also provide all addon info in a convenient way.
Most users do not want to manually parse request data!

Finally, CI's convenience methods make using cursepy much easier.
Expand Down Expand Up @@ -124,7 +124,7 @@ More info can be found in the API reference for CIs.
assume that 'inst' is a valid CurseInstance
of the type being described.

Before we gte into CI types,
Before we get into CI types,
we will first go over common features every CI has.

Every CI should have attributes which store the raw data and metadata of the request.
Expand Down Expand Up @@ -303,7 +303,7 @@ Here is an example of a custom formatter that appends 'Super Slick!' to the end
# Import BaseFormat:
from cursepy.formatters import BaseFormat
class SuperFormatter(BaseFormat):
def format(self, data: str) -> str:
Expand Down Expand Up @@ -369,7 +369,7 @@ and provides methods for getting sub and parent catagories.
* icon - Icon of the category(CurseAttachment)
* date - Date this category was created

If you read the into tutorial
If you read the intro tutorial
(You did read the into tutorial right?),
then you will know that catagories can have
parent and sub-catagories.
Expand Down Expand Up @@ -529,6 +529,12 @@ You can also compare the type of the dependency with the 'REQUIRED' and 'OPTIONA
To get the addon and addon file this dependency is a member of,
you can use the 'addon()' and 'file()' methods.

Sometimes, the CurseDependency will have limited information.
This can happen when you use the ForgeSVC handlers and get all addon files.
In this case, the CurseDependency will have limited information,
where the 'id' and 'file_id' will be None.
This is a limitation of the ForgeSVC backend, not cursepy.

Conclusion
==========

Expand Down
4 changes: 2 additions & 2 deletions docs/source/basic/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,13 @@ cursepy Terms
cursepy has a few concepts that might be better understood with some explanation.
We only touch on these concepts, we will go into much greater detail later in this tutorial.
Don't worry about completely understanding these for now,
just keep these concepts in mind.
just keep these concepts in mind.

CurseInstance
-------------

A 'CurseInstance' is a class that represents
CF information, and makes getting related info easier.
CF information, and makes getting related info easier.

For example, there is a class called 'CurseAddon'
which represents a game addon. The class
Expand Down
6 changes: 6 additions & 0 deletions docs/source/basic/wrap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ We have the following constants:
* MODPACKS - ID of the modpack category(4471)
* MODS - ID of the mods category(6)
* WORLDS - ID of the worlds category(17)
* BUKKIT - ID of the bukkit category(5)

You can use these constants in the entry point methods.
MinecraftWrapper also provides some methods to make searching easier.
Expand All @@ -65,6 +66,11 @@ search_worlds

Searches the worlds category for addons.

search_plugins
--------------

Searches the Bukkit plugins category for addons.

Conclusion
==========

Expand Down
Loading

0 comments on commit b3d5ad5

Please sign in to comment.