Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
de9603d
added link to bracket if we have one in the database (only challonge …
Argonaut6 Aug 1, 2016
1581194
Add external url to challonge/smashgg tournaments. run add_url.py to …
Argonaut6 Aug 4, 2016
14eb96a
suggested changes
Argonaut6 Aug 4, 2016
87b306d
no message
Argonaut6 Aug 5, 2016
5acb08a
"fixed" import (just created config parameters manually)
Argonaut6 Aug 5, 2016
44ebbf1
move url script back to root, pulling config from config file
Argonaut6 Aug 5, 2016
ab8d68d
point to correct collection when updating pending_tournaments
Argonaut6 Aug 6, 2016
835a6c2
Update README.md
jschnei Aug 15, 2016
c612524
Update README.md
jschnei Aug 15, 2016
5e2529b
Added start script with Windows ending lines. Any Windows developer s…
Aug 20, 2016
5cd4a85
quickfix: remove console.logs from webapp (especially ones that write…
jschnei Aug 22, 2016
d72829a
ignore OOR players (#62)
jhertz Aug 22, 2016
f6974ea
hotfix: Added Georgia to the list of regions. Renamed instaces of NJ …
Aug 22, 2016
b16bf92
hotfix: SmashGG Importer now natively ignores empty phases
Aug 22, 2016
76c88a4
quickfix: save alias mappings on submit
jschnei Aug 23, 2016
7c207a6
Added new regions to the UI.
Aug 23, 2016
5cf989b
Added new regions to the UI.
Aug 23, 2016
420cf02
Hotfix: Sort players properly, not case sensitive
Aug 24, 2016
21eb860
SmashGG - Importer GUI Phase Listings (#91)
BrandonCookeDev Aug 24, 2016
c1db0f1
make TestServer faster by not setting up db every time (#107)
jschnei Aug 29, 2016
fac61c5
added link to bracket if we have one in the database (only challonge …
Argonaut6 Aug 1, 2016
1a34d29
Add external url to challonge/smashgg tournaments. run add_url.py to …
Argonaut6 Aug 4, 2016
ac7b086
suggested changes
Argonaut6 Aug 4, 2016
816f3e3
no message
Argonaut6 Aug 5, 2016
5cad70e
"fixed" import (just created config parameters manually)
Argonaut6 Aug 5, 2016
082860b
move url script back to root, pulling config from config file
Argonaut6 Aug 5, 2016
2199fdf
point to correct collection when updating pending_tournaments
Argonaut6 Aug 6, 2016
4451e4a
Merge remote-tracking branch 'origin/feature/bracket_url' into featur…
jschnei Aug 29, 2016
bf2b809
test fixes
Argonaut6 Aug 29, 2016
718e045
use named paramater in function call
Argonaut6 Sep 2, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
Welcome to GarPR Development

Our dev environment uses vagrant.
we have a CI cycle, with a big test suite, and auto-push to
We have a CI cycle, with a big test suite, and auto-push to
production based on jenkins when we push to master and pass all tests

garpr is written using Restful Flask on the backend, with an AngularJS frontend
garpr is written using Restful Flask on the backend, with an AngularJS frontend.

Developers should make changes in a branch, and then make a pull request
Developers should make changes in a branch, and then make a pull request (also see [here](https://github.com/ripgarpr/garpr/blob/master/jenkins/README.md)).

Admins or users, submit bug reports on the [issues page](https://github.com/ripgarpr/garpr/issues).

Expand All @@ -15,10 +15,10 @@ Interested in getting GarPR in your region? Contact one of the devs.
Interested in being a dev? Also contact one of us. We have an active slack channel :D

Local Development Using Vagrant
=======================
==============================
### Requirements
1. [Vagrant](https://www.vagrantup.com/downloads.html)
2. [VirturalBox](https://www.virtualbox.org/wiki/Downloads)
2. [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
3. 1024 MB of memory

### Setup Steps
Expand Down Expand Up @@ -54,7 +54,9 @@ The API and webapp will now be started on the VM, and the webapp can be visited

To pull in any changes made to the project on the host into the VM, use the command `sync_vm`. This will allow you to use the text/project editors on your host.

1. (Host): Make edits to some files..
1. (Host): Make edits to some files.
2. (VM): Run the command: `sync_vm`
3. (VM): Restart the system
4. (Host): Vist 192.168.33.10:8000 to view the new changes
3. (VM): Restart the system (often the system will auto-restart; to force restart, type `bash stop.sh` followed by `bash start.sh`).
4. (Host): Visit 192.168.33.10:8000 to view the new changes.

If stuff goes very wrong (or you would like to restore the initial copy of your db), you can restore the initial state of the Vagrant VM by typing `vagrant destroy` followed by `vagrant up`.
37 changes: 37 additions & 0 deletions add_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from pymongo import MongoClient
from config.config import Config

config = Config()
DATABASE_NAME = config.get_db_name()
TOURNAMENTS_COLLECTION_NAME = 'tournaments'
PENDING_TOURNAMENTS_COLLECTION_NAME = 'pending_tournaments'
mongo_client = MongoClient(host=config.get_mongo_url())


tournaments_col = mongo_client[DATABASE_NAME][TOURNAMENTS_COLLECTION_NAME]
pending_tournaments_col = mongo_client[DATABASE_NAME][PENDING_TOURNAMENTS_COLLECTION_NAME]

tournaments = tournaments_col.find()
pending_tournaments = pending_tournaments_col.find()

tournaments_col.update({},{"$set": {"url": None}})
pending_tournaments_col.update({}, {"$set": {"url": None}})

for t in tournaments:
if(t['type'] =='challonge' and t['raw'] != ""):
print t['type'], t['name'], "yes"
tournaments_col.update({"_id": t["_id"]},{"$set": {"url": t['raw']['tournament']['tournament']['full_challonge_url']}})
else:
print t['type'], t['name']
tournaments_col.update({"_id": t["_id"]},{"$set": {"url": ''}})

for x in range(1,5):
print '--------------------------'

for pt in pending_tournaments:
if(pt['type'] == 'challonge' and pt['raw'] != ""):
print pt['type'], pt['name'], "yes"
pending_tournaments_col.update({"_id": pt["_id"]},{"$set": {"url": pt['raw']['tournament']['tournament']['full_challonge_url']}})
else:
print t['type'], t['name']
pending_tournaments_col.update({"_id": pt["_id"]},{"$set": {"url": ''}})
1 change: 0 additions & 1 deletion dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ def get_all_players(self, all_regions=False, include_merged=False):
mongo_request['regions'] = {'$in': [self.region_id]}
if not include_merged:
mongo_request['merged'] = False

return [Player.from_json(p) for p in self.players_col.find(mongo_request).sort([('name', 1)])]


Expand Down
22 changes: 19 additions & 3 deletions jenkins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,32 @@ To make continuous deployment easy, we've set up Jenkins on the production serve
Overview
========

At any time, there should be two copies of GarPR running in separate environments on the production server. The first environment, the *stage* environment, is intended for testing the most recent build of GarPR. Any update to master on the github repo will cause Jenkins to update stage, run nosetests, and restart the stage environment. Currently the stage environment is accessible at http://www.notgarpr.com:8013 (with the api being served at http://www.notgarpr.com:3013). You can also access the stage environment at http://stage.notgarpr.com.
At any time, there should be two copies of GarPR running in separate environments on the production server. The first environment, the *stage* environment, is intended for testing the most recent build of GarPR. Any update to master on the github repo will cause Jenkins to update stage, run nosetests, and restart the stage environment. Currently the stage environment is accessible at https://www.notgarpr.com:8443 (with the api being served at https://www.notgarpr.com:3013). You can also access the stage environment at https://stage.notgarpr.com.

When you're convinced that the stage copy is working as intended, you can manually tell Jenkins to push the changes to the *prod* environment. This is the version of GarPR that all users will interact with. Currently the prod environment is accessible at http://www.notgarpr.com (with the API being served at http://www.notgarpr.com:3001).
When you're convinced that the stage copy is working as intended, you can manually tell Jenkins to push the changes to the *prod* environment. This is the version of GarPR that all users will interact with. Currently the prod environment is accessible at https://www.notgarpr.com (with the API being served at https://www.notgarpr.com:3001).

Using Jenkins
=============

Starting builds through Slack
----------------------------

The recommended way to stage/deploy new builds is via Slack commands on our Slack channel. Typing "/stage <branchname>" anywhere in Slack will prompt Jenkins to run tests on and (if they succeed) stage branch <branchname> on the stage copy. Typing "/deploy" will deploy the most recent version of master that has successfully been staged to the prod copy. In particular, note that you should "/stage master" before you "/deploy". A typical workflow for deploying a feature should look as follows:

1. Open branch "featurename" for your new feature.
2. Code your feature in this branch.
3. When the feature is ready, push this branch to GH and open a PR for this feature.
4. Stage this branch on the staging copy by typing "/stage featurename".
5. If step 4 is successful and the feature works fine on stage, merge your PR.
6. Stage the merged copy of master by typing "/stage master".
7. If the staged copy of master looks fine, deploy to prod by typing "/deploy".

Starting builds through Jenkins
-------------------------------

Currently the Jenkins web interface is being served at www.notgarpr.com:8080. You will need a username and password to log in: ask in Slack for the appropriate credentials.

There are currently two projects in Jenkins, "garpr_stage" and "garpr_prod", corresponding to updating the stage and prod environment. In a project, click "Build Now" on the left menu to manually trigger a build (for "garpr_prod" this is necessary; "garpr_stage" will also be built whenever anything is pushed to master). You can see the currently active builds in the "Build Queue" on the left (or by clicking "Builds"). On the page for any given build, you can see whether it failed or succeeded, along with any console output it may have generated.
There are currently two projects in Jenkins, "garpr_stage" and "garpr_prod", corresponding to updating the stage and prod environment. In a project, click "Build Now" on the left menu to manually trigger a build. You can see the currently active builds in the "Build Queue" on the left (or by clicking "Builds"). On the page for any given build, you can see whether it failed or succeeded, along with any console output it may have generated.

Backups
=======
Expand Down
22 changes: 15 additions & 7 deletions model.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def from_json(cls, json_dict):
id=json_dict.get('_id', None))

class Tournament(object):
def __init__(self, type, raw, date, name, players, matches, regions, orig_ids=None, id=None):
def __init__(self, type, raw, date, name, players, matches, regions, orig_ids=None, id=None, url=None):
'''
:param type: string, either "tio", "challonge", or "smashgg"
:param raw: for tio, this is an xml string. for challonge its a dict from string --> string
Expand All @@ -192,6 +192,7 @@ def __init__(self, type, raw, date, name, players, matches, regions, orig_ids=No
self.id = id
self.type = type
self.raw = raw
self.url = url
self.date = date
self.name = name
self.matches = matches
Expand Down Expand Up @@ -230,6 +231,8 @@ def get_json_dict(self):

if self.id:
json_dict['_id'] = self.id
if self.url:
json_dict['url'] = self.url

json_dict['type'] = self.type
json_dict['raw'] = self.raw
Expand All @@ -246,7 +249,6 @@ def get_json_dict(self):
def from_json(cls, json_dict):
if json_dict == None:
return None

return cls(
json_dict['type'],
json_dict['raw'],
Expand All @@ -256,7 +258,8 @@ def from_json(cls, json_dict):
[MatchResult.from_json(m) for m in json_dict['matches']],
json_dict['regions'],
json_dict.get('orig_ids', None),
id=json_dict['_id'] if '_id' in json_dict else None)
id=json_dict['_id'] if '_id' in json_dict else None,
url=json_dict['url'] if 'url' in json_dict else None)

# TODO "sanity checks"
@classmethod
Expand Down Expand Up @@ -284,7 +287,8 @@ def _get_player_id_from_map_or_throw(alias_to_id_map, alias):
pending_tournament.matches,
pending_tournament.regions,
players,
pending_tournament.id)
pending_tournament.id,
pending_tournament.url)

# TODO this should go away as we should never build a Tournament straight from a scraper
# it should be from a PendingTournament
Expand All @@ -299,7 +303,7 @@ def from_scraper(cls, type, scraper, alias_to_id_map, region_id):
class PendingTournament(object):
'''Same as a Tournament, except it uses aliases for players instead of ids.
Used during tournament import, before aliases are mapped to player ids.'''
def __init__(self, type, raw, date, name, players, matches, regions, alias_to_id_map=None, id=None):
def __init__(self, type, raw, date, name, players, matches, regions, alias_to_id_map=None, id=None, url=None):
'''
:param type: string, either "tio", "challonge", "smashgg"
:param raw: for tio, this is an xml string. for challonge its a dict from string --> string.
Expand All @@ -316,6 +320,7 @@ def __init__(self, type, raw, date, name, players, matches, regions, alias_to_id
self.id = id
self.type = type
self.raw = raw
self.url = url
self.date = date
self.name = name
self.matches = matches
Expand All @@ -335,6 +340,7 @@ def get_json_dict(self):

json_dict['type'] = self.type
json_dict['raw'] = self.raw
json_dict['url'] = self.url
json_dict['date'] = self.date
json_dict['name'] = self.name
json_dict['players'] = self.players
Expand All @@ -358,7 +364,8 @@ def from_json(cls, json_dict):
[MatchResult.from_json(m) for m in json_dict['matches']],
json_dict['regions'],
json_dict['alias_to_id_map'],
id=json_dict['_id'] if '_id' in json_dict else None)
id=json_dict['_id'] if '_id' in json_dict else None,
url=json_dict['url'] if 'url' in json_dict else None)

def set_alias_id_mapping(self, alias, id):
for mapping in self.alias_to_id_map:
Expand Down Expand Up @@ -389,7 +396,8 @@ def from_scraper(cls, type, scraper, region_id):
scraper.get_name(),
scraper.get_players(),
scraper.get_matches(),
regions)
regions,
url=scraper.get_url())

#TODO: untested/unused!
@classmethod
Expand Down
9 changes: 9 additions & 0 deletions rankings.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ def generate_ranking(dao, now=datetime.now(), day_limit=60, num_tourneys=2):

# TODO add a default rating entry when we add it to the map
for match in tournament.matches:

#don't count matches where either player is OOR
winner = dao.get_player_by_id(match.winner)
if not dao.region_id in winner.regions:
continue
loser = dao.get_player_by_id(match.loser)
if not dao.region_id in loser.regions:
continue

if not match.winner in player_id_to_player_map:
db_player = dao.get_player_by_id(match.winner)
db_player.ratings[dao.region_id] = DEFAULT_RATING
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ iso8601==0.1.10
itsdangerous==0.24
lxml==3.3.5
mock==1.0.1
mongomock==2.0.0
mongomock==3.5.0
nose==1.3.4
oauth2client==1.3.2
passlib==1.6.5
Expand Down
3 changes: 3 additions & 0 deletions scraper/challonge.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ def get_raw(self):

return self.raw_dict

def get_url(self):
return self.get_raw()['tournament']['tournament']['full_challonge_url']

def get_name(self):
return self.get_raw()['tournament']['tournament']['name'].strip()

Expand Down
51 changes: 46 additions & 5 deletions scraper/smashgg.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@ def check_for_200(response):
return response

class SmashGGScraper(object):
def __init__(self, path):
def __init__(self, path, included_phases):
"""
:param path: url to go to the bracket
"""
self.path = path

# DATA STRUCTURE TO INCLUDE PHASES USER WANTS TO IMPORT
self.included_phases = included_phases
for p in self.included_phases:
print p

#GET IMPORTANT DATA FROM THE URL
self.event_id = SmashGGScraper.get_tournament_event_id_from_url(self.path)
self.name = SmashGGScraper.get_tournament_name_from_url(self.path)
Expand All @@ -36,8 +41,18 @@ def __init__(self, path):
# JSON DUMPED FROM THE API

self.event_dict = SmashGGScraper.get_event_dict(self.event_id)
self.group_ids = self.get_group_ids()

self.group_sets = []
for phase in self.included_phases:
self.group_sets.append(SmashGGScraper.get_group_ids_from_phase(phase))

self.group_ids = []
for group_set in self.group_sets:
for group_id in group_set:
self.group_ids.append(group_id)

self.group_dicts = [SmashGGScraper.get_group_dict(group_id) for group_id in self.group_ids]
self.group_dicts = [dict for dict in self.group_dicts if dict is not None] #REMOVE EMPTY PHASES FROM IMPORT

#DATA STRUCTURES THAT HOLD IMPORTANT THINGS
self.get_smashgg_players()
Expand All @@ -56,6 +71,10 @@ def get_raw(self):
return {'event': self.event_dict,
'groups': self.group_dicts}


def get_url(self):
return self.path

def get_name(self):
return self.name

Expand Down Expand Up @@ -160,6 +179,10 @@ def get_smashgg_matches(self):
smashgg_match = SmashGGMatch(round_name, winner_id, loser_id, round_num, best_of)
self.matches.append(smashgg_match)

def get_phase_ids(self):
group_ids = [str(group['phaseId']).strip() for group in self.event_dict['entities']['groups']]
return list(set(group_ids))

def get_group_ids(self):
group_ids = [str(group['id']).strip() for group in self.event_dict['entities']['groups']]
return list(set(group_ids))
Expand Down Expand Up @@ -208,7 +231,10 @@ def get_event_dict(event_id):

@staticmethod
def get_group_dict(group_id):
return check_for_200(requests.get(GROUP_URL % group_id)).json()
dict = check_for_200(requests.get(GROUP_URL % group_id)).json()
hasSets = dict['entities']['groups']['hasSets']
if hasSets is True:
return dict

@staticmethod
def get_event_name(event_id):
Expand All @@ -222,6 +248,15 @@ def get_phase_bracket_name(phase_id):
phase_name = phase_raw['entities']['phase']['name']
return phase_name

@staticmethod
def get_group_ids_from_phase(phase_id):
phase_ids = []
phase_raw = check_for_200(requests.get(PHASE_URL % phase_id)).json()
groups = phase_raw['entities']['groups']
for group in groups:
phase_ids.append(group['id'])
return phase_ids

@staticmethod
def get_phase_ids(event_id):
ids = []
Expand All @@ -240,8 +275,6 @@ def get_phasename_id_map(event_id):
map[phase_id] = SmashGGScraper.get_phase_bracket_name(phase_id)
return map



class SmashGGPlayer(object):
def __init__(self, smashgg_id, entrant_id, name, smash_tag, region, country, state, final_placement):
"""
Expand Down Expand Up @@ -287,6 +320,14 @@ def __init__(self, roundName, winner_id, loser_id, roundNumber, bestOf):
self.roundNumber = roundNumber
self.bestOf = bestOf

class SmashGGEvent(object):
"""
This is currently unused
"""
def __init__(self, event_id, phase_map):
self.event_id = event_id
self.phase_map = phase_map

class SmashGGException(Exception):
def __init__(self, message):
self.message = message
4 changes: 4 additions & 0 deletions scraper/tio.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def __init__(self, raw, bracket_name):

self.text = raw
self.soup = BeautifulSoup(self.text, 'xml')
self.url = None # no url for Tio

@classmethod
def from_file(cls, filepath, bracket_name):
Expand All @@ -24,6 +25,9 @@ def from_file(cls, filepath, bracket_name):
def get_raw(self):
return self.text

def get_url(self):
return self.url

def get_name(self):
return self.soup.Event.Name.text

Expand Down
Loading