From c3873b16d0547a071747284d64f224e4ab7bcd8e Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Tue, 26 Dec 2017 10:41:46 -0500 Subject: [PATCH 1/4] ignore virtualenv folders --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 0932424..6d8db3b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ *.pyc +__pycache__/ madras/local_settings.py db.sqlite3 + +venv/ +env/ From 9a07a68488ca274296da8b7109df5ee84d6c2c56 Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Tue, 26 Dec 2017 10:47:48 -0500 Subject: [PATCH 2/4] switch to all environment variables --- .gitignore | 3 ++- madras/local_settings_sample.py | 18 ------------------ madras/settings.py | 24 +++++++++++------------- 3 files changed, 13 insertions(+), 32 deletions(-) delete mode 100644 madras/local_settings_sample.py diff --git a/.gitignore b/.gitignore index 6d8db3b..5c3be03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ *.pyc __pycache__/ -madras/local_settings.py + db.sqlite3 +db.sqlite3-journal venv/ env/ diff --git a/madras/local_settings_sample.py b/madras/local_settings_sample.py deleted file mode 100644 index fe00914..0000000 --- a/madras/local_settings_sample.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copy me to local_settings.py ! -import os - -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -SECRET_KEY = "foobar" - -DEBUG = True - -# Database -# https://docs.djangoproject.com/en/1.8/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } -} diff --git a/madras/settings.py b/madras/settings.py index 213139d..bc2825a 100644 --- a/madras/settings.py +++ b/madras/settings.py @@ -20,12 +20,13 @@ # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = os.environ.get("SECRET_KEY") +SECRET_KEY = os.environ.get("SECRET_KEY", "=u!#c-2hid%(4lq3w--$64!%qmbmmo-ae=l2_&*jpf47l84iv4") # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = os.environ.get("DEBUG", "true").lower() == "true" -ALLOWED_HOSTS = [] +# Allow all host headers +ALLOWED_HOSTS = ['*'] # Application definition @@ -107,15 +108,17 @@ # Heroku settings below # Parse database configuration from $DATABASE_URL import dj_database_url -DATABASES = {} -DATABASES['default'] = dj_database_url.config() +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} +DATABASES['default'].update(dj_database_url.config()) # Honor the 'X-Forwarded-Proto' header for request.is_secure() # SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') -# Allow all host headers -ALLOWED_HOSTS = ['*'] - # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.9/howto/static-files/ @@ -135,8 +138,3 @@ STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage' # SECURE_SSL_REDIRECT = True - -try: - from local_settings import * -except ImportError: - pass From a23ff528ca885788133533b48059258ffa0f57d1 Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Tue, 26 Dec 2017 10:49:59 -0500 Subject: [PATCH 3/4] add setup instructions --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index aa540a5..f2ad5cc 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,13 @@ # Madras Madras is the backend for a cloud-based hackathon management system that aims to bring together the many different hackathon organizing systems into one centralized service to minimize the need to migrate data between hacker applications, registration, check-in, announcements, reimbursement, judging, and prizes. Created with the goal of making organizing hackathons easier for everyone. + +## Setup + +```bash +virtualenv --python=python3 venv +source venv/bin/activate +pip install -r requirements.txt +./manage.py migrate +./manage.py runserver +``` From 50295f4d09ad4552b1f8d09e7fae8947c167a7a7 Mon Sep 17 00:00:00 2001 From: mike2151 Date: Sat, 6 Jan 2018 21:07:27 -0500 Subject: [PATCH 4/4] travel_reimbursement --- apps/director/models.py | 2 +- apps/reader/models.py | 12 +++---- apps/reader/utils.py | 66 ++++++++++++++++++++++++++++++++++++- apps/reader/views.py | 9 +++-- apps/registration/models.py | 15 +++++---- requirements.txt | 1 + 6 files changed, 88 insertions(+), 17 deletions(-) diff --git a/apps/director/models.py b/apps/director/models.py index f3da99f..345e703 100644 --- a/apps/director/models.py +++ b/apps/director/models.py @@ -10,7 +10,7 @@ def __str__(self): class Hackathon(models.Model): name = models.CharField(max_length=128) - organization = models.ForeignKey(Organization, related_name="hackathons") + organization = models.ForeignKey(Organization, related_name="hackathons", on_delete=models.CASCADE) def __str__(self): return self.name diff --git a/apps/reader/models.py b/apps/reader/models.py index 8a71019..6bde2b7 100644 --- a/apps/reader/models.py +++ b/apps/reader/models.py @@ -6,13 +6,13 @@ class Reader(models.Model): - user = models.OneToOneField(User, related_name="reader") - organization = models.ForeignKey(Organization, related_name="readers") + user = models.OneToOneField(User, related_name="reader", on_delete=models.CASCADE) + organization = models.ForeignKey(Organization, related_name="readers", on_delete=models.CASCADE) hackathons = models.ManyToManyField(Hackathon, related_name="readers") class Rating(models.Model): - application = models.OneToOneField(Application, related_name="rating") + application = models.OneToOneField(Application, related_name="rating", on_delete=models.CASCADE) class RatingField(models.Model): @@ -24,7 +24,7 @@ class RatingField(models.Model): (TYPE_MULTIPLE_CHOICE, "Multiple choice"), ) - rating = models.ForeignKey(Rating, related_name="fields") + rating = models.ForeignKey(Rating, related_name="fields", on_delete=models.CASCADE) type = models.CharField(max_length=16, choices=TYPE_CHOICES) prompt = models.CharField(max_length=64) min_number = models.IntegerField(default=-1) @@ -33,6 +33,6 @@ class RatingField(models.Model): class RatingResponse(models.Model): - reader = models.ForeignKey(Reader, related_name="ratings") - applicant = models.ForeignKey(Applicant, related_name="ratings") + reader = models.ForeignKey(Reader, related_name="ratings", on_delete=models.CASCADE) + applicant = models.ForeignKey(Applicant, related_name="ratings", on_delete=models.CASCADE) data = models.TextField() diff --git a/apps/reader/utils.py b/apps/reader/utils.py index 085c166..bdec968 100644 --- a/apps/reader/utils.py +++ b/apps/reader/utils.py @@ -1,7 +1,9 @@ import requests +import geopy.distance from github3 import login +#helper function that gets the number of contributions in the last year def get_contributions(github_user_name): url = "https://github.com/" + github_user_name resp = requests.get(url) @@ -11,7 +13,9 @@ def get_contributions(github_user_name): index_of_contribution + 37: index_of_contribution + 45] return [int(s) for s in substring_of_contribution.split() if s.isdigit()][0] - +#gets the number of followers, number of repositories, +#number of contributions in past year and number of +#self starred repositories def get_metrics_github(github_user_name): gh = login("GITHUB_USER_NAME", password="GITHUB_PASSWORD") user = gh.user(github_user_name) @@ -32,3 +36,63 @@ def get_metrics_github(github_user_name): "num_contributions": get_contributions(github_user_name), "self_star_repos": len(star_repos), } + + +# +#Travel reimburstment +# + +def address_to_lat_long(address): + location = geolocator.geocode(address) + return { + "lat": location.latitude, + "long": location.longitude + } + +KM_TO_MILES = 0.621371 + +#Source: http://www.tps.ucsb.edu/commuter-cost-calculator +AVERAGE_TRANSIT_COST_PER_MILE = .608 + +#Source: https://goo.gl/rDjXe3 +#Fare = $30 + (Distance * $0.08) +AVERAGE_AIRLINE_FARE = 30 +AVERAGE_AIRLINE_COST_PER_MILE = .08 + +#because numbers are from 2015 +INFLATION_FACTOR = 1.03 + +#constants to define +#DISTANCE_TO_NEAREST_AIRPORT is the distance to the nearest airport from +#from the venue in miles +VENUE_LAT = 0 +VENUE_LONG = 0 +NEAREST_AIRPORT_LAT = 0 +NEAREST_AIRPORT_LONG = 0 +DISTANCE_TO_NEAREST_AIRPORT = 0 + +#estimates the cost of travel from lat/long coordinates of user +#all lat and long are doubles + +def calculate_travel_est(lat1, long1): + origin = (lat1, long1) + nearest_airport = (NEAREST_AIRPORT_LAT, NEAREST_AIRPORT_LONG) + venue = (VENUE_LAT, VENUE_LONG) + #multiply by 2 to travel both ways + origin_to_venue = (geopy.distance.vincenty(origin, venue).km)* \ + KM_TO_MILES * 2 + origin_to_airport = (geopy.distance.vincenty(origin, nearest_airport).km)* \ + KM_TO_MILES * 2 + just_transit_costs = .608 * origin_to_venue * INFLATION_FACTOR + airline_costs = origin_to_airport * AVERAGE_AIRLINE_COST_PER_MILE + \ + AVERAGE_AIRLINE_FARE + airport_to_uni_costs = DISTANCE_TO_NEAREST_AIRPORT * \ + AVERAGE_TRANSIT_COST_PER_MILE * 2 + total_airline_costs = (airline_costs + airport_to_uni_costs) * \ + INFLATION_FACTOR + #now compare prices + if (just_transit_costs >= total_airline_costs): + return(total_airline_costs) + else: + return(just_transit_costs) + diff --git a/apps/reader/views.py b/apps/reader/views.py index 9e5f8ca..532eaa7 100644 --- a/apps/reader/views.py +++ b/apps/reader/views.py @@ -12,7 +12,8 @@ from apps.reader import serializers from apps.reader.models import Applicant, RatingResponse from apps.reader.utils import get_metrics_github - +from apps.reader.utils import calculate_travel_est +from apps.reader.utils import address_to_lat_long class Rating(APIView): permission_classes = (IsAuthenticated,) @@ -42,6 +43,7 @@ def get(self, request): rand_pk = random.randint(0, Applicant.objects.all().count() - 1) rand_app = Applicant.objects.get(pk=rand_pk) github_array = get_metrics_github(rand_app.github_user_name) + user_addresss_coords = address_to_lat_long(rand_app.address) return Response( { "applicant_id": rand_app.pk, @@ -51,7 +53,10 @@ def get(self, request): "num_followers": github_array["NumFollowers"], "num_repos": github_array["NumRepos"], "num_contributions": github_array["NumContributions"], - "self_star_repos": github_array["selfStarRepos"] + "self_star_repos": github_array["selfStarRepos"], + "travel_reimbursement" : \ + calculate_travel_est(user_addresss_coords["lat"], \ + user_addresss_coords["long"]) }, status=status.HTTP_200_OK, ) diff --git a/apps/registration/models.py b/apps/registration/models.py index c98bcac..06eb6bf 100644 --- a/apps/registration/models.py +++ b/apps/registration/models.py @@ -17,7 +17,7 @@ class Application(models.Model): name = models.CharField(max_length=128) status = models.CharField(max_length=16, choices=STATUS_CHOICES) - hackathon = models.ForeignKey(Hackathon, related_name="applications") + hackathon = models.ForeignKey(Hackathon, related_name="applications", on_delete=models.CASCADE) def __str__(self): return "{} ({})".format(self.name, self.hackathon) @@ -36,7 +36,7 @@ class ApplicationField(models.Model): (TYPE_LONG_ANSWER, "Long Answer"), ) - application = models.ForeignKey(Application, related_name="fields") + application = models.ForeignKey(Application, related_name="fields", on_delete=models.CASCADE) ordering = models.IntegerField() type = models.CharField(max_length=32, choices=TYPE_CHOICES) prompt = models.CharField(max_length=256) @@ -48,7 +48,7 @@ def __str__(self): class ApplicantTeam(models.Model): - hackathon = models.ForeignKey(Hackathon) + hackathon = models.ForeignKey(Hackathon, on_delete=models.CASCADE) name = models.CharField(max_length=64) entry_code = models.CharField(max_length=64) @@ -57,12 +57,13 @@ def __str__(self): class Applicant(models.Model): - user = models.OneToOneField(User, related_name="applicant", blank=True, null=True) - hackathon = models.ForeignKey(Hackathon, related_name="applicants") - application = models.ForeignKey(Application, related_name="applicants") - team = models.ForeignKey(ApplicantTeam, related_name="applicants", blank=True, null=True) + user = models.OneToOneField(User, related_name="applicant", blank=True, null=True, on_delete=models.CASCADE) + hackathon = models.ForeignKey(Hackathon, related_name="applicants", on_delete=models.CASCADE) + application = models.ForeignKey(Application, related_name="applicants", on_delete=models.CASCADE) + team = models.ForeignKey(ApplicantTeam, related_name="applicants", blank=True, null=True, on_delete=models.CASCADE) data = models.TextField(blank=True, null=True) github_user_name = models.CharField(max_length=39, unique=True, blank=True, null=True) + address = models.CharField(max_length=128) def __str__(self): return "{}'s Application".format(self.user) diff --git a/requirements.txt b/requirements.txt index a4455c7..c8a83ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,7 @@ django-toolbelt==0.0.1 djangorestframework==3.6.4 enum34==1.1.6 github3.py==1.0.0a4 +geopy==1.11.0 gunicorn==19.7.1 idna==2.6 ipaddress==1.0.18